@designbasekorea/ui-wc 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +18 -0
  3. package/dist/esm/components/animation-background/controls.js +1 -1
  4. package/dist/esm/components/animation-background/layers.js +1 -1
  5. package/dist/esm/components/db-activity-heatmap.d.ts +38 -0
  6. package/dist/esm/components/db-activity-heatmap.js +1 -0
  7. package/dist/esm/components/db-animation-background.d.ts +2 -0
  8. package/dist/esm/components/db-animation-background.js +1 -1
  9. package/dist/esm/components/db-app-badge.d.ts +2 -0
  10. package/dist/esm/components/db-app-badge.js +1 -1
  11. package/dist/esm/components/db-audio-player.d.ts +4 -0
  12. package/dist/esm/components/db-audio-player.js +1 -1
  13. package/dist/esm/components/db-avatar.js +1 -1
  14. package/dist/esm/components/db-backdrop.d.ts +4 -0
  15. package/dist/esm/components/db-backdrop.js +1 -1
  16. package/dist/esm/components/db-badge.d.ts +2 -2
  17. package/dist/esm/components/db-badge.js +1 -1
  18. package/dist/esm/components/db-bottom-navigation.js +1 -1
  19. package/dist/esm/components/db-bottom-sheet.d.ts +5 -0
  20. package/dist/esm/components/db-bottom-sheet.js +1 -1
  21. package/dist/esm/components/db-button.d.ts +13 -0
  22. package/dist/esm/components/db-button.js +1 -1
  23. package/dist/esm/components/db-calendar.js +1 -1
  24. package/dist/esm/components/db-card.js +1 -1
  25. package/dist/esm/components/db-carousel.js +1 -1
  26. package/dist/esm/components/db-chat-composer.d.ts +14 -0
  27. package/dist/esm/components/db-chat-composer.js +1 -1
  28. package/dist/esm/components/db-chat-message.d.ts +15 -2
  29. package/dist/esm/components/db-chat-message.js +1 -1
  30. package/dist/esm/components/db-chat-orb.d.ts +6 -8
  31. package/dist/esm/components/db-chat-orb.js +1 -1
  32. package/dist/esm/components/db-chat-room.d.ts +12 -1
  33. package/dist/esm/components/db-chat-room.js +1 -1
  34. package/dist/esm/components/db-chat-welcome.js +1 -1
  35. package/dist/esm/components/db-chat-widget.d.ts +43 -10
  36. package/dist/esm/components/db-chat-widget.js +1 -1
  37. package/dist/esm/components/db-checkbox.d.ts +11 -0
  38. package/dist/esm/components/db-checkbox.js +1 -1
  39. package/dist/esm/components/db-chip.js +1 -1
  40. package/dist/esm/components/db-color-picker.js +1 -1
  41. package/dist/esm/components/db-confirm.d.ts +5 -0
  42. package/dist/esm/components/db-confirm.js +1 -1
  43. package/dist/esm/components/db-container.js +1 -1
  44. package/dist/esm/components/db-cookie-banner.js +1 -1
  45. package/dist/esm/components/db-date-picker.d.ts +3 -0
  46. package/dist/esm/components/db-date-picker.js +1 -1
  47. package/dist/esm/components/db-divider.js +1 -1
  48. package/dist/esm/components/db-download-progress.d.ts +3 -4
  49. package/dist/esm/components/db-download-progress.js +1 -1
  50. package/dist/esm/components/db-drawer.d.ts +5 -1
  51. package/dist/esm/components/db-drawer.js +1 -1
  52. package/dist/esm/components/db-dropdown.js +1 -1
  53. package/dist/esm/components/db-dropzone.js +1 -1
  54. package/dist/esm/components/db-email-verify.d.ts +2 -2
  55. package/dist/esm/components/db-email-verify.js +1 -1
  56. package/dist/esm/components/db-file-uploader.js +1 -1
  57. package/dist/esm/components/db-floating-action-button.d.ts +1 -0
  58. package/dist/esm/components/db-floating-action-button.js +1 -1
  59. package/dist/esm/components/db-footer.js +1 -1
  60. package/dist/esm/components/db-gantt.js +1 -1
  61. package/dist/esm/components/db-indicator.js +1 -1
  62. package/dist/esm/components/db-input.d.ts +7 -0
  63. package/dist/esm/components/db-input.js +1 -1
  64. package/dist/esm/components/db-kanban.js +1 -1
  65. package/dist/esm/components/db-lightbox.d.ts +4 -0
  66. package/dist/esm/components/db-lightbox.js +1 -1
  67. package/dist/esm/components/db-list.js +1 -1
  68. package/dist/esm/components/db-masonry.js +1 -1
  69. package/dist/esm/components/db-menu-item.js +1 -1
  70. package/dist/esm/components/db-modal.d.ts +10 -0
  71. package/dist/esm/components/db-modal.js +1 -1
  72. package/dist/esm/components/db-navbar.d.ts +4 -2
  73. package/dist/esm/components/db-navbar.js +1 -1
  74. package/dist/esm/components/db-onboarding-modal.d.ts +3 -0
  75. package/dist/esm/components/db-onboarding-modal.js +1 -1
  76. package/dist/esm/components/db-otp-input.d.ts +3 -2
  77. package/dist/esm/components/db-otp-input.js +1 -1
  78. package/dist/esm/components/db-page-header.js +1 -1
  79. package/dist/esm/components/db-phone-verify.d.ts +2 -2
  80. package/dist/esm/components/db-phone-verify.js +1 -1
  81. package/dist/esm/components/db-popover.js +1 -1
  82. package/dist/esm/components/db-progress-step.js +1 -1
  83. package/dist/esm/components/db-progress.js +1 -1
  84. package/dist/esm/components/db-progressbar.js +1 -1
  85. package/dist/esm/components/db-radio.d.ts +1 -0
  86. package/dist/esm/components/db-radio.js +1 -1
  87. package/dist/esm/components/db-range-slider.d.ts +9 -4
  88. package/dist/esm/components/db-range-slider.js +1 -1
  89. package/dist/esm/components/db-rating.js +1 -1
  90. package/dist/esm/components/db-resizable-panels.d.ts +14 -0
  91. package/dist/esm/components/db-resizable-panels.js +1 -1
  92. package/dist/esm/components/db-search-bar.js +1 -1
  93. package/dist/esm/components/db-section-cta.js +1 -1
  94. package/dist/esm/components/db-section-feature.js +1 -1
  95. package/dist/esm/components/db-section-pricing.js +1 -1
  96. package/dist/esm/components/db-section.js +1 -1
  97. package/dist/esm/components/db-segment-control.js +1 -1
  98. package/dist/esm/components/db-select.d.ts +8 -0
  99. package/dist/esm/components/db-select.js +1 -1
  100. package/dist/esm/components/db-sidebar.d.ts +41 -6
  101. package/dist/esm/components/db-sidebar.js +1 -1
  102. package/dist/esm/components/db-skeleton.js +1 -1
  103. package/dist/esm/components/db-spinner.d.ts +4 -0
  104. package/dist/esm/components/db-spinner.js +1 -1
  105. package/dist/esm/components/db-split-view.js +1 -1
  106. package/dist/esm/components/db-stat.js +1 -1
  107. package/dist/esm/components/db-stepper.js +1 -1
  108. package/dist/esm/components/db-table.js +1 -1
  109. package/dist/esm/components/db-tabs.js +1 -1
  110. package/dist/esm/components/db-testimonial.js +1 -1
  111. package/dist/esm/components/db-textarea.js +1 -1
  112. package/dist/esm/components/db-time-picker.d.ts +5 -2
  113. package/dist/esm/components/db-time-picker.js +1 -1
  114. package/dist/esm/components/db-timeline.js +1 -1
  115. package/dist/esm/components/db-toast.d.ts +8 -1
  116. package/dist/esm/components/db-toast.js +1 -1
  117. package/dist/esm/components/db-toggle.d.ts +2 -0
  118. package/dist/esm/components/db-toggle.js +1 -1
  119. package/dist/esm/components/db-tooltip.js +1 -1
  120. package/dist/esm/components/db-top-banner.js +1 -1
  121. package/dist/esm/components/db-tutorial.d.ts +1 -0
  122. package/dist/esm/components/db-tutorial.js +1 -1
  123. package/dist/esm/components/db-two-factor.d.ts +2 -2
  124. package/dist/esm/components/db-two-factor.js +1 -1
  125. package/dist/esm/components/db-video-player.d.ts +3 -0
  126. package/dist/esm/components/db-video-player.js +1 -1
  127. package/dist/esm/components/section-block-header-utils.js +1 -1
  128. package/dist/esm/core/a11y.d.ts +40 -0
  129. package/dist/esm/core/a11y.js +1 -0
  130. package/dist/esm/core/base-element.d.ts +41 -0
  131. package/dist/esm/core/base-element.js +1 -0
  132. package/dist/esm/core/dom.d.ts +35 -0
  133. package/dist/esm/core/dom.js +1 -0
  134. package/dist/esm/core/focus-trap.d.ts +41 -0
  135. package/dist/esm/core/focus-trap.js +1 -0
  136. package/dist/esm/core/i18n.d.ts +82 -0
  137. package/dist/esm/core/i18n.js +1 -0
  138. package/dist/esm/core/scroll-lock.d.ts +19 -0
  139. package/dist/esm/core/scroll-lock.js +1 -0
  140. package/dist/esm/core/uid.d.ts +23 -0
  141. package/dist/esm/core/uid.js +1 -0
  142. package/dist/esm/helpers/toast.d.ts +10 -10
  143. package/dist/esm/helpers/toast.js +1 -1
  144. package/dist/esm/index.d.ts +12 -3
  145. package/dist/esm/index.js +1 -1
  146. package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -0
  147. package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/primitives.js +1 -0
  148. package/dist/index.cjs +1 -1
  149. package/dist/index.css +1 -1
  150. package/dist/index.d.ts +583 -59
  151. package/dist/index.esm.js +1 -1
  152. package/dist/react/index.cjs +1 -1
  153. package/dist/react/index.d.ts +433 -55
  154. package/dist/react/index.esm.js +1 -1
  155. package/package.json +12 -3
  156. /package/dist/chunks/{lottie-26a5e98f.js → lottie-3f885725.js} +0 -0
@@ -1 +1 @@
1
- import{createDbIcon as t}from"./db-icons.js";import"./db-button.js";import"./db-input.js";import"./db-otp-input.js";import"./db-segment-control.js";const e=[{value:"app",label:"인증 앱"},{value:"sms",label:"SMS"},{value:"email",label:"이메일"}],i=["s","m","l"];function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbTwoFactor extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0,this.backupMode=!1,this.resendCount=0}static get observedAttributes(){return["method","code","size","disabled","error","error-message","title","description","verify-label","code-length","show-backup","countdown","destination","manual","sending","verifying","auto-submit","resend-label","max-resends"]}get method(){const t=this.getAttribute("method");return"sms"===t||"email"===t||"app"===t?t:"app"}set method(t){this.setAttribute("method",t)}get size(){return function pick(t,e,i){return t.includes(e)?e:i}(i,this.getAttribute("size"),"m")}set size(t){this.setAttribute("size",t)}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}set countdown(t){this.setAttribute("countdown",String(t))}get maxResends(){const t=Number(this.getAttribute("max-resends")||3);return Number.isFinite(t)?Math.max(1,Math.min(5,Math.round(t))):3}connectedCallback(){this.build(),this.sync(),"app"===this.method||this.hasAttribute("manual")||this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"method"!==t&&"manual"!==t||e===i||("method"===t&&(this.resendCount=0,this.backupMode=!1),"app"===this.method||this.hasAttribute("manual")?this.clearTimer():this.startTimer(this.countdown)))}restartCountdown(){"app"!==this.method&&this.startTimer(this.countdown)}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0,this.timerEl&&(this.timerEl.textContent="")}startTimer(t){this.clearTimer(),this.remaining=t,this.renderTimer(),this.timerId=setInterval(()=>{this.remaining-=1,this.renderTimer(),this.remaining<=0&&(this.clearTimer(),this.resendBtn.disabled=!1,this.dispatchEvent(new CustomEvent("db-countdown-complete",{bubbles:!0,composed:!0})))},1e3)}renderTimer(){const t=Math.max(0,Math.floor(this.remaining/60)),e=Math.max(0,this.remaining%60);this.timerEl.textContent="app"===this.method?"":`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`,"app"!==this.method?this.timerEl.setAttribute("aria-label",`남은 시간 ${t}분 ${e}초`):this.timerEl.removeAttribute("aria-label");const i=this.resendCount>=this.maxResends;this.resendBtn.disabled="app"===this.method||this.remaining>0||i||this.hasAttribute("disabled"),"app"===this.method?this.resendStatus.textContent="":i?this.resendStatus.textContent="재전송 한도에 도달했습니다. 새 인증을 시작해 주세요.":this.remaining>0?this.resendStatus.textContent=`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")} 후 재전송 가능 · 남은 횟수 ${this.maxResends-this.resendCount}회`:this.resendStatus.textContent=`재전송 가능 · 남은 횟수 ${this.maxResends-this.resendCount}회`}build(){if(this.built)return;this.built=!0;const i="designbase-wc-two-factor";this.replaceChildren();const s=el("div",`${i}__header`),n=el("div",`${i}__icon`);n.append(t("lock",22));const r=el("div",`${i}__titles`),h=el("h3",`${i}__title`);h.dataset.role="title";const a=el("p",`${i}__description`);a.dataset.role="description",r.append(h,a),s.append(n,r),this.segment=document.createElement("db-segment-control"),this.segment.setAttribute("options",JSON.stringify(e.map(t=>({value:t.value,label:t.label})))),this.segment.setAttribute("full-width",""),this.segment.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"app");e!==this.method&&(this.setAttribute("code",""),this.method=e,this.dispatchEvent(new CustomEvent("db-method-change",{detail:{method:e},bubbles:!0,composed:!0})),"app"!==e&&this.dispatchEvent(new CustomEvent("db-send-code",{detail:{method:e,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})))}),this.hintEl=el("p",`${i}__hint`),this.hintEl.dataset.role="hint",this.codeHead=el("div",`${i}__code-head`);const o=el("span",`${i}__code-label`);o.textContent="인증 코드",this.timerEl=el("span",`${i}__timer`),this.codeHead.append(o,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:e},bubbles:!0,composed:!0}))}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.dispatchEvent(new CustomEvent("db-code-complete",{detail:{code:e,method:this.method},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()}),this.channelActions=el("div",`${i}__channel`),this.resendBtn=el("button",`${i}__resend`),this.resendBtn.type="button",this.resendBtn.textContent="코드 재전송",this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.resendCount+=1,this.dispatchEvent(new CustomEvent("db-resend",{detail:{method:this.method,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})),this.hasAttribute("manual")?this.renderTimer():this.startTimer(this.countdown))}),this.resendStatus=el("p",`${i}__resend-status`),this.resendStatus.setAttribute("aria-live","polite"),this.channelActions.append(this.resendBtn,this.resendStatus),this.backupBlock=el("div",`${i}__backup-block`);const d=el("p",`${i}__hint`);d.textContent="계정 설정 시 저장한 8자리 백업 코드 중 사용하지 않은 코드를 입력하세요.",this.backupInput=document.createElement("db-input"),this.backupInput.setAttribute("label","백업 코드"),this.backupInput.setAttribute("placeholder","XXXX-XXXX"),this.backupInput.setAttribute("autocomplete","one-time-code"),this.backupInput.setAttribute("full-width",""),this.backupInput.addEventListener("db-input",t=>{t.stopPropagation();const e=String(t.detail?.value||"").toUpperCase().replace(/[^A-Z0-9]/g,"").slice(0,8),i=e.length>4?`${e.slice(0,4)}-${e.slice(4)}`:e;this.backupInput.value=i,this.sync()}),this.backupCancelBtn=el("button",`${i}__backup`),this.backupCancelBtn.type="button",this.backupCancelBtn.textContent="인증 코드로 돌아가기",this.backupCancelBtn.addEventListener("click",()=>{this.backupMode=!1,this.sync()}),this.backupBlock.append(d,this.backupInput,this.backupCancelBtn),this.verifyBtn=document.createElement("db-button"),this.verifyBtn.setAttribute("variant","primary"),this.verifyBtn.setAttribute("full-width",""),this.verifyBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestVerify()}),this.backupBtn=el("button",`${i}__backup`),this.backupBtn.type="button",this.backupBtn.textContent="백업 코드로 인증",this.backupBtn.addEventListener("click",()=>{this.backupMode=!0,this.sync(),requestAnimationFrame(()=>this.backupInput?.focus?.()),this.dispatchEvent(new CustomEvent("db-backup-request",{bubbles:!0,composed:!0}))}),this.errorEl=el("p",`${i}__error`),this.errorEl.hidden=!0,this.append(s,this.segment,this.hintEl,this.codeHead,this.otpInput,this.channelActions,this.backupBlock,this.verifyBtn,this.backupBtn,this.errorEl)}sync(){const t="designbase-wc-two-factor",e=this.size,i=this.method,s=this.hasAttribute("disabled"),n=this.getAttribute("code-length")||"6",r=this.getAttribute("destination")||"";this.className=[t,`${t}--${e}`,`${t}--${i}`,s&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" ");const h=this.querySelector('[data-role="title"]'),a=this.querySelector('[data-role="description"]');h.textContent=this.getAttribute("title")||"2단계 인증",a.textContent=this.getAttribute("description")||"계속하려면 인증 코드를 입력해 주세요.",this.segment.setAttribute("value",i),this.segment.setAttribute("size",e),this.segment.toggleAttribute("disabled",s),this.segment.toggleAttribute("hidden",this.backupMode);const o=this.querySelector('[data-role="hint"]');o.textContent="app"===i?"인증 앱(Google Authenticator, 1Password 등)에 표시된 6자리 코드를 입력하세요.":"sms"===i?r?`${r} 으로 보낸 SMS 인증 코드를 입력하세요.`:"등록된 휴대폰으로 보낸 SMS 인증 코드를 입력하세요.":r?`${r} 으로 보낸 이메일 인증 코드를 입력하세요.`:"등록된 이메일로 보낸 인증 코드를 입력하세요.",this.otpInput.setAttribute("length",n),this.otpInput.setAttribute("size",e),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",s),this.otpInput.toggleAttribute("error",this.hasAttribute("error")),this.getAttribute("error-message")?this.otpInput.setAttribute("error-message",this.getAttribute("error-message")||""):this.otpInput.removeAttribute("error-message"),this.hintEl.hidden=this.backupMode,this.codeHead.hidden=this.backupMode,this.otpInput.hidden=this.backupMode,this.channelActions.hidden="app"===i||this.backupMode,this.backupBlock.hidden=!this.backupMode,this.backupInput.setAttribute("size",e),this.backupInput.toggleAttribute("disabled",s),this.resendBtn.textContent=this.hasAttribute("sending")?"전송 중…":this.getAttribute("resend-label")||"코드 재전송",this.renderTimer(),this.hasAttribute("sending")&&(this.resendBtn.disabled=!0),this.verifyBtn.setAttribute("size",e),this.verifyBtn.textContent=this.backupMode?"백업 코드로 인증":this.getAttribute("verify-label")||"인증하기";const d=this.getAttribute("code")||"",u=String(this.backupInput.value||"").replace(/-/g,"");this.verifyBtn.toggleAttribute("disabled",s||(this.backupMode?8!==u.length:d.length<Number(n))),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.backupBtn.hidden=!this.hasAttribute("show-backup")||this.backupMode;const l=this.getAttribute("error-message")||"";this.errorEl.textContent=l,this.errorEl.hidden=!this.hasAttribute("error")||!l}requestVerify(){if(this.backupMode){const t=String(this.backupInput.value||"").replace(/-/g,"");if(8!==t.length||this.hasAttribute("disabled"))return;return void this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:"backup",code:t},bubbles:!0,composed:!0}))}const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:this.method,code:t},bubbles:!0,composed:!0}))}}customElements.get("db-two-factor")||customElements.define("db-two-factor",DbTwoFactor);export{DbTwoFactor};
1
+ import{createDbIcon as t}from"./db-icons.js";import"./db-button.js";import"./db-input.js";import"./db-otp-input.js";import"./db-segment-control.js";import{TWO_FACTOR_SIZES as e}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const i=[{value:"app",label:"인증 앱"},{value:"sms",label:"SMS"},{value:"email",label:"이메일"}];function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbTwoFactor extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0,this.backupMode=!1,this.resendCount=0}static get observedAttributes(){return["method","code","size","disabled","error","error-message","title","description","verify-label","code-length","show-backup","countdown","destination","manual","sending","verifying","auto-submit","resend-label","max-resends"]}get method(){const t=this.getAttribute("method");return"sms"===t||"email"===t||"app"===t?t:"app"}set method(t){this.setAttribute("method",t)}get size(){return function pick(t,e,i){return t.includes(e)?e:i}(e,this.getAttribute("size"),"m")}set size(t){this.setAttribute("size",t)}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}set countdown(t){this.setAttribute("countdown",String(t))}get maxResends(){const t=Number(this.getAttribute("max-resends")||3);return Number.isFinite(t)?Math.max(1,Math.min(5,Math.round(t))):3}connectedCallback(){this.build(),this.sync(),"app"===this.method||this.hasAttribute("manual")||this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"method"!==t&&"manual"!==t||e===i||("method"===t&&(this.resendCount=0,this.backupMode=!1),"app"===this.method||this.hasAttribute("manual")?this.clearTimer():this.startTimer(this.countdown)))}restartCountdown(){"app"!==this.method&&this.startTimer(this.countdown)}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0,this.timerEl&&(this.timerEl.textContent="")}startTimer(t){this.clearTimer(),this.remaining=t,this.renderTimer(),this.timerId=setInterval(()=>{this.remaining-=1,this.renderTimer(),this.remaining<=0&&(this.clearTimer(),this.resendBtn.disabled=!1,this.dispatchEvent(new CustomEvent("db-countdown-complete",{bubbles:!0,composed:!0})))},1e3)}renderTimer(){const t=Math.max(0,Math.floor(this.remaining/60)),e=Math.max(0,this.remaining%60);this.timerEl.textContent="app"===this.method?"":`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`,"app"!==this.method?this.timerEl.setAttribute("aria-label",`남은 시간 ${t}분 ${e}초`):this.timerEl.removeAttribute("aria-label");const i=this.resendCount>=this.maxResends;this.resendBtn.disabled="app"===this.method||this.remaining>0||i||this.hasAttribute("disabled"),"app"===this.method?this.resendStatus.textContent="":i?this.resendStatus.textContent="재전송 한도에 도달했습니다. 새 인증을 시작해 주세요.":this.remaining>0?this.resendStatus.textContent=`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")} 후 재전송 가능 · 남은 횟수 ${this.maxResends-this.resendCount}회`:this.resendStatus.textContent=`재전송 가능 · 남은 횟수 ${this.maxResends-this.resendCount}회`}build(){if(this.built)return;this.built=!0;const e="designbase-wc-two-factor";this.replaceChildren();const s=el("div",`${e}__header`),n=el("div",`${e}__icon`);n.append(t("lock",22));const r=el("div",`${e}__titles`),h=el("h3",`${e}__title`);h.dataset.role="title";const a=el("p",`${e}__description`);a.dataset.role="description",r.append(h,a),s.append(n,r),this.segment=document.createElement("db-segment-control"),this.segment.setAttribute("options",JSON.stringify(i.map(t=>({value:t.value,label:t.label})))),this.segment.setAttribute("full-width",""),this.segment.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"app");e!==this.method&&(this.setAttribute("code",""),this.method=e,this.dispatchEvent(new CustomEvent("db-method-change",{detail:{method:e},bubbles:!0,composed:!0})),"app"!==e&&this.dispatchEvent(new CustomEvent("db-send-code",{detail:{method:e,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})))}),this.hintEl=el("p",`${e}__hint`),this.hintEl.dataset.role="hint",this.codeHead=el("div",`${e}__code-head`);const o=el("span",`${e}__code-label`);o.textContent="인증 코드",this.timerEl=el("span",`${e}__timer`),this.codeHead.append(o,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:e},bubbles:!0,composed:!0}))}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.dispatchEvent(new CustomEvent("db-code-complete",{detail:{code:e,method:this.method},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()}),this.channelActions=el("div",`${e}__channel`),this.resendBtn=el("button",`${e}__resend`),this.resendBtn.type="button",this.resendBtn.textContent="코드 재전송",this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.resendCount+=1,this.dispatchEvent(new CustomEvent("db-resend",{detail:{method:this.method,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})),this.hasAttribute("manual")?this.renderTimer():this.startTimer(this.countdown))}),this.resendStatus=el("p",`${e}__resend-status`),this.resendStatus.setAttribute("aria-live","polite"),this.channelActions.append(this.resendBtn,this.resendStatus),this.backupBlock=el("div",`${e}__backup-block`);const d=el("p",`${e}__hint`);d.textContent="계정 설정 시 저장한 8자리 백업 코드 중 사용하지 않은 코드를 입력하세요.",this.backupInput=document.createElement("db-input"),this.backupInput.setAttribute("label","백업 코드"),this.backupInput.setAttribute("placeholder","XXXX-XXXX"),this.backupInput.setAttribute("autocomplete","one-time-code"),this.backupInput.setAttribute("full-width",""),this.backupInput.addEventListener("db-input",t=>{t.stopPropagation();const e=String(t.detail?.value||"").toUpperCase().replace(/[^A-Z0-9]/g,"").slice(0,8),i=e.length>4?`${e.slice(0,4)}-${e.slice(4)}`:e;this.backupInput.value=i,this.sync()}),this.backupCancelBtn=el("button",`${e}__backup`),this.backupCancelBtn.type="button",this.backupCancelBtn.textContent="인증 코드로 돌아가기",this.backupCancelBtn.addEventListener("click",()=>{this.backupMode=!1,this.sync()}),this.backupBlock.append(d,this.backupInput,this.backupCancelBtn),this.verifyBtn=document.createElement("db-button"),this.verifyBtn.setAttribute("variant","primary"),this.verifyBtn.setAttribute("full-width",""),this.verifyBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestVerify()}),this.backupBtn=el("button",`${e}__backup`),this.backupBtn.type="button",this.backupBtn.textContent="백업 코드로 인증",this.backupBtn.addEventListener("click",()=>{this.backupMode=!0,this.sync(),requestAnimationFrame(()=>this.backupInput?.focus?.()),this.dispatchEvent(new CustomEvent("db-backup-request",{bubbles:!0,composed:!0}))}),this.errorEl=el("p",`${e}__error`),this.errorEl.hidden=!0,this.append(s,this.segment,this.hintEl,this.codeHead,this.otpInput,this.channelActions,this.backupBlock,this.verifyBtn,this.backupBtn,this.errorEl)}sync(){const t="designbase-wc-two-factor",e=this.size,i=this.method,s=this.hasAttribute("disabled"),n=this.getAttribute("code-length")||"6",r=this.getAttribute("destination")||"";this.className=[t,`${t}--${e}`,`${t}--${i}`,s&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" ");const h=this.querySelector('[data-role="title"]'),a=this.querySelector('[data-role="description"]');h.textContent=this.getAttribute("title")||"2단계 인증",a.textContent=this.getAttribute("description")||"계속하려면 인증 코드를 입력해 주세요.",this.segment.setAttribute("value",i),this.segment.setAttribute("size",e),this.segment.toggleAttribute("disabled",s),this.segment.toggleAttribute("hidden",this.backupMode);const o=this.querySelector('[data-role="hint"]');o.textContent="app"===i?"인증 앱(Google Authenticator, 1Password 등)에 표시된 6자리 코드를 입력하세요.":"sms"===i?r?`${r} 으로 보낸 SMS 인증 코드를 입력하세요.`:"등록된 휴대폰으로 보낸 SMS 인증 코드를 입력하세요.":r?`${r} 으로 보낸 이메일 인증 코드를 입력하세요.`:"등록된 이메일로 보낸 인증 코드를 입력하세요.",this.otpInput.setAttribute("length",n),this.otpInput.setAttribute("size",e),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",s),this.otpInput.toggleAttribute("error",this.hasAttribute("error")),this.getAttribute("error-message")?this.otpInput.setAttribute("error-message",this.getAttribute("error-message")||""):this.otpInput.removeAttribute("error-message"),this.hintEl.hidden=this.backupMode,this.codeHead.hidden=this.backupMode,this.otpInput.hidden=this.backupMode,this.channelActions.hidden="app"===i||this.backupMode,this.backupBlock.hidden=!this.backupMode,this.backupInput.setAttribute("size",e),this.backupInput.toggleAttribute("disabled",s),this.resendBtn.textContent=this.hasAttribute("sending")?"전송 중…":this.getAttribute("resend-label")||"코드 재전송",this.renderTimer(),this.hasAttribute("sending")&&(this.resendBtn.disabled=!0),this.verifyBtn.setAttribute("size",e),this.verifyBtn.textContent=this.backupMode?"백업 코드로 인증":this.getAttribute("verify-label")||"인증하기";const d=this.getAttribute("code")||"",u=String(this.backupInput.value||"").replace(/-/g,"");this.verifyBtn.toggleAttribute("disabled",s||(this.backupMode?8!==u.length:d.length<Number(n))),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.backupBtn.hidden=!this.hasAttribute("show-backup")||this.backupMode;const c=this.getAttribute("error-message")||"";this.errorEl.textContent=c,this.errorEl.hidden=!this.hasAttribute("error")||!c}requestVerify(){if(this.backupMode){const t=String(this.backupInput.value||"").replace(/-/g,"");if(8!==t.length||this.hasAttribute("disabled"))return;return void this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:"backup",code:t},bubbles:!0,composed:!0}))}const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:this.method,code:t},bubbles:!0,composed:!0}))}}customElements.get("db-two-factor")||customElements.define("db-two-factor",DbTwoFactor);export{DbTwoFactor};
@@ -35,6 +35,9 @@ declare class DbVideoPlayer extends HTMLElement {
35
35
  private getPlaylist;
36
36
  private getCurrentSrc;
37
37
  private formatTime;
38
+ /**
39
+ * 아이콘만 있는 컨트롤 버튼. `label` 이 유일한 접근 가능한 이름이다. (A11Y-NAME)
40
+ */
38
41
  private iconButton;
39
42
  private skipButton;
40
43
  private syncFromAttributes;
@@ -1 +1 @@
1
- import{createDbIcon as t}from"./db-icons.js";const e="designbase-wc-video-player",s=20;class DbVideoPlayer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.rafId=null,this.playlistIndex=0,this.isFullscreen=!1,this.showControlsOverlay=!1,this.isPlaying=!1,this.isMuted=!1,this.volume=1,this.lastVolumeBeforeMute=1,this.currentTime=0,this.duration=0,this.isLoading=!1,this.error=null,this.onFullscreenChange=()=>{this.isFullscreen=document.fullscreenElement===this,this.dispatchEvent(new CustomEvent("db-fullscreen-change",{detail:{isFullscreen:this.isFullscreen},bubbles:!0,composed:!0})),this.updateUI()},this.onMouseEnter=()=>{this.showControlsOverlay=!0,this.updateUI()},this.onMouseLeave=()=>{this.showControlsOverlay=!1,this.updateUI()}}static get observedAttributes(){return["src","poster","title","description","size","variant","theme","auto-play","loop","muted","show-controls","enable-fullscreen","show-progress","show-time","show-volume","playlist","current-index"]}connectedCallback(){this.built||(this.built=!0,this.build(),this.bindVideoEvents()),this.syncFromAttributes(),this.updateUI(),document.addEventListener("fullscreenchange",this.onFullscreenChange),this.addEventListener("mouseenter",this.onMouseEnter),this.addEventListener("mouseleave",this.onMouseLeave)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.onFullscreenChange),this.removeEventListener("mouseenter",this.onMouseEnter),this.removeEventListener("mouseleave",this.onMouseLeave),this.stopProgressAnimation()}attributeChangedCallback(t){this.built&&("src"!==t&&"playlist"!==t&&"current-index"!==t?("muted"===t&&this.videoEl&&(this.videoEl.muted=this.getBool("muted")),this.updateUI()):this.loadVideoSource())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const s=this.getAttribute(t);return""===s||"true"===s}getPlaylist(){try{const t=this.getAttribute("playlist"),e=t?JSON.parse(t):[];return Array.isArray(e)?e:[]}catch{return[]}}getCurrentSrc(){const t=this.getPlaylist();if(t.length>0){const e=Number(this.getAttribute("current-index")||this.playlistIndex||0);return t[Math.max(0,Math.min(t.length-1,e))]}return this.getAttribute("src")||""}formatTime(t){if(!Number.isFinite(t)||t<0)return"0:00";return`${Math.floor(t/60)}:${Math.floor(t%60).toString().padStart(2,"0")}`}iconButton(s,i,l){const n=document.createElement("button");return n.type="button",n.className=`${e}__control-button`,n.append(t(s,i)),n.addEventListener("click",t=>{t.stopPropagation(),l()}),n}skipButton(t,s){const i=document.createElement("button");return i.type="button",i.className=`${e}__skip-button`,i.textContent=t,i.setAttribute("aria-label",s<0?"10초 되감기":"10초 빨리감기"),i.addEventListener("click",t=>{t.stopPropagation(),this.seekBy(s)}),i}syncFromAttributes(){this.playlistIndex=Number(this.getAttribute("current-index")||0),this.isMuted=this.getBool("muted"),this.volume=this.isMuted?0:1,this.lastVolumeBeforeMute=this.volume>0?this.volume:1,this.loadVideoSource()}loadVideoSource(){if(!this.videoEl)return;const t=this.getCurrentSrc();if(!t)return;const e=new URL(t,window.location.href).href;this.videoEl.src!==e&&(this.videoEl.src=t,this.videoEl.load()),this.videoEl.poster=this.getAttribute("poster")||"",this.videoEl.autoplay=this.getBool("auto-play"),this.videoEl.loop=this.getBool("loop"),this.videoEl.muted=this.getBool("muted"),this.videoEl.playsInline=!0}bindVideoEvents(){const t=this.videoEl;t&&(t.addEventListener("play",()=>{this.isPlaying=!0,this.startProgressAnimation(),this.dispatchEvent(new CustomEvent("db-play",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("pause",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-pause",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("timeupdate",()=>{this.currentTime=t.currentTime,this.duration=Number.isFinite(t.duration)?t.duration:0,this.dispatchEvent(new CustomEvent("db-time-update",{detail:{currentTime:this.currentTime,duration:this.duration},bubbles:!0,composed:!0})),this.updateProgress(),this.updateTime()}),t.addEventListener("volumechange",()=>{this.volume=t.volume,this.isMuted=t.muted||0===t.volume,!this.isMuted&&this.volume>0&&(this.lastVolumeBeforeMute=this.volume),this.dispatchEvent(new CustomEvent("db-volume-change",{detail:{volume:this.volume},bubbles:!0,composed:!0})),this.updateVolumeUI()}),t.addEventListener("ended",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-ended",{bubbles:!0,composed:!0}));const e=this.getPlaylist(),s=this.playlistIndex+1;e.length>0&&s<e.length&&(this.playlistIndex=s,this.loadVideoSource(),t.play()),this.updateUI()}),t.addEventListener("error",()=>{this.error="비디오를 재생할 수 없습니다.",this.dispatchEvent(new CustomEvent("db-error",{detail:{error:this.error},bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("loadstart",()=>{this.isLoading=!0,this.error=null,this.updateUI()}),t.addEventListener("loadedmetadata",()=>{this.duration=Number.isFinite(t.duration)?t.duration:0,this.updateTime()}),t.addEventListener("canplay",()=>{this.isLoading=!1,this.updateUI()}),t.addEventListener("click",()=>this.togglePlay()))}togglePlay(){this.videoEl&&(this.videoEl.paused?this.videoEl.play():this.videoEl.pause())}toggleMute(){if(this.videoEl){if(this.isMuted)return this.videoEl.muted=!1,void(this.videoEl.volume=this.lastVolumeBeforeMute>0?this.lastVolumeBeforeMute:1);this.videoEl.volume>0&&(this.lastVolumeBeforeMute=this.videoEl.volume),this.videoEl.muted=!0,this.videoEl.volume=0}}seekBy(t){if(!this.videoEl)return;const e=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,s=e>0?e:Number.MAX_SAFE_INTEGER,i=Math.max(0,Math.min(s,this.videoEl.currentTime+t));this.videoEl.currentTime=i}toggleFullscreen(){this.getBool("enable-fullscreen",!0)&&(this.isFullscreen?document.exitFullscreen?.():this.requestFullscreen?.())}build(){this.videoEl=document.createElement("video"),this.videoEl.className=`${e}__video`;const t=document.createElement("div");t.className=`${e}__loading`;const i=document.createElement("div");i.className=`${e}__spinner`,t.append(i);const l=document.createElement("div");l.className=`${e}__error`;const n=document.createElement("p");n.textContent="비디오를 재생할 수 없습니다.";const o=document.createElement("button");o.type="button",o.textContent="다시 시도",o.addEventListener("click",()=>{this.error=null,this.loadVideoSource(),this.updateUI()}),l.append(n,o),this.dimOverlayEl=document.createElement("div"),this.dimOverlayEl.className=`${e}__dim-overlay`,this.overlayEl=document.createElement("div"),this.overlayEl.className=`${e}__overlay`;const r=document.createElement("div");r.className=`${e}__top-controls`;const a=document.createElement("div");a.className=`${e}__title`;const h=document.createElement("h3"),u=document.createElement("p");a.append(h,u),r.append(a),this._titleEl=h,this._descEl=u;const d=document.createElement("div");d.className=`${e}__center-controls`;const c=this.skipButton("-10",-10);this.centerPlayEl=document.createElement("button"),this.centerPlayEl.type="button",this.centerPlayEl.className=`${e}__play-button`,this.centerPlayEl.addEventListener("click",t=>{t.stopPropagation(),this.togglePlay()});const m=this.skipButton("+10",10);d.append(c,this.centerPlayEl,m);const E=document.createElement("div");E.className=`${e}__bottom-controls`,this.progressEl=document.createElement("div"),this.progressEl.className=`${e}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${e}__progress-bar`,this.progressHandleEl=document.createElement("div"),this.progressHandleEl.className=`${e}__progress-handle`,this.progressEl.append(this.progressBarEl,this.progressHandleEl),this.progressEl.addEventListener("click",t=>{if(!this.videoEl||!Number.isFinite(this.duration)||this.duration<=0)return;const e=this.progressEl.getBoundingClientRect(),s=(t.clientX-e.left)/e.width;this.videoEl.currentTime=s*this.duration});const p=document.createElement("div");p.className=`${e}__controls`;const v=document.createElement("div");v.className=`${e}__left-controls`,this.togglePlayEl=this.iconButton("play",s,()=>this.togglePlay()),this.volumeWrapEl=document.createElement("div"),this.volumeWrapEl.className=`${e}__volume`,this.muteButtonEl=this.iconButton("volume-up",s,()=>this.toggleMute());const g=document.createElement("div");g.className=`${e}__volume-slider`,this.volumeInputEl=document.createElement("input"),this.volumeInputEl.type="range",this.volumeInputEl.min="0",this.volumeInputEl.max="1",this.volumeInputEl.step="0.1",this.volumeInputEl.addEventListener("input",()=>{if(!this.videoEl||!this.volumeInputEl)return;const t=parseFloat(this.volumeInputEl.value);t>0&&(this.lastVolumeBeforeMute=t),this.videoEl.volume=t,this.videoEl.muted=0===t}),g.append(this.volumeInputEl),this.volumeWrapEl.append(this.muteButtonEl,g),this.timeEl=document.createElement("div"),this.timeEl.className=`${e}__time`,this.timeCurrentEl=document.createElement("span");const b=document.createElement("span");b.textContent="/",this.timeDurationEl=document.createElement("span"),this.timeEl.append(this.timeCurrentEl,b,this.timeDurationEl),v.append(this.togglePlayEl,this.volumeWrapEl,this.timeEl);const y=document.createElement("div");y.className=`${e}__right-controls`,this.fullscreenButtonEl=this.iconButton("expand",s,()=>this.toggleFullscreen()),y.append(this.fullscreenButtonEl),p.append(v,y),E.append(this.progressEl,p),this.overlayEl.append(r,d,E),this.append(this.videoEl,t,l,this.dimOverlayEl,this.overlayEl)}get titleEl(){return this._titleEl}get descEl(){return this._descEl}updateProgress(){const t=this.duration>0?this.currentTime/this.duration*100:0;this.progressBarEl&&(this.progressBarEl.style.width=`${t}%`),this.progressHandleEl&&(this.progressHandleEl.style.left=`${t}%`)}updateTime(){this.timeCurrentEl&&(this.timeCurrentEl.textContent=this.formatTime(this.currentTime)),this.timeDurationEl&&(this.timeDurationEl.textContent=this.formatTime(this.duration))}updateVolumeUI(){const e=this.isMuted?0:this.volume;this.volumeInputEl&&(this.volumeInputEl.value=String(e)),this.muteButtonEl&&this.muteButtonEl.replaceChildren(t(this.isMuted||0===e?"mute-filled":"volume-up",s))}startProgressAnimation(){this.stopProgressAnimation();const tick=()=>{!this.videoEl||this.videoEl.paused||this.videoEl.ended||(this.currentTime=this.videoEl.currentTime,this.duration=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,this.updateProgress(),this.updateTime(),this.rafId=requestAnimationFrame(tick))};this.rafId=requestAnimationFrame(tick)}stopProgressAnimation(){null!==this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}updatePlayIcons(){const e=this.isPlaying?"pause":"play";this.centerPlayEl&&this.centerPlayEl.replaceChildren(t(e,48)),this.togglePlayEl&&this.togglePlayEl.replaceChildren(t(e,s))}updateUI(){const i=this.getAttribute("size")||"m",l=this.getAttribute("variant")||"default",n=this.getAttribute("theme")||"auto",o=this.getBool("show-controls",!0),r=this.getBool("show-progress",!0),a=this.getBool("show-time",!0),h=this.getBool("show-volume",!0),u=this.getBool("enable-fullscreen",!0);this.className=[e,`${e}--size-${i}`,`${e}--variant-${l}`,`${e}--theme-${n}`,this.isPlaying&&`${e}--playing`,!this.isPlaying&&`${e}--paused`,this.isMuted&&`${e}--muted`,this.isLoading&&`${e}--loading`,this.error&&`${e}--error`,o&&this.showControlsOverlay&&`${e}--show-controls`,this.isFullscreen&&`${e}--fullscreen`].filter(Boolean).join(" ");const d=this.getAttribute("title"),c=this.getAttribute("description");this.titleEl&&(d?(this.titleEl.textContent=d,this.titleEl.hidden=!1):this.titleEl.hidden=!0),this.descEl&&(c?(this.descEl.textContent=c,this.descEl.hidden=!1):this.descEl.hidden=!0),this.overlayEl&&(this.overlayEl.hidden=!o),this.dimOverlayEl&&(this.dimOverlayEl.hidden=!o),this.progressEl&&(this.progressEl.hidden=!r),this.timeEl&&(this.timeEl.hidden=!a),this.volumeWrapEl&&(this.volumeWrapEl.hidden=!h),this.fullscreenButtonEl&&(this.fullscreenButtonEl.hidden=!u),this.fullscreenButtonEl&&this.fullscreenButtonEl.replaceChildren(t(this.isFullscreen?"shrink":"expand",s)),this.updatePlayIcons(),this.updateProgress(),this.updateTime(),this.updateVolumeUI()}}customElements.get("db-video-player")||customElements.define("db-video-player",DbVideoPlayer);export{DbVideoPlayer};
1
+ import{createDbIcon as t}from"./db-icons.js";import{t as e}from"../core/i18n.js";const s="designbase-wc-video-player",i=20;class DbVideoPlayer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.rafId=null,this.playlistIndex=0,this.isFullscreen=!1,this.showControlsOverlay=!1,this.isPlaying=!1,this.isMuted=!1,this.volume=1,this.lastVolumeBeforeMute=1,this.currentTime=0,this.duration=0,this.isLoading=!1,this.error=null,this.onFullscreenChange=()=>{this.isFullscreen=document.fullscreenElement===this,this.dispatchEvent(new CustomEvent("db-fullscreen-change",{detail:{isFullscreen:this.isFullscreen},bubbles:!0,composed:!0})),this.updateUI()},this.onMouseEnter=()=>{this.showControlsOverlay=!0,this.updateUI()},this.onMouseLeave=()=>{this.showControlsOverlay=!1,this.updateUI()}}static get observedAttributes(){return["src","poster","title","description","size","variant","theme","auto-play","loop","muted","show-controls","enable-fullscreen","show-progress","show-time","show-volume","playlist","current-index"]}connectedCallback(){this.built||(this.built=!0,this.build(),this.bindVideoEvents()),this.syncFromAttributes(),this.updateUI(),document.addEventListener("fullscreenchange",this.onFullscreenChange),this.addEventListener("mouseenter",this.onMouseEnter),this.addEventListener("mouseleave",this.onMouseLeave)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.onFullscreenChange),this.removeEventListener("mouseenter",this.onMouseEnter),this.removeEventListener("mouseleave",this.onMouseLeave),this.stopProgressAnimation()}attributeChangedCallback(t){this.built&&("src"!==t&&"playlist"!==t&&"current-index"!==t?("muted"===t&&this.videoEl&&(this.videoEl.muted=this.getBool("muted")),this.updateUI()):this.loadVideoSource())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const s=this.getAttribute(t);return""===s||"true"===s}getPlaylist(){try{const t=this.getAttribute("playlist"),e=t?JSON.parse(t):[];return Array.isArray(e)?e:[]}catch{return[]}}getCurrentSrc(){const t=this.getPlaylist();if(t.length>0){const e=Number(this.getAttribute("current-index")||this.playlistIndex||0);return t[Math.max(0,Math.min(t.length-1,e))]}return this.getAttribute("src")||""}formatTime(t){if(!Number.isFinite(t)||t<0)return"0:00";return`${Math.floor(t/60)}:${Math.floor(t%60).toString().padStart(2,"0")}`}iconButton(e,i,l,n){const o=document.createElement("button");return o.type="button",o.className=`${s}__control-button`,o.setAttribute("aria-label",l),o.append(t(e,i)),o.addEventListener("click",t=>{t.stopPropagation(),n()}),o}skipButton(t,e){const i=document.createElement("button");return i.type="button",i.className=`${s}__skip-button`,i.textContent=t,i.setAttribute("aria-label",e<0?"10초 되감기":"10초 빨리감기"),i.addEventListener("click",t=>{t.stopPropagation(),this.seekBy(e)}),i}syncFromAttributes(){this.playlistIndex=Number(this.getAttribute("current-index")||0),this.isMuted=this.getBool("muted"),this.volume=this.isMuted?0:1,this.lastVolumeBeforeMute=this.volume>0?this.volume:1,this.loadVideoSource()}loadVideoSource(){if(!this.videoEl)return;const t=this.getCurrentSrc();if(!t)return;const e=new URL(t,window.location.href).href;this.videoEl.src!==e&&(this.videoEl.src=t,this.videoEl.load()),this.videoEl.poster=this.getAttribute("poster")||"",this.videoEl.autoplay=this.getBool("auto-play"),this.videoEl.loop=this.getBool("loop"),this.videoEl.muted=this.getBool("muted"),this.videoEl.playsInline=!0}bindVideoEvents(){const t=this.videoEl;t&&(t.addEventListener("play",()=>{this.isPlaying=!0,this.startProgressAnimation(),this.dispatchEvent(new CustomEvent("db-play",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("pause",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-pause",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("timeupdate",()=>{this.currentTime=t.currentTime,this.duration=Number.isFinite(t.duration)?t.duration:0,this.dispatchEvent(new CustomEvent("db-time-update",{detail:{currentTime:this.currentTime,duration:this.duration},bubbles:!0,composed:!0})),this.updateProgress(),this.updateTime()}),t.addEventListener("volumechange",()=>{this.volume=t.volume,this.isMuted=t.muted||0===t.volume,!this.isMuted&&this.volume>0&&(this.lastVolumeBeforeMute=this.volume),this.dispatchEvent(new CustomEvent("db-volume-change",{detail:{volume:this.volume},bubbles:!0,composed:!0})),this.updateVolumeUI()}),t.addEventListener("ended",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-ended",{bubbles:!0,composed:!0}));const e=this.getPlaylist(),s=this.playlistIndex+1;e.length>0&&s<e.length&&(this.playlistIndex=s,this.loadVideoSource(),t.play()),this.updateUI()}),t.addEventListener("error",()=>{this.error="비디오를 재생할 수 없습니다.",this.dispatchEvent(new CustomEvent("db-error",{detail:{error:this.error},bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("loadstart",()=>{this.isLoading=!0,this.error=null,this.updateUI()}),t.addEventListener("loadedmetadata",()=>{this.duration=Number.isFinite(t.duration)?t.duration:0,this.updateTime()}),t.addEventListener("canplay",()=>{this.isLoading=!1,this.updateUI()}),t.addEventListener("click",()=>this.togglePlay()))}togglePlay(){this.videoEl&&(this.videoEl.paused?this.videoEl.play():this.videoEl.pause())}toggleMute(){if(this.videoEl){if(this.isMuted)return this.videoEl.muted=!1,void(this.videoEl.volume=this.lastVolumeBeforeMute>0?this.lastVolumeBeforeMute:1);this.videoEl.volume>0&&(this.lastVolumeBeforeMute=this.videoEl.volume),this.videoEl.muted=!0,this.videoEl.volume=0}}seekBy(t){if(!this.videoEl)return;const e=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,s=e>0?e:Number.MAX_SAFE_INTEGER,i=Math.max(0,Math.min(s,this.videoEl.currentTime+t));this.videoEl.currentTime=i}toggleFullscreen(){this.getBool("enable-fullscreen",!0)&&(this.isFullscreen?document.exitFullscreen?.():this.requestFullscreen?.())}build(){this.videoEl=document.createElement("video"),this.videoEl.className=`${s}__video`;const t=document.createElement("div");t.className=`${s}__loading`;const l=document.createElement("div");l.className=`${s}__spinner`,t.append(l);const n=document.createElement("div");n.className=`${s}__error`;const o=document.createElement("p");o.textContent="비디오를 재생할 수 없습니다.";const r=document.createElement("button");r.type="button",r.textContent="다시 시도",r.addEventListener("click",()=>{this.error=null,this.loadVideoSource(),this.updateUI()}),n.append(o,r),this.dimOverlayEl=document.createElement("div"),this.dimOverlayEl.className=`${s}__dim-overlay`,this.overlayEl=document.createElement("div"),this.overlayEl.className=`${s}__overlay`;const a=document.createElement("div");a.className=`${s}__top-controls`;const h=document.createElement("div");h.className=`${s}__title`;const u=document.createElement("h3"),d=document.createElement("p");h.append(u,d),a.append(h),this._titleEl=u,this._descEl=d;const c=document.createElement("div");c.className=`${s}__center-controls`;const m=this.skipButton("-10",-10);this.centerPlayEl=document.createElement("button"),this.centerPlayEl.type="button",this.centerPlayEl.className=`${s}__play-button`,this.centerPlayEl.addEventListener("click",t=>{t.stopPropagation(),this.togglePlay()});const E=this.skipButton("+10",10);c.append(m,this.centerPlayEl,E);const p=document.createElement("div");p.className=`${s}__bottom-controls`,this.progressEl=document.createElement("div"),this.progressEl.className=`${s}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${s}__progress-bar`,this.progressHandleEl=document.createElement("div"),this.progressHandleEl.className=`${s}__progress-handle`,this.progressEl.append(this.progressBarEl,this.progressHandleEl),this.progressEl.addEventListener("click",t=>{if(!this.videoEl||!Number.isFinite(this.duration)||this.duration<=0)return;const e=this.progressEl.getBoundingClientRect(),s=(t.clientX-e.left)/e.width;this.videoEl.currentTime=s*this.duration});const v=document.createElement("div");v.className=`${s}__controls`;const g=document.createElement("div");g.className=`${s}__left-controls`,this.togglePlayEl=this.iconButton("play",i,e("media.play"),()=>this.togglePlay()),this.volumeWrapEl=document.createElement("div"),this.volumeWrapEl.className=`${s}__volume`,this.muteButtonEl=this.iconButton("volume-up",i,e("media.mute"),()=>this.toggleMute());const b=document.createElement("div");b.className=`${s}__volume-slider`,this.volumeInputEl=document.createElement("input"),this.volumeInputEl.type="range",this.volumeInputEl.min="0",this.volumeInputEl.max="1",this.volumeInputEl.step="0.1",this.volumeInputEl.addEventListener("input",()=>{if(!this.videoEl||!this.volumeInputEl)return;const t=parseFloat(this.volumeInputEl.value);t>0&&(this.lastVolumeBeforeMute=t),this.videoEl.volume=t,this.videoEl.muted=0===t}),b.append(this.volumeInputEl),this.volumeWrapEl.append(this.muteButtonEl,b),this.timeEl=document.createElement("div"),this.timeEl.className=`${s}__time`,this.timeCurrentEl=document.createElement("span");const y=document.createElement("span");y.textContent="/",this.timeDurationEl=document.createElement("span"),this.timeEl.append(this.timeCurrentEl,y,this.timeDurationEl),g.append(this.togglePlayEl,this.volumeWrapEl,this.timeEl);const f=document.createElement("div");f.className=`${s}__right-controls`,this.fullscreenButtonEl=this.iconButton("expand",i,e("media.fullscreen"),()=>this.toggleFullscreen()),f.append(this.fullscreenButtonEl),v.append(g,f),p.append(this.progressEl,v),this.overlayEl.append(a,c,p),this.append(this.videoEl,t,n,this.dimOverlayEl,this.overlayEl)}get titleEl(){return this._titleEl}get descEl(){return this._descEl}updateProgress(){const t=this.duration>0?this.currentTime/this.duration*100:0;this.progressBarEl&&(this.progressBarEl.style.width=`${t}%`),this.progressHandleEl&&(this.progressHandleEl.style.left=`${t}%`)}updateTime(){this.timeCurrentEl&&(this.timeCurrentEl.textContent=this.formatTime(this.currentTime)),this.timeDurationEl&&(this.timeDurationEl.textContent=this.formatTime(this.duration))}updateVolumeUI(){const e=this.isMuted?0:this.volume;this.volumeInputEl&&(this.volumeInputEl.value=String(e)),this.muteButtonEl&&this.muteButtonEl.replaceChildren(t(this.isMuted||0===e?"mute-filled":"volume-up",i))}startProgressAnimation(){this.stopProgressAnimation();const tick=()=>{!this.videoEl||this.videoEl.paused||this.videoEl.ended||(this.currentTime=this.videoEl.currentTime,this.duration=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,this.updateProgress(),this.updateTime(),this.rafId=requestAnimationFrame(tick))};this.rafId=requestAnimationFrame(tick)}stopProgressAnimation(){null!==this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}updatePlayIcons(){const e=this.isPlaying?"pause":"play";this.centerPlayEl&&this.centerPlayEl.replaceChildren(t(e,48)),this.togglePlayEl&&this.togglePlayEl.replaceChildren(t(e,i))}updateUI(){const e=this.getAttribute("size")||"m",l=this.getAttribute("variant")||"default",n=this.getAttribute("theme")||"auto",o=this.getBool("show-controls",!0),r=this.getBool("show-progress",!0),a=this.getBool("show-time",!0),h=this.getBool("show-volume",!0),u=this.getBool("enable-fullscreen",!0);this.className=[s,`${s}--size-${e}`,`${s}--variant-${l}`,`${s}--theme-${n}`,this.isPlaying&&`${s}--playing`,!this.isPlaying&&`${s}--paused`,this.isMuted&&`${s}--muted`,this.isLoading&&`${s}--loading`,this.error&&`${s}--error`,o&&this.showControlsOverlay&&`${s}--show-controls`,this.isFullscreen&&`${s}--fullscreen`].filter(Boolean).join(" ");const d=this.getAttribute("title"),c=this.getAttribute("description");this.titleEl&&(d?(this.titleEl.textContent=d,this.titleEl.hidden=!1):this.titleEl.hidden=!0),this.descEl&&(c?(this.descEl.textContent=c,this.descEl.hidden=!1):this.descEl.hidden=!0),this.overlayEl&&(this.overlayEl.hidden=!o),this.dimOverlayEl&&(this.dimOverlayEl.hidden=!o),this.progressEl&&(this.progressEl.hidden=!r),this.timeEl&&(this.timeEl.hidden=!a),this.volumeWrapEl&&(this.volumeWrapEl.hidden=!h),this.fullscreenButtonEl&&(this.fullscreenButtonEl.hidden=!u),this.fullscreenButtonEl&&this.fullscreenButtonEl.replaceChildren(t(this.isFullscreen?"shrink":"expand",i)),this.updatePlayIcons(),this.updateProgress(),this.updateTime(),this.updateVolumeUI()}}customElements.get("db-video-player")||customElements.define("db-video-player",DbVideoPlayer);export{DbVideoPlayer};
@@ -1 +1 @@
1
- function applySectionBlockHeader(e,t){if(!e)return;const{headline:n="",subtitle:i="",description:s="",badge:a,align:d="center",extraClassName:o}=t;e.className=["designbase-wc-section-block-header",`designbase-wc-section-block-header--${d}`,o].filter(Boolean).join(" ");let r=e.querySelector("db-badge");a?.text?(r||(r=document.createElement("db-badge"),r.className="designbase-wc-section-block-header__badge",e.prepend(r)),r.removeAttribute("hidden"),r.style.display="",r.setAttribute("variant",a.variant||"primary"),r.setAttribute("badge-style",a.style||"text"),r.setAttribute("size",a.size||"m"),r.textContent=a.text):r&&(r.textContent="",r.setAttribute("hidden",""),r.style.display="none");const c=e.querySelector("h2"),l=e.querySelector(".designbase-wc-section-block-header__subtitle"),b=e.querySelector(".designbase-wc-section-block-header__description");c&&(c.textContent=n,c.hidden=!n),l&&(l.textContent=i,l.hidden=!i),b&&(b.textContent=s,b.hidden=!s)}export{applySectionBlockHeader};
1
+ function applySectionBlockHeader(e,t){if(!e)return;const{headline:n="",subtitle:i="",description:s="",badge:a,align:d="center",extraClassName:o}=t;e.className=["designbase-wc-section-block-header",`designbase-wc-section-block-header--${d}`,o].filter(Boolean).join(" ");let r=e.querySelector("db-badge");a?.text?(r||(r=document.createElement("db-badge"),r.className="designbase-wc-section-block-header__badge",e.prepend(r)),r.removeAttribute("hidden"),r.style.display="",r.setAttribute("variant",a.variant||"primary"),r.setAttribute("type",a.style||"text"),r.setAttribute("size",a.size||"m"),r.textContent=a.text):r&&(r.textContent="",r.setAttribute("hidden",""),r.style.display="none");const c=e.querySelector("h2"),l=e.querySelector(".designbase-wc-section-block-header__subtitle"),b=e.querySelector(".designbase-wc-section-block-header__description");c&&(c.textContent=n,c.hidden=!n),l&&(l.textContent=i,l.hidden=!i),b&&(b.textContent=s,b.hidden=!s)}export{applySectionBlockHeader};
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 접근성 런타임 유틸.
3
+ *
4
+ * 규격: A11Y-LIVE, A11Y-NAME
5
+ */
6
+ type Politeness = 'polite' | 'assertive';
7
+ /**
8
+ * 스크린리더에 메시지를 알린다.
9
+ *
10
+ * @param message 읽어줄 문장
11
+ * @param politeness `polite` 는 현재 읽는 것을 끝낸 뒤, `assertive` 는 즉시 끼어든다.
12
+ * 오류·경고에만 assertive 를 쓴다.
13
+ */
14
+ declare function announce(message: string, politeness?: Politeness): void;
15
+ /** 피드백 톤에 맞는 politeness. danger/warning 만 즉시 끼어든다. */
16
+ declare function politenessForTone(tone: string): Politeness;
17
+ /**
18
+ * 접근 가능한 이름을 붙인다.
19
+ *
20
+ * 보이는 텍스트가 이미 있으면 `aria-label` 로 덮어쓰지 않는다 — 스크린리더 사용자와
21
+ * 음성 제어 사용자가 서로 다른 이름을 보게 되는 문제를 막는다.
22
+ */
23
+ declare function setAccessibleName(el: HTMLElement, name: string): void;
24
+ /** 시각적으로만 감춘 텍스트 노드를 만든다 (스크린리더는 읽음) */
25
+ declare function createSrOnly(text: string): HTMLSpanElement;
26
+ /**
27
+ * 네이티브가 아닌 요소(`role="button"` 을 얹은 div 등)에 Enter/Space 활성화를 붙인다.
28
+ *
29
+ * 네이티브 `<button>` 은 브라우저가 이걸 해 주지만, 커스텀 엘리먼트 호스트에
30
+ * role 만 얹은 경우에는 직접 배선해야 마우스와 키보드가 같아진다.
31
+ *
32
+ * 규격: A11Y-KEYBOARD
33
+ *
34
+ * @returns 등록을 해제하는 함수
35
+ */
36
+ declare function activateOnKey(el: HTMLElement, options?: {
37
+ isDisabled?: () => boolean;
38
+ }): () => void;
39
+
40
+ export { activateOnKey, announce, createSrOnly, politenessForTone, setAccessibleName };
@@ -0,0 +1 @@
1
+ const e={};function announce(t,n="polite"){if("undefined"==typeof document||!t)return;const r=function getRegion(t){const n=e[t];if(n&&n.isConnected)return n;const r=document.createElement("div");return r.setAttribute("aria-live",t),r.setAttribute("aria-atomic","true"),r.setAttribute("role","assertive"===t?"alert":"status"),r.setAttribute("data-wc-internal","live-region"),r.className="designbase-wc-sr-only",document.body.appendChild(r),e[t]=r,r}(n);r.textContent="",requestAnimationFrame(()=>{r.textContent=t})}function politenessForTone(e){return"danger"===e||"warning"===e?"assertive":"polite"}function setAccessibleName(e,t){if(!t)return;(e.textContent||"").trim().length>0||e.setAttribute("aria-label",t)}function createSrOnly(e){const t=document.createElement("span");return t.className="designbase-wc-sr-only",t.textContent=e,t}function activateOnKey(e,t={}){const handler=n=>{"Enter"!==n.key&&" "!==n.key||t.isDisabled?.()||(n.preventDefault(),e.click())};return e.addEventListener("keydown",handler),()=>e.removeEventListener("keydown",handler)}export{activateOnKey,announce,createSrOnly,politenessForTone,setAccessibleName};
@@ -0,0 +1,41 @@
1
+ import { CleanupBag } from './dom.js';
2
+
3
+ /**
4
+ * 선택적 베이스 클래스.
5
+ *
6
+ * 기존 130개 컴포넌트는 전부 `extends HTMLElement` 이며 그대로 동작한다.
7
+ * 새 컴포넌트나 개선 대상만 이 클래스를 상속하면 정리(cleanup)와 이벤트 발행이
8
+ * 규격에 맞게 강제된다. **상속은 강제가 아니다.**
9
+ *
10
+ * 규격: PERF-CLEANUP, STD-EVENT
11
+ */
12
+
13
+ declare abstract class DbElement extends HTMLElement {
14
+ /**
15
+ * `connectedCallback` 에서 등록한 것들을 담아두는 가방.
16
+ * `disconnectedCallback` 에서 자동으로 비워진다.
17
+ */
18
+ protected readonly cleanups: CleanupBag;
19
+ /**
20
+ * 하위 클래스는 `disconnectedCallback` 을 오버라이드할 때 반드시
21
+ * `super.disconnectedCallback()` 을 호출해야 한다.
22
+ */
23
+ disconnectedCallback(): void;
24
+ /**
25
+ * 규격에 맞는 커스텀 이벤트를 발행한다 — `db-` 접두사 + bubbles + composed.
26
+ *
27
+ * @returns `preventDefault()` 되지 않았으면 true
28
+ */
29
+ protected dbEmit<T>(name: string, detail?: T, options?: {
30
+ cancelable?: boolean;
31
+ }): boolean;
32
+ /**
33
+ * 커스텀 엘리먼트가 정의되기 **전에** 프레임워크가 프로퍼티로 값을 넣어둔 경우,
34
+ * 그 값은 인스턴스 자체 프로퍼티로 남아 접근자를 가린다. 정의 후 회수한다.
35
+ *
36
+ * 규격: STD-UPGRADE
37
+ */
38
+ protected upgradeProperties(names: string[]): void;
39
+ }
40
+
41
+ export { DbElement };
@@ -0,0 +1 @@
1
+ import{CleanupBag as e}from"./dom.js";class DbElement extends HTMLElement{constructor(){super(...arguments),this.cleanups=new e}disconnectedCallback(){this.cleanups.run()}dbEmit(e,t,s){const n=e.startsWith("db-")?e:`db-${e}`;return this.dispatchEvent(new CustomEvent(n,{detail:t,bubbles:!0,composed:!0,cancelable:s?.cancelable??!1}))}upgradeProperties(e){for(const t of e){if(!Object.prototype.hasOwnProperty.call(this,t))continue;const e=this[t];delete this[t],this[t]=e}}}export{DbElement};
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 정리(cleanup) 관리.
3
+ *
4
+ * `connectedCallback` 에서 등록한 리스너·타이머·observer 를 `disconnectedCallback`
5
+ * 에서 빠짐없이 해제하기 위한 도구. 등록과 해제를 한 줄로 묶어두면
6
+ * "add 는 했는데 remove 를 빠뜨리는" 실수가 구조적으로 불가능해진다.
7
+ *
8
+ * 규격: PERF-CLEANUP
9
+ */
10
+ type Cleanup = () => void;
11
+ /** 해제 함수를 모아뒀다가 한 번에 실행하는 가방 */
12
+ declare class CleanupBag {
13
+ private items;
14
+ /** 해제 함수를 직접 등록 */
15
+ add(fn: Cleanup): void;
16
+ /** 리스너를 달고 해제 함수를 자동 등록 */
17
+ listen<T extends EventTarget>(target: T, type: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
18
+ /** setTimeout — 해제 시 자동 clear */
19
+ timeout(fn: () => void, ms: number): number;
20
+ /** setInterval — 해제 시 자동 clear */
21
+ interval(fn: () => void, ms: number): number;
22
+ /** MutationObserver / ResizeObserver / IntersectionObserver 등록 */
23
+ observe<T extends {
24
+ disconnect(): void;
25
+ }>(observer: T): T;
26
+ /** 등록된 모든 해제 함수를 실행하고 비운다. 여러 번 호출해도 안전하다. */
27
+ run(): void;
28
+ }
29
+ /** 스크롤/리사이즈처럼 고빈도로 발생하는 핸들러를 프레임당 1회로 병합한다. (PERF-RAF) */
30
+ declare function rafThrottle<A extends unknown[]>(fn: (...args: A) => void): ((...args: A) => void) & {
31
+ cancel(): void;
32
+ };
33
+
34
+ export { CleanupBag, rafThrottle };
35
+ export type { Cleanup };
@@ -0,0 +1 @@
1
+ class CleanupBag{constructor(){this.items=[]}add(t){this.items.push(t)}listen(t,e,s,n){t.addEventListener(e,s,n),this.items.push(()=>t.removeEventListener(e,s,n))}timeout(t,e){const s=window.setTimeout(t,e);return this.items.push(()=>window.clearTimeout(s)),s}interval(t,e){const s=window.setInterval(t,e);return this.items.push(()=>window.clearInterval(s)),s}observe(t){return this.items.push(()=>t.disconnect()),t}run(){const t=this.items;this.items=[];for(const e of t)try{e()}catch{}}}function rafThrottle(t){let e,s=0;const wrapped=(...n)=>{e=n,s||(s=requestAnimationFrame(()=>{s=0,t(...e)}))};return wrapped.cancel=()=>{s&&cancelAnimationFrame(s),s=0},wrapped}export{CleanupBag,rafThrottle};
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 포커스 트랩.
3
+ *
4
+ * 모달·드로어·바텀시트 같은 오버레이가 열려 있는 동안 Tab 이 오버레이 밖으로
5
+ * 새어나가지 않게 하고, 닫을 때 **열기 직전에 포커스를 갖고 있던 요소로 되돌린다**.
6
+ * 포커스 복원이 없으면 키보드 사용자는 모달을 닫는 순간 문서 맨 앞으로 튕긴다.
7
+ *
8
+ * 규격: A11Y-TRAP, A11Y-AUTOFOCUS
9
+ */
10
+ /** 컨테이너 안에서 Tab 순서대로 포커스 가능한 요소들 */
11
+ declare function getTabbables(container: HTMLElement): HTMLElement[];
12
+ interface FocusTrapOptions {
13
+ /**
14
+ * 활성화 시 포커스를 받을 요소.
15
+ * 지정하지 않으면 `[autofocus]` → 첫 포커서블 → 컨테이너 순으로 결정한다.
16
+ */
17
+ initialFocus?: HTMLElement | (() => HTMLElement | null) | null;
18
+ /** 해제 시 포커스를 돌려줄 요소. 기본값은 활성화 직전의 `document.activeElement` */
19
+ returnFocus?: HTMLElement | null;
20
+ /** 포커스 복원을 끄고 싶을 때 (기본 true) */
21
+ restoreFocus?: boolean;
22
+ }
23
+ interface FocusTrap {
24
+ activate(): void;
25
+ deactivate(): void;
26
+ /** 컨테이너 내용이 바뀌었을 때 포커서블 목록을 다시 계산할 필요는 없다 — 매번 조회하므로 no-op 호환용 */
27
+ readonly active: boolean;
28
+ }
29
+ /**
30
+ * `container` 안에 포커스를 가두는 트랩을 만든다.
31
+ *
32
+ * ```ts
33
+ * this.trap = createFocusTrap(panel);
34
+ * this.trap.activate(); // 열 때
35
+ * this.trap.deactivate(); // 닫을 때 — 포커스가 트리거로 돌아간다
36
+ * ```
37
+ */
38
+ declare function createFocusTrap(container: HTMLElement, options?: FocusTrapOptions): FocusTrap;
39
+
40
+ export { createFocusTrap, getTabbables };
41
+ export type { FocusTrap, FocusTrapOptions };
@@ -0,0 +1 @@
1
+ const e=["a[href]","area[href]","button","input","select","textarea","details > summary:first-of-type","iframe","object","embed","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable="false"])',"[tabindex]"].join(",");function isTabbable(e){return e instanceof HTMLElement&&(!e.hasAttribute("disabled")&&("true"!==e.getAttribute("aria-hidden")&&(!(e.tabIndex<0)&&(("function"!=typeof e.closest||!e.closest("[inert]"))&&(!e.hidden&&(!(!e.offsetParent&&"fixed"!==getComputedStyle(e).position)&&"hidden"!==getComputedStyle(e).visibility))))))}function getTabbables(t){return Array.from(t.querySelectorAll(e)).filter(isTabbable)}function createFocusTrap(e,t={}){let n=!1,o=null;const handleKeyDown=t=>{if("Tab"!==t.key||!n)return;const o=getTabbables(e);if(0===o.length)return t.preventDefault(),void e.focus();const r=o[0],i=o[o.length-1],a=document.activeElement;if(!a||!e.contains(a))return t.preventDefault(),void(t.shiftKey?i:r).focus();t.shiftKey&&a===r?(t.preventDefault(),i.focus()):t.shiftKey||a!==i||(t.preventDefault(),r.focus())},handleFocusIn=t=>{if(!n)return;const o=t.target;if(o&&e.contains(o))return;(getTabbables(e)[0]||e).focus()};return{get active(){return n},activate(){if(n)return;n=!0,o=t.returnFocus??document.activeElement,e.hasAttribute("tabindex")||e.setAttribute("tabindex","-1"),document.addEventListener("keydown",handleKeyDown,!0),document.addEventListener("focusin",handleFocusIn,!0);((()=>{const{initialFocus:n}=t;if("function"==typeof n)return n();if(n)return n;const o=e.querySelector("[autofocus]");return o&&isTabbable(o)?o:getTabbables(e)[0]||null})()||e).focus()},deactivate(){n&&(n=!1,document.removeEventListener("keydown",handleKeyDown,!0),document.removeEventListener("focusin",handleFocusIn,!0),!1!==t.restoreFocus&&(o&&o.isConnected&&"function"==typeof o.focus&&o.focus(),o=null))}}}export{createFocusTrap,getTabbables};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * 사용자 노출 문자열.
3
+ *
4
+ * 컴포넌트 소스에 `'모달 닫기'` 같은 한국어를 박아두면 한국어 외 서비스에서는
5
+ * 스크린리더가 엉뚱한 언어를 읽는다. 문자열은 전부 여기를 통과시키고,
6
+ * 소비자가 `dbSetLocale()` 로 덮어쓸 수 있게 한다.
7
+ *
8
+ * 기본값은 현재 소스에 박혀 있던 한국어 그대로다 — 동작 변화 없이 교체 지점만 생긴다.
9
+ *
10
+ * 규격: DX-I18N
11
+ */
12
+ /** 컴포넌트가 쓰는 문자열 키 → 기본(한국어) 값 */
13
+ declare const DEFAULT_STRINGS: {
14
+ readonly close: "닫기";
15
+ readonly 'modal.close': "모달 닫기";
16
+ readonly 'drawer.close': "드로어 닫기";
17
+ readonly 'dialog.close': "대화상자 닫기";
18
+ readonly 'toast.close': "알림 닫기";
19
+ readonly clear: "지우기";
20
+ readonly clearAll: "모든 값 지우기";
21
+ readonly loading: "로딩 중";
22
+ readonly required: "필수";
23
+ readonly optional: "선택";
24
+ readonly error: "오류";
25
+ readonly success: "성공";
26
+ readonly warning: "경고";
27
+ readonly info: "안내";
28
+ readonly previous: "이전";
29
+ readonly next: "다음";
30
+ readonly 'select.placeholder': "선택하세요";
31
+ readonly 'select.noResults': "결과 없음";
32
+ readonly 'tabs.label': "탭 목록";
33
+ readonly 'pagination.label': "페이지 번호";
34
+ readonly 'search.label': "검색";
35
+ readonly 'media.play': "재생";
36
+ readonly 'media.pause': "일시정지";
37
+ readonly 'media.mute': "음소거";
38
+ readonly 'media.unmute': "음소거 해제";
39
+ readonly 'media.settings': "설정";
40
+ readonly 'media.shuffle': "셔플";
41
+ readonly 'media.repeat': "반복";
42
+ readonly 'media.previous': "이전 트랙";
43
+ readonly 'media.next': "다음 트랙";
44
+ readonly 'media.fullscreen': "전체화면";
45
+ readonly 'carousel.previous': "이전 슬라이드";
46
+ readonly 'carousel.next': "다음 슬라이드";
47
+ readonly 'breadcrumbs.expand': "생략된 경로 펼치기";
48
+ readonly 'input.showPassword': "비밀번호 보기";
49
+ readonly 'input.hidePassword': "비밀번호 숨기기";
50
+ readonly 'input.clearSearch': "검색어 지우기";
51
+ readonly 'verify.done': "인증 완료";
52
+ readonly 'verify.phoneDone': "휴대폰 번호 인증이 완료되었습니다.";
53
+ };
54
+ type DbStringKey = keyof typeof DEFAULT_STRINGS;
55
+ type DbStrings = Partial<Record<DbStringKey, string>> & Record<string, string>;
56
+ /**
57
+ * 문자열을 전역으로 덮어쓴다. 앱 부트스트랩에서 한 번 호출한다.
58
+ *
59
+ * ```ts
60
+ * dbSetLocale({ 'modal.close': 'Close dialog', close: 'Close' });
61
+ * ```
62
+ */
63
+ declare function dbSetLocale(strings: DbStrings): void;
64
+ /** 현재 적용된 오버라이드를 모두 지운다 (테스트용) */
65
+ declare function dbResetLocale(): void;
66
+ /**
67
+ * 문자열을 조회한다.
68
+ *
69
+ * @param key 문자열 키
70
+ * @param fallback 키가 사전에 없을 때 쓸 값
71
+ */
72
+ declare function t(key: DbStringKey | string, fallback?: string): string;
73
+ /**
74
+ * 엘리먼트에서 라벨을 읽는다 — 소비자가 준 속성이 항상 우선한다.
75
+ *
76
+ * 컴포넌트는 `label-close` 같은 속성으로 개별 오버라이드를 받고,
77
+ * 없으면 전역 로케일로 폴백한다.
78
+ */
79
+ declare function labelFrom(el: Element, attribute: string, key: DbStringKey | string): string;
80
+
81
+ export { dbResetLocale, dbSetLocale, labelFrom, t };
82
+ export type { DbStringKey, DbStrings };
@@ -0,0 +1 @@
1
+ const e={close:"닫기","modal.close":"모달 닫기","drawer.close":"드로어 닫기","dialog.close":"대화상자 닫기","toast.close":"알림 닫기",clear:"지우기",clearAll:"모든 값 지우기",loading:"로딩 중",required:"필수",optional:"선택",error:"오류",success:"성공",warning:"경고",info:"안내",previous:"이전",next:"다음","select.placeholder":"선택하세요","select.noResults":"결과 없음","tabs.label":"탭 목록","pagination.label":"페이지 번호","search.label":"검색","media.play":"재생","media.pause":"일시정지","media.mute":"음소거","media.unmute":"음소거 해제","media.settings":"설정","media.shuffle":"셔플","media.repeat":"반복","media.previous":"이전 트랙","media.next":"다음 트랙","media.fullscreen":"전체화면","carousel.previous":"이전 슬라이드","carousel.next":"다음 슬라이드","breadcrumbs.expand":"생략된 경로 펼치기","input.showPassword":"비밀번호 보기","input.hidePassword":"비밀번호 숨기기","input.clearSearch":"검색어 지우기","verify.done":"인증 완료","verify.phoneDone":"휴대폰 번호 인증이 완료되었습니다."};let a={};function dbSetLocale(e){a={...a,...e}}function dbResetLocale(){a={}}function t(l,o){return l in a?a[l]:l in e?e[l]:o??l}function labelFrom(e,a,l){return e.getAttribute(a)||t(l)}export{dbResetLocale,dbSetLocale,labelFrom,t};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 문서 스크롤 잠금.
3
+ *
4
+ * 각 오버레이가 `document.body.style.overflow` 를 직접 만지면, 모달 위에 드로어를
5
+ * 띄웠다가 드로어만 닫는 순간 스크롤락이 통째로 풀린다. 잠금은 참조 카운트로만 관리한다.
6
+ *
7
+ * 규격: PERF-GLOBAL-STATE
8
+ */
9
+ /**
10
+ * 문서 스크롤을 잠근다. 중첩 호출은 카운트되며, 같은 횟수만큼 `unlockScroll()`
11
+ * 해야 실제로 풀린다.
12
+ */
13
+ declare function lockScroll(): void;
14
+ /** `lockScroll()` 을 되돌린다. 카운트가 0 이 될 때만 실제로 해제된다. */
15
+ declare function unlockScroll(): void;
16
+ /** 현재 잠금 카운트 — 테스트/디버깅용 */
17
+ declare function scrollLockDepth(): number;
18
+
19
+ export { lockScroll, scrollLockDepth, unlockScroll };
@@ -0,0 +1 @@
1
+ let t=0,e="",n="";function lockScroll(){if("undefined"==typeof document)return;if(t+=1,t>1)return;const{body:o}=document;e=o.style.overflow,n=o.style.paddingRight;const l=function scrollbarWidth(){return window.innerWidth-document.documentElement.clientWidth}();if(l>0){const t=parseFloat(getComputedStyle(o).paddingRight)||0;o.style.paddingRight=`${t+l}px`}o.style.overflow="hidden"}function unlockScroll(){if("undefined"==typeof document)return;if(0===t)return;if(t-=1,t>0)return;const{body:o}=document;o.style.overflow=e,o.style.paddingRight=n,e="",n=""}function scrollLockDepth(){return t}export{lockScroll,scrollLockDepth,unlockScroll};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 엘리먼트 id 생성.
3
+ *
4
+ * 각 컴포넌트가 `Math.random().toString(36)` 을 따로 굴리면 충돌 가능성이 있고,
5
+ * 무엇보다 SSR/스냅샷에서 diff 노이즈가 된다. 생성은 여기서만 한다.
6
+ *
7
+ * 규격: DX-UID
8
+ */
9
+ /**
10
+ * `prefix` 로 시작하는 문서 내 고유 id 를 만든다.
11
+ *
12
+ * `crypto.randomUUID` 가 있으면 그 앞 8자리를 쓰고, 없으면 증가 카운터로 폴백한다.
13
+ * 어느 쪽이든 한 문서 안에서는 절대 겹치지 않는다.
14
+ */
15
+ declare function dbUid(prefix?: string): string;
16
+ /**
17
+ * 엘리먼트에 id 가 없으면 만들어 붙이고, 그 id 를 돌려준다.
18
+ *
19
+ * `aria-labelledby` / `aria-controls` 배선에 쓴다 — 소비자가 직접 지정한 id 는 존중한다.
20
+ */
21
+ declare function ensureId(el: Element, prefix?: string): string;
22
+
23
+ export { dbUid, ensureId };
@@ -0,0 +1 @@
1
+ let d=0;function dbUid(n="db"){d+=1;const o="undefined"!=typeof globalThis?globalThis.crypto:void 0;return`${n}-${o&&"function"==typeof o.randomUUID?o.randomUUID().slice(0,8):Math.floor(4294967295*Math.random()).toString(36)}-${d}`}function ensureId(d,n="db"){return d.id||(d.id=dbUid(n)),d.id}export{dbUid,ensureId};
@@ -1,4 +1,6 @@
1
1
  import { DbToast } from '../components/db-toast.js';
2
+ import { ToastVariant, ToastPosition } from '@designbasekorea/ui-contracts';
3
+ export { ToastPosition as DbToastPosition, ToastVariant as DbToastVariant } from '@designbasekorea/ui-contracts';
2
4
 
3
5
  /**
4
6
  * 명령형 toast() 헬퍼 — 컨테이너/스택/제거를 자동 관리합니다.
@@ -6,24 +8,22 @@ import { DbToast } from '../components/db-toast.js';
6
8
  * @example
7
9
  * import { toast } from '@designbasekorea/ui-wc';
8
10
  * toast.success('저장되었습니다');
9
- * toast.error('오류가 발생했어요', { description: '잠시 후 다시 시도해 주세요' });
10
- * const t = toast({ title: '업로드 중…', status: 'info', duration: 0 });
11
+ * toast.danger('오류가 발생했어요', { description: '잠시 후 다시 시도해 주세요' });
12
+ * const t = toast({ title: '업로드 중…', variant: 'info', duration: 0 });
11
13
  * t.close();
12
14
  */
13
15
 
14
- type DbToastStatus = 'info' | 'success' | 'warning' | 'error';
15
- type DbToastPosition = 'top-right' | 'top-center' | 'top-left' | 'bottom-right' | 'bottom-center' | 'bottom-left';
16
16
  interface DbToastOptions {
17
17
  /** 제목 (필수) */
18
18
  title: string;
19
19
  /** 보조 설명 */
20
20
  description?: string;
21
21
  /** 상태 색상/아이콘. 기본 'info' */
22
- status?: DbToastStatus;
22
+ variant?: ToastVariant;
23
23
  /** 자동 닫힘 시간(ms). 0 이하이면 자동으로 닫히지 않음. 기본 5000 */
24
24
  duration?: number;
25
25
  /** 표시 위치. 기본 'top-right' */
26
- position?: DbToastPosition;
26
+ position?: ToastPosition;
27
27
  /** 닫기 버튼 표시. 기본 true */
28
28
  showClose?: boolean;
29
29
  /** 진행 바 표시. 기본 false */
@@ -37,20 +37,20 @@ interface DbToastHandle {
37
37
  /** 토스트를 수동으로 닫습니다(애니메이션 포함). */
38
38
  close: () => void;
39
39
  }
40
- type DbToastShortcut = (title: string, options?: Omit<DbToastOptions, 'title' | 'status'>) => DbToastHandle;
40
+ type DbToastShortcut = (title: string, options?: Omit<DbToastOptions, 'title' | 'variant'>) => DbToastHandle;
41
41
  interface DbToastApi {
42
42
  (options: DbToastOptions): DbToastHandle;
43
43
  show: (options: DbToastOptions) => DbToastHandle;
44
44
  success: DbToastShortcut;
45
- error: DbToastShortcut;
45
+ danger: DbToastShortcut;
46
46
  warning: DbToastShortcut;
47
47
  info: DbToastShortcut;
48
48
  /** 열려 있는 토스트를 모두 닫습니다. position 지정 시 해당 위치만. */
49
- dismissAll: (position?: DbToastPosition) => void;
49
+ dismissAll: (position?: ToastPosition) => void;
50
50
  }
51
51
  /** 토스트를 띄웁니다. `toast(options)` 와 동일합니다. */
52
52
  declare function showToast(options: DbToastOptions): DbToastHandle;
53
53
  declare const toast: DbToastApi;
54
54
 
55
55
  export { showToast, toast };
56
- export type { DbToastApi, DbToastHandle, DbToastOptions, DbToastPosition, DbToastStatus };
56
+ export type { DbToastApi, DbToastHandle, DbToastOptions };
@@ -1 +1 @@
1
- const t="designbase-wc-toast-container",e="top-right";let o=0;function isBrowser(){return"undefined"!=typeof document}function showToast(s){const r=`db-toast-${o+=1}`;if(!isBrowser())return{id:r,element:null,close:()=>{}};const{title:i,description:n,status:u="info",duration:a=5e3,position:c=e,showClose:d=!0,showProgress:l=!1}=s,h=function getContainer(e){const o=document.querySelector(`.${t}[data-position="${e}"]`);if(o)return o;const s=document.createElement("div");return s.className=`${t} ${t}--${e}`,s.dataset.position=e,s.setAttribute("role","region"),s.setAttribute("aria-live","polite"),s.setAttribute("aria-label","알림"),document.body.appendChild(s),s}(c),b=document.createElement("db-toast");return b.setAttribute("toast-id",r),b.setAttribute("status",u),b.setAttribute("title",i),n&&b.setAttribute("description",n),b.setAttribute("duration",String(a)),b.setAttribute("show-close",String(d)),l&&b.setAttribute("show-progress",""),b.setAttribute("auto-remove","true"),c.startsWith("bottom")?h.insertBefore(b,h.firstChild):h.appendChild(b),b.addEventListener("db-close",()=>window.requestAnimationFrame(()=>function cleanupContainer(t){0===t.childElementCount&&t.remove()}(h)),{once:!0}),{id:r,element:b,close:()=>b.close()}}function shortcut(t){return(e,o)=>showToast({...o,title:e,status:t})}const toast=t=>showToast(t);toast.show=showToast,toast.success=shortcut("success"),toast.error=shortcut("error"),toast.warning=shortcut("warning"),toast.info=shortcut("info"),toast.dismissAll=e=>{if(!isBrowser())return;const o=e?`.${t}[data-position="${e}"] db-toast`:`.${t} db-toast`;document.querySelectorAll(o).forEach(t=>t.close())};export{showToast,toast};
1
+ const t="designbase-wc-toast-container",e="top-right";let o=0;function isBrowser(){return"undefined"!=typeof document}function showToast(s){const n=`db-toast-${o+=1}`;if(!isBrowser())return{id:n,element:null,close:()=>{}};const{title:r,description:i,variant:a="info",duration:u=5e3,position:c=e,showClose:d=!0,showProgress:l=!1}=s,h=function getContainer(e){const o=document.querySelector(`.${t}[data-position="${e}"]`);if(o)return o;const s=document.createElement("div");return s.className=`${t} ${t}--${e}`,s.dataset.position=e,s.setAttribute("role","region"),s.setAttribute("aria-live","polite"),s.setAttribute("aria-label","알림"),document.body.appendChild(s),s}(c),b=document.createElement("db-toast");return b.setAttribute("toast-id",n),b.setAttribute("variant",a),b.setAttribute("title",r),i&&b.setAttribute("description",i),b.setAttribute("duration",String(u)),b.setAttribute("show-close",String(d)),l&&b.setAttribute("show-progress",""),b.setAttribute("auto-remove","true"),c.startsWith("bottom")?h.insertBefore(b,h.firstChild):h.appendChild(b),b.addEventListener("db-close",()=>window.requestAnimationFrame(()=>function cleanupContainer(t){0===t.childElementCount&&t.remove()}(h)),{once:!0}),{id:n,element:b,close:()=>b.close()}}function shortcut(t){return(e,o)=>showToast({...o,title:e,variant:t})}const toast=t=>showToast(t);toast.show=showToast,toast.success=shortcut("success"),toast.danger=shortcut("danger"),toast.warning=shortcut("warning"),toast.info=shortcut("info"),toast.dismissAll=e=>{if(!isBrowser())return;const o=e?`.${t}[data-position="${e}"] db-toast`:`.${t} db-toast`;document.querySelectorAll(o).forEach(t=>t.close())};export{showToast,toast};
@@ -1,3 +1,10 @@
1
+ export { dbUid, ensureId } from './core/uid.js';
2
+ export { Cleanup, CleanupBag, rafThrottle } from './core/dom.js';
3
+ export { FocusTrap, FocusTrapOptions, createFocusTrap, getTabbables } from './core/focus-trap.js';
4
+ export { lockScroll, scrollLockDepth, unlockScroll } from './core/scroll-lock.js';
5
+ export { activateOnKey, announce, createSrOnly, politenessForTone, setAccessibleName } from './core/a11y.js';
6
+ export { DbStringKey, DbStrings, dbResetLocale, dbSetLocale, labelFrom, t } from './core/i18n.js';
7
+ export { DbElement } from './core/base-element.js';
1
8
  export { DbButton } from './components/db-button.js';
2
9
  export { DbInput } from './components/db-input.js';
3
10
  export { DbCheckbox } from './components/db-checkbox.js';
@@ -39,6 +46,7 @@ export { DbShare, DbSharePlatform, DbShareVariant } from './components/db-share.
39
46
  export { DbTopBanner } from './components/db-top-banner.js';
40
47
  export { DbCookieBanner } from './components/db-cookie-banner.js';
41
48
  export { DbStat, DbStatChange } from './components/db-stat.js';
49
+ export { DbActivityHeatmap, DbActivityHeatmapCellDetail, DbActivityHeatmapItem } from './components/db-activity-heatmap.js';
42
50
  export { DbImage } from './components/db-image.js';
43
51
  export { DbImagePlaceholder } from './components/db-image-placeholder.js';
44
52
  export { DbImageList, DbImageListItem } from './components/db-image-list.js';
@@ -119,12 +127,13 @@ export { DbChatOrb } from './components/db-chat-orb.js';
119
127
  export { DbChatWelcome } from './components/db-chat-welcome.js';
120
128
  export { DbChatAttachOption, DbChatComposer, DbChatTool } from './components/db-chat-composer.js';
121
129
  export { DbChatSuggestion, DbChatSuggestions } from './components/db-chat-suggestions.js';
122
- export { DbChatMessage } from './components/db-chat-message.js';
130
+ export { DbChatMessage, DbChatMessageAttachment } from './components/db-chat-message.js';
123
131
  export { DbChatTyping } from './components/db-chat-typing.js';
124
- export { DbChatWidget, DbChatWidgetConversation, DbChatWidgetMessage } from './components/db-chat-widget.js';
132
+ export { DbChatWidget, DbChatWidgetArticle, DbChatWidgetConversation, DbChatWidgetMenuAction, DbChatWidgetMenuItem, DbChatWidgetMessage } from './components/db-chat-widget.js';
125
133
  export { DbAiChat, DbArtifactOptions, DbChatArtifactRef, DbChatMessageData } from './components/db-ai-chat.js';
126
134
  export { DbAiChatItem, DbAiChatList } from './components/db-ai-chat-list.js';
127
135
  export { DbChatList, DbChatListItem } from './components/db-chat-list.js';
128
136
  export { DbChatRoom, DbChatRoomMessage } from './components/db-chat-room.js';
129
- export { DbToastApi, DbToastHandle, DbToastOptions, DbToastPosition, DbToastStatus, toast as dbToast, showToast, toast } from './helpers/toast.js';
137
+ export { DbToastApi, DbToastHandle, DbToastOptions, toast as dbToast, showToast, toast } from './helpers/toast.js';
130
138
  export { DbConfirmOptions, DbConfirmVariant, confirm, confirm as dbConfirm } from './helpers/confirm.js';
139
+ export { ToastPosition as DbToastPosition, ToastVariant as DbToastVariant } from '@designbasekorea/ui-contracts';