@designbasekorea/ui-wc 0.9.4 → 0.10.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 (55) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +2 -0
  3. package/dist/esm/components/db-accordion.d.ts +22 -10
  4. package/dist/esm/components/db-accordion.js +1 -1
  5. package/dist/esm/components/db-avatar.js +1 -1
  6. package/dist/esm/components/db-button.d.ts +33 -1
  7. package/dist/esm/components/db-button.js +1 -1
  8. package/dist/esm/components/db-chat-orb.d.ts +13 -0
  9. package/dist/esm/components/db-chat-orb.js +1 -1
  10. package/dist/esm/components/db-chat-room.js +1 -1
  11. package/dist/esm/components/db-chat-widget.js +1 -1
  12. package/dist/esm/components/db-checkbox.d.ts +3 -0
  13. package/dist/esm/components/db-checkbox.js +1 -1
  14. package/dist/esm/components/db-confetti.d.ts +11 -5
  15. package/dist/esm/components/db-confetti.js +1 -1
  16. package/dist/esm/components/db-email-verify.d.ts +36 -7
  17. package/dist/esm/components/db-email-verify.js +1 -1
  18. package/dist/esm/components/db-floating-action-button.d.ts +1 -1
  19. package/dist/esm/components/db-floating-action-button.js +1 -1
  20. package/dist/esm/components/db-input.d.ts +8 -1
  21. package/dist/esm/components/db-input.js +1 -1
  22. package/dist/esm/components/db-navbar.d.ts +3 -0
  23. package/dist/esm/components/db-navbar.js +1 -1
  24. package/dist/esm/components/db-otp-input.js +1 -1
  25. package/dist/esm/components/db-page-header.d.ts +11 -0
  26. package/dist/esm/components/db-page-header.js +1 -1
  27. package/dist/esm/components/db-phone-verify.d.ts +33 -11
  28. package/dist/esm/components/db-phone-verify.js +1 -1
  29. package/dist/esm/components/db-radio.d.ts +3 -0
  30. package/dist/esm/components/db-radio.js +1 -1
  31. package/dist/esm/components/db-range-slider.d.ts +35 -7
  32. package/dist/esm/components/db-range-slider.js +1 -1
  33. package/dist/esm/components/db-select.d.ts +35 -6
  34. package/dist/esm/components/db-select.js +1 -1
  35. package/dist/esm/components/db-tabs.d.ts +36 -13
  36. package/dist/esm/components/db-tabs.js +1 -1
  37. package/dist/esm/components/db-textarea.d.ts +13 -1
  38. package/dist/esm/components/db-textarea.js +1 -1
  39. package/dist/esm/components/db-toggle.d.ts +33 -4
  40. package/dist/esm/components/db-toggle.js +1 -1
  41. package/dist/esm/components/db-two-factor.d.ts +35 -12
  42. package/dist/esm/components/db-two-factor.js +1 -1
  43. package/dist/esm/core/control-label.js +1 -0
  44. package/dist/esm/core/i18n-copy.js +1 -1
  45. package/dist/esm/core/range-values.js +1 -0
  46. package/dist/esm/core/text-field-state.js +1 -0
  47. package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -1
  48. package/dist/index.cjs +1 -1
  49. package/dist/index.css +1 -1
  50. package/dist/index.d.ts +362 -77
  51. package/dist/index.esm.js +1 -1
  52. package/dist/react/index.cjs +1 -1
  53. package/dist/react/index.d.ts +5431 -634
  54. package/dist/react/index.esm.js +1 -1
  55. package/package.json +7 -3
@@ -1 +1 @@
1
- import{dbFormat as t}from"../core/i18n.js";import{dbUid as e}from"../core/uid.js";import{coerceToContract as i}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{OTP_INPUT_SIZES as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbOtpInput extends HTMLElement{constructor(){super(...arguments),this.internals=this.attachInternals?.(),this.built=!1,this.digits=[],this.suppressSync=!1,this.initialValue="",this.lastCompleteValue="",this.onDigitFocus=t=>{const e=t.currentTarget;requestAnimationFrame(()=>e.select())},this.onDigitInput=t=>{if(this.suppressSync)return;const e=t.currentTarget,i=Number(e.dataset.index||0),s=e.value.replace(/\D/g,"");if(s.length>1){const t=this.digits.slice(0,i).map(t=>t.value).join("");return void this.applyValue(t+s,Math.min(this.length-1,t.length+s.length))}e.value=s.slice(-1),e.classList.toggle("designbase-wc-otp-input__digit--filled",Boolean(e.value));const a=this.digits.map(t=>t.value).join("");this.setAttribute("value",a),this.emit("db-input"),this.emit("db-change"),e.value&&i<this.digits.length-1&&this.focusAt(i+1)},this.onDigitKeydown=t=>{const e=t.currentTarget,i=Number(e.dataset.index||0);if("Backspace"!==t.key)return"ArrowLeft"===t.key?(t.preventDefault(),void this.focusAt(i-1)):"ArrowRight"===t.key?(t.preventDefault(),void this.focusAt(i+1)):void("Delete"===t.key&&(e.value="",e.classList.remove("designbase-wc-otp-input__digit--filled"),this.emit("db-input"),this.emit("db-change")));if(e.value)e.value="",e.classList.remove("designbase-wc-otp-input__digit--filled"),this.emit("db-input"),this.emit("db-change");else if(i>0){t.preventDefault(),this.focusAt(i-1);const e=this.digits[i-1];e&&(e.value="",e.classList.remove("designbase-wc-otp-input__digit--filled"),this.emit("db-input"),this.emit("db-change"))}},this.onPaste=t=>{t.preventDefault();const e=t.clipboardData?.getData("text")||"",i=Number(t.currentTarget.dataset.index||0),s=this.digits.slice(0,i).map(t=>t.value).join("");this.applyValue(s+e,Math.min(this.length-1,s.length+e.replace(/\D/g,"").length))}}static get observedAttributes(){return["length","value","size","disabled","readonly","error","error-message","label","auto-focus","masked","name","required"]}get length(){const t=Number(this.getAttribute("length")||6);return Number.isFinite(t)?Math.min(8,Math.max(4,Math.round(t))):6}set length(t){this.setAttribute("length",String(t))}get value(){return this.digits.map(t=>t.value).join("")||this.getAttribute("value")||""}set value(t){this.setAttribute("value",t)}get size(){return i(this.getAttribute("size"),s,"m")}set size(t){this.setAttribute("size",t)}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",t)}connectedCallback(){this.built||(this.initialValue=this.getAttribute("value")||""),this.build(),this.sync(),this.hasAttribute("auto-focus")&&!this.disabled&&requestAnimationFrame(()=>this.focusFirstEmpty())}attributeChangedCallback(t){this.built&&("length"===t&&(this.built=!1,this.build()),this.sync())}focus(){this.disabled||this.focusFirstEmpty()}clear(){this.suppressSync=!0,this.digits.forEach(t=>{t.value=""}),this.suppressSync=!1,this.lastCompleteValue="",this.setValueAttribute(""),this.emit("db-input"),this.emit("db-change"),this.focusFirstEmpty()}formResetCallback(){this.lastCompleteValue="",this.value=this.initialValue}formDisabledCallback(t){this.toggleAttribute("disabled",t)}build(){this.replaceChildren(),this.digits=[];const i=this.length,s="designbase-wc-otp-input",a=this.getAttribute("label");if(a){const t=el("div",`${s}__label`);t.textContent=a,this.append(t)}const r=el("div",`${s}__row`);r.setAttribute("role","group"),r.setAttribute("aria-label",a||"인증 코드");for(let e=0;e<i;e+=1){const i=el("div",`${s}__cell`),a=document.createElement("input");a.className=`${s}__digit`,a.type=this.hasAttribute("masked")?"password":"text",a.inputMode="numeric",a.autocomplete=0===e?"one-time-code":"off",a.maxLength=1,a.pattern="[0-9]*",a.setAttribute("aria-label",t("{0}번째 자리",[e+1])),a.dataset.index=String(e),a.addEventListener("input",this.onDigitInput),a.addEventListener("keydown",this.onDigitKeydown),a.addEventListener("paste",this.onPaste),a.addEventListener("focus",this.onDigitFocus),i.append(a),r.append(i),this.digits.push(a)}this.append(r),this.errorEl=el("p",`${s}__error`),this.errorEl.id=`${this.id||e("db-otp")}-error`,this.errorEl.setAttribute("role","alert"),this.errorEl.hidden=!0,this.append(this.errorEl),this.built=!0}sync(){const t="designbase-wc-otp-input",e=this.size,i=this.hasAttribute("error"),s=this.disabled,a=this.readOnly,r=this.getAttribute("error-message")||"";this.className=[t,`${t}--${e}`,i&&`${t}--error`,s&&`${t}--disabled`].filter(Boolean).join(" ");const l=(this.getAttribute("value")||"").replace(/\D/g,"").slice(0,this.length);this.suppressSync=!0,this.digits.forEach((e,n)=>{e.value=l[n]||"",e.disabled=s,e.readOnly=a,e.type=this.hasAttribute("masked")?"password":"text",e.classList.toggle(`${t}__digit--filled`,Boolean(e.value)),e.setAttribute("aria-invalid",String(i)),i&&r?e.setAttribute("aria-describedby",this.errorEl?.id||""):e.removeAttribute("aria-describedby")}),this.suppressSync=!1,this.errorEl&&(this.errorEl.textContent=r,this.errorEl.hidden=!i||!r),this.setAttribute("role","group"),this.setAttribute("aria-disabled",String(s)),this.setAttribute("aria-invalid",String(i)),this.syncFormValue(l)}emit(t){const e=this.digits.map(t=>t.value).join("");if(this.setValueAttribute(e),this.dispatchEvent(new CustomEvent(t,{detail:{value:e},bubbles:!0,composed:!0})),"db-change"===t&&e.length===this.length&&/^\d+$/.test(e)){if(e===this.lastCompleteValue)return;this.lastCompleteValue=e,this.dispatchEvent(new CustomEvent("db-complete",{detail:{value:e},bubbles:!0,composed:!0}))}else e.length!==this.length&&(this.lastCompleteValue="")}setValueAttribute(t){this.getAttribute("value")!==t&&this.setAttribute("value",t),this.syncFormValue(t)}syncFormValue(t){this.internals?.setFormValue(t||null),this.internals&&(this.hasAttribute("error")?this.internals.setValidity({customError:!0},this.getAttribute("error-message")||"인증 코드가 올바르지 않습니다.",this.digits[0]):this.hasAttribute("required")&&t.length!==this.length?this.internals.setValidity({valueMissing:!0},"인증 코드를 모두 입력해 주세요.",this.digits.find(t=>!t.value)||this.digits[0]):this.internals.setValidity({}))}focusFirstEmpty(){const t=this.digits.find(t=>!t.value)||this.digits[this.digits.length-1];t?.focus(),t?.select()}focusAt(t){const e=this.digits[Math.max(0,Math.min(this.digits.length-1,t))];e?.focus(),e?.select()}applyValue(t,e){const i=t.replace(/\D/g,"").slice(0,this.length);this.suppressSync=!0,this.digits.forEach((t,e)=>{t.value=i[e]||"",t.classList.toggle("designbase-wc-otp-input__digit--filled",Boolean(t.value))}),this.suppressSync=!1,this.emit("db-input"),this.emit("db-change"),void 0!==e?this.focusAt(e):i.length<this.length?this.focusAt(i.length):this.focusAt(this.length-1)}}DbOtpInput.formAssociated=!0,customElements.get("db-otp-input")||customElements.define("db-otp-input",DbOtpInput);export{DbOtpInput};
1
+ import{dbFormat as t}from"../core/i18n.js";import{dbUid as e}from"../core/uid.js";import{coerceToContract as i}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{OTP_INPUT_SIZES as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbOtpInput extends HTMLElement{constructor(){super(...arguments),this.internals=this.attachInternals?.(),this.built=!1,this.digits=[],this.suppressSync=!1,this.initialValue="",this.lastCompleteValue="",this.onDigitFocus=t=>{const e=t.currentTarget;requestAnimationFrame(()=>e.select())},this.onDigitInput=t=>{if(this.suppressSync)return;const e=t.currentTarget,i=Number(e.dataset.index||0),s=e.value.replace(/\D/g,"");if(s.length>1){const t=this.digits.slice(0,i).map(t=>t.value).join("");return void this.applyValue(t+s,Math.min(this.length-1,t.length+s.length))}e.value=s.slice(-1),e.classList.toggle("designbase-wc-otp-input__digit--filled",Boolean(e.value));const a=this.digits.map(t=>t.value).join("");this.setAttribute("value",a),this.emit("db-input"),this.emit("db-change"),e.value&&i<this.digits.length-1&&this.focusAt(i+1)},this.onDigitKeydown=t=>{const e=t.currentTarget,i=Number(e.dataset.index||0);if("Backspace"!==t.key)return"ArrowLeft"===t.key?(t.preventDefault(),void this.focusAt(i-1)):"ArrowRight"===t.key?(t.preventDefault(),void this.focusAt(i+1)):void("Delete"===t.key&&(e.value="",e.classList.remove("designbase-wc-otp-input__digit--filled"),this.emit("db-input"),this.emit("db-change")));if(e.value)e.value="",e.classList.remove("designbase-wc-otp-input__digit--filled"),this.emit("db-input"),this.emit("db-change");else if(i>0){t.preventDefault(),this.focusAt(i-1);const e=this.digits[i-1];e&&(e.value="",e.classList.remove("designbase-wc-otp-input__digit--filled"),this.emit("db-input"),this.emit("db-change"))}},this.onPaste=t=>{t.preventDefault();const e=t.clipboardData?.getData("text")||"",i=Number(t.currentTarget.dataset.index||0),s=this.digits.slice(0,i).map(t=>t.value).join("");this.applyValue(s+e,Math.min(this.length-1,s.length+e.replace(/\D/g,"").length))}}static get observedAttributes(){return["length","value","size","disabled","readonly","error","error-message","label","auto-focus","masked","name","required","full-width"]}get length(){const t=Number(this.getAttribute("length")||6);return Number.isFinite(t)?Math.min(8,Math.max(4,Math.round(t))):6}set length(t){this.setAttribute("length",String(t))}get value(){return this.digits.map(t=>t.value).join("")||this.getAttribute("value")||""}set value(t){this.setAttribute("value",t)}get size(){return i(this.getAttribute("size"),s,"m")}set size(t){this.setAttribute("size",t)}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",t)}connectedCallback(){this.built||(this.initialValue=this.getAttribute("value")||""),this.build(),this.sync(),this.hasAttribute("auto-focus")&&!this.disabled&&requestAnimationFrame(()=>this.focusFirstEmpty())}attributeChangedCallback(t){this.built&&("length"===t&&(this.built=!1,this.build()),this.sync())}focus(){this.disabled||this.focusFirstEmpty()}clear(){this.suppressSync=!0,this.digits.forEach(t=>{t.value=""}),this.suppressSync=!1,this.lastCompleteValue="",this.setValueAttribute(""),this.emit("db-input"),this.emit("db-change"),this.focusFirstEmpty()}formResetCallback(){this.lastCompleteValue="",this.value=this.initialValue}formDisabledCallback(t){this.toggleAttribute("disabled",t)}build(){this.replaceChildren(),this.digits=[];const i=this.length,s="designbase-wc-otp-input",a=this.getAttribute("label");if(a){const t=el("div",`${s}__label`);t.textContent=a,this.append(t)}const l=el("div",`${s}__row`);l.setAttribute("role","group"),l.setAttribute("aria-label",a||"인증 코드");for(let e=0;e<i;e+=1){const i=el("div",`${s}__cell`),a=document.createElement("input");a.className=`${s}__digit`,a.type=this.hasAttribute("masked")?"password":"text",a.inputMode="numeric",a.autocomplete=0===e?"one-time-code":"off",a.maxLength=1,a.pattern="[0-9]*",a.setAttribute("aria-label",t("{0}번째 자리",[e+1])),a.dataset.index=String(e),a.addEventListener("input",this.onDigitInput),a.addEventListener("keydown",this.onDigitKeydown),a.addEventListener("paste",this.onPaste),a.addEventListener("focus",this.onDigitFocus),i.append(a),l.append(i),this.digits.push(a)}this.append(l),this.errorEl=el("p",`${s}__error`),this.errorEl.id=`${this.id||e("db-otp")}-error`,this.errorEl.setAttribute("role","alert"),this.errorEl.hidden=!0,this.append(this.errorEl),this.built=!0}sync(){const t="designbase-wc-otp-input",e=this.size,i=this.hasAttribute("error"),s=this.disabled,a=this.readOnly,l=this.getAttribute("error-message")||"";this.className=[t,`${t}--${e}`,i&&`${t}--error`,s&&`${t}--disabled`,this.hasAttribute("full-width")&&`${t}--full-width`].filter(Boolean).join(" ");const r=(this.getAttribute("value")||"").replace(/\D/g,"").slice(0,this.length);this.suppressSync=!0,this.digits.forEach((e,n)=>{e.value=r[n]||"",e.disabled=s,e.readOnly=a,e.type=this.hasAttribute("masked")?"password":"text",e.classList.toggle(`${t}__digit--filled`,Boolean(e.value)),e.setAttribute("aria-invalid",String(i)),i&&l?e.setAttribute("aria-describedby",this.errorEl?.id||""):e.removeAttribute("aria-describedby")}),this.suppressSync=!1,this.errorEl&&(this.errorEl.textContent=l,this.errorEl.hidden=!i||!l),this.setAttribute("role","group"),this.setAttribute("aria-disabled",String(s)),this.setAttribute("aria-invalid",String(i)),this.syncFormValue(r)}emit(t){const e=this.digits.map(t=>t.value).join("");if(this.setValueAttribute(e),this.dispatchEvent(new CustomEvent(t,{detail:{value:e},bubbles:!0,composed:!0})),"db-change"===t&&e.length===this.length&&/^\d+$/.test(e)){if(e===this.lastCompleteValue)return;this.lastCompleteValue=e,this.dispatchEvent(new CustomEvent("db-complete",{detail:{value:e},bubbles:!0,composed:!0}))}else e.length!==this.length&&(this.lastCompleteValue="")}setValueAttribute(t){this.getAttribute("value")!==t&&this.setAttribute("value",t),this.syncFormValue(t)}syncFormValue(t){this.internals?.setFormValue(t||null),this.internals&&(this.hasAttribute("error")?this.internals.setValidity({customError:!0},this.getAttribute("error-message")||"인증 코드가 올바르지 않습니다.",this.digits[0]):this.hasAttribute("required")&&t.length!==this.length?this.internals.setValidity({valueMissing:!0},"인증 코드를 모두 입력해 주세요.",this.digits.find(t=>!t.value)||this.digits[0]):this.internals.setValidity({}))}focusFirstEmpty(){const t=this.digits.find(t=>!t.value)||this.digits[this.digits.length-1];t?.focus(),t?.select()}focusAt(t){const e=this.digits[Math.max(0,Math.min(this.digits.length-1,t))];e?.focus(),e?.select()}applyValue(t,e){const i=t.replace(/\D/g,"").slice(0,this.length);this.suppressSync=!0,this.digits.forEach((t,e)=>{t.value=i[e]||"",t.classList.toggle("designbase-wc-otp-input__digit--filled",Boolean(t.value))}),this.suppressSync=!1,this.emit("db-input"),this.emit("db-change"),void 0!==e?this.focusAt(e):i.length<this.length?this.focusAt(i.length):this.focusAt(this.length-1)}}DbOtpInput.formAssociated=!0,customElements.get("db-otp-input")||customElements.define("db-otp-input",DbOtpInput);export{DbOtpInput};
@@ -14,7 +14,18 @@ declare class DbPageHeader extends HTMLElement {
14
14
  private stickyMarker?;
15
15
  private stickyObserver?;
16
16
  private compact;
17
+ private motionArmed;
18
+ private stickySpacer?;
19
+ private sizeObserver?;
20
+ private expandedHeight;
21
+ private motionFrame?;
22
+ private settling;
23
+ private updateStickySpace;
24
+ private stickyOffset;
25
+ private scrollRoot?;
26
+ private releaseAnchor?;
17
27
  private refreshSticky;
28
+ private refreshOffset;
18
29
  private observeSticky;
19
30
  private setCompact;
20
31
  connectedCallback(): void;
@@ -1 +1 @@
1
- import{sanitizeHref as e}from"../core/safe-url.js";import"./db-badge.js";import{PAGE_HEADER_VARIANTS as t,PAGE_HEADER_ALIGNS as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbPageHeader extends HTMLElement{constructor(){super(...arguments),this.internalTabId="",this.isRendering=!1,this.compact=!1,this.refreshSticky=()=>this.observeSticky(),this.handleBack=()=>{const t=new CustomEvent("db-back",{bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(t),t.defaultPrevented||function navigatePageHeaderBack(t="/"){if("undefined"==typeof window)return;const s=window.history.state?.idx;if("number"==typeof s&&s>0)return void window.history.back();(()=>{try{return!!document.referrer&&new URL(document.referrer).origin===window.location.origin}catch{return!1}})()||window.history.length>1?window.history.back():window.location.assign(e(t,"/"))}(this.getAttribute("back-fallback-href")||"/")},this.handleTabChange=e=>{const t=e.detail,s=t?.selectedId??this.parseTabs()[t?.index??0]?.id??String(t?.index??0);this.hasAttribute("selected-tab-id")||(this.internalTabId=s),this.dispatchEvent(new CustomEvent("db-tab-change",{bubbles:!0,composed:!0,detail:{tabId:s}}))}}static get observedAttributes(){return["title","subtitle","description","breadcrumbs","variant","show-back-button","back-fallback-href","back-label","tabs","selected-tab-id","default-selected-tab-id","align","sticky","compact-on-scroll","background","count"]}observeSticky(){if(this.stickyObserver?.disconnect(),!this.hasAttribute("sticky")||!this.hasAttribute("compact-on-scroll"))return this.stickyMarker?.remove(),this.stickyMarker=void 0,void this.setCompact(!1);this.stickyMarker||(this.stickyMarker=document.createElement("span"),this.stickyMarker.setAttribute("aria-hidden","true"),this.stickyMarker.style.cssText="display:block;height:1px;margin-bottom:-1px;pointer-events:none;overflow-anchor:none",this.before(this.stickyMarker));let e=this.parentElement;for(;e&&e!==document.body&&e!==document.documentElement&&!/(auto|scroll)/.test(getComputedStyle(e).overflowY);)e=e.parentElement;e!==document.body&&e!==document.documentElement||(e=null);const t=parseFloat(getComputedStyle(this).top)||0;this.stickyObserver=new IntersectionObserver(([e])=>{this.setCompact(!e.isIntersecting&&e.boundingClientRect.top<(e.rootBounds?.top??t))},{root:e,rootMargin:`-${t}px 0px 0px 0px`,threshold:0}),this.stickyObserver.observe(this.stickyMarker)}setCompact(e){this.compact=e,this.toggleAttribute("data-compact",e)}connectedCallback(){this.syncDefaultTab(),this.render(),this.observeSticky(),window.addEventListener("resize",this.refreshSticky),this.childObserver=new MutationObserver(()=>{this.isRendering||this.hasPendingSlottedChildren()&&this.render()}),this.childObserver.observe(this,{childList:!0})}disconnectedCallback(){this.childObserver?.disconnect(),this.stickyObserver?.disconnect(),this.stickyMarker?.remove(),this.stickyMarker=void 0,window.removeEventListener("resize",this.refreshSticky)}attributeChangedCallback(e){this.isConnected&&("default-selected-tab-id"!==e&&"tabs"!==e||this.syncDefaultTab(),this.render(),"sticky"!==e&&"compact-on-scroll"!==e||this.observeSticky())}hasPendingSlottedChildren(){return Array.from(this.children).some(e=>{const t=e.getAttribute("slot");return"actions"===t||"meta"===t||"description"===t||"leading"===t})}syncDefaultTab(){if(this.internalTabId)return;const e=this.parseTabs(),t=this.getAttribute("default-selected-tab-id");this.internalTabId=t||e[0]?.id||""}parseTabs(){try{const e=JSON.parse(this.getAttribute("tabs")||"[]");return Array.isArray(e)?e:[]}catch{return[]}}parseBreadcrumbs(){try{const e=JSON.parse(this.getAttribute("breadcrumbs")||"[]");return Array.isArray(e)?e:[]}catch{return[]}}getActiveTabId(){return this.getAttribute("selected-tab-id")||this.internalTabId}getVariant(){const e=this.getAttribute("variant")||"default";return t.includes(e)?e:"default"}getAlign(){const e=this.getAttribute("align")||"center";return s.includes(e)?e:"center"}collectLeading(){const e=Array.from(this.querySelectorAll(':scope > [slot="leading"]'));if(e.length)return e;const t=this.querySelector(".designbase-wc-page-header__leading");return t?.children.length?Array.from(t.children):[]}collectActions(){const e=Array.from(this.querySelectorAll(':scope > [slot="actions"]'));if(e.length)return e;const t=this.querySelector(".designbase-wc-page-header__cta");if(t?.children.length)return Array.from(t.children);const s=this.querySelector(".designbase-wc-page-header__actions");return s?.children.length?Array.from(s.children):[]}collectMeta(){const e=Array.from(this.querySelectorAll(':scope > [slot="meta"]'));if(e.length)return e;const t=this.querySelector(".designbase-wc-page-header__meta");return t?.children.length?Array.from(t.children):[]}renderMinimalAside(e,t){if(!e.length&&!t.length)return null;const s=document.createElement("div");if(s.className="designbase-wc-page-header__aside",e.length){const t=document.createElement("div");t.className="designbase-wc-page-header__meta",e.forEach(e=>t.append(e)),s.append(t)}if(t.length){const e=document.createElement("div");e.className="designbase-wc-page-header__cta",t.forEach(t=>e.append(t)),s.append(e)}return s}collectDescriptionSlot(){const e=this.querySelector(':scope > [slot="description"]');if(e)return e;const t=this.querySelector(":scope .designbase-wc-page-header__description"),s=t?.firstElementChild;return s||null}renderTitleBlock(e){const t=document.createDocumentFragment(),s=this.getAttribute("title")?.trim()||"",i=this.getAttribute("subtitle")?.trim()||"",n=this.getAttribute("description")?.trim()||"",r=Boolean(e&&(e.textContent?.trim()||e.children.length));if(s){const e=document.createElement("h1");if(e.className="designbase-wc-page-header__title",e.textContent=s,this.hasAttribute("count")){const t=document.createElement("db-badge");t.setAttribute("variant","secondary"),t.setAttribute("size","s"),t.setAttribute("aria-label",this.getAttribute("count")||"0"),t.textContent=this.getAttribute("count"),e.append(t)}t.append(e)}if(i){const e=document.createElement("h2");e.className="designbase-wc-page-header__subtitle",e.textContent=i,t.append(e)}if(r||n){const s=document.createElement("div");s.className="designbase-wc-page-header__description",r&&e?s.append(e):s.textContent=n,t.append(s)}return t}render(){this.isRendering=!0;const e=this.collectActions(),t=this.collectMeta(),s=this.collectLeading(),i=this.collectDescriptionSlot(),n="minimal"===this.getVariant(),r=this.getAlign(),a=this.parseBreadcrumbs(),c=this.parseTabs(),d=c.length>0,o=this.hasAttribute("show-back-button");if(this.className=["designbase-wc-page-header",n?"designbase-wc-page-header--minimal":`designbase-wc-page-header--${r}`,n&&d?"designbase-wc-page-header--has-tabs":""].filter(Boolean).join(" "),this.replaceChildren(),this.setCompact(this.compact),a.length){const e=document.createElement("div");e.className="designbase-wc-page-header__breadcrumbs";const t=document.createElement("db-breadcrumbs");t.setAttribute("size","s"),t.setAttribute("items",JSON.stringify(a)),e.append(t),this.append(e)}const l=document.createElement("div");l.className="designbase-wc-page-header__main";const h=document.createElement("div");if(h.className="designbase-wc-page-header__content",n&&(o||s.length)){const e=document.createElement("div");if(e.className="designbase-wc-page-header__title-row",s.length){const t=document.createElement("div");t.className="designbase-wc-page-header__leading",s.forEach(e=>t.append(e)),e.append(t)}if(o){const t=document.createElement("db-button");t.className="designbase-wc-page-header__back-button",t.setAttribute("variant","tertiary"),t.setAttribute("size","m"),t.setAttribute("icon-only",""),t.setAttribute("start-icon","chevron-left"),t.setAttribute("aria-label",this.getAttribute("back-label")||"뒤로가기"),t.setAttribute("tooltip",this.getAttribute("back-label")||"뒤로가기"),t.addEventListener("db-click",this.handleBack),e.append(t)}const t=document.createElement("div");t.className="designbase-wc-page-header__title-group",t.append(this.renderTitleBlock(i)),e.append(t),h.append(e)}else h.append(this.renderTitleBlock(i));if(l.append(h),n){const s=this.renderMinimalAside(t,e);s&&l.append(s)}if(this.append(l),!n&&e.length){const t=document.createElement("div");t.className="designbase-wc-page-header__actions",e.forEach(e=>t.append(e)),this.append(t)}if(!n&&t.length){const e=document.createElement("div");e.className="designbase-wc-page-header__meta",t.forEach(t=>e.append(t)),this.append(e)}this.renderMinimalTabs(n,d,c),queueMicrotask(()=>{this.isRendering=!1})}renderMinimalTabs(e,t,s){if(!e||!t)return;const i=document.createElement("div");i.className="designbase-wc-page-header__toolbar";const n=document.createElement("div");n.className="designbase-wc-page-header__tabs";const r=document.createElement("db-tabs");r.setAttribute("size","m"),r.setAttribute("items",JSON.stringify(s)),r.setAttribute("selected-id",this.getActiveTabId()),r.addEventListener("db-change",this.handleTabChange),n.append(r),i.append(n),this.append(i)}}customElements.get("db-page-header")||customElements.define("db-page-header",DbPageHeader);export{DbPageHeader};
1
+ import{sanitizeHref as e}from"../core/safe-url.js";import"./db-badge.js";import{PAGE_HEADER_VARIANTS as t,PAGE_HEADER_ALIGNS as i}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const s=new WeakMap;class DbPageHeader extends HTMLElement{constructor(){super(...arguments),this.internalTabId="",this.isRendering=!1,this.compact=!1,this.motionArmed=!1,this.expandedHeight=0,this.settling=!1,this.updateStickySpace=()=>{if(!this.stickySpacer)return;const e=this.getBoundingClientRect().height;this.compact||this.settling||(this.expandedHeight=e),this.stickySpacer.style.height=`${Math.max(0,this.expandedHeight-e)}px`},this.stickyOffset=0,this.refreshSticky=()=>this.observeSticky(),this.refreshOffset=()=>{this.stickyMarker&&(parseFloat(getComputedStyle(this).top)||0)!==this.stickyOffset&&this.observeSticky()},this.handleBack=()=>{const t=new CustomEvent("db-back",{bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(t),t.defaultPrevented||function navigatePageHeaderBack(t="/"){if("undefined"==typeof window)return;const i=window.history.state?.idx;if("number"==typeof i&&i>0)return void window.history.back();(()=>{try{return!!document.referrer&&new URL(document.referrer).origin===window.location.origin}catch{return!1}})()||window.history.length>1?window.history.back():window.location.assign(e(t,"/"))}(this.getAttribute("back-fallback-href")||"/")},this.handleTabChange=e=>{const t=e.detail,i=t?.selectedId??this.parseTabs()[t?.index??0]?.id??String(t?.index??0);this.hasAttribute("selected-tab-id")||(this.internalTabId=i),this.dispatchEvent(new CustomEvent("db-tab-change",{bubbles:!0,composed:!0,detail:{tabId:i}}))}}static get observedAttributes(){return["title","subtitle","description","breadcrumbs","variant","show-back-button","back-fallback-href","back-label","tabs","selected-tab-id","default-selected-tab-id","align","sticky","compact-on-scroll","background","count"]}observeSticky(){if(this.stickyObserver?.disconnect(),!this.hasAttribute("sticky")||!this.hasAttribute("compact-on-scroll"))return this.stickyMarker?.remove(),this.stickyMarker=void 0,this.stickySpacer?.remove(),this.stickySpacer=void 0,this.sizeObserver?.disconnect(),void this.setCompact(!1);this.stickyMarker||(this.stickyMarker=document.createElement("span"),this.stickyMarker.setAttribute("aria-hidden","true"),this.stickyMarker.style.cssText="display:block;height:1px;margin-bottom:-1px;pointer-events:none;overflow-anchor:none",this.before(this.stickyMarker)),this.stickySpacer||(this.stickySpacer=document.createElement("span"),this.stickySpacer.setAttribute("aria-hidden","true"),this.stickySpacer.style.cssText="display:block;height:0;pointer-events:none;overflow-anchor:none",this.after(this.stickySpacer),this.expandedHeight=this.getBoundingClientRect().height,this.sizeObserver=new ResizeObserver(this.updateStickySpace),this.sizeObserver.observe(this));let e=this.parentElement;for(;e&&e!==document.body&&e!==document.documentElement&&!/(auto|scroll)/.test(getComputedStyle(e).overflowY);)e=e.parentElement;e!==document.body&&e!==document.documentElement||(e=null),this.scrollRoot=e||document.documentElement;const t=parseFloat(getComputedStyle(this).top)||0;this.stickyOffset=t,this.stickyObserver=new IntersectionObserver(([e])=>{this.setCompact(!e.isIntersecting&&e.boundingClientRect.top<(e.rootBounds?.top??t)),this.motionArmed||(this.motionArmed=!0,requestAnimationFrame(()=>requestAnimationFrame(()=>{this.isConnected&&this.motionArmed&&this.setAttribute("data-motion","")})))},{root:e,rootMargin:`-${t}px 0px 0px 0px`,threshold:0}),this.stickyObserver.observe(this.stickyMarker)}setCompact(e){if(this.compact===e)return;if(void 0!==this.motionFrame&&cancelAnimationFrame(this.motionFrame),this.releaseAnchor?.(),this.releaseAnchor=void 0,e&&!this.settling&&(this.expandedHeight=this.getBoundingClientRect().height),this.compact=e,this.settling=!!this.stickySpacer,this.settling&&(this.releaseAnchor=function holdScrollAnchor(e){let t=s.get(e);return t||(t={count:0,value:e.style.getPropertyValue("overflow-anchor"),priority:e.style.getPropertyPriority("overflow-anchor")},s.set(e,t),e.style.setProperty("overflow-anchor","none")),t.count++,()=>{const t=s.get(e);!t||--t.count>0||("none"===e.style.getPropertyValue("overflow-anchor")&&(t.value?e.style.setProperty("overflow-anchor",t.value,t.priority):e.style.removeProperty("overflow-anchor")),s.delete(e))}}(this.scrollRoot||document.documentElement)),this.toggleAttribute("data-compact",e),this.updateStickySpace(),!this.settling)return;let t=-1,i=0;const settle=()=>{this.updateStickySpace();const e=this.getBoundingClientRect().height,s=this.getAnimations({subtree:!0}).some(e=>e instanceof CSSTransition&&"running"===e.playState);i=!s&&Math.abs(e-t)<.1?i+1:0,t=e,i<2?this.motionFrame=requestAnimationFrame(settle):(this.motionFrame=void 0,this.settling=!1,this.updateStickySpace(),this.releaseAnchor?.(),this.releaseAnchor=void 0)};this.motionFrame=requestAnimationFrame(settle)}connectedCallback(){this.syncDefaultTab(),this.render(),this.observeSticky(),window.addEventListener("resize",this.refreshSticky),window.addEventListener("scroll",this.refreshOffset,!0),this.childObserver=new MutationObserver(()=>{this.isRendering||this.hasPendingSlottedChildren()&&this.render()}),this.childObserver.observe(this,{childList:!0})}disconnectedCallback(){this.childObserver?.disconnect(),this.stickyObserver?.disconnect(),this.sizeObserver?.disconnect(),void 0!==this.motionFrame&&cancelAnimationFrame(this.motionFrame),this.motionFrame=void 0,this.settling=!1,this.compact=!1,this.motionArmed=!1,this.removeAttribute("data-compact"),this.removeAttribute("data-motion"),this.releaseAnchor?.(),this.releaseAnchor=void 0,this.stickySpacer?.remove(),this.stickySpacer=void 0,this.stickyMarker?.remove(),this.stickyMarker=void 0,window.removeEventListener("resize",this.refreshSticky),window.removeEventListener("scroll",this.refreshOffset,!0)}attributeChangedCallback(e){this.isConnected&&("default-selected-tab-id"!==e&&"tabs"!==e||this.syncDefaultTab(),this.render(),"sticky"!==e&&"compact-on-scroll"!==e||this.observeSticky())}hasPendingSlottedChildren(){return Array.from(this.children).some(e=>{const t=e.getAttribute("slot");return"actions"===t||"meta"===t||"description"===t||"leading"===t})}syncDefaultTab(){if(this.internalTabId)return;const e=this.parseTabs(),t=this.getAttribute("default-selected-tab-id");this.internalTabId=t||e[0]?.id||""}parseTabs(){try{const e=JSON.parse(this.getAttribute("tabs")||"[]");return Array.isArray(e)?e:[]}catch{return[]}}parseBreadcrumbs(){try{const e=JSON.parse(this.getAttribute("breadcrumbs")||"[]");return Array.isArray(e)?e:[]}catch{return[]}}getActiveTabId(){return this.getAttribute("selected-tab-id")||this.internalTabId}getVariant(){const e=this.getAttribute("variant")||"default";return t.includes(e)?e:"default"}getAlign(){const e=this.getAttribute("align")||"center";return i.includes(e)?e:"center"}collectLeading(){const e=Array.from(this.querySelectorAll(':scope > [slot="leading"]'));if(e.length)return e;const t=this.querySelector(".designbase-wc-page-header__leading");return t?.children.length?Array.from(t.children):[]}collectActions(){const e=Array.from(this.querySelectorAll(':scope > [slot="actions"]'));if(e.length)return e;const t=this.querySelector(".designbase-wc-page-header__cta");if(t?.children.length)return Array.from(t.children);const i=this.querySelector(".designbase-wc-page-header__actions");return i?.children.length?Array.from(i.children):[]}collectMeta(){const e=Array.from(this.querySelectorAll(':scope > [slot="meta"]'));if(e.length)return e;const t=this.querySelector(".designbase-wc-page-header__meta");return t?.children.length?Array.from(t.children):[]}renderMinimalAside(e,t){if(!e.length&&!t.length)return null;const i=document.createElement("div");if(i.className="designbase-wc-page-header__aside",e.length){const t=document.createElement("div");t.className="designbase-wc-page-header__meta",e.forEach(e=>t.append(e)),i.append(t)}if(t.length){const e=document.createElement("div");e.className="designbase-wc-page-header__cta",t.forEach(t=>e.append(t)),i.append(e)}return i}collectDescriptionSlot(){const e=this.querySelector(':scope > [slot="description"]');if(e)return e;const t=this.querySelector(":scope .designbase-wc-page-header__description"),i=t?.firstElementChild;return i||null}renderTitleBlock(e){const t=document.createDocumentFragment(),i=this.getAttribute("title")?.trim()||"",s=this.getAttribute("subtitle")?.trim()||"",r=this.getAttribute("description")?.trim()||"",n=Boolean(e&&(e.textContent?.trim()||e.children.length));if(i){const e=document.createElement("h1");if(e.className="designbase-wc-page-header__title",e.textContent=i,this.hasAttribute("count")){const t=document.createElement("db-badge");t.setAttribute("variant","secondary"),t.setAttribute("size","s"),t.setAttribute("aria-label",this.getAttribute("count")||"0"),t.textContent=this.getAttribute("count"),e.append(t)}t.append(e)}if(s){const e=document.createElement("h2");e.className="designbase-wc-page-header__subtitle",e.textContent=s,t.append(e)}if(n||r){const i=document.createElement("div");i.className="designbase-wc-page-header__description",n&&e?i.append(e):i.textContent=r,t.append(i)}return t}render(){this.isRendering=!0;const e=this.collectActions(),t=this.collectMeta(),i=this.collectLeading(),s=this.collectDescriptionSlot(),r="minimal"===this.getVariant(),n=this.getAlign(),a=this.parseBreadcrumbs(),c=this.parseTabs(),o=c.length>0,d=this.hasAttribute("show-back-button");if(this.className=["designbase-wc-page-header",r?"designbase-wc-page-header--minimal":`designbase-wc-page-header--${n}`,r&&o?"designbase-wc-page-header--has-tabs":""].filter(Boolean).join(" "),this.replaceChildren(),this.setCompact(this.compact),a.length){const e=document.createElement("div");e.className="designbase-wc-page-header__breadcrumbs";const t=document.createElement("db-breadcrumbs");t.setAttribute("size","s"),t.setAttribute("items",JSON.stringify(a)),e.append(t),this.append(e)}const h=document.createElement("div");h.className="designbase-wc-page-header__main";const l=document.createElement("div");if(l.className="designbase-wc-page-header__content",r&&(d||i.length)){const e=document.createElement("div");if(e.className="designbase-wc-page-header__title-row",i.length){const t=document.createElement("div");t.className="designbase-wc-page-header__leading",i.forEach(e=>t.append(e)),e.append(t)}if(d){const t=document.createElement("db-button");t.className="designbase-wc-page-header__back-button",t.setAttribute("variant","tertiary"),t.setAttribute("size","m"),t.setAttribute("icon-only",""),t.setAttribute("start-icon","chevron-left"),t.setAttribute("aria-label",this.getAttribute("back-label")||"뒤로가기"),t.setAttribute("tooltip",this.getAttribute("back-label")||"뒤로가기"),t.addEventListener("db-click",this.handleBack),e.append(t)}const t=document.createElement("div");t.className="designbase-wc-page-header__title-group",t.append(this.renderTitleBlock(s)),e.append(t),l.append(e)}else l.append(this.renderTitleBlock(s));if(h.append(l),r){const i=this.renderMinimalAside(t,e);i&&h.append(i)}if(this.append(h),!r&&e.length){const t=document.createElement("div");t.className="designbase-wc-page-header__actions",e.forEach(e=>t.append(e)),this.append(t)}if(!r&&t.length){const e=document.createElement("div");e.className="designbase-wc-page-header__meta",t.forEach(t=>e.append(t)),this.append(e)}this.renderMinimalTabs(r,o,c),queueMicrotask(()=>{this.isRendering=!1})}renderMinimalTabs(e,t,i){if(!e||!t)return;const s=document.createElement("div");s.className="designbase-wc-page-header__toolbar";const r=document.createElement("div");r.className="designbase-wc-page-header__tabs";const n=document.createElement("db-tabs");n.setAttribute("size","m"),n.setAttribute("items",JSON.stringify(i)),n.setAttribute("selected-id",this.getActiveTabId()),n.addEventListener("db-change",this.handleTabChange),r.append(n),s.append(r),this.append(s)}}customElements.get("db-page-header")||customElements.define("db-page-header",DbPageHeader);export{DbPageHeader};
@@ -4,31 +4,44 @@ import './db-otp-input.js';
4
4
  import './db-select.js';
5
5
  import { PHONE_VERIFY_SIZES } from '../ui-contracts/dist/components.d.js';
6
6
 
7
- /**
8
- * ui-wc — db-phone-verify (수동 작성, 자동 동기화 대상 아님)
9
- *
10
- * 한국형 휴대폰 본인인증: 통신사 → 번호 → 인증번호 발송 → OTP + 타이머/재전송.
11
- */
12
-
13
7
  type Size = (typeof PHONE_VERIFY_SIZES)[number];
8
+ type Step = 'phone' | 'code' | 'verified';
14
9
  declare class DbPhoneVerify extends HTMLElement {
15
10
  static get observedAttributes(): string[];
16
11
  private built;
17
12
  private timerId;
13
+ /** 인증번호 유효 시간(초) */
18
14
  private remaining;
15
+ /** 재전송 버튼이 다시 열리기까지 남은 시간(초) */
16
+ private cooldownLeft;
17
+ private expired;
18
+ private closeBtn;
19
+ private headerEl;
20
+ private eyebrowEl;
21
+ private titleEl;
22
+ private descEl;
23
+ private formEl;
19
24
  private carrierSelect;
20
25
  private phoneInput;
21
26
  private sendBtn;
27
+ private codeBlock;
28
+ private codeLabelEl;
22
29
  private otpInput;
23
30
  private timerEl;
24
- private resendBtn;
31
+ private expiredEl;
25
32
  private verifyBtn;
26
- private codeBlock;
33
+ private resendHintEl;
34
+ private resendBtn;
35
+ private changeBtn;
36
+ private verifiedBlock;
37
+ private verifiedTitleEl;
38
+ private verifiedDescEl;
39
+ private doneBtn;
27
40
  private errorEl;
28
41
  get size(): Size;
29
42
  set size(v: Size);
30
- get step(): 'phone' | 'code' | 'verified';
31
- set step(v: 'phone' | 'code' | 'verified');
43
+ get step(): Step;
44
+ set step(v: Step);
32
45
  get phone(): string;
33
46
  set phone(v: string);
34
47
  get carrier(): string;
@@ -37,6 +50,9 @@ declare class DbPhoneVerify extends HTMLElement {
37
50
  set code(v: string);
38
51
  get countdown(): number;
39
52
  set countdown(v: number);
53
+ /** 재전송을 다시 누를 수 있기까지 기다리는 시간(초). 기본 30초. 0이면 바로 누를 수 있다. */
54
+ get resendCooldown(): number;
55
+ set resendCooldown(v: number);
40
56
  connectedCallback(): void;
41
57
  disconnectedCallback(): void;
42
58
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
@@ -46,13 +62,19 @@ declare class DbPhoneVerify extends HTMLElement {
46
62
  restartCountdown(): void;
47
63
  /** 인증 완료 상태로 전환합니다. */
48
64
  markVerified(): void;
65
+ /** 번호 입력 단계로 돌아갑니다 (입력한 코드는 지웁니다). */
66
+ editPhone(): void;
49
67
  private clearTimer;
50
- private startTimer;
68
+ private startTimers;
51
69
  private renderTimer;
70
+ private emit;
52
71
  private build;
53
72
  private sync;
54
73
  private isPhoneValid;
74
+ private requestSend;
55
75
  private requestVerify;
76
+ /** 번역 문장 안의 {0} 자리에 값을 굵게 넣는다 — 언어마다 어순이 달라 조각내 번역하지 않는다. */
77
+ private setTextWithValue;
56
78
  }
57
79
  declare global {
58
80
  interface HTMLElementTagNameMap {
@@ -1 +1 @@
1
- import{dbFormat as t,dbText as e,t as i}from"../core/i18n.js";import"./db-button.js";import"./db-input.js";import"./db-otp-input.js";import"./db-select.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{PHONE_VERIFY_SIZES as r}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const DEFAULT_CARRIERS=()=>[{value:"skt",label:"SKT"},{value:"kt",label:"KT"},{value:"lgu",label:"LG U+"},{value:"mvno",label:e("알뜰폰")}];function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}function formatPhone(t){const e=t.replace(/\D/g,"").slice(0,11);return e.length<=3?e:e.length<=7?`${e.slice(0,3)}-${e.slice(3)}`:`${e.slice(0,3)}-${e.slice(3,7)}-${e.slice(7)}`}class DbPhoneVerify extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0}static get observedAttributes(){return["carrier","phone","code","step","size","disabled","error","error-message","carrier-options","countdown","title","description","send-label","resend-label","verify-label","code-length","manual","sending","verifying","auto-submit"]}get size(){return s(this.getAttribute("size"),r,"m")}set size(t){this.setAttribute("size",t)}get step(){const t=this.getAttribute("step");return"code"===t||"verified"===t?t:"phone"}set step(t){this.setAttribute("step",t)}get phone(){return(this.getAttribute("phone")||"").replace(/\D/g,"")}set phone(t){this.setAttribute("phone",formatPhone(t))}get carrier(){return this.getAttribute("carrier")||"skt"}set carrier(t){this.setAttribute("carrier",t)}get code(){return this.getAttribute("code")||""}set code(t){this.setAttribute("code",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))}connectedCallback(){this.build(),this.sync(),"code"===this.step&&this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"step"===t&&e!==i&&("code"===this.step?this.startTimer(this.countdown):this.clearTimer()))}startCodeStep(){"code"===this.step?this.startTimer(this.countdown):this.step="code",requestAnimationFrame(()=>this.otpInput?.focus?.())}restartCountdown(){"code"===this.step&&this.startTimer(this.countdown)}markVerified(){this.step="verified",this.clearTimer()}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0}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 e=Math.max(0,Math.floor(this.remaining/60)),i=Math.max(0,this.remaining%60);this.timerEl.textContent=`${String(e).padStart(2,"0")}:${String(i).padStart(2,"0")}`,this.timerEl.setAttribute("aria-label",t("남은 시간 {0}분 {1}초",[e,i])),this.resendBtn.disabled=this.remaining>0||this.hasAttribute("disabled")}build(){if(this.built)return;this.built=!0;const t="designbase-wc-phone-verify";this.replaceChildren();const s=el("div",`${t}__header`),r=el("h3",`${t}__title`);r.dataset.role="title";const n=el("p",`${t}__description`);n.dataset.role="description",s.append(r,n);const o=el("div",`${t}__field`);this.carrierSelect=document.createElement("db-select"),this.carrierSelect.setAttribute("label","통신사"),this.carrierSelect.setAttribute("full-width",""),this.carrierSelect.setAttribute("placeholder",e("통신사 선택")),this.carrierSelect.addEventListener("db-change",t=>{t.stopPropagation();const e=t.detail?.value??this.carrierSelect.value;this.carrier=String(e||""),this.dispatchEvent(new CustomEvent("db-carrier-change",{detail:{carrier:this.carrier},bubbles:!0,composed:!0}))}),o.append(this.carrierSelect);const h=el("div",`${t}__phone-row`);this.phoneInput=document.createElement("db-input"),this.phoneInput.setAttribute("type","tel"),this.phoneInput.setAttribute("label","휴대폰 번호"),this.phoneInput.setAttribute("placeholder","010-0000-0000"),this.phoneInput.setAttribute("full-width",""),this.phoneInput.setAttribute("start-icon","user"),this.phoneInput.addEventListener("db-input",t=>{t.stopPropagation();const e=formatPhone(String(t.detail?.value??this.phoneInput.value??""));this.phoneInput.value=e,this.setAttribute("phone",e),this.dispatchEvent(new CustomEvent("db-phone-change",{detail:{phone:e.replace(/\D/g,""),formatted:e},bubbles:!0,composed:!0}))}),this.sendBtn=document.createElement("db-button"),this.sendBtn.setAttribute("variant","primary"),this.sendBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.isPhoneValid()&&(this.dispatchEvent(new CustomEvent("db-send-code",{detail:{carrier:this.carrier,phone:this.phone},bubbles:!0,composed:!0})),this.hasAttribute("manual")||this.startCodeStep())}),h.append(this.phoneInput,this.sendBtn),this.codeBlock=el("div",`${t}__code`);const a=el("div",`${t}__code-head`),d=el("span",`${t}__code-label`);d.textContent=e("인증번호"),this.timerEl=el("span",`${t}__timer`),a.append(d,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,carrier:this.carrier,phone:this.phone},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()});const l=el("div",`${t}__code-actions`);this.resendBtn=el("button",`${t}__resend`),this.resendBtn.type="button",this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.dispatchEvent(new CustomEvent("db-resend",{detail:{carrier:this.carrier,phone:this.phone},bubbles:!0,composed:!0})),this.hasAttribute("manual")||this.startTimer(this.countdown))}),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()}),l.append(this.resendBtn,this.verifyBtn),this.codeBlock.append(a,this.otpInput,l),this.errorEl=el("p",`${t}__error`),this.errorEl.hidden=!0;const c=el("div",`${t}__verified`);c.dataset.role="verified";const u=el("span",`${t}__verified-badge`);u.textContent=i("verify.done");const p=document.createElement("p");p.className=`${t}__verified-text`,p.textContent=i("verify.phoneDone"),c.append(u,p),this.append(s,o,h,this.codeBlock,this.errorEl,c)}sync(){const t="designbase-wc-phone-verify",e=this.size,i=this.step,s=this.hasAttribute("disabled"),r=function parseCarriers(t){if(!t)return DEFAULT_CARRIERS();try{const e=JSON.parse(t);return Array.isArray(e)&&e.length?e:DEFAULT_CARRIERS()}catch{return DEFAULT_CARRIERS()}}(this.getAttribute("carrier-options")),n=this.getAttribute("code-length")||"6";this.className=[t,`${t}--${e}`,`${t}--step-${i}`,s&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" ");const o=this.querySelector('[data-role="title"]'),h=this.querySelector('[data-role="description"]');o.textContent=this.getAttribute("title")||"휴대폰 인증",h.textContent=this.getAttribute("description")||"본인 명의의 휴대폰 번호를 입력하고 인증번호를 받아 주세요.",this.carrierSelect.setAttribute("options",JSON.stringify(r)),this.carrierSelect.setAttribute("value",this.carrier),this.carrierSelect.setAttribute("size",e),this.carrierSelect.toggleAttribute("disabled",s||"verified"===i);const a=this.getAttribute("phone")||"";this.phoneInput.value=a,this.phoneInput.setAttribute("size",e),this.phoneInput.toggleAttribute("disabled",s||"phone"!==i),this.phoneInput.toggleAttribute("error",this.hasAttribute("error")&&"phone"===i),this.sendBtn.setAttribute("size",e),this.sendBtn.textContent=this.getAttribute("send-label")||"인증번호 받기",this.sendBtn.toggleAttribute("disabled",s||"phone"!==i||!this.isPhoneValid()),this.sendBtn.toggleAttribute("loading",this.hasAttribute("sending")),this.otpInput.setAttribute("length",n),this.otpInput.setAttribute("size",e),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",s||"code"!==i),this.otpInput.toggleAttribute("error",this.hasAttribute("error")&&"code"===i),this.resendBtn.textContent=this.getAttribute("resend-label")||"재전송",this.verifyBtn.setAttribute("size",e),this.verifyBtn.textContent=this.getAttribute("verify-label")||"인증하기",this.verifyBtn.toggleAttribute("disabled",s||(this.getAttribute("code")||"").length<Number(n)),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.codeBlock.hidden="phone"===i;this.querySelector('[data-role="verified"]').hidden="verified"!==i;const d=this.getAttribute("error-message")||"";this.errorEl.textContent=d,this.errorEl.hidden=!this.hasAttribute("error")||!d}isPhoneValid(){return/^01[016789]\d{7,8}$/.test(this.phone)&&Boolean(this.carrier)}requestVerify(){const t=(this.code||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);"code"!==this.step||t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{carrier:this.carrier,phone:this.phone,code:t},bubbles:!0,composed:!0}))}}customElements.get("db-phone-verify")||customElements.define("db-phone-verify",DbPhoneVerify);export{DbPhoneVerify};
1
+ import{dbFormat as t,dbText as e}from"../core/i18n.js";import{createDbIcon as i}from"./db-icons.js";import"./db-button.js";import"./db-input.js";import"./db-otp-input.js";import"./db-select.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{PHONE_VERIFY_SIZES as r}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const DEFAULT_CARRIERS=()=>[{value:"skt",label:"SKT"},{value:"kt",label:"KT"},{value:"lgu",label:"LG U+"},{value:"mvno",label:e("알뜰폰")}];function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}function textButton(t,e){const i=el("button",t);return i.type="button",i.addEventListener("click",()=>{i.disabled||e()}),i}function formatPhone(t){const e=t.replace(/\D/g,"").slice(0,11);return e.length<=3?e:e.length<=7?`${e.slice(0,3)}-${e.slice(3)}`:`${e.slice(0,3)}-${e.slice(3,7)}-${e.slice(7)}`}class DbPhoneVerify extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0,this.cooldownLeft=0,this.expired=!1}static get observedAttributes(){return["carrier","phone","code","step","size","disabled","error","error-message","carrier-options","countdown","resend-cooldown","eyebrow","title","description","send-label","resend-label","verify-label","done-label","code-length","closable","manual","sending","verifying","auto-submit"]}get size(){return s(this.getAttribute("size"),r,"m")}set size(t){this.setAttribute("size",t)}get step(){const t=this.getAttribute("step");return"code"===t||"verified"===t?t:"phone"}set step(t){this.setAttribute("step",t)}get phone(){return(this.getAttribute("phone")||"").replace(/\D/g,"")}set phone(t){this.setAttribute("phone",formatPhone(t))}get carrier(){return this.getAttribute("carrier")||"skt"}set carrier(t){this.setAttribute("carrier",t)}get code(){return this.getAttribute("code")||""}set code(t){this.setAttribute("code",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 resendCooldown(){const t=Number(this.getAttribute("resend-cooldown")??30);return Number.isFinite(t)&&t>=0?t:30}set resendCooldown(t){this.setAttribute("resend-cooldown",String(t))}connectedCallback(){this.build(),this.sync(),"code"===this.step&&this.startTimers()}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&("step"===t&&e!==i&&("code"===this.step?this.startTimers():this.clearTimer()),this.sync())}startCodeStep(){"code"===this.step?this.startTimers():this.step="code",requestAnimationFrame(()=>this.otpInput?.focus?.())}restartCountdown(){"code"===this.step&&this.startTimers()}markVerified(){this.step="verified"}editPhone(){this.removeAttribute("code"),this.step="phone",requestAnimationFrame(()=>this.phoneInput?.focus?.())}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0,this.cooldownLeft=0,this.expired=!1}startTimers(){this.clearTimer(),this.remaining=this.countdown,this.cooldownLeft=Math.min(this.resendCooldown,this.countdown),this.renderTimer(),this.timerId=setInterval(()=>{this.cooldownLeft>0&&(this.cooldownLeft-=1),this.remaining>0&&(this.remaining-=1,this.remaining<=0&&(this.expired=!0,this.cooldownLeft=0,this.emit("db-countdown-complete"))),this.renderTimer(),this.remaining<=0&&this.cooldownLeft<=0&&this.timerId&&(clearInterval(this.timerId),this.timerId=null)},1e3)}renderTimer(){if(!this.built)return;const i=Math.max(0,Math.floor(this.remaining/60)),s=Math.max(0,this.remaining%60);this.timerEl.textContent=`${String(i).padStart(2,"0")}:${String(s).padStart(2,"0")}`,this.timerEl.setAttribute("aria-label",t("남은 시간 {0}분 {1}초",[i,s])),this.timerEl.classList.toggle("designbase-wc-phone-verify__timer--expired",this.expired),this.expiredEl.hidden=!this.expired,this.otpInput.toggleAttribute("disabled",this.hasAttribute("disabled")||"code"!==this.step||this.expired);const r=this.cooldownLeft>0;this.resendBtn.textContent=r?t("재전송 ({0}초)",[this.cooldownLeft]):this.getAttribute("resend-label")||e("재전송"),this.resendBtn.disabled=r||this.hasAttribute("disabled");const n=Number(this.getAttribute("code-length")||6);this.verifyBtn.toggleAttribute("disabled",this.hasAttribute("disabled")||"code"!==this.step||this.expired||this.code.length!==n)}emit(t,e={}){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}build(){if(this.built)return;this.built=!0;const t="designbase-wc-phone-verify";this.replaceChildren();const s=el("div",`${t}__shell`);this.closeBtn=el("button",`${t}__close`),this.closeBtn.type="button",this.closeBtn.setAttribute("aria-label",e("닫기")),this.closeBtn.append(i("close",18)),this.closeBtn.addEventListener("click",()=>this.emit("db-close")),this.headerEl=el("div",`${t}__header`),this.eyebrowEl=el("span",`${t}__eyebrow`),this.titleEl=el("h3",`${t}__title`),this.descEl=el("p",`${t}__description`),this.headerEl.append(this.eyebrowEl,this.titleEl,this.descEl),this.formEl=el("div",`${t}__form`),this.carrierSelect=document.createElement("db-select"),this.carrierSelect.setAttribute("full-width",""),this.carrierSelect.addEventListener("db-change",t=>{t.stopPropagation();const e=t.detail?.value??this.carrierSelect.value;this.carrier=String(e||""),this.emit("db-carrier-change",{carrier:this.carrier})});const r=el("div",`${t}__phone-row`);this.phoneInput=document.createElement("db-input"),this.phoneInput.setAttribute("type","tel"),this.phoneInput.setAttribute("inputmode","numeric"),this.phoneInput.setAttribute("autocomplete","tel-national"),this.phoneInput.setAttribute("placeholder","010-0000-0000"),this.phoneInput.setAttribute("full-width",""),this.phoneInput.setAttribute("start-icon","mobile"),this.phoneInput.addEventListener("db-input",t=>{t.stopPropagation();const e=formatPhone(String(t.detail?.value??this.phoneInput.value??""));this.phoneInput.value=e,this.setAttribute("phone",e),this.emit("db-phone-change",{phone:e.replace(/\D/g,""),formatted:e})}),this.phoneInput.addEventListener("keydown",t=>{"Enter"===t.key&&this.requestSend()}),this.sendBtn=document.createElement("db-button"),this.sendBtn.setAttribute("variant","primary"),this.sendBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestSend()}),r.append(this.phoneInput,this.sendBtn),this.formEl.append(this.carrierSelect,r),this.codeBlock=el("div",`${t}__code`);const n=el("div",`${t}__code-head`);this.codeLabelEl=el("span",`${t}__code-label`),this.timerEl=el("span",`${t}__timer`),this.timerEl.setAttribute("role","timer"),n.append(this.codeLabelEl,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.setAttribute("full-width",""),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.emit("db-code-change",{code:e})}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.emit("db-code-complete",{code:e,carrier:this.carrier,phone:this.phone}),this.hasAttribute("auto-submit")&&this.requestVerify()}),this.expiredEl=el("p",`${t}__expired`),this.expiredEl.setAttribute("role","alert"),this.expiredEl.hidden=!0,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.resendBtn=textButton(`${t}__link`,()=>{this.emit("db-resend",{carrier:this.carrier,phone:this.phone}),this.hasAttribute("manual")||(this.removeAttribute("code"),this.startTimers())}),this.changeBtn=textButton(`${t}__link ${t}__link--muted`,()=>{this.emit("db-change-phone",{carrier:this.carrier,phone:this.phone}),this.hasAttribute("manual")||this.editPhone()});const h=el("div",`${t}__footer`),o=el("span",`${t}__footer-resend`);this.resendHintEl=el("span",`${t}__footer-hint`),o.append(this.resendHintEl,this.resendBtn),h.append(o,this.changeBtn),this.codeBlock.append(n,this.otpInput,this.expiredEl,this.verifyBtn,h),this.verifiedBlock=el("div",`${t}__result ${t}__result--success`);const d=el("div",`${t}__result-icon`);d.append(i("circle-checked",36)),this.verifiedTitleEl=el("h3",`${t}__result-title`),this.verifiedDescEl=el("p",`${t}__result-desc`),this.doneBtn=document.createElement("db-button"),this.doneBtn.setAttribute("variant","primary"),this.doneBtn.setAttribute("full-width",""),this.doneBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.emit("db-done",{carrier:this.carrier,phone:this.phone})}),this.verifiedBlock.append(d,this.verifiedTitleEl,this.verifiedDescEl,this.doneBtn),this.errorEl=el("p",`${t}__error`),this.errorEl.setAttribute("role","alert"),this.errorEl.hidden=!0,s.append(this.closeBtn,this.headerEl,this.formEl,this.errorEl,this.codeBlock,this.verifiedBlock),this.append(s)}sync(){const t="designbase-wc-phone-verify",i=this.size,s=this.step,r=this.hasAttribute("disabled"),n=function parseCarriers(t){if(!t)return DEFAULT_CARRIERS();try{const e=JSON.parse(t);return Array.isArray(e)&&e.length?e:DEFAULT_CARRIERS()}catch{return DEFAULT_CARRIERS()}}(this.getAttribute("carrier-options")),h=this.getAttribute("code-length")||"6",o=formatPhone(this.getAttribute("phone")||"")||"010-0000-0000";this.className=[t,`${t}--${i}`,`${t}--step-${s}`,this.hasAttribute("closable")&&`${t}--closable`,r&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" "),this.closeBtn.hidden=!this.hasAttribute("closable"),this.headerEl.hidden="verified"===s;const d=this.getAttribute("eyebrow")||"";this.eyebrowEl.textContent=d,this.eyebrowEl.hidden=!d,"code"===s?(this.titleEl.textContent=e("인증번호를 입력해 주세요"),this.setTextWithValue(this.descEl,"{0} 번호로 {1}자리 인증번호를 보냈어요.",o,[h])):(this.titleEl.textContent=this.getAttribute("title")||e("휴대폰 인증"),this.descEl.textContent=this.getAttribute("description")||e("본인 명의의 휴대폰 번호를 입력해 주세요. 입력한 번호로 인증번호를 보내 드려요.")),this.formEl.hidden="phone"!==s,this.carrierSelect.setAttribute("label",e("통신사")),this.carrierSelect.setAttribute("placeholder",e("통신사 선택")),this.carrierSelect.setAttribute("options",JSON.stringify(n)),this.carrierSelect.setAttribute("value",this.carrier),this.carrierSelect.setAttribute("size",i),this.carrierSelect.toggleAttribute("disabled",r),this.phoneInput.setAttribute("label",e("휴대폰 번호")),this.phoneInput.value=this.getAttribute("phone")||"",this.phoneInput.setAttribute("size",i),this.phoneInput.toggleAttribute("disabled",r),this.phoneInput.toggleAttribute("error",this.hasAttribute("error")&&"phone"===s),this.sendBtn.setAttribute("size",i),this.sendBtn.textContent=this.getAttribute("send-label")||e("인증번호 받기"),this.sendBtn.toggleAttribute("disabled",r||!this.isPhoneValid()),this.sendBtn.toggleAttribute("loading",this.hasAttribute("sending")),this.codeBlock.hidden="code"!==s,this.codeLabelEl.textContent=e("인증번호"),this.otpInput.setAttribute("length",h),this.otpInput.setAttribute("size",i),this.otpInput.setAttribute("value",this.code),this.otpInput.toggleAttribute("error",this.hasAttribute("error")&&"code"===s),this.expiredEl.textContent=e("인증 시간이 만료되었어요. 인증번호를 다시 받아 주세요."),this.verifyBtn.setAttribute("size",i),this.verifyBtn.textContent=this.getAttribute("verify-label")||e("인증하기"),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.resendHintEl.textContent=e("문자를 받지 못하셨나요?"),this.changeBtn.textContent=e("번호 변경"),this.changeBtn.disabled=r,this.verifiedBlock.hidden="verified"!==s,this.verifiedTitleEl.textContent=e("휴대폰 인증 완료"),this.setTextWithValue(this.verifiedDescEl,"{0} 번호가 인증되었어요.",o),this.doneBtn.setAttribute("size",i),this.doneBtn.textContent=this.getAttribute("done-label")||e("확인");const l=this.getAttribute("error-message")||"";this.errorEl.textContent=l,this.errorEl.hidden=!this.hasAttribute("error")||!l||"verified"===s;const a="code"===s?this.otpInput:this.formEl;this.errorEl.previousElementSibling!==a&&a.after(this.errorEl),this.renderTimer()}isPhoneValid(){return/^01[016789]\d{7,8}$/.test(this.phone)&&Boolean(this.carrier)}requestSend(){"phone"===this.step&&(this.hasAttribute("disabled")||this.hasAttribute("sending")||!this.isPhoneValid()||(this.emit("db-send-code",{carrier:this.carrier,phone:this.phone}),this.hasAttribute("manual")||this.startCodeStep()))}requestVerify(){const t=(this.code||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);"code"!==this.step||this.expired||t.length!==e||this.hasAttribute("disabled")||this.emit("db-verify",{carrier:this.carrier,phone:this.phone,code:t})}setTextWithValue(t,i,s,r=[]){const n=e(i).replace(/\{(\d+)\}/g,(t,e)=>"0"===e?t:String(r[Number(e)-1]??"")),[h,o=""]=n.split("{0}"),d=document.createElement("strong");d.textContent=s,t.replaceChildren(document.createTextNode(h),d,document.createTextNode(o))}}customElements.get("db-phone-verify")||customElements.define("db-phone-verify",DbPhoneVerify);export{DbPhoneVerify};
@@ -5,11 +5,14 @@ declare class DbRadio extends HTMLElement {
5
5
  private visual?;
6
6
  private dot?;
7
7
  private labelEl?;
8
+ private readonly labelContent;
8
9
  private built;
9
10
  private userInteracted;
10
11
  private resetChecked?;
11
12
  private readonly cleanups;
12
13
  focus(options?: FocusOptions): void;
14
+ get label(): string;
15
+ set label(value: string);
13
16
  get name(): string;
14
17
  set name(value: string);
15
18
  get value(): string;
@@ -1 +1 @@
1
- import{CleanupBag as t}from"../core/dom.js";import{coerceToContract as e}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{RADIO_SIZES as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbRadio 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 t=this.input.checked;this.removeAttribute("selected"),this.toggleAttribute("checked",t),this.syncGroup(),this.syncVisual(),this.syncClasses(),this.dispatchEvent(new CustomEvent("db-change",{detail:{checked:t,selected:t,value:this.input.value},bubbles:!0,composed:!0}))}}static get observedAttributes(){return["checked","selected","default-checked","default-selected","size","disabled","readonly","required","name","value","has-label","aria-label","aria-labelledby","aria-describedby"]}focus(t){this.input?.focus(t)}get name(){return this.getAttribute("name")||""}set name(t){this.setAttribute("name",t)}get value(){return this.getAttribute("value")??"on"}set value(t){this.setAttribute("value",t)}get defaultChecked(){return this.hasAttribute("default-checked")||this.hasAttribute("default-selected")}set defaultChecked(t){this.removeAttribute("default-selected"),this.toggleAttribute("default-checked",t)}get defaultSelected(){return this.defaultChecked}set defaultSelected(t){this.defaultChecked=t}upgradeProperties(){for(const t of["checked","selected","defaultChecked","defaultSelected","name","value","disabled","readOnly","required","hasLabel","size"]){if(!Object.prototype.hasOwnProperty.call(this,t))continue;const e=this,s=e[t];delete e[t],e[t]=s}}get checked(){return this.input?.checked??(this.hasAttribute("checked")||this.hasAttribute("selected"))}set checked(t){this.userInteracted=!0,this.removeAttribute("selected"),this.toggleAttribute("checked",t),this.input&&(this.input.checked=t,this.syncVisual(),this.syncClasses(),this.syncGroup())}get selected(){return this.checked}set selected(t){this.checked=t}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t),this.input&&(this.input.disabled=t),this.syncClasses()}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",t),this.input&&(this.input.readOnly=t),this.syncClasses()}get required(){return this.hasAttribute("required")}set required(t){this.toggleAttribute("required",t),this.input&&(this.input.required=t)}get size(){return e(this.getAttribute("size"),s,"m")}set size(t){this.setAttribute("size",t)}connectedCallback(){this.upgradeProperties(),this.style.display="contents",this.built||this.build(),this.syncState(),this.input?.form&&this.cleanups.listen(this.input.form,"reset",()=>{window.setTimeout(()=>{this.input&&this.isConnected&&(this.toggleAttribute("checked",this.input.checked),this.removeAttribute("selected"),this.syncClasses())},0)})}disconnectedCallback(){this.cleanups.run()}attributeChangedCallback(t){if(this.built){if("default-checked"!==t&&"default-selected"!==t||(this.resetChecked=this.hasAttribute("default-checked")||this.hasAttribute("default-selected")),"checked"===t||"selected"===t)return this.userInteracted=!0,this.input&&(this.input.checked=this.hasAttribute("checked")||this.hasAttribute("selected")),this.syncVisual(),this.syncClasses(),void this.syncGroup();this.syncState()}}get hasLabel(){return!this.hasAttribute("has-label")||"false"!==this.getAttribute("has-label")}set hasLabel(t){this.setAttribute("has-label",String(t))}build(){for(this.root=document.createElement("label"),this.root.className="designbase-wc-radio",this.input=document.createElement("input"),this.input.type="radio",this.input.className="designbase-wc-radio__input",this.visual=document.createElement("span"),this.visual.className="designbase-wc-radio__visual",this.visual.setAttribute("aria-hidden","true"),this.dot=document.createElement("span"),this.dot.className="designbase-wc-radio__dot",this.visual.appendChild(this.dot),this.labelEl=document.createElement("span"),this.labelEl.className="designbase-wc-radio__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",t=>{this.readOnly&&t.preventDefault()}),this.input.addEventListener("keydown",t=>{const e=["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"];if(this.readOnly&&(" "===t.key||e.includes(t.key)))return void t.preventDefault();const s=this.groupPeers();if(!e.includes(t.key)||!s.some(t=>t.readOnly))return;const i=s.filter(t=>!t.disabled&&!t.readOnly);if(!i.length)return;t.preventDefault();const r="rtl"===getComputedStyle(this).direction,a="ArrowDown"===t.key||t.key===(r?"ArrowLeft":"ArrowRight"),h=i[(i.indexOf(this)+(a?1:-1)+i.length)%i.length];h.input?.focus(),h.input?.click()}),this.input.addEventListener("change",this.handleChange),this.built=!0}groupPeers(){if(!this.input?.name)return[this];const t=this.getRootNode();return Array.from(t.querySelectorAll("db-radio")).filter(t=>t.input?.name===this.input.name&&t.input?.form===this.input.form)}syncGroup(){if(this.input?.checked&&this.input.name){const t=this.getRootNode();for(const e of t.querySelectorAll("db-radio"))e!==this&&e.input&&e.input.name===this.input.name&&e.input.form===this.input.form&&(e.userInteracted=!0,e.removeAttribute("checked"),e.removeAttribute("selected"),e.syncClasses())}}syncState(){if(!this.root||!this.input||!this.visual)return;const t=this.hasAttribute("checked")||this.hasAttribute("selected")||!this.userInteracted&&(this.hasAttribute("default-checked")||this.hasAttribute("default-selected"));this.resetChecked??(this.resetChecked=t),this.input.defaultChecked=this.resetChecked,this.input.checked=t,this.input.disabled=this.disabled,this.input.readOnly=this.readOnly,this.input.setAttribute("aria-disabled",String(this.disabled||this.readOnly)),this.input.required=this.required,this.input.name=this.getAttribute("name")||"",this.input.value=this.getAttribute("value")??"on";for(const t of["aria-label","aria-labelledby","aria-describedby"]){const e=this.getAttribute(t);e?this.input.setAttribute(t,e):this.input.removeAttribute(t)}this.hasLabel?this.labelEl?.removeAttribute("hidden"):this.labelEl?.setAttribute("hidden",""),this.syncVisual(),this.syncClasses()}syncVisual(){}syncClasses(){if(!this.root||!this.input)return;const t=s.includes(this.size)?this.size:"m",e=this.hasLabel&&Boolean(this.labelEl?.textContent?.trim());this.root.className=["designbase-wc-radio",`designbase-wc-radio--${t}`,this.input.checked&&"designbase-wc-radio--selected",this.disabled&&"designbase-wc-radio--disabled",this.readOnly&&"designbase-wc-radio--readonly",!e&&"designbase-wc-radio--no-label"].filter(Boolean).join(" ")}}customElements.get("db-radio")||customElements.define("db-radio",DbRadio);export{DbRadio};
1
+ import{ControlLabel as t}from"../core/control-label.js";import{CleanupBag as e}from"../core/dom.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{RADIO_SIZES as i}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbRadio extends HTMLElement{constructor(){super(...arguments),this.labelContent=new t(this),this.built=!1,this.userInteracted=!1,this.cleanups=new e,this.handleChange=()=>{if(!this.input||this.disabled||this.readOnly)return;this.userInteracted=!0;const t=this.input.checked;this.removeAttribute("selected"),this.toggleAttribute("checked",t),this.syncGroup(),this.syncVisual(),this.syncClasses(),this.dispatchEvent(new CustomEvent("db-change",{detail:{checked:t,selected:t,value:this.input.value},bubbles:!0,composed:!0}))}}static get observedAttributes(){return["checked","selected","default-checked","default-selected","size","disabled","readonly","required","name","value","label","has-label","aria-label","aria-labelledby","aria-describedby"]}focus(t){this.input?.focus(t)}get label(){return this.labelContent.value}set label(t){this.setAttribute("label",t)}get name(){return this.getAttribute("name")||""}set name(t){this.setAttribute("name",t)}get value(){return this.getAttribute("value")??"on"}set value(t){this.setAttribute("value",t)}get defaultChecked(){return this.hasAttribute("default-checked")||this.hasAttribute("default-selected")}set defaultChecked(t){this.removeAttribute("default-selected"),this.toggleAttribute("default-checked",t)}get defaultSelected(){return this.defaultChecked}set defaultSelected(t){this.defaultChecked=t}upgradeProperties(){for(const t of["checked","selected","defaultChecked","defaultSelected","name","value","label","disabled","readOnly","required","hasLabel","size"]){if(!Object.prototype.hasOwnProperty.call(this,t))continue;const e=this,s=e[t];delete e[t],e[t]=s}}get checked(){return this.input?.checked??(this.hasAttribute("checked")||this.hasAttribute("selected"))}set checked(t){this.userInteracted=!0,this.removeAttribute("selected"),this.toggleAttribute("checked",t),this.input&&(this.input.checked=t,this.syncVisual(),this.syncClasses(),this.syncGroup())}get selected(){return this.checked}set selected(t){this.checked=t}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t),this.input&&(this.input.disabled=t),this.syncClasses()}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",t),this.input&&(this.input.readOnly=t),this.syncClasses()}get required(){return this.hasAttribute("required")}set required(t){this.toggleAttribute("required",t),this.input&&(this.input.required=t)}get size(){return s(this.getAttribute("size"),i,"m")}set size(t){this.setAttribute("size",t)}connectedCallback(){this.upgradeProperties(),this.style.display="contents",this.built||this.build(),this.labelContent.connect(this.labelEl,()=>this.syncClasses()),this.syncState(),this.input?.form&&this.cleanups.listen(this.input.form,"reset",()=>{window.setTimeout(()=>{this.input&&this.isConnected&&(this.toggleAttribute("checked",this.input.checked),this.removeAttribute("selected"),this.syncClasses())},0)})}disconnectedCallback(){this.cleanups.run(),this.labelContent.disconnect()}attributeChangedCallback(t){if(this.built){if("default-checked"!==t&&"default-selected"!==t||(this.resetChecked=this.hasAttribute("default-checked")||this.hasAttribute("default-selected")),"checked"===t||"selected"===t)return this.userInteracted=!0,this.input&&(this.input.checked=this.hasAttribute("checked")||this.hasAttribute("selected")),this.syncVisual(),this.syncClasses(),void this.syncGroup();this.syncState()}}get hasLabel(){return!this.hasAttribute("has-label")||"false"!==this.getAttribute("has-label")}set hasLabel(t){this.setAttribute("has-label",String(t))}build(){for(this.root=document.createElement("label"),this.root.className="designbase-wc-radio",this.input=document.createElement("input"),this.input.type="radio",this.input.className="designbase-wc-radio__input",this.visual=document.createElement("span"),this.visual.className="designbase-wc-radio__visual",this.visual.setAttribute("aria-hidden","true"),this.dot=document.createElement("span"),this.dot.className="designbase-wc-radio__dot",this.visual.appendChild(this.dot),this.labelEl=document.createElement("span"),this.labelEl.className="designbase-wc-radio__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",t=>{this.readOnly&&t.preventDefault()}),this.input.addEventListener("keydown",t=>{const e=["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"];if(this.readOnly&&(" "===t.key||e.includes(t.key)))return void t.preventDefault();const s=this.groupPeers();if(!e.includes(t.key)||!s.some(t=>t.readOnly))return;const i=s.filter(t=>!t.disabled&&!t.readOnly);if(!i.length)return;t.preventDefault();const a="rtl"===getComputedStyle(this).direction,r="ArrowDown"===t.key||t.key===(a?"ArrowLeft":"ArrowRight"),h=i[(i.indexOf(this)+(r?1:-1)+i.length)%i.length];h.input?.focus(),h.input?.click()}),this.input.addEventListener("change",this.handleChange),this.built=!0}groupPeers(){if(!this.input?.name)return[this];const t=this.getRootNode();return Array.from(t.querySelectorAll("db-radio")).filter(t=>t.input?.name===this.input.name&&t.input?.form===this.input.form)}syncGroup(){if(this.input?.checked&&this.input.name){const t=this.getRootNode();for(const e of t.querySelectorAll("db-radio"))e!==this&&e.input&&e.input.name===this.input.name&&e.input.form===this.input.form&&(e.userInteracted=!0,e.removeAttribute("checked"),e.removeAttribute("selected"),e.syncClasses())}}syncState(){if(!this.root||!this.input||!this.visual)return;this.labelContent.sync();const t=this.hasAttribute("checked")||this.hasAttribute("selected")||!this.userInteracted&&(this.hasAttribute("default-checked")||this.hasAttribute("default-selected"));this.resetChecked??(this.resetChecked=t),this.input.defaultChecked=this.resetChecked,this.input.checked=t,this.input.disabled=this.disabled,this.input.readOnly=this.readOnly,this.input.setAttribute("aria-disabled",String(this.disabled||this.readOnly)),this.input.required=this.required,this.input.name=this.getAttribute("name")||"",this.input.value=this.getAttribute("value")??"on";for(const t of["aria-label","aria-labelledby","aria-describedby"]){const e=this.getAttribute(t);e?this.input.setAttribute(t,e):this.input.removeAttribute(t)}this.hasLabel?this.labelEl?.removeAttribute("hidden"):this.labelEl?.setAttribute("hidden",""),this.syncVisual(),this.syncClasses()}syncVisual(){}syncClasses(){if(!this.root||!this.input)return;const t=i.includes(this.size)?this.size:"m",e=this.hasLabel&&Boolean(this.label.trim());this.root.className=["designbase-wc-radio",`designbase-wc-radio--${t}`,this.input.checked&&"designbase-wc-radio--selected",this.disabled&&"designbase-wc-radio--disabled",this.readOnly&&"designbase-wc-radio--readonly",!e&&"designbase-wc-radio--no-label"].filter(Boolean).join(" ")}}customElements.get("db-radio")||customElements.define("db-radio",DbRadio);export{DbRadio};
@@ -8,6 +8,11 @@ declare class DbRangeSlider extends HTMLElement {
8
8
  private trackEl;
9
9
  private fillEl;
10
10
  private thumbEls;
11
+ private initialized;
12
+ private reflecting;
13
+ private pointerId;
14
+ private pointerThumb;
15
+ private tooltipResize?;
11
16
  private currentValue;
12
17
  private currentRange;
13
18
  private isDragging;
@@ -15,15 +20,35 @@ declare class DbRangeSlider extends HTMLElement {
15
20
  private boundTrackClick;
16
21
  private boundPointerMove;
17
22
  private boundPointerUp;
23
+ private boundPointerCancel;
18
24
  private eventsBound;
19
25
  connectedCallback(): void;
20
26
  disconnectedCallback(): void;
21
- attributeChangedCallback(name: string): void;
27
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
22
28
  private isStructuralAttribute;
23
29
  get value(): number;
24
30
  set value(v: number);
25
31
  get range(): [number, number] | undefined;
26
32
  set range(v: [number, number] | string | undefined);
33
+ get min(): number;
34
+ set min(value: number);
35
+ get max(): number;
36
+ set max(value: number);
37
+ get step(): number;
38
+ set step(value: number);
39
+ get size(): string;
40
+ set size(value: string);
41
+ get variant(): string;
42
+ set variant(value: string);
43
+ get disabled(): boolean;
44
+ set disabled(value: boolean);
45
+ get readOnly(): boolean;
46
+ set readOnly(value: boolean);
47
+ get vertical(): boolean;
48
+ set vertical(value: boolean);
49
+ focus(options?: FocusOptions): void;
50
+ private get bounds();
51
+ private normalizeValues;
27
52
  private build;
28
53
  private getValuePosition;
29
54
  private shouldShowLabel;
@@ -37,32 +62,35 @@ declare class DbRangeSlider extends HTMLElement {
37
62
  private isMarkActive;
38
63
  private syncVisuals;
39
64
  private syncThumbTooltips;
65
+ private fitFloatingText;
66
+ private syncMarkLabelBounds;
40
67
  private syncA11y;
41
68
  private setThumbPosition;
42
69
  private valueToPercent;
43
70
  private percentToValue;
44
- private snapToStep;
45
71
  private clamp;
72
+ /** snap-marks: the marks are the only selectable values. */
73
+ private snapStops;
74
+ private adjacentStop;
46
75
  private getNumberAttr;
47
76
  private normalizeRangeInput;
48
77
  private parseRangeAttr;
49
78
  private parseMarks;
79
+ private readMarks;
50
80
  private parseMarkLabels;
51
81
  private isInteractive;
52
82
  private getTrackPercent;
53
83
  private handleThumbPointerDown;
54
84
  private handleTrackClick;
55
- /**
56
- * 포인터 이동을 프레임당 1회로 병합한다. preventDefault 를 쓰지 않으므로 안전하다. (PERF-RAF)
57
- * 메서드가 아니라 필드로 선언해야 rafThrottle 로 감쌀 수 있다.
58
- */
59
85
  private handlePointerMove;
60
86
  private handlePointerUp;
87
+ private cancelDrag;
61
88
  private setThumbActive;
89
+ private reflectCurrent;
62
90
  private applyDragValue;
91
+ private applyValue;
63
92
  private commitValueAtPosition;
64
93
  private handleKeyDown;
65
- private afterKeyboard;
66
94
  private dispatchValueChange;
67
95
  private dispatchRangeChange;
68
96
  }
@@ -1 +1 @@
1
- import{rafThrottle as t}from"../core/dom.js";const e="designbase-wc-range-slider";class DbRangeSlider extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.headerEl=null,this.labelEl=null,this.valuesEl=null,this.containerEl=null,this.trackEl=null,this.fillEl=null,this.thumbEls=[],this.currentValue=50,this.currentRange=[25,75],this.isDragging=!1,this.activeThumb=null,this.boundTrackClick=t=>this.handleTrackClick(t),this.boundPointerMove=t=>this.handlePointerMove(t),this.boundPointerUp=()=>this.handlePointerUp(),this.eventsBound=!1,this.handlePointerMove=t(t=>{if(!this.isDragging||!this.isInteractive())return;const e=this.getTrackPercent(t.clientX,t.clientY),i=this.percentToValue(e);this.applyDragValue(i)})}static get observedAttributes(){return["value","range","min","max","step","size","variant","show-value","show-label","label","value-position","value-fixed","show-thumb-tooltip","show-marks","marks","mark-labels","disabled","readonly","full-width","vertical"]}connectedCallback(){this.built||(this.build(),this.built=!0),this.readAttributeValues(),this.syncClasses(),this.syncDomOrder(),this.syncStructure(),this.syncVisuals(),this.syncA11y(),this.bindEvents()}disconnectedCallback(){this.unbindEvents()}attributeChangedCallback(t){this.built&&(this.readAttributeValues(),this.syncClasses(),this.isStructuralAttribute(t)&&(this.syncDomOrder(),this.syncStructure()),this.syncVisuals(),this.syncA11y())}isStructuralAttribute(t){return["range","show-value","show-label","label","value-position","show-marks","marks","mark-labels","vertical"].includes(t)}get value(){return this.currentValue}set value(t){this.currentValue=this.clamp(t),this.setAttribute("value",String(this.currentValue)),this.syncVisuals()}get range(){if(this.hasAttribute("range"))return[...this.currentRange]}set range(t){if(null==t)return this.removeAttribute("range"),void(this.built&&(this.syncStructure(),this.syncVisuals()));const e=this.normalizeRangeInput(t);if(!e)return;const i=[Math.min(e[0],e[1]),Math.max(e[0],e[1])];this.currentRange=[this.clamp(i[0]),this.clamp(i[1])];const s=`${this.currentRange[0]},${this.currentRange[1]}`;this.getAttribute("range")!==s&&this.setAttribute("range",s),this.built&&this.syncVisuals()}build(){this.classList.add(e),this.headerEl=document.createElement("div"),this.headerEl.className=`${e}__header`,this.labelEl=document.createElement("span"),this.labelEl.className=`${e}__label`,this.valuesEl=document.createElement("div"),this.valuesEl.className=`${e}__values`,this.containerEl=document.createElement("div"),this.containerEl.className=`${e}__container`,this.trackEl=document.createElement("div"),this.trackEl.className=`${e}__track`,this.fillEl=document.createElement("div"),this.fillEl.className=`${e}__fill`,this.trackEl.appendChild(this.fillEl),this.containerEl.appendChild(this.trackEl)}getValuePosition(){const t=this.getAttribute("value-position");return t||(this.shouldShowLabel()?"top":"right")}shouldShowLabel(){return this.hasAttribute("show-label")&&Boolean(this.getAttribute("label")?.trim())}formatValue(t){const e=this.getAttribute("value-fixed");if(null!==e&&""!==e){const i=Number(e);if(Number.isFinite(i))return t.toFixed(i)}return String(t)}syncDomOrder(){if(!this.headerEl||!this.valuesEl||!this.containerEl)return;const t=this.getValuePosition(),e=this.shouldShowLabel(),i=this.hasAttribute("show-value"),s=this.hasAttribute("range");return this.replaceChildren(),this.labelEl&&(this.labelEl.textContent=this.getAttribute("label")?.trim()||""),s||"top"===t?(this.headerEl.replaceChildren(),e&&this.labelEl&&this.headerEl.appendChild(this.labelEl),i&&this.headerEl.appendChild(this.valuesEl),this.headerEl.childElementCount>0&&this.append(this.headerEl),void this.append(this.containerEl)):"bottom"===t?(e&&this.labelEl&&(this.headerEl.replaceChildren(this.labelEl),this.append(this.headerEl)),this.append(this.containerEl),void(i&&this.append(this.valuesEl))):void(i?this.append(this.valuesEl,this.containerEl):this.append(this.containerEl))}bindEvents(){this.eventsBound||(this.trackEl?.addEventListener("click",this.boundTrackClick),this.eventsBound=!0)}unbindEvents(){this.eventsBound&&(this.trackEl?.removeEventListener("click",this.boundTrackClick),document.removeEventListener("pointermove",this.boundPointerMove),document.removeEventListener("pointerup",this.boundPointerUp),this.eventsBound=!1)}readAttributeValues(){const t=this.getNumberAttr("min",0),e=this.getNumberAttr("max",100),i=this.getNumberAttr("step",1);if(this.hasAttribute("range")){const s=this.parseRangeAttr(this.getAttribute("range"));s?this.currentRange=[this.clamp(s[0],t,e,i),this.clamp(s[1],t,e,i)]:this.currentRange.every(Number.isFinite)||(this.currentRange=[25,75])}else this.currentValue=this.clamp(this.getNumberAttr("value",50),t,e,i)}syncClasses(){const t=this.getAttribute("size")||"m",i=this.getAttribute("variant")||"default",s=this.hasAttribute("range"),r=this.getValuePosition(),a=s?"top":r,n=this.hasAttribute("show-marks")&&this.parseMarks().length>0&&Object.keys(this.parseMarkLabels()).length>0,h=[e,`${e}--${t}`,`${e}--${i}`,`${e}--value-${a}`,this.shouldShowLabel()?`${e}--has-label`:"",this.hasAttribute("disabled")?`${e}--disabled`:"",this.hasAttribute("readonly")?`${e}--readonly`:"",this.hasAttribute("full-width")?`${e}--full-width`:"",this.hasAttribute("vertical")?`${e}--vertical`:"",this.hasAttribute("show-thumb-tooltip")?`${e}--show-tooltip`:"",s?`${e}--range`:"",n?`${e}--has-mark-labels`:""].filter(Boolean);if(this.className=h.join(" "),this.trackEl){const t=[`${e}__track`];this.hasAttribute("disabled")&&t.push(`${e}__track--disabled`),this.hasAttribute("readonly")&&t.push(`${e}__track--readonly`),this.trackEl.className=t.join(" ")}}syncStructure(){if(!this.valuesEl||!this.trackEl||!this.fillEl)return;const t=this.hasAttribute("show-value"),i=this.hasAttribute("range"),s=this.hasAttribute("show-marks");if(this.valuesEl.innerHTML="",this.valuesEl.hidden=!t,t)if(i){const t=document.createElement("span");t.className=`${e}__value ${e}__value--min`;const i=document.createElement("span");i.className=`${e}__value-separator`,i.textContent="~",i.setAttribute("aria-hidden","true");const s=document.createElement("span");s.className=`${e}__value ${e}__value--max`,this.valuesEl.append(t,i,s)}else{const t=document.createElement("span");t.className=`${e}__value ${e}__value--single`,this.valuesEl.appendChild(t)}if(this.thumbEls.forEach(t=>t.remove()),this.thumbEls=[],this.trackEl.querySelectorAll(`.${e}__mark`).forEach(t=>t.remove()),s){const t=this.parseMarks();if(t.length>0){const i=this.parseMarkLabels();t.forEach(t=>{const s=document.createElement("div");if(s.className=`${e}__mark`,s.setAttribute("aria-hidden","true"),this.isMarkActive(t)&&s.classList.add(`${e}__mark--active`),this.hasAttribute("vertical")?s.style.bottom=`${this.valueToPercent(t)}%`:s.style.left=`${this.valueToPercent(t)}%`,void 0!==i[t]){const r=document.createElement("span");r.className=`${e}__mark-label`,r.textContent=i[t],s.appendChild(r)}this.trackEl.insertBefore(s,this.fillEl)})}}const r=i?2:1,a=this.hasAttribute("disabled")||this.hasAttribute("readonly");for(let t=0;t<r;t++){const s=document.createElement("div");s.className=`${e}__thumb`,s.setAttribute("role","slider"),s.setAttribute("draggable","false"),s.setAttribute("tabindex",a?"-1":"0");const r=i?0===t?"min":"max":"single";i&&(s.dataset.thumb=r),s.addEventListener("pointerdown",t=>this.handleThumbPointerDown(t,r)),s.addEventListener("keydown",t=>this.handleKeyDown(t,r));const n=document.createElement("span");n.className=`${e}__thumb-tooltip`,n.setAttribute("aria-hidden","true"),n.hidden=!0,s.appendChild(n),this.trackEl.appendChild(s),this.thumbEls.push(s)}}isMarkActive(t){if(this.hasAttribute("range")){const[e,i]=this.currentRange;return t>=e&&t<=i}return t<=this.currentValue}syncVisuals(){if(!this.fillEl)return;const t=this.hasAttribute("range"),i=this.getNumberAttr("min",0),s=this.getNumberAttr("max",100);if(t){const[t,r]=this.currentRange,a=this.valueToPercent(t),n=this.valueToPercent(r);this.hasAttribute("vertical")?(this.fillEl.style.left="",this.fillEl.style.width="",this.fillEl.style.bottom=`${a}%`,this.fillEl.style.height=n-a+"%"):(this.fillEl.style.bottom="",this.fillEl.style.height="",this.fillEl.style.left=`${a}%`,this.fillEl.style.width=n-a+"%"),this.thumbEls[0]&&(this.setThumbPosition(this.thumbEls[0],a),this.thumbEls[0].setAttribute("aria-valuemin",String(i)),this.thumbEls[0].setAttribute("aria-valuemax",String(r)),this.thumbEls[0].setAttribute("aria-valuenow",String(t))),this.thumbEls[1]&&(this.setThumbPosition(this.thumbEls[1],n),this.thumbEls[1].setAttribute("aria-valuemin",String(t)),this.thumbEls[1].setAttribute("aria-valuemax",String(s)),this.thumbEls[1].setAttribute("aria-valuenow",String(r)));const h=this.valuesEl?.querySelector(`.${e}__value--min`),l=this.valuesEl?.querySelector(`.${e}__value--max`);h&&(h.textContent=this.formatValue(t)),l&&(l.textContent=this.formatValue(r))}else{const t=this.valueToPercent(this.currentValue);this.hasAttribute("vertical")?(this.fillEl.style.left="",this.fillEl.style.width="",this.fillEl.style.bottom="0",this.fillEl.style.height=`${t}%`):(this.fillEl.style.bottom="",this.fillEl.style.height="",this.fillEl.style.left="0",this.fillEl.style.width=`${t}%`),this.thumbEls[0]&&(this.setThumbPosition(this.thumbEls[0],t),this.thumbEls[0].setAttribute("aria-valuemin",String(i)),this.thumbEls[0].setAttribute("aria-valuemax",String(s)),this.thumbEls[0].setAttribute("aria-valuenow",String(this.currentValue)));const r=this.valuesEl?.querySelector(`.${e}__value--single`);r&&(r.textContent=this.formatValue(this.currentValue))}this.syncThumbTooltips(),this.trackEl?.querySelectorAll(`.${e}__mark`).forEach(t=>{const i=t instanceof HTMLElement?parseFloat(t.style.left):NaN,s=t instanceof HTMLElement?parseFloat(t.style.bottom):NaN,r=Number.isFinite(i)?i:s;if(!Number.isFinite(r))return;const a=this.percentToValue(r);t.classList.toggle(`${e}__mark--active`,this.isMarkActive(a))})}syncThumbTooltips(){const t=this.hasAttribute("range"),i=this.hasAttribute("show-thumb-tooltip");this.thumbEls.forEach((s,r)=>{const a=s.querySelector(`.${e}__thumb-tooltip`);if(!a)return;if(!i)return void(a.hidden=!0);a.hidden=!1;const n=t?0===r?this.currentRange[0]:this.currentRange[1]:this.currentValue;a.textContent=this.formatValue(n)})}syncA11y(){if(!this.containerEl)return;const t=this.getNumberAttr("min",0),e=this.getNumberAttr("max",100),i=this.hasAttribute("range"),s=this.hasAttribute("disabled"),r=this.hasAttribute("readonly");this.containerEl.removeAttribute("role"),this.containerEl.removeAttribute("tabindex"),["aria-valuemin","aria-valuemax","aria-valuenow","aria-valuetext","aria-disabled","aria-readonly"].forEach(t=>this.containerEl?.removeAttribute(t));const a=this.getAttribute("label")?.trim()||"값";this.thumbEls.forEach((n,h)=>{const l=i?0===h?this.currentRange[0]:this.currentRange[1]:this.currentValue,u=i?`${a} ${0===h?"최소값":"최대값"}`:a;n.setAttribute("aria-label",u),n.setAttribute("aria-valuemin",String(i&&1===h?this.currentRange[0]:t)),n.setAttribute("aria-valuemax",String(i&&0===h?this.currentRange[1]:e)),n.setAttribute("aria-valuenow",String(l)),n.setAttribute("aria-valuetext",String(l)),n.setAttribute("aria-disabled",String(s)),n.setAttribute("aria-readonly",String(r)),n.setAttribute("tabindex",s||r?"-1":"0")})}setThumbPosition(t,e){this.hasAttribute("vertical")?(t.style.left="",t.style.bottom=`${e}%`):(t.style.bottom="",t.style.left=`${e}%`)}valueToPercent(t){const e=this.getNumberAttr("min",0),i=this.getNumberAttr("max",100);return i===e?0:(t-e)/(i-e)*100}percentToValue(t){const e=this.getNumberAttr("min",0),i=this.getNumberAttr("max",100),s=this.getNumberAttr("step",1),r=e+t/100*(i-e);return this.snapToStep(r,e,i,s)}snapToStep(t,e,i,s){const r=Math.round(t/s)*s;return Math.min(i,Math.max(e,r))}clamp(t,e=this.getNumberAttr("min",0),i=this.getNumberAttr("max",100),s=this.getNumberAttr("step",1)){return this.snapToStep(Math.min(i,Math.max(e,t)),e,i,s)}getNumberAttr(t,e){const i=this.getAttribute(t);if(null===i||""===i)return e;const s=Number(i);return Number.isFinite(s)?s:e}normalizeRangeInput(t){if("string"==typeof t)return this.parseRangeAttr(t);if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);return Number.isFinite(e)&&Number.isFinite(i)?[e,i]:null}parseRangeAttr(t){if(!t)return null;if(t.startsWith("[")){try{const e=JSON.parse(t);if(Array.isArray(e)&&2===e.length){const t=Number(e[0]),i=Number(e[1]);if(Number.isFinite(t)&&Number.isFinite(i))return[t,i]}}catch{return null}return null}const e=t.split(",").map(t=>Number(t.trim()));return 2!==e.length||e.some(t=>!Number.isFinite(t))?null:[e[0],e[1]]}parseMarks(){const t=this.getAttribute("marks");if(!t)return[];if(t.startsWith("[")){try{const e=JSON.parse(t);if(Array.isArray(e))return e.map(t=>Number(t)).filter(t=>Number.isFinite(t))}catch{return[]}return[]}return t.split(",").map(t=>Number(t.trim())).filter(t=>Number.isFinite(t))}parseMarkLabels(){const t=this.getAttribute("mark-labels");if(!t)return{};try{const e=JSON.parse(t),i={};return Object.entries(e).forEach(([t,e])=>{i[Number(t)]=e}),i}catch{return{}}}isInteractive(){return!this.hasAttribute("disabled")&&!this.hasAttribute("readonly")}getTrackPercent(t,e){if(!this.trackEl)return 0;const i=this.trackEl.getBoundingClientRect();if(this.hasAttribute("vertical")){const t=Math.max(i.top,Math.min(i.bottom,e));return(i.bottom-t)/i.height*100}return(Math.max(i.left,Math.min(i.right,t))-i.left)/i.width*100}handleThumbPointerDown(t,e){this.isInteractive()&&(t.preventDefault(),this.isDragging=!0,this.activeThumb=e,this.setThumbActive(e,!0),document.addEventListener("pointermove",this.boundPointerMove),document.addEventListener("pointerup",this.boundPointerUp))}handleTrackClick(t){if(!this.isInteractive()||!this.trackEl)return;if(t.target.closest(`.${e}__thumb`))return;const i=this.getTrackPercent(t.clientX,t.clientY),s=this.percentToValue(i);this.commitValueAtPosition(s)}handlePointerUp(){this.isDragging&&(this.activeThumb&&this.setThumbActive(this.activeThumb,!1),this.isDragging=!1,this.activeThumb=null,document.removeEventListener("pointermove",this.boundPointerMove),document.removeEventListener("pointerup",this.boundPointerUp),this.syncThumbTooltips(),this.hasAttribute("range")?this.setAttribute("range",`${this.currentRange[0]},${this.currentRange[1]}`):this.setAttribute("value",String(this.currentValue)),this.syncA11y())}setThumbActive(t,i){if(this.hasAttribute("range")){const s=this.thumbEls["min"===t?0:1];return void s?.classList.toggle(`${e}__thumb--active`,i)}this.thumbEls[0]?.classList.toggle(`${e}__thumb--active`,i)}applyDragValue(t){const e=this.clamp(t);if(this.hasAttribute("range")){const[t,i]=this.currentRange;if("min"===this.activeThumb)this.currentRange=[Math.min(e,i),i];else if("max"===this.activeThumb)this.currentRange=[t,Math.max(e,t)];else{Math.abs(e-t)<=Math.abs(e-i)?(this.currentRange=[Math.min(e,i),i],this.activeThumb="min"):(this.currentRange=[t,Math.max(e,t)],this.activeThumb="max")}this.syncVisuals(),this.dispatchRangeChange()}else this.currentValue=e,this.syncVisuals(),this.syncA11y(),this.dispatchValueChange()}commitValueAtPosition(t){const e=this.clamp(t);if(this.hasAttribute("range")){const[t,i]=this.currentRange,s=Math.abs(e-t),r=Math.abs(e-i);this.currentRange=s<=r?[Math.min(e,i),i]:[t,Math.max(e,t)],this.setAttribute("range",`${this.currentRange[0]},${this.currentRange[1]}`),this.syncVisuals(),this.dispatchRangeChange()}else this.currentValue=e,this.setAttribute("value",String(this.currentValue)),this.syncVisuals(),this.syncA11y(),this.dispatchValueChange()}handleKeyDown(t,e="single"){if(!this.isInteractive())return;const i=this.getNumberAttr("step",1),s=this.getNumberAttr("min",0),r=this.getNumberAttr("max",100);let a;if(this.hasAttribute("range")&&"single"!==e){const n="min"===e?this.currentRange[0]:this.currentRange[1];switch(t.key){case"ArrowLeft":case"ArrowDown":a=this.clamp(n-i,s,r);break;case"ArrowRight":case"ArrowUp":a=this.clamp(n+i,s,r);break;case"Home":a=s;break;case"End":a=r;break;default:return}const h=[...this.currentRange];if("min"===e?h[0]=a:h[1]=a,h[0]>h[1])return;return t.preventDefault(),this.currentRange=h,void this.afterKeyboard()}if(!this.hasAttribute("range")){switch(t.key){case"ArrowLeft":case"ArrowDown":a=this.clamp(this.currentValue-i,s,r);break;case"ArrowRight":case"ArrowUp":a=this.clamp(this.currentValue+i,s,r);break;case"Home":a=s;break;case"End":a=r;break;default:return}t.preventDefault(),this.currentValue=a,this.afterKeyboard()}}afterKeyboard(){this.hasAttribute("range")?(this.setAttribute("range",`${this.currentRange[0]},${this.currentRange[1]}`),this.syncVisuals(),this.dispatchRangeChange()):(this.setAttribute("value",String(this.currentValue)),this.syncVisuals(),this.syncA11y(),this.dispatchValueChange())}dispatchValueChange(){this.dispatchEvent(new CustomEvent("db-change",{bubbles:!0,composed:!0,detail:{value:this.currentValue}}))}dispatchRangeChange(){this.dispatchEvent(new CustomEvent("db-change",{bubbles:!0,composed:!0,detail:{range:[...this.currentRange]}}))}}customElements.get("db-range-slider")||customElements.define("db-range-slider",DbRangeSlider);export{DbRangeSlider};
1
+ import{rafThrottle as t}from"../core/dom.js";import{setHostClasses as e}from"../core/host-class.js";import{dbUid as i}from"../core/uid.js";import{sliderBounds as s,sliderPercent as r,sliderValueAt as a,sliderValue as n}from"../core/range-values.js";import{coerceToContract as l}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{RANGE_SLIDER_SIZES as h,RANGE_SLIDER_VARIANTS as u,RANGE_SLIDER_VALUE_POSITIONS as o}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const c="designbase-wc-range-slider";class DbRangeSlider extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.headerEl=null,this.labelEl=null,this.valuesEl=null,this.containerEl=null,this.trackEl=null,this.fillEl=null,this.thumbEls=[],this.initialized=!1,this.reflecting=!1,this.pointerId=null,this.pointerThumb=null,this.currentValue=50,this.currentRange=[25,75],this.isDragging=!1,this.activeThumb=null,this.boundTrackClick=t=>this.handleTrackClick(t),this.boundPointerMove=t=>{t.pointerId===this.pointerId&&this.handlePointerMove(t)},this.boundPointerUp=t=>this.handlePointerUp(t),this.boundPointerCancel=t=>{t.pointerId===this.pointerId&&this.cancelDrag()},this.eventsBound=!1,this.handlePointerMove=t(t=>{this.isDragging&&this.isInteractive()&&t.pointerId===this.pointerId&&this.applyDragValue(this.percentToValue(this.getTrackPercent(t.clientX,t.clientY)))})}static get observedAttributes(){return["value","range","min","max","step","size","variant","show-value","show-label","label","value-position","value-fixed","show-thumb-tooltip","show-marks","marks","mark-labels","snap-marks","disabled","readonly","full-width","vertical","aria-label","aria-labelledby","aria-describedby","label-minimum","label-maximum"]}connectedCallback(){for(const t of["value","range","min","max","step","size","variant","disabled","readOnly","vertical"]){if(!Object.prototype.hasOwnProperty.call(this,t))continue;const e=this,i=e[t];delete e[t],e[t]=i}this.built||(this.build(),this.built=!0),this.readAttributeValues(),this.syncClasses(),this.syncDomOrder(),this.syncStructure(),this.syncVisuals(),this.syncA11y(),this.bindEvents(),this.tooltipResize??(this.tooltipResize=new ResizeObserver(()=>{this.isConnected&&(this.syncThumbTooltips(),this.syncMarkLabelBounds())})),this.tooltipResize.observe(this)}disconnectedCallback(){this.cancelDrag(),this.unbindEvents(),this.tooltipResize?.disconnect()}attributeChangedCallback(t,e,i){if(!this.built||this.reflecting)return;const s="range"===t&&null===e!=(null===i);this.isInteractive()&&!s&&"vertical"!==t||this.cancelDrag();const r=this.thumbEls.findIndex(t=>t===this.ownerDocument.activeElement),a=this.thumbEls[r];this.readAttributeValues(t),this.syncClasses(),(s||this.isStructuralAttribute(t))&&(this.syncDomOrder(),this.syncStructure()),this.syncVisuals(),this.syncA11y();const n=a?.isConnected?a:r>=0?this.thumbEls[Math.min(r,this.thumbEls.length-1)]:void 0;n&&!this.disabled&&this.ownerDocument.activeElement!==n&&n.focus({preventScroll:!0})}isStructuralAttribute(t){return["min","max","step","show-value","show-label","label","value-position","show-marks","marks","mark-labels","vertical","aria-label","aria-labelledby","aria-describedby","label-minimum","label-maximum"].includes(t)}get value(){return this.built?this.currentValue:this.clamp(this.getNumberAttr("value",50))}set value(t){this.currentValue=this.clamp(Number.isFinite(t)?t:50),this.setAttribute("value",String(this.currentValue)),this.syncVisuals(),this.syncA11y()}get range(){if(!this.hasAttribute("range"))return;const t=this.built?this.currentRange:this.parseRangeAttr(this.getAttribute("range"))??this.currentRange;return this.normalizeValues(t)}set range(t){if(null==t)return this.removeAttribute("range"),void(this.built&&(this.syncStructure(),this.syncVisuals()));const e=this.normalizeRangeInput(t);if(!e)return;this.currentRange=this.normalizeValues(e);const i=`${this.currentRange[0]},${this.currentRange[1]}`;this.getAttribute("range")!==i&&this.setAttribute("range",i),this.built&&(this.syncVisuals(),this.syncA11y())}get min(){return this.getNumberAttr("min",0)}set min(t){this.setAttribute("min",String(t))}get max(){return Math.max(this.min,this.getNumberAttr("max",100))}set max(t){this.setAttribute("max",String(t))}get step(){const t=this.getNumberAttr("step",1);return t>0?t:1}set step(t){this.setAttribute("step",String(t))}get size(){return l(this.getAttribute("size"),h,"m")}set size(t){this.setAttribute("size",t)}get variant(){return l(this.getAttribute("variant"),u,"default")}set variant(t){this.setAttribute("variant",t)}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",Boolean(t))}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",Boolean(t))}get vertical(){return this.hasAttribute("vertical")}set vertical(t){this.toggleAttribute("vertical",Boolean(t))}focus(t){this.thumbEls[0]?.focus(t)}get bounds(){return s(this.min,this.max,this.step)}normalizeValues(t){const e=this.clamp(t[0]),i=this.clamp(t[1]);return[Math.min(e,i),Math.max(e,i)]}build(){this.classList.add(c),this.headerEl=document.createElement("div"),this.headerEl.className=`${c}__header`,this.labelEl=document.createElement("span"),this.labelEl.className=`${c}__label`,this.valuesEl=document.createElement("div"),this.valuesEl.className=`${c}__values`,this.containerEl=document.createElement("div"),this.containerEl.className=`${c}__container`,this.trackEl=document.createElement("div"),this.trackEl.className=`${c}__track`,this.fillEl=document.createElement("div"),this.fillEl.className=`${c}__fill`,this.trackEl.appendChild(this.fillEl),this.containerEl.appendChild(this.trackEl)}getValuePosition(){const t=this.getAttribute("value-position");return t?l(t,o,this.shouldShowLabel()?"top":"right"):this.shouldShowLabel()?"top":"right"}shouldShowLabel(){return this.hasAttribute("show-label")&&Boolean(this.getAttribute("label")?.trim())}formatValue(t){const e=this.getAttribute("value-fixed");if(null!==e&&""!==e){const i=Number(e);if(Number.isFinite(i))return t.toFixed(Math.min(100,Math.max(0,Math.trunc(i))))}return String(t)}syncDomOrder(){if(!(this.headerEl&&this.valuesEl&&this.containerEl&&this.labelEl))return;const t=this.getValuePosition(),e=this.shouldShowLabel(),i=this.hasAttribute("show-value"),s=this.hasAttribute("range")||"top"===t;this.labelEl.textContent=this.getAttribute("label")?.trim()||"";const r=[...e?[this.labelEl]:[],...i&&s?[this.valuesEl]:[]];this.headerEl.replaceChildren(...r),this.containerEl.parentNode!==this&&this.append(this.containerEl),i&&!s?"bottom"===t?this.lastElementChild!==this.valuesEl&&this.append(this.valuesEl):this.valuesEl.nextElementSibling!==this.containerEl&&this.insertBefore(this.valuesEl,this.containerEl):s&&i||this.valuesEl.remove();const a=i&&!s&&"bottom"!==t?this.valuesEl:this.containerEl;r.length?this.headerEl.nextElementSibling!==a&&this.insertBefore(this.headerEl,a):this.headerEl.remove();const n=new Set([this.containerEl,...r.length?[this.headerEl]:[],...i&&!s?[this.valuesEl]:[]]);for(const t of Array.from(this.children))n.has(t)||t.remove()}bindEvents(){this.eventsBound||(this.containerEl?.addEventListener("click",this.boundTrackClick),this.eventsBound=!0)}unbindEvents(){this.eventsBound&&(this.containerEl?.removeEventListener("click",this.boundTrackClick),document.removeEventListener("pointermove",this.boundPointerMove),document.removeEventListener("pointerup",this.boundPointerUp),document.removeEventListener("pointercancel",this.boundPointerCancel),this.handlePointerMove.cancel(),this.eventsBound=!1)}readAttributeValues(t){const e=!this.initialized;if((e||"value"===t)&&(this.currentValue=this.clamp(this.getNumberAttr("value",50))),e||"range"===t){const t=this.parseRangeAttr(this.getAttribute("range"))??(this.hasAttribute("range")&&!this.getAttribute("range")?this.parseRangeAttr(this.getAttribute("value")):null);this.currentRange=t?this.normalizeValues(t):this.normalizeValues(this.currentRange)}this.currentValue=this.clamp(this.currentValue),this.currentRange=this.normalizeValues(this.currentRange),this.initialized=!0}syncClasses(){const t=this.size,i=this.variant,s=this.hasAttribute("range"),r=this.getValuePosition(),a=s?"top":r,n=this.hasAttribute("show-marks")&&this.parseMarks().length>0&&Object.keys(this.parseMarkLabels()).length>0,l=[c,`${c}--${t}`,`${c}--${i}`,`${c}--value-${a}`,this.shouldShowLabel()?`${c}--has-label`:"",this.hasAttribute("disabled")?`${c}--disabled`:"",this.hasAttribute("readonly")?`${c}--readonly`:"",this.hasAttribute("full-width")?`${c}--full-width`:"",this.hasAttribute("vertical")?`${c}--vertical`:"",this.hasAttribute("show-thumb-tooltip")?`${c}--show-tooltip`:"",s?`${c}--range`:"",n?`${c}--has-mark-labels`:""].filter(Boolean);if(e(this,l),this.trackEl){const t=[`${c}__track`];this.hasAttribute("disabled")&&t.push(`${c}__track--disabled`),this.hasAttribute("readonly")&&t.push(`${c}__track--readonly`),this.trackEl.className=t.join(" ")}}syncStructure(){if(!this.valuesEl||!this.trackEl||!this.fillEl)return;const t=this.hasAttribute("show-value"),e=this.hasAttribute("range"),i=this.hasAttribute("show-marks");if(this.valuesEl.innerHTML="",this.valuesEl.hidden=!t,t)if(e){const t=document.createElement("span");t.className=`${c}__value ${c}__value--min`;const e=document.createElement("span");e.className=`${c}__value-separator`,e.textContent="~",e.setAttribute("aria-hidden","true");const i=document.createElement("span");i.className=`${c}__value ${c}__value--max`,this.valuesEl.append(t,e,i)}else{const t=document.createElement("span");t.className=`${c}__value ${c}__value--single`,this.valuesEl.appendChild(t)}if(this.trackEl.querySelectorAll(`.${c}__mark`).forEach(t=>t.remove()),i){const t=this.parseMarks();if(t.length>0){const e=this.parseMarkLabels();t.forEach(t=>{const i=document.createElement("div");if(i.className=`${c}__mark`,i.setAttribute("aria-hidden","true"),i.dataset.value=String(t),this.isMarkActive(t)&&i.classList.add(`${c}__mark--active`),this.hasAttribute("vertical")?i.style.bottom=`${this.valueToPercent(t)}%`:i.style.left=`${this.valueToPercent(t)}%`,void 0!==e[t]){const s=document.createElement("span");s.className=`${c}__mark-label`,s.textContent=e[t],i.appendChild(s)}this.trackEl.insertBefore(i,this.fillEl)})}}const s=e?2:1;for(;this.thumbEls.length>s;)this.thumbEls.pop()?.remove();for(;this.thumbEls.length<s;){const t=document.createElement("div");t.className=`${c}__thumb`,t.setAttribute("role","slider"),t.setAttribute("draggable","false");const kind=()=>t.dataset.thumb||"single";t.addEventListener("pointerdown",t=>this.handleThumbPointerDown(t,kind())),t.addEventListener("keydown",t=>this.handleKeyDown(t,kind())),t.addEventListener("lostpointercapture",t=>{t.pointerId===this.pointerId&&this.cancelDrag()});const e=document.createElement("span");e.className=`${c}__thumb-tooltip`,e.setAttribute("aria-hidden","true"),e.hidden=!0,t.append(e),this.trackEl.append(t),this.thumbEls.push(t)}this.thumbEls.forEach((t,i)=>t.dataset.thumb=e?0===i?"min":"max":"single")}isMarkActive(t){if(this.hasAttribute("range")){const[e,i]=this.currentRange;return t>=e&&t<=i}return t<=this.currentValue}syncVisuals(){if(!this.fillEl)return;const t=this.hasAttribute("range"),e=this.min,i=this.max;if(t){const[t,s]=this.currentRange,r=this.valueToPercent(t),a=this.valueToPercent(s);this.hasAttribute("vertical")?(this.fillEl.style.left="",this.fillEl.style.width="",this.fillEl.style.bottom=`${r}%`,this.fillEl.style.height=a-r+"%"):(this.fillEl.style.bottom="",this.fillEl.style.height="",this.fillEl.style.left=`${r}%`,this.fillEl.style.width=a-r+"%"),this.thumbEls[0]&&(this.setThumbPosition(this.thumbEls[0],r),this.thumbEls[0].setAttribute("aria-valuemin",String(e)),this.thumbEls[0].setAttribute("aria-valuemax",String(s)),this.thumbEls[0].setAttribute("aria-valuenow",String(t))),this.thumbEls[1]&&(this.setThumbPosition(this.thumbEls[1],a),this.thumbEls[1].setAttribute("aria-valuemin",String(t)),this.thumbEls[1].setAttribute("aria-valuemax",String(i)),this.thumbEls[1].setAttribute("aria-valuenow",String(s)));const n=this.valuesEl?.querySelector(`.${c}__value--min`),l=this.valuesEl?.querySelector(`.${c}__value--max`);n&&(n.textContent=this.formatValue(t)),l&&(l.textContent=this.formatValue(s))}else{const t=this.valueToPercent(this.currentValue);this.hasAttribute("vertical")?(this.fillEl.style.left="",this.fillEl.style.width="",this.fillEl.style.bottom="0",this.fillEl.style.height=`${t}%`):(this.fillEl.style.bottom="",this.fillEl.style.height="",this.fillEl.style.left="0",this.fillEl.style.width=`${t}%`),this.thumbEls[0]&&(this.setThumbPosition(this.thumbEls[0],t),this.thumbEls[0].setAttribute("aria-valuemin",String(e)),this.thumbEls[0].setAttribute("aria-valuemax",String(i)),this.thumbEls[0].setAttribute("aria-valuenow",String(this.currentValue)));const s=this.valuesEl?.querySelector(`.${c}__value--single`);s&&(s.textContent=this.formatValue(this.currentValue))}this.syncThumbTooltips(),this.syncMarkLabelBounds(),this.trackEl?.querySelectorAll(`.${c}__mark`).forEach(t=>{const e=Number(t.dataset.value);t.classList.toggle(`${c}__mark--active`,this.isMarkActive(e))})}syncThumbTooltips(){const t=this.hasAttribute("range"),e=this.hasAttribute("show-thumb-tooltip");this.thumbEls.forEach((i,s)=>{const r=i.querySelector(`.${c}__thumb-tooltip`);if(!r)return;if(!e)return void(r.hidden=!0);r.hidden=!1;const a=t?0===s?this.currentRange[0]:this.currentRange[1]:this.currentValue;r.textContent=this.formatValue(a),this.fitFloatingText(r)})}fitFloatingText(t){const e=this.getBoundingClientRect();if(!e.width)return;t.style.maxWidth=`${e.width}px`,t.style.marginLeft="0px";const i=t.getBoundingClientRect(),s=i.left<e.left?e.left-i.left:i.right>e.right?e.right-i.right:0;s&&(t.style.marginLeft=`${s}px`)}syncMarkLabelBounds(){this.trackEl?.querySelectorAll(`.${c}__mark-label`).forEach(t=>this.fitFloatingText(t))}syncA11y(){if(!this.containerEl)return;const t=this.min,e=this.max,s=this.hasAttribute("range"),r=this.hasAttribute("disabled"),a=this.hasAttribute("readonly");this.containerEl.removeAttribute("role"),this.containerEl.removeAttribute("tabindex"),["aria-valuemin","aria-valuemax","aria-valuenow","aria-valuetext","aria-disabled","aria-readonly"].forEach(t=>this.containerEl?.removeAttribute(t));const n=this.getAttribute("aria-label")?.trim()||this.getAttribute("label")?.trim()||"Value";this.thumbEls.forEach((l,h)=>{const u=s?0===h?this.currentRange[0]:this.currentRange[1]:this.currentValue,o=s?`${n} ${0===h?this.getAttribute("label-minimum")||"minimum":this.getAttribute("label-maximum")||"maximum"}`:n;l.setAttribute("aria-label",o);const c=this.getAttribute("aria-labelledby");if(c)if(s){const t=l.querySelector("[data-range-endpoint]")??document.createElement("span");t.dataset.rangeEndpoint="",t.hidden=!0,t.id||(t.id=i("slider-endpoint")),t.textContent=0===h?this.getAttribute("label-minimum")||"minimum":this.getAttribute("label-maximum")||"maximum",t.parentNode||l.append(t),l.setAttribute("aria-labelledby",c+" "+t.id)}else l.setAttribute("aria-labelledby",c);else l.removeAttribute("aria-labelledby");const b=this.getAttribute("aria-describedby");b?l.setAttribute("aria-describedby",b):l.removeAttribute("aria-describedby"),l.setAttribute("aria-orientation",this.vertical?"vertical":"horizontal"),l.setAttribute("aria-valuemin",String(s&&1===h?this.currentRange[0]:t)),l.setAttribute("aria-valuemax",String(s&&0===h?this.currentRange[1]:e)),l.setAttribute("aria-valuenow",String(u)),l.setAttribute("aria-valuetext",this.formatValue(u)),l.setAttribute("aria-disabled",String(r)),l.setAttribute("aria-readonly",String(a)),l.setAttribute("tabindex",r?"-1":"0")})}setThumbPosition(t,e){this.hasAttribute("vertical")?(t.style.left="",t.style.bottom=`${e}%`):(t.style.bottom="",t.style.left=`${e}%`)}valueToPercent(t){return r(t,this.bounds)}percentToValue(t){return a(t,this.bounds)}clamp(t,e=this.min,i=this.max,r=this.step){const a=this.snapStops();return a.length>0&&Number.isFinite(t)?a.reduce((e,i)=>Math.abs(i-t)<Math.abs(e-t)?i:e,a[0]):n(t,s(e,i,r))}snapStops(){return this.hasAttribute("snap-marks")?this.parseMarks():[]}adjacentStop(t,e){const i=this.snapStops();return(e>0?i.find(e=>e>t):[...i].reverse().find(e=>e<t))??t}getNumberAttr(t,e){const i=this.getAttribute(t);if(null===i||""===i)return e;const s=Number(i);return Number.isFinite(s)?s:e}normalizeRangeInput(t){if("string"==typeof t)return this.parseRangeAttr(t);if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),i=Number(t[1]);return Number.isFinite(e)&&Number.isFinite(i)?[e,i]:null}parseRangeAttr(t){if(!t)return null;if(t.startsWith("[")){try{const e=JSON.parse(t);if(Array.isArray(e)&&2===e.length){const t=Number(e[0]),i=Number(e[1]);if(Number.isFinite(t)&&Number.isFinite(i))return[t,i]}}catch{return null}return null}const e=t.split(",").map(t=>Number(t.trim()));return 2!==e.length||e.some(t=>!Number.isFinite(t))?null:[e[0],e[1]]}parseMarks(){return[...new Set(this.readMarks())].filter(t=>t>=this.min&&t<=this.max).sort((t,e)=>t-e)}readMarks(){const t=this.getAttribute("marks");if(!t)return[];if(t.startsWith("[")){try{const e=JSON.parse(t);if(Array.isArray(e))return e.map(t=>Number(t)).filter(t=>Number.isFinite(t))}catch{return[]}return[]}return t.split(",").map(t=>Number(t.trim())).filter(t=>Number.isFinite(t))}parseMarkLabels(){const t=this.getAttribute("mark-labels");if(!t)return{};try{const e=JSON.parse(t);if(!e||"object"!=typeof e||Array.isArray(e))return{};const i={};return Object.entries(e).forEach(([t,e])=>{!Number.isFinite(Number(t))||"string"!=typeof e&&"number"!=typeof e||(i[Number(t)]=String(e))}),i}catch{return{}}}isInteractive(){return!this.hasAttribute("disabled")&&!this.hasAttribute("readonly")}getTrackPercent(t,e){if(!this.trackEl)return 0;const i=this.trackEl.getBoundingClientRect();if(!i.width||!i.height)return 0;if(this.hasAttribute("vertical")){const t=Math.max(i.top,Math.min(i.bottom,e));return(i.bottom-t)/i.height*100}return(Math.max(i.left,Math.min(i.right,t))-i.left)/i.width*100}handleThumbPointerDown(t,e){if(this.isInteractive()&&0===t.button&&!1!==t.isPrimary&&!this.isDragging){t.preventDefault(),this.isDragging=!0,this.pointerId=t.pointerId,this.activeThumb=e,this.pointerThumb=t.currentTarget,this.pointerThumb.focus({preventScroll:!0}),this.setThumbActive(e,!0);try{this.pointerThumb.setPointerCapture(t.pointerId)}catch{}document.addEventListener("pointermove",this.boundPointerMove),document.addEventListener("pointerup",this.boundPointerUp),document.addEventListener("pointercancel",this.boundPointerCancel)}}handleTrackClick(t){this.isInteractive()&&this.trackEl&&!t.target.closest(`.${c}__thumb`)&&this.commitValueAtPosition(this.percentToValue(this.getTrackPercent(t.clientX,t.clientY)))}handlePointerUp(t){this.isDragging&&t.pointerId===this.pointerId&&(this.handlePointerMove.cancel(),this.isInteractive()&&this.applyDragValue(this.percentToValue(this.getTrackPercent(t.clientX,t.clientY))),this.cancelDrag())}cancelDrag(){this.handlePointerMove.cancel(),this.activeThumb&&this.setThumbActive(this.activeThumb,!1);const t=this.pointerThumb,e=this.pointerId;this.isDragging=!1,this.activeThumb=null,this.pointerId=null,this.pointerThumb=null,document.removeEventListener("pointermove",this.boundPointerMove),document.removeEventListener("pointerup",this.boundPointerUp),document.removeEventListener("pointercancel",this.boundPointerCancel);try{t&&null!==e&&t.hasPointerCapture(e)&&t.releasePointerCapture(e)}catch{}}setThumbActive(t,e){if(this.hasAttribute("range")){const i=this.thumbEls["min"===t?0:1];return void i?.classList.toggle(`${c}__thumb--active`,e)}this.thumbEls[0]?.classList.toggle(`${c}__thumb--active`,e)}reflectCurrent(){this.reflecting=!0;try{const t=this.hasAttribute("range")?"range":"value",e="range"===t?this.currentRange.join(","):String(this.currentValue);this.getAttribute(t)!==e&&this.setAttribute(t,e)}finally{this.reflecting=!1}}applyDragValue(t){this.applyValue(t,this.activeThumb??"single")}applyValue(t,e){const i=this.clamp(t);if(this.hasAttribute("range")){const[t,s]=this.currentRange,r="min"===e?[Math.min(i,s),s]:[t,Math.max(i,t)];if(r[0]===t&&r[1]===s)return;this.currentRange=r}else{if(i===this.currentValue)return;this.currentValue=i}this.reflectCurrent(),this.syncVisuals(),this.syncA11y(),this.hasAttribute("range")?this.dispatchRangeChange():this.dispatchValueChange()}commitValueAtPosition(t){const e=this.hasAttribute("range")?Math.abs(t-this.currentRange[0])<=Math.abs(t-this.currentRange[1])?"min":"max":"single";this.thumbEls["max"===e?1:0]?.focus({preventScroll:!0}),this.applyValue(t,e)}handleKeyDown(t,e="single"){if(!this.isInteractive())return;const i=this.hasAttribute("range")?this.currentRange["max"===e?1:0]:this.currentValue;let s;if(this.snapStops().length>0){switch(t.key){case"ArrowLeft":case"ArrowDown":case"PageDown":s=this.adjacentStop(i,-1);break;case"ArrowRight":case"ArrowUp":case"PageUp":s=this.adjacentStop(i,1);break;case"Home":s=this.min;break;case"End":s=this.max;break;default:return}return t.preventDefault(),void this.applyValue(s,e)}switch(t.key){case"ArrowLeft":case"ArrowDown":s=i-this.step;break;case"ArrowRight":case"ArrowUp":s=i+this.step;break;case"PageDown":s=i-10*this.step;break;case"PageUp":s=i+10*this.step;break;case"Home":s=this.min;break;case"End":s=this.max;break;default:return}t.preventDefault(),this.applyValue(s,e)}dispatchValueChange(){this.dispatchEvent(new CustomEvent("db-change",{bubbles:!0,composed:!0,detail:{value:this.currentValue}}))}dispatchRangeChange(){const t=[...this.currentRange];for(const e of["db-change","db-range-change"])this.dispatchEvent(new CustomEvent(e,{bubbles:!0,composed:!0,detail:{range:[...t]}}))}}customElements.get("db-range-slider")||customElements.define("db-range-slider",DbRangeSlider);export{DbRangeSlider};
@@ -1,5 +1,3 @@
1
- import './db-checkbox.js';
2
-
3
1
  declare class DbSelect extends HTMLElement {
4
2
  static readonly formAssociated = true;
5
3
  static get observedAttributes(): string[];
@@ -19,18 +17,45 @@ declare class DbSelect extends HTMLElement {
19
17
  private built;
20
18
  private searchTerm;
21
19
  private focusedIndex;
20
+ private initialValue?;
21
+ private awaitingValue;
22
+ private userInteracted;
23
+ private formDisabled;
24
+ private wasOpen;
25
+ private searchFocusFrame?;
26
+ private typeahead;
27
+ private typeaheadAt;
28
+ get defaultValue(): string;
29
+ set defaultValue(value: string | string[]);
30
+ get disabled(): boolean;
31
+ set disabled(value: boolean);
32
+ get readOnly(): boolean;
33
+ set readOnly(value: boolean);
34
+ get required(): boolean;
35
+ set required(value: boolean);
36
+ get form(): HTMLFormElement | null;
37
+ get validity(): ValidityState;
38
+ get validationMessage(): string;
39
+ get willValidate(): boolean;
40
+ checkValidity(): boolean;
41
+ reportValidity(): boolean;
42
+ focus(options?: FocusOptions): void;
43
+ private get unavailable();
44
+ private get locked();
45
+ private captureDefault;
22
46
  get name(): string;
23
47
  set name(value: string);
24
48
  get open(): boolean;
25
49
  set open(value: boolean);
26
50
  get value(): string;
27
- set value(value: string);
51
+ set value(value: string | string[]);
28
52
  connectedCallback(): void;
29
53
  formAssociatedCallback(): void;
30
54
  disconnectedCallback(): void;
31
55
  formResetCallback(): void;
56
+ formStateRestoreCallback(state: string | File | FormData | null): void;
32
57
  formDisabledCallback(disabled: boolean): void;
33
- attributeChangedCallback(name: string): void;
58
+ attributeChangedCallback(name: string, oldValue: string | null): void;
34
59
  get multiple(): boolean;
35
60
  set multiple(value: boolean);
36
61
  get searchable(): boolean;
@@ -50,6 +75,8 @@ declare class DbSelect extends HTMLElement {
50
75
  private handleClear;
51
76
  private handleSearchInput;
52
77
  private handleDocumentMouseDown;
78
+ private handleFocusOut;
79
+ private moveFocus;
53
80
  private handleDocumentKeyDown;
54
81
  private getFilteredOptions;
55
82
  private selectOption;
@@ -58,12 +85,14 @@ declare class DbSelect extends HTMLElement {
58
85
  * combobox 트리거에 접근 가능한 이름을 붙인다.
59
86
  *
60
87
  * 트리거는 `<div role="combobox">` 라서 `<label for>` 로는 연결되지 않는다.
61
- * 라벨이 있으면 `aria-labelledby`, 없으면 소비자가 준 `aria-label`,
62
- * 그것도 없으면 placeholder 를 이름으로 쓴다. (A11Y-NAME)
88
+ * 명시적 aria-labelledby/aria-label, 보이는 label, placeholder 순으로
89
+ * 실제 trigger와 검색 입력 및 목록의 이름을 동기화한다. (A11Y-NAME)
63
90
  */
64
91
  private syncAccessibleName;
65
92
  private syncState;
66
93
  private syncOpenState;
94
+ private cancelSearchFocus;
95
+ private scheduleSearchFocus;
67
96
  private syncTrigger;
68
97
  private syncOptions;
69
98
  private escape;