@designbasekorea/ui-wc 0.9.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE-WORLD-MAP.md +10 -0
  3. package/README.md +60 -0
  4. package/dist/esm/components/db-accordion.d.ts +22 -10
  5. package/dist/esm/components/db-accordion.js +1 -1
  6. package/dist/esm/components/db-achievement-list.d.ts +2 -0
  7. package/dist/esm/components/db-achievement-list.js +1 -1
  8. package/dist/esm/components/db-activity-heatmap.d.ts +1 -0
  9. package/dist/esm/components/db-activity-heatmap.js +1 -1
  10. package/dist/esm/components/db-ad-banner.d.ts +2 -0
  11. package/dist/esm/components/db-ad-banner.js +1 -1
  12. package/dist/esm/components/db-ai-chat-list.d.ts +5 -1
  13. package/dist/esm/components/db-ai-chat-list.js +1 -1
  14. package/dist/esm/components/db-ai-chat.d.ts +2 -0
  15. package/dist/esm/components/db-ai-chat.js +1 -1
  16. package/dist/esm/components/db-ai-file-tree.js +1 -1
  17. package/dist/esm/components/db-alert.d.ts +1 -0
  18. package/dist/esm/components/db-alert.js +1 -1
  19. package/dist/esm/components/db-audio-player.d.ts +2 -0
  20. package/dist/esm/components/db-audio-player.js +1 -1
  21. package/dist/esm/components/db-avatar.d.ts +1 -0
  22. package/dist/esm/components/db-avatar.js +1 -1
  23. package/dist/esm/components/db-banner.d.ts +2 -0
  24. package/dist/esm/components/db-banner.js +1 -1
  25. package/dist/esm/components/db-button.d.ts +40 -2
  26. package/dist/esm/components/db-button.js +1 -1
  27. package/dist/esm/components/db-calendar.d.ts +5 -0
  28. package/dist/esm/components/db-calendar.js +1 -1
  29. package/dist/esm/components/db-card.d.ts +1 -0
  30. package/dist/esm/components/db-card.js +1 -1
  31. package/dist/esm/components/db-chat-composer.d.ts +3 -0
  32. package/dist/esm/components/db-chat-composer.js +1 -1
  33. package/dist/esm/components/db-chat-message.d.ts +2 -0
  34. package/dist/esm/components/db-chat-message.js +1 -1
  35. package/dist/esm/components/db-chat-orb.d.ts +13 -0
  36. package/dist/esm/components/db-chat-orb.js +1 -1
  37. package/dist/esm/components/db-chat-room.d.ts +1 -0
  38. package/dist/esm/components/db-chat-room.js +1 -1
  39. package/dist/esm/components/db-chat-typing.d.ts +2 -0
  40. package/dist/esm/components/db-chat-typing.js +1 -1
  41. package/dist/esm/components/db-chat-widget.d.ts +9 -0
  42. package/dist/esm/components/db-chat-widget.js +1 -1
  43. package/dist/esm/components/db-checkbox.d.ts +3 -0
  44. package/dist/esm/components/db-checkbox.js +1 -1
  45. package/dist/esm/components/db-chip.d.ts +2 -0
  46. package/dist/esm/components/db-chip.js +1 -1
  47. package/dist/esm/components/db-code-block.d.ts +1 -0
  48. package/dist/esm/components/db-code-block.js +1 -1
  49. package/dist/esm/components/db-color-picker.d.ts +4 -0
  50. package/dist/esm/components/db-color-picker.js +1 -1
  51. package/dist/esm/components/db-color-swatch.d.ts +3 -0
  52. package/dist/esm/components/db-color-swatch.js +1 -1
  53. package/dist/esm/components/db-confetti.d.ts +11 -5
  54. package/dist/esm/components/db-confetti.js +1 -1
  55. package/dist/esm/components/db-cookie-banner.d.ts +3 -0
  56. package/dist/esm/components/db-cookie-banner.js +1 -1
  57. package/dist/esm/components/db-countdown.d.ts +2 -1
  58. package/dist/esm/components/db-countdown.js +1 -1
  59. package/dist/esm/components/db-date-picker.d.ts +3 -0
  60. package/dist/esm/components/db-date-picker.js +1 -1
  61. package/dist/esm/components/db-dropdown.d.ts +1 -0
  62. package/dist/esm/components/db-dropdown.js +1 -1
  63. package/dist/esm/components/db-dropzone.d.ts +2 -0
  64. package/dist/esm/components/db-dropzone.js +1 -1
  65. package/dist/esm/components/db-email-verify.d.ts +37 -7
  66. package/dist/esm/components/db-email-verify.js +1 -1
  67. package/dist/esm/components/db-file-uploader.d.ts +2 -0
  68. package/dist/esm/components/db-file-uploader.js +1 -1
  69. package/dist/esm/components/db-floating-action-button.d.ts +1 -1
  70. package/dist/esm/components/db-floating-action-button.js +1 -1
  71. package/dist/esm/components/db-form.d.ts +2 -0
  72. package/dist/esm/components/db-form.js +1 -1
  73. package/dist/esm/components/db-gantt.d.ts +2 -0
  74. package/dist/esm/components/db-gantt.js +1 -1
  75. package/dist/esm/components/db-image-list.d.ts +3 -0
  76. package/dist/esm/components/db-image-list.js +1 -1
  77. package/dist/esm/components/db-image.d.ts +1 -0
  78. package/dist/esm/components/db-image.js +1 -1
  79. package/dist/esm/components/db-input-group.d.ts +30 -0
  80. package/dist/esm/components/db-input-group.js +1 -0
  81. package/dist/esm/components/db-input.d.ts +9 -1
  82. package/dist/esm/components/db-input.js +1 -1
  83. package/dist/esm/components/db-kanban.d.ts +3 -0
  84. package/dist/esm/components/db-kanban.js +1 -1
  85. package/dist/esm/components/db-lightbox.d.ts +2 -0
  86. package/dist/esm/components/db-lightbox.js +1 -1
  87. package/dist/esm/components/db-list.d.ts +2 -0
  88. package/dist/esm/components/db-list.js +1 -1
  89. package/dist/esm/components/db-markdown-editor.d.ts +2 -0
  90. package/dist/esm/components/db-markdown-editor.js +1 -1
  91. package/dist/esm/components/db-menu-item.js +1 -1
  92. package/dist/esm/components/db-navbar.d.ts +6 -0
  93. package/dist/esm/components/db-navbar.js +1 -1
  94. package/dist/esm/components/db-node.d.ts +1 -0
  95. package/dist/esm/components/db-node.js +1 -1
  96. package/dist/esm/components/db-onboarding-modal.d.ts +2 -0
  97. package/dist/esm/components/db-onboarding-modal.js +1 -1
  98. package/dist/esm/components/db-otp-input.d.ts +4 -1
  99. package/dist/esm/components/db-otp-input.js +1 -1
  100. package/dist/esm/components/db-page-header.d.ts +11 -0
  101. package/dist/esm/components/db-page-header.js +1 -1
  102. package/dist/esm/components/db-phone-verify.d.ts +33 -11
  103. package/dist/esm/components/db-phone-verify.js +1 -1
  104. package/dist/esm/components/db-popover.d.ts +1 -0
  105. package/dist/esm/components/db-popover.js +1 -1
  106. package/dist/esm/components/db-progress-step.d.ts +2 -0
  107. package/dist/esm/components/db-progress-step.js +1 -1
  108. package/dist/esm/components/db-radio.d.ts +3 -0
  109. package/dist/esm/components/db-radio.js +1 -1
  110. package/dist/esm/components/db-range-slider.d.ts +36 -7
  111. package/dist/esm/components/db-range-slider.js +1 -1
  112. package/dist/esm/components/db-rating.d.ts +1 -0
  113. package/dist/esm/components/db-rating.js +1 -1
  114. package/dist/esm/components/db-search-bar.d.ts +2 -0
  115. package/dist/esm/components/db-search-bar.js +1 -1
  116. package/dist/esm/components/db-section-logo-cloud.js +1 -1
  117. package/dist/esm/components/db-select.d.ts +35 -6
  118. package/dist/esm/components/db-select.js +1 -1
  119. package/dist/esm/components/db-share.d.ts +5 -0
  120. package/dist/esm/components/db-share.js +1 -1
  121. package/dist/esm/components/db-sidebar.d.ts +3 -0
  122. package/dist/esm/components/db-sidebar.js +1 -1
  123. package/dist/esm/components/db-signature.d.ts +2 -0
  124. package/dist/esm/components/db-signature.js +1 -1
  125. package/dist/esm/components/db-spinner.d.ts +2 -0
  126. package/dist/esm/components/db-spinner.js +1 -1
  127. package/dist/esm/components/db-stepper.d.ts +2 -0
  128. package/dist/esm/components/db-stepper.js +1 -1
  129. package/dist/esm/components/db-table.d.ts +2 -0
  130. package/dist/esm/components/db-table.js +1 -1
  131. package/dist/esm/components/db-tabs.d.ts +37 -13
  132. package/dist/esm/components/db-tabs.js +1 -1
  133. package/dist/esm/components/db-textarea.d.ts +13 -1
  134. package/dist/esm/components/db-textarea.js +1 -1
  135. package/dist/esm/components/db-ticket.js +1 -1
  136. package/dist/esm/components/db-time-picker.d.ts +2 -0
  137. package/dist/esm/components/db-time-picker.js +1 -1
  138. package/dist/esm/components/db-timeline.d.ts +4 -0
  139. package/dist/esm/components/db-timeline.js +1 -1
  140. package/dist/esm/components/db-toast.d.ts +2 -0
  141. package/dist/esm/components/db-toast.js +1 -1
  142. package/dist/esm/components/db-toggle.d.ts +33 -4
  143. package/dist/esm/components/db-toggle.js +1 -1
  144. package/dist/esm/components/db-tool-layout.d.ts +92 -0
  145. package/dist/esm/components/db-tool-layout.js +1 -0
  146. package/dist/esm/components/db-tool-panel.d.ts +50 -0
  147. package/dist/esm/components/db-tool-panel.js +1 -0
  148. package/dist/esm/components/db-top-banner.d.ts +2 -0
  149. package/dist/esm/components/db-top-banner.js +1 -1
  150. package/dist/esm/components/db-two-factor.d.ts +35 -12
  151. package/dist/esm/components/db-two-factor.js +1 -1
  152. package/dist/esm/components/db-video-player.d.ts +3 -0
  153. package/dist/esm/components/db-video-player.js +1 -1
  154. package/dist/esm/components/db-world-map.d.ts +60 -0
  155. package/dist/esm/components/db-world-map.js +1 -0
  156. package/dist/esm/components/db-youtube-player.d.ts +2 -0
  157. package/dist/esm/components/db-youtube-player.js +1 -1
  158. package/dist/esm/components/db-zoom-control.d.ts +52 -0
  159. package/dist/esm/components/db-zoom-control.js +1 -0
  160. package/dist/esm/components/input-validation.js +1 -1
  161. package/dist/esm/components/paper-card.d.ts +2 -0
  162. package/dist/esm/components/paper-card.js +1 -1
  163. package/dist/esm/core/control-label.js +1 -0
  164. package/dist/esm/core/i18n-copy.js +1 -1
  165. package/dist/esm/core/i18n-en.js +1 -1
  166. package/dist/esm/core/i18n.d.ts +31 -1
  167. package/dist/esm/core/i18n.js +1 -1
  168. package/dist/esm/core/range-values.js +1 -0
  169. package/dist/esm/core/text-field-state.js +1 -0
  170. package/dist/esm/helpers/world-countries.js +1 -0
  171. package/dist/esm/helpers/world-land.js +1 -0
  172. package/dist/esm/index.d.ts +6 -1
  173. package/dist/esm/index.js +1 -1
  174. package/dist/esm/node_modules/@designbasekorea/ui-contracts/dist/components.js +1 -1
  175. package/dist/index.cjs +1 -1
  176. package/dist/index.css +1 -1
  177. package/dist/index.d.ts +804 -83
  178. package/dist/index.esm.js +1 -1
  179. package/dist/react/index.cjs +1 -1
  180. package/dist/react/index.d.ts +6211 -648
  181. package/dist/react/index.esm.js +1 -1
  182. package/package.json +9 -4
@@ -0,0 +1,50 @@
1
+ interface ToolPanelTab {
2
+ id: string;
3
+ label: string;
4
+ disabled?: boolean;
5
+ }
6
+ /** Content stays in its original DOM across collapse and tab changes. Nest appearance="section"
7
+ * panels inside a body for independent disclosure groups, or appearance="card" for inset cards. */
8
+ declare class DbToolPanel extends HTMLElement {
9
+ static get observedAttributes(): string[];
10
+ private headingElement?;
11
+ private titleButton?;
12
+ private closeButton?;
13
+ private tabsElement?;
14
+ private observer?;
15
+ private unsubscribe?;
16
+ private uid;
17
+ get heading(): string;
18
+ set heading(value: string);
19
+ get padding(): 'none' | 's' | 'm';
20
+ set padding(value: 'none' | 's' | 'm');
21
+ get collapsible(): boolean;
22
+ set collapsible(value: boolean);
23
+ get collapsed(): boolean;
24
+ set collapsed(value: boolean);
25
+ get closable(): boolean;
26
+ set closable(value: boolean);
27
+ get divider(): boolean;
28
+ set divider(value: boolean);
29
+ get appearance(): 'default' | 'section' | 'card';
30
+ set appearance(value: 'default' | 'section' | 'card');
31
+ get tabs(): ToolPanelTab[];
32
+ set tabs(value: ToolPanelTab[] | string);
33
+ get selectedTab(): string;
34
+ set selectedTab(value: string);
35
+ connectedCallback(): void;
36
+ disconnectedCallback(): void;
37
+ attributeChangedCallback(): void;
38
+ toggle(): void;
39
+ private ensureChrome;
40
+ private makeTitle;
41
+ private sync;
42
+ }
43
+ declare global {
44
+ interface HTMLElementTagNameMap {
45
+ 'db-tool-panel': DbToolPanel;
46
+ }
47
+ }
48
+
49
+ export { DbToolPanel };
50
+ export type { ToolPanelTab };
@@ -0,0 +1 @@
1
+ import{createDbIcon as t}from"./db-icons.js";import{dbUid as e}from"../core/uid.js";import{subscribeDbLocale as s,t as i}from"../core/i18n.js";class DbToolPanel extends HTMLElement{constructor(){super(...arguments),this.uid=e("tool-panel")}static get observedAttributes(){return["heading","padding","collapsible","collapsed","closable","divider","appearance","tabs","selected-tab"]}get heading(){return this.getAttribute("heading")||""}set heading(t){this.setAttribute("heading",t)}get padding(){const t=this.getAttribute("padding");return"none"===t||"s"===t?t:"m"}set padding(t){this.setAttribute("padding",t)}get collapsible(){return this.hasAttribute("collapsible")}set collapsible(t){this.toggleAttribute("collapsible",t)}get collapsed(){return this.hasAttribute("collapsed")}set collapsed(t){this.toggleAttribute("collapsed",t)}get closable(){return this.hasAttribute("closable")}set closable(t){this.toggleAttribute("closable",t)}get divider(){return this.hasAttribute("divider")}set divider(t){this.toggleAttribute("divider",t)}get appearance(){const t=this.getAttribute("appearance");return"section"===t||"card"===t?t:"default"}set appearance(t){this.setAttribute("appearance",t)}get tabs(){try{const t=JSON.parse(this.getAttribute("tabs")||"[]"),e=new Set;return Array.isArray(t)?t.filter(t=>t&&"string"==typeof t.id&&!!t.id&&"string"==typeof t.label&&!!t.label&&!e.has(t.id)&&!!e.add(t.id)):[]}catch{return[]}}set tabs(t){this.setAttribute("tabs","string"==typeof t?t:JSON.stringify(t))}get selectedTab(){const t=this.tabs;return t.find(t=>t.id===this.getAttribute("selected-tab")&&!t.disabled)?.id||t.find(t=>!t.disabled)?.id||""}set selectedTab(t){this.setAttribute("selected-tab",t)}connectedCallback(){this.classList.add("designbase-wc-tool-panel"),this.id||(this.id=this.uid),this.ensureChrome(),this.sync(),this.observer=new MutationObserver(t=>{t.some(t=>t.target===this||t.target.parentElement===this)&&this.sync()}),this.observer.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["slot"]}),this.unsubscribe=s(()=>this.sync())}disconnectedCallback(){this.observer?.disconnect(),this.unsubscribe?.()}attributeChangedCallback(){this.isConnected&&this.sync()}toggle(){this.collapsible&&(this.collapsed=!this.collapsed,this.dispatchEvent(new CustomEvent("db-collapse-change",{bubbles:!0,composed:!0,detail:{collapsed:this.collapsed}})))}ensureChrome(){this.headingElement?.parentElement!==this&&(this.headingElement=document.createElement("div"),this.headingElement.className="designbase-wc-tool-panel__heading",this.headingElement.setAttribute("data-tool-heading",""),this.headingElement.setAttribute("data-tool-chrome",""),this.titleButton=this.makeTitle(),this.headingElement.append(this.titleButton),this.closeButton=document.createElement("button"),this.closeButton.type="button",this.closeButton.className="designbase-wc-tool-panel__close",this.closeButton.setAttribute("data-tool-chrome",""),this.closeButton.append(t("close",14)),this.closeButton.addEventListener("click",()=>this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))),this.tabsElement=document.createElement("db-tabs"),this.tabsElement.className="designbase-wc-tool-panel__tabs",this.tabsElement.setAttribute("data-tool-chrome",""),this.tabsElement.size="s",this.tabsElement.addEventListener("db-change",t=>{t.target===this.tabsElement&&(t.stopPropagation(),this.selectedTab=t.detail.id,this.dispatchEvent(new CustomEvent("db-tab-change",{bubbles:!0,composed:!0,detail:{id:this.selectedTab}})))}),this.prepend(this.headingElement,this.closeButton,this.tabsElement))}makeTitle(){const t=document.createElement(this.collapsible?"button":"span");return t.className="designbase-wc-tool-panel__title",t instanceof HTMLButtonElement&&(t.type="button",t.addEventListener("click",()=>this.toggle())),t}sync(){if(!(this.titleButton&&this.headingElement&&this.tabsElement&&this.closeButton))return;if(this.titleButton instanceof HTMLButtonElement!==this.collapsible){const t=this.makeTitle();this.titleButton.replaceWith(t),this.titleButton=t}const s=this.heading;if(this.titleButton.dataset.heading!==s||this.titleButton.dataset.collapsible!==String(this.collapsible)){if(this.titleButton.replaceChildren(),this.collapsible){const e=t("chevron-down",12);e.classList.add("designbase-wc-tool-panel__chevron"),this.titleButton.append(e)}const e=document.createElement("span");e.textContent=s,this.titleButton.append(e),this.titleButton.dataset.heading=s,this.titleButton.dataset.collapsible=String(this.collapsible)}this.headingElement.hidden=!s,this.collapsible?this.titleButton.tabIndex=0:this.titleButton.removeAttribute("tabindex"),this.collapsible?this.titleButton.setAttribute("aria-expanded",String(!this.collapsed)):this.titleButton.removeAttribute("aria-expanded"),this.closeButton.hidden=!this.closable,this.closeButton.setAttribute("aria-label",i("toolPanel.close")),this.closeButton.title=i("toolPanel.close");const a=this.tabs;this.tabsElement.hidden=!a.length;const l=JSON.stringify(a);this.tabsElement.getAttribute("items")!==l&&(this.tabsElement.items=a),this.tabsElement.selectedId!==this.selectedTab&&(this.tabsElement.selectedId=this.selectedTab),this.tabsElement.setAttribute("aria-label",s||i("toolPanel.tabs"));const n=[];for(const t of Array.from(this.children)){if(t.hasAttribute("data-tool-chrome")||"actions"===t.getAttribute("slot"))continue;const s=t.getAttribute("slot")||"body";t.id||(t.id=e(`${this.uid}-content`)),n.push(t.id);const i=s.startsWith("tab-");if(t.toggleAttribute("data-tool-tab-hidden",i&&s.slice(4)!==this.selectedTab),i){const e=a.findIndex(t=>`tab-${t.id}`===s),i=this.tabsElement.querySelectorAll('[role="tab"]')[e];t.setAttribute("role","tabpanel"),i&&(t.setAttribute("aria-labelledby",i.id),i.setAttribute("aria-controls",t.id))}}this.titleButton.setAttribute("aria-controls",n.join(" ")),this.dataset.padding=this.padding,this.dataset.appearance=this.appearance,this.dataset.collapsed=String(this.collapsible&&this.collapsed)}}customElements.get("db-tool-panel")||customElements.define("db-tool-panel",DbToolPanel);export{DbToolPanel};
@@ -1,6 +1,8 @@
1
1
  declare class DbTopBanner extends HTMLElement {
2
+ private unsubscribeLocale?;
2
3
  static get observedAttributes(): string[];
3
4
  connectedCallback(): void;
5
+ disconnectedCallback(): void;
4
6
  attributeChangedCallback(): void;
5
7
  private isDarkBg;
6
8
  private createLinkButton;
@@ -1 +1 @@
1
- import{dbText as e}from"../core/i18n.js";import{createDbIcon as t}from"./db-icons.js";import{TOP_BANNER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbTopBanner extends HTMLElement{static get observedAttributes(){return["badge-text","content","image","link-text","variant","fixed","dismissed"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}isDarkBg(e){return"primary"===e||"dark"===e}createLinkButton(e,n){const s=this.isDarkBg(e),a=document.createElement("db-button");a.setAttribute("variant",s?"secondary":"ghost"),a.setAttribute("size","s"),a.classList.add("designbase-wc-top-banner__link");const i=document.createElement("span");i.textContent=n;const r=document.createElement("span");return r.className="designbase-wc-button__end-icon",r.append(t("chevron-right",16)),a.append(i,r),a.addEventListener("db-click",()=>{this.dispatchEvent(new CustomEvent("db-link",{bubbles:!0,composed:!0}))}),a}createCloseButton(){const n=document.createElement("db-button");return n.setAttribute("variant","ghost"),n.setAttribute("size","m"),n.setAttribute("icon-only",""),n.setAttribute("aria-label",e("닫기")),n.classList.add("designbase-wc-top-banner__close"),n.append(t("close",20)),n.addEventListener("db-click",()=>{this.setAttribute("dismissed",""),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0})),this.render()}),n}createBadge(e,t){const n=document.createElement("db-badge");return n.setAttribute("size","s"),n.setAttribute("variant",t?"secondary":"primary"),n.setAttribute("type","text"),n.textContent=e,n.classList.add("designbase-wc-top-banner__badge"),n}render(){if(this.hasAttribute("dismissed"))return this.replaceChildren(),this.className="designbase-wc-top-banner designbase-wc-top-banner--dismissed",void(this.style.display="none");this.style.display="";const e=this.getAttribute("badge-text")||"",t=this.getAttribute("content")||"",s=this.getAttribute("image")||"",a=this.getAttribute("link-text")||"",i=this.getAttribute("variant")||"primary",r=n.includes(i)?i:"primary",d=this.hasAttribute("fixed"),o=this.isDarkBg(r);if(this.className=["designbase-wc-top-banner",`designbase-wc-top-banner--${r}`,d&&"designbase-wc-top-banner--fixed",s&&"designbase-wc-top-banner--has-image"].filter(Boolean).join(" "),this.setAttribute("role","alert"),s?this.style.setProperty("--db-top-banner-bg-image",`url("${s}")`):this.style.removeProperty("--db-top-banner-bg-image"),this.replaceChildren(),s){const e=document.createElement("div");e.className="designbase-wc-top-banner__background",e.setAttribute("aria-hidden","true");const t=document.createElement("div");t.className="designbase-wc-top-banner__overlay",t.setAttribute("aria-hidden","true"),this.append(e,t)}const c=document.createElement("div");c.className="designbase-wc-top-banner__container";const b=document.createElement("div");b.className="designbase-wc-top-banner__content",e&&b.append(this.createBadge(e,o));const p=document.createElement("span");p.className="designbase-wc-top-banner__text",p.textContent=t,b.append(p),a&&b.append(this.createLinkButton(r,a)),c.append(b),c.append(this.createCloseButton()),this.append(c)}}customElements.get("db-top-banner")||customElements.define("db-top-banner",DbTopBanner);export{DbTopBanner};
1
+ import{subscribeDbLocale as e,dbText as t}from"../core/i18n.js";import{createDbIcon as s}from"./db-icons.js";import{TOP_BANNER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbTopBanner extends HTMLElement{static get observedAttributes(){return["badge-text","content","image","link-text","variant","fixed","dismissed"]}connectedCallback(){this.render(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.querySelector(".designbase-wc-top-banner__close")?.setAttribute("aria-label",t("닫기")))}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(){this.render()}isDarkBg(e){return"primary"===e||"dark"===e}createLinkButton(e,t){const n=this.isDarkBg(e),a=document.createElement("db-button");a.setAttribute("variant",n?"secondary":"ghost"),a.setAttribute("size","s"),a.classList.add("designbase-wc-top-banner__link");const i=document.createElement("span");i.textContent=t;const r=document.createElement("span");return r.className="designbase-wc-button__end-icon",r.append(s("chevron-right",16)),a.append(i,r),a.addEventListener("db-click",()=>{this.dispatchEvent(new CustomEvent("db-link",{bubbles:!0,composed:!0}))}),a}createCloseButton(){const e=document.createElement("db-button");return e.setAttribute("variant","ghost"),e.setAttribute("size","m"),e.setAttribute("icon-only",""),e.setAttribute("aria-label",t("닫기")),e.classList.add("designbase-wc-top-banner__close"),e.append(s("close",20)),e.addEventListener("db-click",()=>{this.setAttribute("dismissed",""),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0})),this.render()}),e}createBadge(e,t){const s=document.createElement("db-badge");return s.setAttribute("size","s"),s.setAttribute("variant",t?"secondary":"primary"),s.setAttribute("type","text"),s.textContent=e,s.classList.add("designbase-wc-top-banner__badge"),s}render(){if(this.hasAttribute("dismissed"))return this.replaceChildren(),this.className="designbase-wc-top-banner designbase-wc-top-banner--dismissed",void(this.style.display="none");this.style.display="";const e=this.getAttribute("badge-text")||"",t=this.getAttribute("content")||"",s=this.getAttribute("image")||"",a=this.getAttribute("link-text")||"",i=this.getAttribute("variant")||"primary",r=n.includes(i)?i:"primary",c=this.hasAttribute("fixed"),d=this.isDarkBg(r);if(this.className=["designbase-wc-top-banner",`designbase-wc-top-banner--${r}`,c&&"designbase-wc-top-banner--fixed",s&&"designbase-wc-top-banner--has-image"].filter(Boolean).join(" "),this.setAttribute("role","alert"),s?this.style.setProperty("--db-top-banner-bg-image",`url("${s}")`):this.style.removeProperty("--db-top-banner-bg-image"),this.replaceChildren(),s){const e=document.createElement("div");e.className="designbase-wc-top-banner__background",e.setAttribute("aria-hidden","true");const t=document.createElement("div");t.className="designbase-wc-top-banner__overlay",t.setAttribute("aria-hidden","true"),this.append(e,t)}const o=document.createElement("div");o.className="designbase-wc-top-banner__container";const b=document.createElement("div");b.className="designbase-wc-top-banner__content",e&&b.append(this.createBadge(e,d));const l=document.createElement("span");l.className="designbase-wc-top-banner__text",l.textContent=t,b.append(l),a&&b.append(this.createLinkButton(r,a)),o.append(b),o.append(this.createCloseButton()),this.append(o)}}customElements.get("db-top-banner")||customElements.define("db-top-banner",DbTopBanner);export{DbTopBanner};
@@ -20,41 +20,64 @@ declare class DbTwoFactor extends HTMLElement {
20
20
  static get observedAttributes(): string[];
21
21
  private built;
22
22
  private timerId;
23
+ /** 코드 유효 시간(초) — SMS·이메일에서만 쓴다 */
23
24
  private remaining;
25
+ /** 재전송 버튼이 다시 열리기까지 남은 시간(초) */
26
+ private cooldownLeft;
27
+ private expired;
28
+ private backupMode;
29
+ private resendCount;
30
+ private closeBtn;
31
+ private headerEl;
32
+ private eyebrowEl;
33
+ private titleEl;
34
+ private descEl;
24
35
  private segment;
25
- private otpInput;
26
- private hintEl;
36
+ private codeBlock;
37
+ private codeLabelEl;
27
38
  private timerEl;
28
- private codeHead;
29
- private resendBtn;
39
+ private otpInput;
40
+ private expiredEl;
41
+ private backupInput;
30
42
  private verifyBtn;
43
+ private resendGroup;
44
+ private resendHintEl;
45
+ private resendBtn;
31
46
  private backupBtn;
32
- private errorEl;
33
- private channelActions;
34
- private resendStatus;
35
- private backupBlock;
36
- private backupInput;
37
47
  private backupCancelBtn;
38
- private backupMode;
39
- private resendCount;
48
+ private verifiedBlock;
49
+ private verifiedTitleEl;
50
+ private verifiedDescEl;
51
+ private doneBtn;
52
+ private errorEl;
40
53
  get method(): Method;
41
54
  set method(v: Method);
42
55
  get size(): Size;
43
56
  set size(v: Size);
44
57
  get countdown(): number;
45
58
  set countdown(v: number);
59
+ /** 재전송을 다시 누를 수 있기까지 기다리는 시간(초). 기본 30초. 0이면 바로 누를 수 있다. */
60
+ get resendCooldown(): number;
61
+ set resendCooldown(v: number);
46
62
  get maxResends(): number;
63
+ private get usesTimer();
47
64
  connectedCallback(): void;
48
65
  disconnectedCallback(): void;
49
66
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
50
67
  /** 수동 제어 모드에서 코드 발송 성공 후 카운트다운을 시작합니다. */
51
68
  restartCountdown(): void;
69
+ /** 인증 완료 화면으로 전환합니다. */
70
+ markVerified(): void;
52
71
  private clearTimer;
53
- private startTimer;
72
+ private startTimers;
54
73
  private renderTimer;
74
+ private syncVerifyDisabled;
75
+ private emit;
55
76
  private build;
56
77
  private sync;
57
78
  private requestVerify;
79
+ /** 번역 문장 안의 {0} 자리에 값을 굵게 넣는다 — 언어마다 어순이 달라 조각내 번역하지 않는다. */
80
+ private setTextWithValue;
58
81
  }
59
82
  declare global {
60
83
  interface HTMLElementTagNameMap {
@@ -1 +1 @@
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-segment-control.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{TWO_FACTOR_SIZES as n}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 DbTwoFactor extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0,this.backupMode=!1,this.resendCount=0}static get observedAttributes(){return["method","code","size","disabled","error","error-message","title","description","verify-label","code-length","show-backup","countdown","destination","manual","sending","verifying","auto-submit","resend-label","max-resends"]}get method(){const t=this.getAttribute("method");return"sms"===t||"email"===t||"app"===t?t:"app"}set method(t){this.setAttribute("method",t)}get size(){return s(this.getAttribute("size"),n,"m")}set size(t){this.setAttribute("size",t)}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}set countdown(t){this.setAttribute("countdown",String(t))}get maxResends(){const t=Number(this.getAttribute("max-resends")||3);return Number.isFinite(t)?Math.max(1,Math.min(5,Math.round(t))):3}connectedCallback(){this.build(),this.sync(),"app"===this.method||this.hasAttribute("manual")||this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"method"!==t&&"manual"!==t||e===i||("method"===t&&(this.resendCount=0,this.backupMode=!1),"app"===this.method||this.hasAttribute("manual")?this.clearTimer():this.startTimer(this.countdown)))}restartCountdown(){"app"!==this.method&&this.startTimer(this.countdown)}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0,this.timerEl&&(this.timerEl.textContent="")}startTimer(t){this.clearTimer(),this.remaining=t,this.renderTimer(),this.timerId=setInterval(()=>{this.remaining-=1,this.renderTimer(),this.remaining<=0&&(this.clearTimer(),this.resendBtn.disabled=!1,this.dispatchEvent(new CustomEvent("db-countdown-complete",{bubbles:!0,composed:!0})))},1e3)}renderTimer(){const i=Math.max(0,Math.floor(this.remaining/60)),s=Math.max(0,this.remaining%60);this.timerEl.textContent="app"===this.method?"":`${String(i).padStart(2,"0")}:${String(s).padStart(2,"0")}`,"app"!==this.method?this.timerEl.setAttribute("aria-label",t("남은 시간 {0}분 {1}초",[i,s])):this.timerEl.removeAttribute("aria-label");const n=this.resendCount>=this.maxResends;this.resendBtn.disabled="app"===this.method||this.remaining>0||n||this.hasAttribute("disabled"),"app"===this.method?this.resendStatus.textContent="":n?this.resendStatus.textContent=e("재전송 한도에 도달했습니다. 새 인증을 시작해 주세요."):this.remaining>0?this.resendStatus.textContent=t("{0}:{1} 후 재전송 가능 · 남은 횟수 {2}회",[String(i).padStart(2,"0"),String(s).padStart(2,"0"),this.maxResends-this.resendCount]):this.resendStatus.textContent=t("재전송 가능 · 남은 횟수 {0}회",[this.maxResends-this.resendCount])}build(){if(this.built)return;this.built=!0;const t="designbase-wc-two-factor";this.replaceChildren();const s=el("div",`${t}__header`),n=el("div",`${t}__icon`);n.append(i("lock",22));const r=el("div",`${t}__titles`),o=el("h3",`${t}__title`);o.dataset.role="title";const a=el("p",`${t}__description`);a.dataset.role="description",r.append(o,a),s.append(n,r),this.segment=document.createElement("db-segment-control"),this.segment.setAttribute("options",JSON.stringify([{value:"app",label:e("인증 앱")},{value:"sms",label:"SMS"},{value:"email",label:e("이메일")}].map(t=>({value:t.value,label:t.label})))),this.segment.setAttribute("full-width",""),this.segment.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"app");e!==this.method&&(this.setAttribute("code",""),this.method=e,this.dispatchEvent(new CustomEvent("db-method-change",{detail:{method:e},bubbles:!0,composed:!0})),"app"!==e&&this.dispatchEvent(new CustomEvent("db-send-code",{detail:{method:e,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})))}),this.hintEl=el("p",`${t}__hint`),this.hintEl.dataset.role="hint",this.codeHead=el("div",`${t}__code-head`);const h=el("span",`${t}__code-label`);h.textContent=e("인증 코드"),this.timerEl=el("span",`${t}__timer`),this.codeHead.append(h,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:e},bubbles:!0,composed:!0}))}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.dispatchEvent(new CustomEvent("db-code-complete",{detail:{code:e,method:this.method},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()}),this.channelActions=el("div",`${t}__channel`),this.resendBtn=el("button",`${t}__resend`),this.resendBtn.type="button",this.resendBtn.textContent=e("코드 재전송"),this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.resendCount+=1,this.dispatchEvent(new CustomEvent("db-resend",{detail:{method:this.method,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})),this.hasAttribute("manual")?this.renderTimer():this.startTimer(this.countdown))}),this.resendStatus=el("p",`${t}__resend-status`),this.resendStatus.setAttribute("aria-live","polite"),this.channelActions.append(this.resendBtn,this.resendStatus),this.backupBlock=el("div",`${t}__backup-block`);const d=el("p",`${t}__hint`);d.textContent=e("계정 설정 시 저장한 8자리 백업 코드 중 사용하지 않은 코드를 입력하세요."),this.backupInput=document.createElement("db-input"),this.backupInput.setAttribute("label","백업 코드"),this.backupInput.setAttribute("placeholder","XXXX-XXXX"),this.backupInput.setAttribute("autocomplete","one-time-code"),this.backupInput.setAttribute("full-width",""),this.backupInput.addEventListener("db-input",t=>{t.stopPropagation();const e=String(t.detail?.value||"").toUpperCase().replace(/[^A-Z0-9]/g,"").slice(0,8),i=e.length>4?`${e.slice(0,4)}-${e.slice(4)}`:e;this.backupInput.value=i,this.sync()}),this.backupCancelBtn=el("button",`${t}__backup`),this.backupCancelBtn.type="button",this.backupCancelBtn.textContent=e("인증 코드로 돌아가기"),this.backupCancelBtn.addEventListener("click",()=>{this.backupMode=!1,this.sync()}),this.backupBlock.append(d,this.backupInput,this.backupCancelBtn),this.verifyBtn=document.createElement("db-button"),this.verifyBtn.setAttribute("variant","primary"),this.verifyBtn.setAttribute("full-width",""),this.verifyBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestVerify()}),this.backupBtn=el("button",`${t}__backup`),this.backupBtn.type="button",this.backupBtn.textContent=e("백업 코드로 인증"),this.backupBtn.addEventListener("click",()=>{this.backupMode=!0,this.sync(),requestAnimationFrame(()=>this.backupInput?.focus?.()),this.dispatchEvent(new CustomEvent("db-backup-request",{bubbles:!0,composed:!0}))}),this.errorEl=el("p",`${t}__error`),this.errorEl.hidden=!0,this.append(s,this.segment,this.hintEl,this.codeHead,this.otpInput,this.channelActions,this.backupBlock,this.verifyBtn,this.backupBtn,this.errorEl)}sync(){const t="designbase-wc-two-factor",i=this.size,s=this.method,n=this.hasAttribute("disabled"),r=this.getAttribute("code-length")||"6",o=this.getAttribute("destination")||"";this.className=[t,`${t}--${i}`,`${t}--${s}`,n&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" ");const a=this.querySelector('[data-role="title"]'),h=this.querySelector('[data-role="description"]');a.textContent=this.getAttribute("title")||"2단계 인증",h.textContent=this.getAttribute("description")||"계속하려면 인증 코드를 입력해 주세요.",this.segment.setAttribute("value",s),this.segment.setAttribute("size",i),this.segment.toggleAttribute("disabled",n),this.segment.toggleAttribute("hidden",this.backupMode);const d=this.querySelector('[data-role="hint"]');d.textContent="app"===s?e("인증 앱(Google Authenticator, 1Password 등)에 표시된 6자리 코드를 입력하세요."):"sms"===s?o?`${o} 으로 보낸 SMS 인증 코드를 입력하세요.`:"등록된 휴대폰으로 보낸 SMS 인증 코드를 입력하세요.":o?`${o} 으로 보낸 이메일 인증 코드를 입력하세요.`:"등록된 이메일로 보낸 인증 코드를 입력하세요.",this.otpInput.setAttribute("length",r),this.otpInput.setAttribute("size",i),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",n),this.otpInput.toggleAttribute("error",this.hasAttribute("error")),this.getAttribute("error-message")?this.otpInput.setAttribute("error-message",this.getAttribute("error-message")||""):this.otpInput.removeAttribute("error-message"),this.hintEl.hidden=this.backupMode,this.codeHead.hidden=this.backupMode,this.otpInput.hidden=this.backupMode,this.channelActions.hidden="app"===s||this.backupMode,this.backupBlock.hidden=!this.backupMode,this.backupInput.setAttribute("size",i),this.backupInput.toggleAttribute("disabled",n),this.resendBtn.textContent=this.hasAttribute("sending")?"전송 중…":this.getAttribute("resend-label")||"코드 재전송",this.renderTimer(),this.hasAttribute("sending")&&(this.resendBtn.disabled=!0),this.verifyBtn.setAttribute("size",i),this.verifyBtn.textContent=this.backupMode?"백업 코드로 인증":this.getAttribute("verify-label")||"인증하기";const u=this.getAttribute("code")||"",c=String(this.backupInput.value||"").replace(/-/g,"");this.verifyBtn.toggleAttribute("disabled",n||(this.backupMode?8!==c.length:u.length<Number(r))),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.backupBtn.hidden=!this.hasAttribute("show-backup")||this.backupMode;const l=this.getAttribute("error-message")||"";this.errorEl.textContent=l,this.errorEl.hidden=!this.hasAttribute("error")||!l}requestVerify(){if(this.backupMode){const t=String(this.backupInput.value||"").replace(/-/g,"");if(8!==t.length||this.hasAttribute("disabled"))return;return void this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:"backup",code:t},bubbles:!0,composed:!0}))}const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:this.method,code:t},bubbles:!0,composed:!0}))}}customElements.get("db-two-factor")||customElements.define("db-two-factor",DbTwoFactor);export{DbTwoFactor};
1
+ import{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-segment-control.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{TWO_FACTOR_SIZES as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";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}class DbTwoFactor extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0,this.cooldownLeft=0,this.expired=!1,this.backupMode=!1,this.resendCount=0}static get observedAttributes(){return["method","code","size","disabled","error","error-message","eyebrow","title","description","verify-label","done-label","code-length","show-backup","countdown","resend-cooldown","destination","closable","verified","manual","sending","verifying","auto-submit","resend-label","max-resends"]}get method(){const t=this.getAttribute("method");return"sms"===t||"email"===t||"app"===t?t:"app"}set method(t){this.setAttribute("method",t)}get size(){return s(this.getAttribute("size"),n,"m")}set size(t){this.setAttribute("size",t)}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}set countdown(t){this.setAttribute("countdown",String(t))}get 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))}get maxResends(){const t=Number(this.getAttribute("max-resends")||3);return Number.isFinite(t)?Math.max(1,Math.min(5,Math.round(t))):3}get usesTimer(){return"app"!==this.method&&!this.hasAttribute("verified")}connectedCallback(){this.build(),this.sync(),this.usesTimer&&!this.hasAttribute("manual")&&this.startTimers()}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&("method"!==t&&"manual"!==t&&"verified"!==t||e===i||("method"===t&&(this.resendCount=0,this.backupMode=!1),!this.usesTimer||this.hasAttribute("manual")?this.clearTimer():this.startTimers()),this.sync())}restartCountdown(){this.usesTimer&&this.startTimers()}markVerified(){this.setAttribute("verified","")}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="app"===this.method,s=Math.max(0,Math.floor(this.remaining/60)),n=Math.max(0,this.remaining%60);this.timerEl.hidden=i||this.backupMode||this.hasAttribute("manual")&&this.remaining<=0&&!this.expired,this.timerEl.textContent=`${String(s).padStart(2,"0")}:${String(n).padStart(2,"0")}`,this.timerEl.setAttribute("aria-label",t("남은 시간 {0}분 {1}초",[s,n])),this.timerEl.classList.toggle("designbase-wc-two-factor__timer--expired",this.expired),this.expiredEl.hidden=i||!this.expired||this.backupMode,this.otpInput.toggleAttribute("disabled",this.hasAttribute("disabled")||!i&&this.expired);const r=this.resendCount>=this.maxResends,h=this.cooldownLeft>0;this.resendGroup.hidden=i||this.backupMode,this.resendHintEl.textContent=e(r?"재전송 한도를 모두 사용했어요.":"코드를 받지 못하셨나요?"),this.resendBtn.hidden=r,this.resendBtn.textContent=this.hasAttribute("sending")?e("전송 중…"):h?t("재전송 ({0}초)",[this.cooldownLeft]):this.getAttribute("resend-label")||e("재전송"),this.resendBtn.disabled=h||this.hasAttribute("sending")||this.hasAttribute("disabled"),this.syncVerifyDisabled()}syncVerifyDisabled(){const t=this.hasAttribute("disabled");if(this.backupMode){const e=String(this.backupInput.value||"").replace(/-/g,"");return void this.verifyBtn.toggleAttribute("disabled",t||8!==e.length)}const e=Number(this.getAttribute("code-length")||6),i=this.getAttribute("code")||"";this.verifyBtn.toggleAttribute("disabled",t||"app"!==this.method&&this.expired||i.length!==e)}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-two-factor";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.segment=document.createElement("db-segment-control"),this.segment.setAttribute("full-width",""),this.segment.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"app");e!==this.method&&(this.setAttribute("code",""),this.method=e,this.emit("db-method-change",{method:e}),"app"!==e&&this.emit("db-send-code",{method:e,destination:this.getAttribute("destination")||""}))}),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,method:this.method}),this.hasAttribute("auto-submit")&&this.requestVerify()}),this.backupInput=document.createElement("db-input"),this.backupInput.setAttribute("placeholder","XXXX-XXXX"),this.backupInput.setAttribute("autocomplete","one-time-code"),this.backupInput.setAttribute("full-width",""),this.backupInput.addEventListener("db-input",t=>{t.stopPropagation();const e=String(t.detail?.value||"").toUpperCase().replace(/[^A-Z0-9]/g,"").slice(0,8),i=e.length>4?`${e.slice(0,4)}-${e.slice(4)}`:e;this.backupInput.value=i,this.syncVerifyDisabled()}),this.backupInput.addEventListener("keydown",t=>{"Enter"===t.key&&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.resendCount+=1,this.emit("db-resend",{method:this.method,destination:this.getAttribute("destination")||""}),this.hasAttribute("manual")?this.renderTimer():(this.setAttribute("code",""),this.startTimers())}),this.backupBtn=textButton(`${t}__link ${t}__link--muted`,()=>{this.backupMode=!0,this.sync(),requestAnimationFrame(()=>this.backupInput?.focus?.()),this.emit("db-backup-request")}),this.backupCancelBtn=textButton(`${t}__link ${t}__link--muted`,()=>{this.backupMode=!1,this.sync()});const r=el("div",`${t}__footer`);this.resendGroup=el("span",`${t}__footer-resend`),this.resendHintEl=el("span",`${t}__footer-hint`),this.resendGroup.append(this.resendHintEl,this.resendBtn),r.append(this.resendGroup,this.backupBtn,this.backupCancelBtn),this.codeBlock.append(n,this.otpInput,this.backupInput,this.expiredEl,this.verifyBtn,r),this.verifiedBlock=el("div",`${t}__result ${t}__result--success`);const h=el("div",`${t}__result-icon`);h.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",{method:this.method})}),this.verifiedBlock.append(h,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.segment,this.codeBlock,this.errorEl,this.verifiedBlock),this.append(s)}sync(){const i="designbase-wc-two-factor",s=this.size,n=this.method,r=this.hasAttribute("disabled"),h=this.hasAttribute("verified"),o=this.backupMode&&!h,d=this.getAttribute("code-length")||"6",l=this.getAttribute("destination")||"";this.className=[i,`${i}--${s}`,`${i}--${n}`,o&&`${i}--backup`,h&&`${i}--verified`,this.hasAttribute("closable")&&`${i}--closable`,r&&`${i}--disabled`,this.hasAttribute("error")&&`${i}--error`].filter(Boolean).join(" "),this.closeBtn.hidden=!this.hasAttribute("closable"),this.headerEl.hidden=h;const a=this.getAttribute("eyebrow")||"";if(this.eyebrowEl.textContent=a,this.eyebrowEl.hidden=!a,o)this.titleEl.textContent=e("백업 코드로 인증"),this.descEl.textContent=e("계정 설정 때 저장한 8자리 백업 코드 중 아직 쓰지 않은 코드를 입력해 주세요.");else{this.titleEl.textContent=this.getAttribute("title")||e("2단계 인증");const i=this.getAttribute("description");i?this.descEl.textContent=i:"app"===n?this.descEl.textContent=t("인증 앱(Google Authenticator, 1Password 등)에 표시된 {0}자리 코드를 입력해 주세요.",[d]):l?this.setTextWithValue(this.descEl,"sms"===n?"{0} 번호로 보낸 {1}자리 인증번호를 입력해 주세요.":"{0} 주소로 보낸 {1}자리 인증번호를 입력해 주세요.",l,[d]):this.descEl.textContent=t("sms"===n?"등록된 휴대폰으로 보낸 {0}자리 인증번호를 입력해 주세요.":"등록된 이메일로 보낸 {0}자리 인증번호를 입력해 주세요.",[d])}this.segment.hidden=o||h,this.segment.setAttribute("options",JSON.stringify([{value:"app",label:e("인증 앱")},{value:"sms",label:"SMS"},{value:"email",label:e("이메일")}].map(t=>({value:t.value,label:t.label})))),this.segment.setAttribute("value",n),this.segment.setAttribute("size",s),this.segment.toggleAttribute("disabled",r),this.codeBlock.hidden=h,this.codeLabelEl.textContent=e(o?"백업 코드":"인증 코드"),this.otpInput.hidden=o,this.otpInput.setAttribute("length",d),this.otpInput.setAttribute("size",s),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("error",this.hasAttribute("error")&&!o),this.backupInput.hidden=!o,this.backupInput.setAttribute("size",s),this.backupInput.setAttribute("aria-label",e("백업 코드")),this.backupInput.toggleAttribute("disabled",r),this.backupInput.toggleAttribute("error",this.hasAttribute("error")&&o),this.expiredEl.textContent=e("인증 시간이 만료되었어요. 인증번호를 다시 받아 주세요."),this.verifyBtn.setAttribute("size",s),this.verifyBtn.textContent=this.getAttribute("verify-label")||e("인증하기"),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.backupBtn.textContent=e("백업 코드 사용"),this.backupBtn.hidden=!this.hasAttribute("show-backup")||o,this.backupBtn.disabled=r,this.backupCancelBtn.textContent=e("인증 코드로 돌아가기"),this.backupCancelBtn.hidden=!o,this.verifiedBlock.hidden=!h,this.verifiedTitleEl.textContent=e("2단계 인증 완료"),this.verifiedDescEl.textContent=e("본인 확인이 완료되었어요."),this.doneBtn.setAttribute("size",s),this.doneBtn.textContent=this.getAttribute("done-label")||e("확인");const u=this.getAttribute("error-message")||"";this.errorEl.textContent=u,this.errorEl.hidden=!this.hasAttribute("error")||!u||h;const c=o?this.backupInput:this.otpInput;this.errorEl.previousElementSibling!==c&&c.after(this.errorEl),this.renderTimer()}requestVerify(){if(this.hasAttribute("disabled")||this.hasAttribute("verified"))return;if(this.backupMode){const t=String(this.backupInput.value||"").replace(/-/g,"");if(8!==t.length)return;return void this.emit("db-verify",{method:"backup",code:t})}const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);t.length!==e||"app"!==this.method&&this.expired||this.emit("db-verify",{method:this.method,code:t})}setTextWithValue(t,i,s,n=[]){const r=e(i).replace(/\{(\d+)\}/g,(t,e)=>"0"===e?t:String(n[Number(e)-1]??"")),[h,o=""]=r.split("{0}"),d=document.createElement("strong");d.textContent=s,t.replaceChildren(document.createTextNode(h),d,document.createTextNode(o))}}customElements.get("db-two-factor")||customElements.define("db-two-factor",DbTwoFactor);export{DbTwoFactor};
@@ -26,6 +26,7 @@ declare class DbVideoPlayer extends HTMLElement {
26
26
  private lastVolumeBeforeMute;
27
27
  private currentTime;
28
28
  private duration;
29
+ private unsubscribeLocale?;
29
30
  private isLoading;
30
31
  private error;
31
32
  connectedCallback(): void;
@@ -59,6 +60,8 @@ declare class DbVideoPlayer extends HTMLElement {
59
60
  private startProgressAnimation;
60
61
  private stopProgressAnimation;
61
62
  private updatePlayIcons;
63
+ private refreshLocale;
64
+ private updateControlLabels;
62
65
  private updateUI;
63
66
  }
64
67
  declare global {
@@ -1 +1 @@
1
- import{dbText as t,t as e}from"../core/i18n.js";import{createDbIcon as s}from"./db-icons.js";import{coerceToContract as i}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{VIDEO_PLAYER_SIZES as l,VIDEO_PLAYER_VARIANTS as o}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const n="designbase-wc-video-player",r=20;class DbVideoPlayer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.rafId=null,this.playlistIndex=0,this.isFullscreen=!1,this.showControlsOverlay=!1,this.isPlaying=!1,this.isMuted=!1,this.volume=1,this.lastVolumeBeforeMute=1,this.currentTime=0,this.duration=0,this.isLoading=!1,this.error=null,this.onFullscreenChange=()=>{this.isFullscreen=document.fullscreenElement===this,this.dispatchEvent(new CustomEvent("db-fullscreen-change",{detail:{isFullscreen:this.isFullscreen},bubbles:!0,composed:!0})),this.updateUI()},this.onMouseEnter=()=>{this.showControlsOverlay=!0,this.updateUI()},this.onMouseLeave=()=>{this.showControlsOverlay=!1,this.updateUI()}}static get observedAttributes(){return["src","poster","title","description","size","variant","theme","auto-play","loop","muted","show-controls","enable-fullscreen","show-progress","show-time","show-volume","playlist","current-index"]}connectedCallback(){this.built||(this.built=!0,this.build(),this.bindVideoEvents()),this.syncFromAttributes(),this.updateUI(),document.addEventListener("fullscreenchange",this.onFullscreenChange),this.addEventListener("mouseenter",this.onMouseEnter),this.addEventListener("mouseleave",this.onMouseLeave)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.onFullscreenChange),this.removeEventListener("mouseenter",this.onMouseEnter),this.removeEventListener("mouseleave",this.onMouseLeave),this.stopProgressAnimation()}attributeChangedCallback(t){this.built&&("src"!==t&&"playlist"!==t&&"current-index"!==t?("muted"===t&&this.videoEl&&(this.videoEl.muted=this.getBool("muted")),this.updateUI()):this.loadVideoSource())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const s=this.getAttribute(t);return""===s||"true"===s}getPlaylist(){try{const t=this.getAttribute("playlist"),e=t?JSON.parse(t):[];return Array.isArray(e)?e:[]}catch{return[]}}getCurrentSrc(){const t=this.getPlaylist();if(t.length>0){const e=Number(this.getAttribute("current-index")||this.playlistIndex||0);return t[Math.max(0,Math.min(t.length-1,e))]}return this.getAttribute("src")||""}formatTime(t){if(!Number.isFinite(t)||t<0)return"0:00";return`${Math.floor(t/60)}:${Math.floor(t%60).toString().padStart(2,"0")}`}iconButton(t,e,i,l){const o=document.createElement("button");return o.type="button",o.className=`${n}__control-button`,o.setAttribute("aria-label",i),o.append(s(t,e)),o.addEventListener("click",t=>{t.stopPropagation(),l()}),o}skipButton(t,e){const s=document.createElement("button");return s.type="button",s.className=`${n}__skip-button`,s.textContent=t,s.setAttribute("aria-label",e<0?"10초 되감기":"10초 빨리감기"),s.addEventListener("click",t=>{t.stopPropagation(),this.seekBy(e)}),s}syncFromAttributes(){this.playlistIndex=Number(this.getAttribute("current-index")||0),this.isMuted=this.getBool("muted"),this.volume=this.isMuted?0:1,this.lastVolumeBeforeMute=this.volume>0?this.volume:1,this.loadVideoSource()}loadVideoSource(){if(!this.videoEl)return;const t=this.getCurrentSrc();if(!t)return;const e=new URL(t,window.location.href).href;this.videoEl.src!==e&&(this.videoEl.src=t,this.videoEl.load()),this.videoEl.poster=this.getAttribute("poster")||"",this.videoEl.autoplay=this.getBool("auto-play"),this.videoEl.loop=this.getBool("loop"),this.videoEl.muted=this.getBool("muted"),this.videoEl.playsInline=!0}bindVideoEvents(){const t=this.videoEl;t&&(t.addEventListener("play",()=>{this.isPlaying=!0,this.startProgressAnimation(),this.dispatchEvent(new CustomEvent("db-play",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("pause",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-pause",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("timeupdate",()=>{this.currentTime=t.currentTime,this.duration=Number.isFinite(t.duration)?t.duration:0,this.dispatchEvent(new CustomEvent("db-time-update",{detail:{currentTime:this.currentTime,duration:this.duration},bubbles:!0,composed:!0})),this.updateProgress(),this.updateTime()}),t.addEventListener("volumechange",()=>{this.volume=t.volume,this.isMuted=t.muted||0===t.volume,!this.isMuted&&this.volume>0&&(this.lastVolumeBeforeMute=this.volume),this.dispatchEvent(new CustomEvent("db-volume-change",{detail:{volume:this.volume},bubbles:!0,composed:!0})),this.updateVolumeUI()}),t.addEventListener("ended",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-ended",{bubbles:!0,composed:!0}));const e=this.getPlaylist(),s=this.playlistIndex+1;e.length>0&&s<e.length&&(this.playlistIndex=s,this.loadVideoSource(),t.play()),this.updateUI()}),t.addEventListener("error",()=>{this.error="비디오를 재생할 수 없습니다.",this.dispatchEvent(new CustomEvent("db-error",{detail:{error:this.error},bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("loadstart",()=>{this.isLoading=!0,this.error=null,this.updateUI()}),t.addEventListener("loadedmetadata",()=>{this.duration=Number.isFinite(t.duration)?t.duration:0,this.updateTime()}),t.addEventListener("canplay",()=>{this.isLoading=!1,this.updateUI()}),t.addEventListener("click",()=>this.togglePlay()))}togglePlay(){this.videoEl&&(this.videoEl.paused?this.videoEl.play():this.videoEl.pause())}toggleMute(){if(this.videoEl){if(this.isMuted)return this.videoEl.muted=!1,void(this.videoEl.volume=this.lastVolumeBeforeMute>0?this.lastVolumeBeforeMute:1);this.videoEl.volume>0&&(this.lastVolumeBeforeMute=this.videoEl.volume),this.videoEl.muted=!0,this.videoEl.volume=0}}seekBy(t){if(!this.videoEl)return;const e=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,s=e>0?e:Number.MAX_SAFE_INTEGER,i=Math.max(0,Math.min(s,this.videoEl.currentTime+t));this.videoEl.currentTime=i}toggleFullscreen(){this.getBool("enable-fullscreen",!0)&&(this.isFullscreen?document.exitFullscreen?.():this.requestFullscreen?.())}build(){this.videoEl=document.createElement("video"),this.videoEl.className=`${n}__video`;const s=document.createElement("div");s.className=`${n}__loading`;const i=document.createElement("div");i.className=`${n}__spinner`,s.append(i);const l=document.createElement("div");l.className=`${n}__error`;const o=document.createElement("p");o.textContent=t("비디오를 재생할 수 없습니다.");const a=document.createElement("button");a.type="button",a.textContent=t("다시 시도"),a.addEventListener("click",()=>{this.error=null,this.loadVideoSource(),this.updateUI()}),l.append(o,a),this.dimOverlayEl=document.createElement("div"),this.dimOverlayEl.className=`${n}__dim-overlay`,this.overlayEl=document.createElement("div"),this.overlayEl.className=`${n}__overlay`;const u=document.createElement("div");u.className=`${n}__top-controls`;const h=document.createElement("div");h.className=`${n}__title`;const d=document.createElement("h3"),m=document.createElement("p");h.append(d,m),u.append(h),this._titleEl=d,this._descEl=m;const c=document.createElement("div");c.className=`${n}__center-controls`;const E=this.skipButton("-10",-10);this.centerPlayEl=document.createElement("button"),this.centerPlayEl.type="button",this.centerPlayEl.className=`${n}__play-button`,this.centerPlayEl.addEventListener("click",t=>{t.stopPropagation(),this.togglePlay()});const p=this.skipButton("+10",10);c.append(E,this.centerPlayEl,p);const v=document.createElement("div");v.className=`${n}__bottom-controls`,this.progressEl=document.createElement("div"),this.progressEl.className=`${n}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${n}__progress-bar`,this.progressHandleEl=document.createElement("div"),this.progressHandleEl.className=`${n}__progress-handle`,this.progressEl.append(this.progressBarEl,this.progressHandleEl),this.progressEl.addEventListener("click",t=>{if(!this.videoEl||!Number.isFinite(this.duration)||this.duration<=0)return;const e=this.progressEl.getBoundingClientRect(),s=(t.clientX-e.left)/e.width;this.videoEl.currentTime=s*this.duration});const g=document.createElement("div");g.className=`${n}__controls`;const b=document.createElement("div");b.className=`${n}__left-controls`,this.togglePlayEl=this.iconButton("play",r,e("media.play"),()=>this.togglePlay()),this.volumeWrapEl=document.createElement("div"),this.volumeWrapEl.className=`${n}__volume`,this.muteButtonEl=this.iconButton("volume-up",r,e("media.mute"),()=>this.toggleMute());const y=document.createElement("div");y.className=`${n}__volume-slider`,this.volumeInputEl=document.createElement("input"),this.volumeInputEl.type="range",this.volumeInputEl.min="0",this.volumeInputEl.max="1",this.volumeInputEl.step="0.1",this.volumeInputEl.addEventListener("input",()=>{if(!this.videoEl||!this.volumeInputEl)return;const t=parseFloat(this.volumeInputEl.value);t>0&&(this.lastVolumeBeforeMute=t),this.videoEl.volume=t,this.videoEl.muted=0===t}),y.append(this.volumeInputEl),this.volumeWrapEl.append(this.muteButtonEl,y),this.timeEl=document.createElement("div"),this.timeEl.className=`${n}__time`,this.timeCurrentEl=document.createElement("span");const f=document.createElement("span");f.textContent="/",this.timeDurationEl=document.createElement("span"),this.timeEl.append(this.timeCurrentEl,f,this.timeDurationEl),b.append(this.togglePlayEl,this.volumeWrapEl,this.timeEl);const _=document.createElement("div");_.className=`${n}__right-controls`,this.fullscreenButtonEl=this.iconButton("expand",r,e("media.fullscreen"),()=>this.toggleFullscreen()),_.append(this.fullscreenButtonEl),g.append(b,_),v.append(this.progressEl,g),this.overlayEl.append(u,c,v),this.append(this.videoEl,s,l,this.dimOverlayEl,this.overlayEl)}get titleEl(){return this._titleEl}get descEl(){return this._descEl}updateProgress(){const t=this.duration>0?this.currentTime/this.duration*100:0;this.progressBarEl&&(this.progressBarEl.style.width=`${t}%`),this.progressHandleEl&&(this.progressHandleEl.style.left=`${t}%`)}updateTime(){this.timeCurrentEl&&(this.timeCurrentEl.textContent=this.formatTime(this.currentTime)),this.timeDurationEl&&(this.timeDurationEl.textContent=this.formatTime(this.duration))}updateVolumeUI(){const t=this.isMuted?0:this.volume;this.volumeInputEl&&(this.volumeInputEl.value=String(t)),this.muteButtonEl&&this.muteButtonEl.replaceChildren(s(this.isMuted||0===t?"mute-filled":"volume-up",r))}startProgressAnimation(){this.stopProgressAnimation();const tick=()=>{!this.videoEl||this.videoEl.paused||this.videoEl.ended||(this.currentTime=this.videoEl.currentTime,this.duration=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,this.updateProgress(),this.updateTime(),this.rafId=requestAnimationFrame(tick))};this.rafId=requestAnimationFrame(tick)}stopProgressAnimation(){null!==this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}updatePlayIcons(){const t=this.isPlaying?"pause":"play";this.centerPlayEl&&this.centerPlayEl.replaceChildren(s(t,48)),this.togglePlayEl&&this.togglePlayEl.replaceChildren(s(t,r))}updateUI(){const t=i(this.getAttribute("size"),l,"m"),e=i(this.getAttribute("variant"),o,"default"),a=this.getAttribute("theme")||"auto",u=this.getBool("show-controls",!0),h=this.getBool("show-progress",!0),d=this.getBool("show-time",!0),m=this.getBool("show-volume",!0),c=this.getBool("enable-fullscreen",!0);this.className=[n,`${n}--size-${t}`,`${n}--variant-${e}`,`${n}--theme-${a}`,this.isPlaying&&`${n}--playing`,!this.isPlaying&&`${n}--paused`,this.isMuted&&`${n}--muted`,this.isLoading&&`${n}--loading`,this.error&&`${n}--error`,u&&this.showControlsOverlay&&`${n}--show-controls`,this.isFullscreen&&`${n}--fullscreen`].filter(Boolean).join(" ");const E=this.getAttribute("title"),p=this.getAttribute("description");this.titleEl&&(E?(this.titleEl.textContent=E,this.titleEl.hidden=!1):this.titleEl.hidden=!0),this.descEl&&(p?(this.descEl.textContent=p,this.descEl.hidden=!1):this.descEl.hidden=!0),this.overlayEl&&(this.overlayEl.hidden=!u),this.dimOverlayEl&&(this.dimOverlayEl.hidden=!u),this.progressEl&&(this.progressEl.hidden=!h),this.timeEl&&(this.timeEl.hidden=!d),this.volumeWrapEl&&(this.volumeWrapEl.hidden=!m),this.fullscreenButtonEl&&(this.fullscreenButtonEl.hidden=!c),this.fullscreenButtonEl&&this.fullscreenButtonEl.replaceChildren(s(this.isFullscreen?"shrink":"expand",r)),this.updatePlayIcons(),this.updateProgress(),this.updateTime(),this.updateVolumeUI()}}customElements.get("db-video-player")||customElements.define("db-video-player",DbVideoPlayer);export{DbVideoPlayer};
1
+ import{subscribeDbLocale as t,dbText as e,t as s}from"../core/i18n.js";import{createDbIcon as i}from"./db-icons.js";import{coerceToContract as l}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{VIDEO_PLAYER_SIZES as o,VIDEO_PLAYER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const r="designbase-wc-video-player",a=20;class DbVideoPlayer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.rafId=null,this.playlistIndex=0,this.isFullscreen=!1,this.showControlsOverlay=!1,this.isPlaying=!1,this.isMuted=!1,this.volume=1,this.lastVolumeBeforeMute=1,this.currentTime=0,this.duration=0,this.isLoading=!1,this.error=null,this.onFullscreenChange=()=>{this.isFullscreen=document.fullscreenElement===this,this.dispatchEvent(new CustomEvent("db-fullscreen-change",{detail:{isFullscreen:this.isFullscreen},bubbles:!0,composed:!0})),this.updateUI()},this.onMouseEnter=()=>{this.showControlsOverlay=!0,this.updateUI()},this.onMouseLeave=()=>{this.showControlsOverlay=!1,this.updateUI()}}static get observedAttributes(){return["src","poster","title","description","size","variant","theme","auto-play","loop","muted","show-controls","enable-fullscreen","show-progress","show-time","show-volume","playlist","current-index"]}connectedCallback(){this.built||(this.built=!0,this.build(),this.bindVideoEvents()),this.syncFromAttributes(),this.unsubscribeLocale?.(),this.unsubscribeLocale=t(()=>this.refreshLocale()),this.refreshLocale(),this.updateUI(),document.addEventListener("fullscreenchange",this.onFullscreenChange),this.addEventListener("mouseenter",this.onMouseEnter),this.addEventListener("mouseleave",this.onMouseLeave)}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,document.removeEventListener("fullscreenchange",this.onFullscreenChange),this.removeEventListener("mouseenter",this.onMouseEnter),this.removeEventListener("mouseleave",this.onMouseLeave),this.stopProgressAnimation()}attributeChangedCallback(t){this.built&&("src"!==t&&"playlist"!==t&&"current-index"!==t?("muted"===t&&this.videoEl&&(this.videoEl.muted=this.getBool("muted")),this.updateUI()):this.loadVideoSource())}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const s=this.getAttribute(t);return""===s||"true"===s}getPlaylist(){try{const t=this.getAttribute("playlist"),e=t?JSON.parse(t):[];return Array.isArray(e)?e:[]}catch{return[]}}getCurrentSrc(){const t=this.getPlaylist();if(t.length>0){const e=Number(this.getAttribute("current-index")||this.playlistIndex||0);return t[Math.max(0,Math.min(t.length-1,e))]}return this.getAttribute("src")||""}formatTime(t){if(!Number.isFinite(t)||t<0)return"0:00";return`${Math.floor(t/60)}:${Math.floor(t%60).toString().padStart(2,"0")}`}iconButton(t,e,s,l){const o=document.createElement("button");return o.type="button",o.className=`${r}__control-button`,o.setAttribute("aria-label",s),o.append(i(t,e)),o.addEventListener("click",t=>{t.stopPropagation(),l()}),o}skipButton(t,s){const i=document.createElement("button");return i.type="button",i.className=`${r}__skip-button`,i.textContent=t,i.dataset.skipSeconds=String(s),i.setAttribute("aria-label",e(s<0?"10초 되감기":"10초 빨리감기")),i.addEventListener("click",t=>{t.stopPropagation(),this.seekBy(s)}),i}syncFromAttributes(){this.playlistIndex=Number(this.getAttribute("current-index")||0),this.isMuted=this.getBool("muted"),this.volume=this.isMuted?0:1,this.lastVolumeBeforeMute=this.volume>0?this.volume:1,this.loadVideoSource()}loadVideoSource(){if(!this.videoEl)return;const t=this.getCurrentSrc();if(!t)return;const e=new URL(t,window.location.href).href;this.videoEl.src!==e&&(this.videoEl.src=t,this.videoEl.load()),this.videoEl.poster=this.getAttribute("poster")||"",this.videoEl.autoplay=this.getBool("auto-play"),this.videoEl.loop=this.getBool("loop"),this.videoEl.muted=this.getBool("muted"),this.videoEl.playsInline=!0}bindVideoEvents(){const t=this.videoEl;t&&(t.addEventListener("play",()=>{this.isPlaying=!0,this.startProgressAnimation(),this.dispatchEvent(new CustomEvent("db-play",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("pause",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-pause",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("timeupdate",()=>{this.currentTime=t.currentTime,this.duration=Number.isFinite(t.duration)?t.duration:0,this.dispatchEvent(new CustomEvent("db-time-update",{detail:{currentTime:this.currentTime,duration:this.duration},bubbles:!0,composed:!0})),this.updateProgress(),this.updateTime()}),t.addEventListener("volumechange",()=>{this.volume=t.volume,this.isMuted=t.muted||0===t.volume,!this.isMuted&&this.volume>0&&(this.lastVolumeBeforeMute=this.volume),this.dispatchEvent(new CustomEvent("db-volume-change",{detail:{volume:this.volume},bubbles:!0,composed:!0})),this.updateVolumeUI()}),t.addEventListener("ended",()=>{this.isPlaying=!1,this.stopProgressAnimation(),this.dispatchEvent(new CustomEvent("db-ended",{bubbles:!0,composed:!0}));const e=this.getPlaylist(),s=this.playlistIndex+1;e.length>0&&s<e.length&&(this.playlistIndex=s,this.loadVideoSource(),t.play()),this.updateUI()}),t.addEventListener("error",()=>{this.error=e("비디오를 재생할 수 없습니다."),this.dispatchEvent(new CustomEvent("db-error",{detail:{error:this.error},bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("loadstart",()=>{this.isLoading=!0,this.error=null,this.updateUI()}),t.addEventListener("loadedmetadata",()=>{this.duration=Number.isFinite(t.duration)?t.duration:0,this.updateTime()}),t.addEventListener("canplay",()=>{this.isLoading=!1,this.updateUI()}),t.addEventListener("click",()=>this.togglePlay()))}togglePlay(){this.videoEl&&(this.videoEl.paused?this.videoEl.play():this.videoEl.pause())}toggleMute(){if(this.videoEl){if(this.isMuted)return this.videoEl.muted=!1,void(this.videoEl.volume=this.lastVolumeBeforeMute>0?this.lastVolumeBeforeMute:1);this.videoEl.volume>0&&(this.lastVolumeBeforeMute=this.videoEl.volume),this.videoEl.muted=!0,this.videoEl.volume=0}}seekBy(t){if(!this.videoEl)return;const e=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,s=e>0?e:Number.MAX_SAFE_INTEGER,i=Math.max(0,Math.min(s,this.videoEl.currentTime+t));this.videoEl.currentTime=i}toggleFullscreen(){this.getBool("enable-fullscreen",!0)&&(this.isFullscreen?document.exitFullscreen?.():this.requestFullscreen?.())}build(){this.videoEl=document.createElement("video"),this.videoEl.className=`${r}__video`;const t=document.createElement("div");t.className=`${r}__loading`;const i=document.createElement("div");i.className=`${r}__spinner`,t.append(i);const l=document.createElement("div");l.className=`${r}__error`;const o=document.createElement("p");o.textContent=e("비디오를 재생할 수 없습니다.");const n=document.createElement("button");n.type="button",n.textContent=e("다시 시도"),n.addEventListener("click",()=>{this.error=null,this.loadVideoSource(),this.updateUI()}),l.append(o,n),this.dimOverlayEl=document.createElement("div"),this.dimOverlayEl.className=`${r}__dim-overlay`,this.overlayEl=document.createElement("div"),this.overlayEl.className=`${r}__overlay`;const u=document.createElement("div");u.className=`${r}__top-controls`;const h=document.createElement("div");h.className=`${r}__title`;const d=document.createElement("h3"),c=document.createElement("p");h.append(d,c),u.append(h),this._titleEl=d,this._descEl=c;const m=document.createElement("div");m.className=`${r}__center-controls`;const E=this.skipButton("-10",-10);this.centerPlayEl=document.createElement("button"),this.centerPlayEl.type="button",this.centerPlayEl.className=`${r}__play-button`,this.centerPlayEl.addEventListener("click",t=>{t.stopPropagation(),this.togglePlay()});const p=this.skipButton("+10",10);m.append(E,this.centerPlayEl,p);const v=document.createElement("div");v.className=`${r}__bottom-controls`,this.progressEl=document.createElement("div"),this.progressEl.className=`${r}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${r}__progress-bar`,this.progressHandleEl=document.createElement("div"),this.progressHandleEl.className=`${r}__progress-handle`,this.progressEl.append(this.progressBarEl,this.progressHandleEl),this.progressEl.addEventListener("click",t=>{if(!this.videoEl||!Number.isFinite(this.duration)||this.duration<=0)return;const e=this.progressEl.getBoundingClientRect(),s=(t.clientX-e.left)/e.width;this.videoEl.currentTime=s*this.duration});const g=document.createElement("div");g.className=`${r}__controls`;const b=document.createElement("div");b.className=`${r}__left-controls`,this.togglePlayEl=this.iconButton("play",a,s("media.play"),()=>this.togglePlay()),this.volumeWrapEl=document.createElement("div"),this.volumeWrapEl.className=`${r}__volume`,this.muteButtonEl=this.iconButton("volume-up",a,s("media.mute"),()=>this.toggleMute());const y=document.createElement("div");y.className=`${r}__volume-slider`,this.volumeInputEl=document.createElement("input"),this.volumeInputEl.type="range",this.volumeInputEl.min="0",this.volumeInputEl.max="1",this.volumeInputEl.step="0.1",this.volumeInputEl.addEventListener("input",()=>{if(!this.videoEl||!this.volumeInputEl)return;const t=parseFloat(this.volumeInputEl.value);t>0&&(this.lastVolumeBeforeMute=t),this.videoEl.volume=t,this.videoEl.muted=0===t}),y.append(this.volumeInputEl),this.volumeWrapEl.append(this.muteButtonEl,y),this.timeEl=document.createElement("div"),this.timeEl.className=`${r}__time`,this.timeCurrentEl=document.createElement("span");const f=document.createElement("span");f.textContent="/",this.timeDurationEl=document.createElement("span"),this.timeEl.append(this.timeCurrentEl,f,this.timeDurationEl),b.append(this.togglePlayEl,this.volumeWrapEl,this.timeEl);const _=document.createElement("div");_.className=`${r}__right-controls`,this.fullscreenButtonEl=this.iconButton("expand",a,s("media.fullscreen"),()=>this.toggleFullscreen()),_.append(this.fullscreenButtonEl),g.append(b,_),v.append(this.progressEl,g),this.overlayEl.append(u,m,v),this.append(this.videoEl,t,l,this.dimOverlayEl,this.overlayEl)}get titleEl(){return this._titleEl}get descEl(){return this._descEl}updateProgress(){const t=this.duration>0?this.currentTime/this.duration*100:0;this.progressBarEl&&(this.progressBarEl.style.width=`${t}%`),this.progressHandleEl&&(this.progressHandleEl.style.left=`${t}%`)}updateTime(){this.timeCurrentEl&&(this.timeCurrentEl.textContent=this.formatTime(this.currentTime)),this.timeDurationEl&&(this.timeDurationEl.textContent=this.formatTime(this.duration))}updateVolumeUI(){const t=this.isMuted?0:this.volume;this.volumeInputEl&&(this.volumeInputEl.value=String(t)),this.muteButtonEl&&this.muteButtonEl.replaceChildren(i(this.isMuted||0===t?"mute-filled":"volume-up",a))}startProgressAnimation(){this.stopProgressAnimation();const tick=()=>{!this.videoEl||this.videoEl.paused||this.videoEl.ended||(this.currentTime=this.videoEl.currentTime,this.duration=Number.isFinite(this.videoEl.duration)?this.videoEl.duration:0,this.updateProgress(),this.updateTime(),this.rafId=requestAnimationFrame(tick))};this.rafId=requestAnimationFrame(tick)}stopProgressAnimation(){null!==this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}updatePlayIcons(){const t=this.isPlaying?"pause":"play";this.centerPlayEl&&this.centerPlayEl.replaceChildren(i(t,48)),this.togglePlayEl&&this.togglePlayEl.replaceChildren(i(t,a))}refreshLocale(){this.querySelectorAll("button[data-skip-seconds]").forEach(t=>{t.setAttribute("aria-label",e(Number(t.dataset.skipSeconds)<0?"10초 되감기":"10초 빨리감기"))});const t=this.querySelector(`.${r}__error p`),s=this.querySelector(`.${r}__error button`);t&&(t.textContent=e("비디오를 재생할 수 없습니다.")),s&&(s.textContent=e("다시 시도")),this.updateControlLabels()}updateControlLabels(){const t=s(this.isPlaying?"media.pause":"media.play");this.centerPlayEl?.setAttribute("aria-label",t),this.togglePlayEl?.setAttribute("aria-label",t),this.muteButtonEl?.setAttribute("aria-label",s(this.isMuted||0===this.volume?"media.unmute":"media.mute")),this.fullscreenButtonEl?.setAttribute("aria-label",s("media.fullscreen"))}updateUI(){const t=l(this.getAttribute("size"),o,"m"),e=l(this.getAttribute("variant"),n,"default"),s=this.getAttribute("theme")||"auto",u=this.getBool("show-controls",!0),h=this.getBool("show-progress",!0),d=this.getBool("show-time",!0),c=this.getBool("show-volume",!0),m=this.getBool("enable-fullscreen",!0);this.className=[r,`${r}--size-${t}`,`${r}--variant-${e}`,`${r}--theme-${s}`,this.isPlaying&&`${r}--playing`,!this.isPlaying&&`${r}--paused`,this.isMuted&&`${r}--muted`,this.isLoading&&`${r}--loading`,this.error&&`${r}--error`,u&&this.showControlsOverlay&&`${r}--show-controls`,this.isFullscreen&&`${r}--fullscreen`].filter(Boolean).join(" ");const E=this.getAttribute("title"),p=this.getAttribute("description");this.titleEl&&(E?(this.titleEl.textContent=E,this.titleEl.hidden=!1):this.titleEl.hidden=!0),this.descEl&&(p?(this.descEl.textContent=p,this.descEl.hidden=!1):this.descEl.hidden=!0),this.overlayEl&&(this.overlayEl.hidden=!u),this.dimOverlayEl&&(this.dimOverlayEl.hidden=!u),this.progressEl&&(this.progressEl.hidden=!h),this.timeEl&&(this.timeEl.hidden=!d),this.volumeWrapEl&&(this.volumeWrapEl.hidden=!c),this.fullscreenButtonEl&&(this.fullscreenButtonEl.hidden=!m),this.fullscreenButtonEl&&this.fullscreenButtonEl.replaceChildren(i(this.isFullscreen?"shrink":"expand",a)),this.updateControlLabels(),this.updatePlayIcons(),this.updateProgress(),this.updateTime(),this.updateVolumeUI()}}customElements.get("db-video-player")||customElements.define("db-video-player",DbVideoPlayer);export{DbVideoPlayer};
@@ -0,0 +1,60 @@
1
+ type WorldMapVariant = 'flat' | 'wireframe' | 'solid';
2
+ interface WorldMapLocation {
3
+ id: string;
4
+ label: string;
5
+ latitude: number;
6
+ longitude: number;
7
+ color?: string;
8
+ size?: number;
9
+ }
10
+ interface WorldMapConnection {
11
+ from: string;
12
+ to: string;
13
+ }
14
+ /** Actual reusable map element: local geographic data, orthographic projection and accessible location selection. */
15
+ declare class DbWorldMap extends HTMLElement {
16
+ static get observedAttributes(): string[];
17
+ private hover?;
18
+ private texture?;
19
+ private textureKey;
20
+ private sphereCanvas?;
21
+ private places;
22
+ private links;
23
+ private canvas?;
24
+ private legend?;
25
+ private ctx?;
26
+ private resize?;
27
+ private visible?;
28
+ private theme?;
29
+ private frame;
30
+ private inView;
31
+ private last;
32
+ private yaw;
33
+ private pitch;
34
+ private dragging;
35
+ private previousX;
36
+ private previousY;
37
+ private reduced;
38
+ private abort?;
39
+ get variant(): WorldMapVariant;
40
+ set variant(v: WorldMapVariant);
41
+ get locations(): WorldMapLocation[];
42
+ set locations(value: WorldMapLocation[]);
43
+ get connections(): WorldMapConnection[];
44
+ set connections(value: WorldMapConnection[]);
45
+ connectedCallback(): void;
46
+ disconnectedCallback(): void;
47
+ attributeChangedCallback(name: string): void;
48
+ private number;
49
+ private angle;
50
+ private readAttributes;
51
+ private buttons;
52
+ private syncAnimation;
53
+ private project;
54
+ private draw;
55
+ private drawSolid;
56
+ private line;
57
+ }
58
+
59
+ export { DbWorldMap };
60
+ export type { WorldMapConnection, WorldMapLocation, WorldMapVariant };
@@ -0,0 +1 @@
1
+ import{WORLD_COUNTRIES as t}from"../helpers/world-countries.js";import{WORLD_LAND as e}from"../helpers/world-land.js";const i=Math.PI/180,inside=(t,e,i)=>{let s=!1;for(let a=0,n=i.length-1;a<i.length;n=a++){const r=i[a],o=i[n];r[1]>e!=o[1]>e&&t<(o[0]-r[0])*(e-r[1])/(o[1]-r[1])+r[0]&&(s=!s)}return s},s=e.map(t=>({r:t,minX:Math.min(...t.map(t=>t[0])),maxX:Math.max(...t.map(t=>t[0])),minY:Math.min(...t.map(t=>t[1])),maxY:Math.max(...t.map(t=>t[1]))})),a={};class DbWorldMap extends HTMLElement{constructor(){super(...arguments),this.textureKey="",this.places=[],this.links=[],this.frame=0,this.inView=!0,this.last=0,this.yaw=20,this.pitch=18,this.dragging=!1,this.previousX=0,this.previousY=0,this.reduced=matchMedia("(prefers-reduced-motion: reduce)")}static get observedAttributes(){return["variant","longitude","latitude","auto-rotate","locations","connections","selected-id","aria-label","dot-size","dot-color","color-theme","hover-highlight","hover-radius","hover-color","marker-size","hide-legend"]}get variant(){const t=this.getAttribute("variant");return"flat"===t||"solid"===t?t:"wireframe"}set variant(t){this.setAttribute("variant",t)}get locations(){return this.places.map(t=>({...t}))}set locations(t){this.places=Array.isArray(t)?t.filter(t=>t&&"string"==typeof t.id&&"string"==typeof t.label&&Number.isFinite(t.latitude)&&Math.abs(t.latitude)<=90&&Number.isFinite(t.longitude)&&Math.abs(t.longitude)<=180).map(t=>({...t})):[],this.buttons(),this.draw()}get connections(){return this.links.map(t=>({...t}))}set connections(t){this.links=Array.isArray(t)?t.filter(t=>t&&"string"==typeof t.from&&"string"==typeof t.to).map(t=>({...t})):[],this.draw()}connectedCallback(){for(const t of["locations","connections","variant"])if(Object.prototype.hasOwnProperty.call(this,t)){const e=this[t];delete this[t],this[t]=e}if(!this.canvas){this.classList.add("designbase-wc-world-map");const t=document.createElement("style");t.textContent=".designbase-wc-world-map{display:block;position:relative;min-width:0;color:var(--db-text-primary)}.designbase-wc-world-map canvas{display:block;width:100%;height:var(--db-world-map-height,420px);max-width:100%;touch-action:pan-y;cursor:grab}.designbase-wc-world-map canvas:active{cursor:grabbing}.designbase-wc-world-map canvas:focus-visible{outline:2px solid currentColor;outline-offset:3px}.designbase-wc-world-map__locations{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.designbase-wc-world-map__locations button{font:inherit;font-size:12px;color:inherit;background:transparent;border:1px solid var(--db-border-base,#aaa);border-radius:30px;padding:8px 15px;cursor:pointer}.designbase-wc-world-map__locations button[aria-pressed=true]{border-color:currentColor;background:color-mix(in srgb,currentColor 8%,transparent)}.designbase-wc-world-map__locations button:focus-visible{outline:2px solid currentColor;outline-offset:3px}@media(max-width:767px){.designbase-wc-world-map canvas{height:var(--db-world-map-mobile-height,300px)}}",this.canvas=document.createElement("canvas"),this.canvas.tabIndex=0,this.canvas.setAttribute("role","img"),this.legend=document.createElement("div"),this.legend.className="designbase-wc-world-map__locations",this.append(t,this.canvas,this.legend),this.ctx=this.canvas.getContext("2d")}this.abort=new AbortController;const t={signal:this.abort.signal};this.canvas.addEventListener("pointerdown",t=>{"flat"!==this.variant&&(this.dragging=!0,this.previousX=t.clientX,this.previousY=t.clientY,this.canvas.setPointerCapture(t.pointerId))},t),this.canvas.addEventListener("pointermove",t=>{const e=this.canvas.getBoundingClientRect();if(this.hover={x:t.clientX-e.left,y:t.clientY-e.top},!this.dragging)return void(this.hasAttribute("hover-highlight")&&this.draw());const i=t.clientX-this.previousX,s=t.clientY-this.previousY;this.yaw-=.35*i,this.pitch=Math.max(-60,Math.min(60,this.pitch+.25*s)),this.previousX=t.clientX,this.previousY=t.clientY,this.draw()},t),this.canvas.addEventListener("pointerleave",()=>{this.hover=void 0,this.draw()},t);for(const e of["pointerup","pointercancel","lostpointercapture"])this.canvas.addEventListener(e,()=>{this.dragging=!1},t);this.canvas.addEventListener("keydown",t=>{"flat"!==this.variant&&["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(t.key)&&(t.preventDefault(),this.yaw+="ArrowRight"===t.key?10:"ArrowLeft"===t.key?-10:0,this.pitch=Math.max(-60,Math.min(60,this.pitch+("ArrowUp"===t.key?10:"ArrowDown"===t.key?-10:0))),this.draw())},t),this.resize=new ResizeObserver(()=>this.draw()),this.resize.observe(this),this.visible=new IntersectionObserver(([t])=>{this.inView=t.isIntersecting,this.syncAnimation()}),this.visible.observe(this),this.theme=new MutationObserver(()=>this.draw()),this.theme.observe(document.documentElement,{attributes:!0,attributeFilter:["class","style","data-theme"]}),this.reduced.addEventListener("change",()=>this.syncAnimation(),t),document.addEventListener("visibilitychange",()=>this.syncAnimation(),t),this.readAttributes(),this.buttons(),this.draw(),this.syncAnimation()}disconnectedCallback(){this.abort?.abort(),this.resize?.disconnect(),this.visible?.disconnect(),this.theme?.disconnect(),cancelAnimationFrame(this.frame),this.frame=0}attributeChangedCallback(t){"locations"!==t&&"connections"!==t||this.readAttributes(),"longitude"===t&&(this.yaw=this.angle(t,20)),"latitude"===t&&(this.pitch=this.angle(t,18)),this.buttons(),this.draw(),this.syncAnimation()}number(t,e,i,s){const a=Number(this.getAttribute(t));return this.hasAttribute(t)&&Number.isFinite(a)?Math.max(i,Math.min(s,a)):e}angle(t,e){const i=Number(this.getAttribute(t));return this.hasAttribute(t)&&Number.isFinite(i)?i:e}readAttributes(){this.yaw=this.angle("longitude",this.yaw),this.pitch=this.angle("latitude",this.pitch);for(const t of["locations","connections"]){const e=this.getAttribute(t);if(e)try{this[t]=JSON.parse(e)}catch{this[t]=[]}}}buttons(){if(!this.legend)return;this.legend.hidden=this.hasAttribute("hide-legend"),this.legend.style.display=this.legend.hidden?"none":"";const t=this.places.map(t=>t.id).join("|");if(this.legend.dataset.items!==t){this.legend.replaceChildren(),this.legend.dataset.items=t;for(const t of this.places){const e=document.createElement("button");e.type="button",e.dataset.locationId=t.id,e.onclick=()=>{const t=this.places.find(t=>t.id===e.dataset.locationId);t&&(this.setAttribute("selected-id",t.id),this.yaw=t.longitude,this.pitch=t.latitude,this.draw(),this.dispatchEvent(new CustomEvent("db-location-select",{detail:{...t},bubbles:!0,composed:!0})))},this.legend.append(e)}}for(const[t,e]of this.places.entries()){const i=this.legend.children[t];i.textContent=e.label,i.setAttribute("aria-pressed",String(this.getAttribute("selected-id")===e.id))}}syncAnimation(){if(cancelAnimationFrame(this.frame),this.frame=0,!this.isConnected||!this.inView||document.hidden||this.reduced.matches||!this.hasAttribute("auto-rotate")||"flat"===this.variant)return;this.last=performance.now();const tick=t=>{t-this.last>32&&(this.dragging||(this.yaw+=.004*(t-this.last)),this.last=t,this.draw()),this.frame=requestAnimationFrame(tick)};this.frame=requestAnimationFrame(tick)}project(t,e,s,a){if("flat"===this.variant)return{x:.06*s+(t+180)/360*s*.88,y:.12*a+(85-e)/150*a*.76,z:1};const n=(t-this.yaw)*i,r=e*i,o=this.pitch*i,h=Math.cos(r)*Math.sin(n),l=Math.sin(r)*Math.cos(o)-Math.cos(r)*Math.cos(n)*Math.sin(o),c=Math.sin(r)*Math.sin(o)+Math.cos(r)*Math.cos(n)*Math.cos(o),d=Math.min(.45*s,.44*a);return{x:s/2+h*d,y:a/2-l*d,z:c}}draw(){const t=this.canvas,e=this.ctx;if(!t||!e||!this.isConnected)return;const n=Math.max(1,t.clientWidth),r=t.clientHeight;if(!r)return;const o=Math.min(devicePixelRatio||1,2);t.width===Math.round(n*o)&&t.height===Math.round(r*o)||(t.width=Math.round(n*o),t.height=Math.round(r*o)),e.setTransform(o,0,0,o,0,0),e.clearRect(0,0,n,r);const h=getComputedStyle(this),l=h.color||"#ddd",c=this.getAttribute("color-theme")||"mono",d=h.getPropertyValue("--map-accent").trim()||("ocean"===c?"#327db0":"lime"===c||"solid"===this.variant?"#d8ed55":l),m=this.getAttribute("dot-color")||h.getPropertyValue("--map-dot-color").trim()||("ocean"===c?"#668fa9":"lime"===c?"#92977b":l),u=this.getAttribute("hover-color")||d;if(e.lineWidth=.65,t.setAttribute("aria-label",this.getAttribute("aria-label")||"World overview. Use arrow keys or drag to rotate. Select a location below."),"solid"===this.variant&&this.drawSolid(n,r,c),"wireframe"===this.variant){for(let t=-60;t<=60;t+=30)this.line(Array.from({length:181},(e,i)=>[2*i-180,t]),n,r,l,.14);for(let t=-180;t<180;t+=30)this.line(Array.from({length:91},(e,i)=>[t,2*i-90]),n,r,l,.14)}if("solid"!==this.variant)for(const[t,o]of function getLandDots(t=!1){const e=t?"sphere":"flat";if(a[e])return a[e];const n=[];for(let e=-57;e<83;e+=2.4)for(let a=-180;a<180;a+=t?2.4/Math.max(.15,Math.cos(e*i)):2.4)s.some(({r:t,minX:i,maxX:s,minY:n,maxY:r})=>a>=i&&a<=s&&e>=n&&e<=r&&inside(a,e,t))&&n.push([a,e]);return a[e]=n,n}("flat"!==this.variant)){const i=this.project(t,o,n,r);if(i.z<0)continue;const s=this.hover?Math.hypot(i.x-this.hover.x,i.y-this.hover.y):1/0,a=this.hasAttribute("hover-highlight")&&s<this.number("hover-radius",100,20,500);e.globalAlpha=a?1:"flat"===this.variant?.48:.18+.55*i.z,e.fillStyle=a?u:m,e.beginPath();const h=this.number("dot-size","flat"===this.variant?Math.max(.7,n/600):Math.max(.6,n/750),.25,8);e.arc(i.x,i.y,h*(a?1+.3*(1-s/this.number("hover-radius",100,20,500)):1),0,2*Math.PI),e.fill()}e.globalAlpha=1;for(const t of this.links){const i=this.places.find(e=>e.id===t.from),s=this.places.find(e=>e.id===t.to);if(!i||!s)continue;const a=Array.from({length:61},(t,e)=>{const a=e/60;let n=s.longitude-i.longitude;return n>180&&(n-=360),n<-180&&(n+=360),[i.longitude+n*a,i.latitude+(s.latitude-i.latitude)*a+12*Math.sin(a*Math.PI)]});"solid"===this.variant&&e.setLineDash([2,4]),this.line(a,n,r,d,.85),e.setLineDash([])}for(const t of this.places){const i=this.project(t.longitude,t.latitude,n,r);if(i.z<0)continue;const s=t.color||d,a=Number.isFinite(t.size)?Math.max(2,Math.min(24,t.size)):this.number("marker-size","solid"===this.variant?8:4.5,2,24);e.fillStyle=s,e.beginPath(),e.arc(i.x,i.y,a,0,2*Math.PI),e.fill(),e.strokeStyle=s,e.globalAlpha=.35,e.beginPath(),e.arc(i.x,i.y,a+4,0,2*Math.PI),e.stroke(),e.globalAlpha=1,e.fillStyle=l,e.font="11px sans-serif";const o=t.label.length>28?t.label.slice(0,27)+"…":t.label,c=e.measureText(o).width,m=Math.max(6,Math.min(n-c-8,i.x+13)),u=Math.max(20,i.y-12);e.fillStyle=h.getPropertyValue("--db-surface-base").trim()||"#fff",e.beginPath(),e.roundRect(m-5,u-13,c+10,20,10),e.fill(),e.fillStyle=l,e.fillText(o,m,u)}this.dataset.rendered="true"}drawSolid(e,s,a){const n="ocean"===a?["#e9f0f4","#c4d4df","#b2c5d2"]:"lime"===a?["#f1f2e9","#d6d9c8","#c8cdb9"]:["#f2f2f0","#d9dad7","#caccc8"],r=n.join();if(!this.texture||this.textureKey!==r){const e=document.createElement("canvas");e.width=1024,e.height=512;const i=e.getContext("2d");i.fillStyle=n[0],i.fillRect(0,0,1024,512),i.fillStyle=n[1],i.strokeStyle=n[2],i.lineWidth=.65;for(const e of t){const t=[];let s=e[0][0];for(const[i,a]of e){let e=i;for(;e-s>180;)e-=360;for(;e-s<-180;)e+=360;t.push([e,a]),s=e}for(const e of[-1024,0,1024]){i.beginPath();for(const[s,[a,n]]of t.entries()){const t=(a+180)/360*1024+e,r=(90-n)/180*512;s?i.lineTo(t,r):i.moveTo(t,r)}i.closePath(),i.fill(),i.stroke()}}this.texture=i.getImageData(0,0,1024,512),this.textureKey=r}const o=Math.min(.45*e,.44*s),h=Math.max(100,Math.min(520,Math.round(2*o))),l=this.sphereCanvas??(this.sphereCanvas=document.createElement("canvas"));l.width=h,l.height=h;const c=l.getContext("2d"),d=c.createImageData(h,h),m=d.data,u=this.texture.data,p=Math.cos(this.pitch*i),g=Math.sin(this.pitch*i);for(let t=0;t<h;t++)for(let e=0;e<h;e++){const s=(e+.5)/h*2-1,a=1-(t+.5)/h*2,n=s*s+a*a;if(n>1)continue;const r=Math.sqrt(1-n),o=Math.asin(Math.max(-1,Math.min(1,a*p+r*g)))/i,l=this.yaw+Math.atan2(s,r*p-a*g)/i,c=Math.floor(((l+180)%360+360)%360/360*1024),d=4*(1024*Math.max(0,Math.min(511,Math.floor((90-o)/180*512)))+c),b=4*(t*h+e),f=Math.max(.74,Math.min(1.04,.85+.15*r-.08*s+.04*a));for(let t=0;t<3;t++)m[b+t]=Math.min(255,u[d+t]*f);m[b+3]=Math.min(255,(1-n)*h*255)}c.putImageData(d,0,0),this.ctx.drawImage(l,e/2-o,s/2-o,2*o,2*o)}line(t,e,i,s,a){const n=this.ctx;n.beginPath();let r=!1;for(const[s,a]of t){const t=this.project(s,a,e,i);t.z<0?r=!1:r?n.lineTo(t.x,t.y):(n.moveTo(t.x,t.y),r=!0)}n.strokeStyle=s,n.globalAlpha=a,n.stroke(),n.globalAlpha=1}}customElements.get("db-world-map")||customElements.define("db-world-map",DbWorldMap);export{DbWorldMap};
@@ -1,10 +1,12 @@
1
1
  declare class DbYouTubePlayer extends HTMLElement {
2
+ private unsubscribeLocale?;
2
3
  private isFullscreen;
3
4
  private playlistIndex;
4
5
  static get observedAttributes(): string[];
5
6
  connectedCallback(): void;
6
7
  disconnectedCallback(): void;
7
8
  attributeChangedCallback(): void;
9
+ private refreshLocale;
8
10
  private getBool;
9
11
  private extractVideoId;
10
12
  private getPlaylist;
@@ -1 +1 @@
1
- import{dbText as e}from"../core/i18n.js";import{coerceToContract as t}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{YOUTUBE_PLAYER_SIZES as s,YOUTUBE_PLAYER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbYouTubePlayer extends HTMLElement{constructor(){super(...arguments),this.isFullscreen=!1,this.playlistIndex=0,this.onFullscreenChange=()=>{this.isFullscreen=!!document.fullscreenElement,this.dispatchEvent(new CustomEvent("db-fullscreen-change",{detail:{isFullscreen:this.isFullscreen},bubbles:!0,composed:!0})),this.render()}}static get observedAttributes(){return["video-id","title","description","size","variant","theme","auto-play","loop","muted","show-controls","enable-fullscreen","enable-keyboard","enable-touch","show-subtitles","default-subtitle-lang","playback-rate","start-time","end-time","show-related-videos","show-info","show-channel-info","show-like-buttons","show-comments","playlist","current-index"]}connectedCallback(){this.style.display="block",this.playlistIndex=Number(this.getAttribute("current-index")||0),this.render(),document.addEventListener("fullscreenchange",this.onFullscreenChange)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.onFullscreenChange)}attributeChangedCallback(){this.render()}getBool(e,t=!1){if(!this.hasAttribute(e))return t;const s=this.getAttribute(e);return""===s||"true"===s}extractVideoId(e){if(!e)return"";if(11===e.length&&/^[a-zA-Z0-9_-]{11}$/.test(e))return e;const t=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/|youtube\.com\/watch\?.*&v=)([^&\n?#]+)/,/youtube\.com\/watch\?.*v=([^&\n?#]+)/];for(const s of t){const t=e.match(s);if(t)return t[1]}return e}getPlaylist(){try{const e=this.getAttribute("playlist"),t=e?JSON.parse(e):[];return Array.isArray(t)?t:[]}catch{return[]}}getCurrentVideoId(){const e=this.getPlaylist();if(e.length>0){const t=Math.max(0,Math.min(e.length-1,this.playlistIndex));return this.extractVideoId(e[t])}return this.extractVideoId(this.getAttribute("video-id")||"")}generateEmbedUrl(e){const t=new URLSearchParams;if(this.getBool("auto-play")&&t.append("autoplay","1"),this.getBool("loop")&&t.append("loop","1"),this.getBool("muted")&&t.append("mute","1"),this.getBool("show-controls",!0)||t.append("controls","0"),this.getBool("enable-fullscreen",!0)||t.append("fs","0"),this.getBool("enable-keyboard",!0)||t.append("disablekb","1"),this.getBool("enable-touch",!0)||t.append("modestbranding","1"),this.getBool("show-subtitles")){t.append("cc_load_policy","1");const e=this.getAttribute("default-subtitle-lang")||"ko";t.append("cc_lang_pref",e)}const s=this.getAttribute("playback-rate")||"1";"1"!==s&&t.append("vq",s);const n=Number(this.getAttribute("start-time")||0);n>0&&t.append("start",String(n));const o=Number(this.getAttribute("end-time")||0);o>0&&t.append("end",String(o)),this.getBool("show-related-videos",!0)||t.append("rel","0"),this.getBool("show-info",!0)||t.append("showinfo","0"),this.getBool("show-channel-info",!0)||t.append("modestbranding","1"),this.getBool("show-like-buttons",!0)||t.append("modestbranding","1"),this.getBool("show-comments",!1)||t.append("modestbranding","1");const i=this.getPlaylist().map(e=>this.extractVideoId(e)).filter(Boolean);i.length>0?t.append("playlist",i.join(",")):this.getBool("loop")&&t.append("playlist",e);const r=t.toString();return`https://www.youtube.com/embed/${e}${r?`?${r}`:""}`}render(){const o=t(this.getAttribute("size"),s,"m"),i=t(this.getAttribute("variant"),n,"default"),r=this.getAttribute("theme")||"auto";this.className=["designbase-wc-youtube-player",`designbase-wc-youtube-player--${o}`,`designbase-wc-youtube-player--${i}`,`designbase-wc-youtube-player--${r}`,this.isFullscreen&&"designbase-wc-youtube-player--fullscreen"].filter(Boolean).join(" ");const a=this.getCurrentVideoId();if(!a){const t=document.createElement("div");return t.className="designbase-wc-youtube-player__error",t.textContent=e("동영상을 불러올 수 없습니다."),void this.replaceChildren(t)}const l=document.createElement("div");l.className="designbase-wc-youtube-player__container";const c=document.createElement("iframe");c.className="designbase-wc-youtube-player__iframe",c.src=this.generateEmbedUrl(a),c.title=this.getAttribute("title")||`YouTube 동영상: ${a}`,c.setAttribute("frameborder","0"),c.setAttribute("allow","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"),c.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),c.referrerPolicy="strict-origin-when-cross-origin",c.allowFullscreen=!0,l.append(c);const u=this.getAttribute("description"),d=document.createDocumentFragment();if(d.append(l),u&&"minimal"!==i){const e=document.createElement("div");e.className="designbase-wc-youtube-player__description";const t=document.createElement("p");t.textContent=u,e.append(t),d.append(e)}this.replaceChildren(d)}}customElements.get("db-youtube-player")||customElements.define("db-youtube-player",DbYouTubePlayer);export{DbYouTubePlayer};
1
+ import{subscribeDbLocale as e,dbFormat as t,dbText as s}from"../core/i18n.js";import{coerceToContract as o}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{YOUTUBE_PLAYER_SIZES as n,YOUTUBE_PLAYER_VARIANTS as i}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbYouTubePlayer extends HTMLElement{constructor(){super(...arguments),this.isFullscreen=!1,this.playlistIndex=0,this.onFullscreenChange=()=>{this.isFullscreen=!!document.fullscreenElement,this.dispatchEvent(new CustomEvent("db-fullscreen-change",{detail:{isFullscreen:this.isFullscreen},bubbles:!0,composed:!0})),this.render()}}static get observedAttributes(){return["video-id","title","description","size","variant","theme","auto-play","loop","muted","show-controls","enable-fullscreen","enable-keyboard","enable-touch","show-subtitles","default-subtitle-lang","playback-rate","start-time","end-time","show-related-videos","show-info","show-channel-info","show-like-buttons","show-comments","playlist","current-index"]}connectedCallback(){this.style.display="block",this.playlistIndex=Number(this.getAttribute("current-index")||0),this.render(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.refreshLocale()),document.addEventListener("fullscreenchange",this.onFullscreenChange)}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,document.removeEventListener("fullscreenchange",this.onFullscreenChange)}attributeChangedCallback(){this.render()}refreshLocale(){const e=this.querySelector(".designbase-wc-youtube-player__iframe");e&&!this.getAttribute("title")&&(e.title=t("YouTube 동영상: {0}",[this.getCurrentVideoId()]));const o=this.querySelector(".designbase-wc-youtube-player__error");o&&(o.textContent=s("동영상을 불러올 수 없습니다."))}getBool(e,t=!1){if(!this.hasAttribute(e))return t;const s=this.getAttribute(e);return""===s||"true"===s}extractVideoId(e){if(!e)return"";if(11===e.length&&/^[a-zA-Z0-9_-]{11}$/.test(e))return e;const t=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/|youtube\.com\/watch\?.*&v=)([^&\n?#]+)/,/youtube\.com\/watch\?.*v=([^&\n?#]+)/];for(const s of t){const t=e.match(s);if(t)return t[1]}return e}getPlaylist(){try{const e=this.getAttribute("playlist"),t=e?JSON.parse(e):[];return Array.isArray(t)?t:[]}catch{return[]}}getCurrentVideoId(){const e=this.getPlaylist();if(e.length>0){const t=Math.max(0,Math.min(e.length-1,this.playlistIndex));return this.extractVideoId(e[t])}return this.extractVideoId(this.getAttribute("video-id")||"")}generateEmbedUrl(e){const t=new URLSearchParams;if(this.getBool("auto-play")&&t.append("autoplay","1"),this.getBool("loop")&&t.append("loop","1"),this.getBool("muted")&&t.append("mute","1"),this.getBool("show-controls",!0)||t.append("controls","0"),this.getBool("enable-fullscreen",!0)||t.append("fs","0"),this.getBool("enable-keyboard",!0)||t.append("disablekb","1"),this.getBool("enable-touch",!0)||t.append("modestbranding","1"),this.getBool("show-subtitles")){t.append("cc_load_policy","1");const e=this.getAttribute("default-subtitle-lang")||"ko";t.append("cc_lang_pref",e)}const s=this.getAttribute("playback-rate")||"1";"1"!==s&&t.append("vq",s);const o=Number(this.getAttribute("start-time")||0);o>0&&t.append("start",String(o));const n=Number(this.getAttribute("end-time")||0);n>0&&t.append("end",String(n)),this.getBool("show-related-videos",!0)||t.append("rel","0"),this.getBool("show-info",!0)||t.append("showinfo","0"),this.getBool("show-channel-info",!0)||t.append("modestbranding","1"),this.getBool("show-like-buttons",!0)||t.append("modestbranding","1"),this.getBool("show-comments",!1)||t.append("modestbranding","1");const i=this.getPlaylist().map(e=>this.extractVideoId(e)).filter(Boolean);i.length>0?t.append("playlist",i.join(",")):this.getBool("loop")&&t.append("playlist",e);const r=t.toString();return`https://www.youtube.com/embed/${e}${r?`?${r}`:""}`}render(){const e=o(this.getAttribute("size"),n,"m"),r=o(this.getAttribute("variant"),i,"default"),a=this.getAttribute("theme")||"auto";this.className=["designbase-wc-youtube-player",`designbase-wc-youtube-player--${e}`,`designbase-wc-youtube-player--${r}`,`designbase-wc-youtube-player--${a}`,this.isFullscreen&&"designbase-wc-youtube-player--fullscreen"].filter(Boolean).join(" ");const l=this.getCurrentVideoId();if(!l){const e=document.createElement("div");return e.className="designbase-wc-youtube-player__error",e.textContent=s("동영상을 불러올 수 없습니다."),void this.replaceChildren(e)}const c=document.createElement("div");c.className="designbase-wc-youtube-player__container";const u=document.createElement("iframe");u.className="designbase-wc-youtube-player__iframe",u.src=this.generateEmbedUrl(l),u.title=this.getAttribute("title")||t("YouTube 동영상: {0}",[l]),u.setAttribute("frameborder","0"),u.setAttribute("allow","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"),u.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),u.referrerPolicy="strict-origin-when-cross-origin",u.allowFullscreen=!0,c.append(u);const d=this.getAttribute("description"),b=document.createDocumentFragment();if(b.append(c),d&&"minimal"!==r){const e=document.createElement("div");e.className="designbase-wc-youtube-player__description";const t=document.createElement("p");t.textContent=d,e.append(t),b.append(e)}this.replaceChildren(b)}}customElements.get("db-youtube-player")||customElements.define("db-youtube-player",DbYouTubePlayer);export{DbYouTubePlayer};
@@ -0,0 +1,52 @@
1
+ type ZoomControlAction = 'out' | 'value' | 'in' | 'fit' | 'fullscreen';
2
+ type ZoomSource = 'button' | 'menu' | 'shortcut' | 'fit';
3
+ interface ZoomChangeDetail {
4
+ value: number;
5
+ previousValue: number;
6
+ source: ZoomSource;
7
+ }
8
+ /** Zoom state and scoped shortcuts. Consumers apply db-change.value / 100 to their content. */
9
+ declare class DbZoomControl extends HTMLElement {
10
+ static get observedAttributes(): string[];
11
+ private toolbar?;
12
+ private status?;
13
+ private target?;
14
+ private observer?;
15
+ private unsubscribe?;
16
+ private fitting;
17
+ private writingValue;
18
+ private fullscreenPending;
19
+ private number;
20
+ get min(): number;
21
+ get max(): number;
22
+ get value(): number;
23
+ set value(value: number);
24
+ get controls(): ZoomControlAction[];
25
+ set controls(value: ZoomControlAction[] | string);
26
+ private hasControl;
27
+ get disabled(): boolean;
28
+ get shortcuts(): boolean;
29
+ connectedCallback(): void;
30
+ disconnectedCallback(): void;
31
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
32
+ private bindTarget;
33
+ private emit;
34
+ private change;
35
+ zoomIn(source?: ZoomSource): void;
36
+ zoomOut(source?: ZoomSource): void;
37
+ reset(source?: ZoomSource): void;
38
+ fit(): void;
39
+ toggleFullscreen(): Promise<void>;
40
+ private fullscreenChanged;
41
+ private action;
42
+ private keydown;
43
+ private sync;
44
+ }
45
+ declare global {
46
+ interface HTMLElementTagNameMap {
47
+ 'db-zoom-control': DbZoomControl;
48
+ }
49
+ }
50
+
51
+ export { DbZoomControl };
52
+ export type { ZoomChangeDetail, ZoomControlAction, ZoomSource };
@@ -0,0 +1 @@
1
+ import{subscribeDbLocale as t,t as e}from"../core/i18n.js";const i=["out","value","in","fit","fullscreen"];class DbZoomControl extends HTMLElement{constructor(){super(...arguments),this.fitting=!1,this.writingValue=!1,this.fullscreenPending=!1,this.fullscreenChanged=()=>{this.sync(),this.emit("db-fullscreen-change",{fullscreen:this.ownerDocument.fullscreenElement===this.target})},this.action=t=>{if(t.stopPropagation(),this.disabled)return;const{id:e,optionId:i}=t.detail;"out"===e?this.zoomOut():"in"===e?this.zoomIn():"fit"===e||"fit"===i?this.fit():"fullscreen"===e?this.toggleFullscreen():"value"===e&&i&&Number.isFinite(Number(i))&&this.change(Number(i),"menu")},this.keydown=t=>{if(!this.shortcuts||this.disabled||t.defaultPrevented||t.isComposing||t.ctrlKey||t.metaKey||t.altKey)return;const e=t.composedPath();if(e.some(t=>t instanceof HTMLElement&&(t.matches('input,textarea,select,[role="textbox"],[role^="menu"]')||t.isContentEditable)))return;if(!(e.includes(this)||this.target&&e.includes(this.target)))return;const i=this.ownerDocument.querySelectorAll("db-zoom-control"),s=t.key.toLowerCase();let n;if(!this.hasControl("in")||"+"!==s&&"="!==s?!this.hasControl("out")||"-"!==s&&"_"!==s?this.hasControl("fit")&&t.shiftKey&&("Digit0"===t.code||")"===s)?n=()=>this.fit():this.hasControl("value")&&t.shiftKey&&("Digit1"===t.code||"!"===s)?n=()=>this.reset("shortcut"):this.hasControl("fullscreen")&&t.shiftKey&&"f"===s&&!t.repeat&&(n=()=>{this.toggleFullscreen()}):n=()=>this.zoomOut("shortcut"):n=()=>this.zoomIn("shortcut"),n){const o="+"===s||"="===s?"in":"-"===s||"_"===s?"out":"f"===s?"fullscreen":"Digit0"===t.code||")"===s?"fit":"value";if(!e.includes(this)&&Array.from(i).find(t=>t.target===this.target&&t.shortcuts&&!t.disabled&&t.hasControl(o))!==this)return;t.preventDefault(),t.stopPropagation(),n()}}}static get observedAttributes(){return["controls","zoom-style","value","min","max","step","size","tone","disabled","shortcuts","target-id","content-width","content-height","fit-padding","hide-fullscreen","aria-label"]}number(t,e){const i=this.getAttribute(t),s=null===i||""===i.trim()?NaN:Number(i);return Number.isFinite(s)?s:e}get min(){return Math.max(1,this.number("min",10))}get max(){return Math.max(this.min,this.number("max",400))}get value(){return Math.min(this.max,Math.max(this.min,this.number("value",100)))}set value(t){Number.isFinite(t)&&this.setAttribute("value",String(t))}get controls(){try{const t=JSON.parse(this.getAttribute("controls")||"null");if(Array.isArray(t))return i.filter(e=>t.includes(e))}catch{}return[...i]}set controls(t){this.setAttribute("controls","string"==typeof t?t:JSON.stringify(t))}hasControl(t){return this.controls.includes(t)&&!("fullscreen"===t&&this.hasAttribute("hide-fullscreen"))}get disabled(){return this.hasAttribute("disabled")}get shortcuts(){return"false"!==this.getAttribute("shortcuts")}connectedCallback(){this.classList.add("designbase-wc-zoom-control"),this.toolbar||(this.toolbar=document.createElement("db-design-toolbar"),this.toolbar.addEventListener("db-tool-action",this.action),this.status=document.createElement("span"),this.status.className="designbase-wc-zoom-control__status",this.status.setAttribute("role","status"),this.status.setAttribute("aria-live","polite"),this.append(this.toolbar,this.status)),this.bindTarget(),this.ownerDocument.addEventListener("keydown",this.keydown),this.ownerDocument.addEventListener("fullscreenchange",this.fullscreenChanged),this.unsubscribe=t(()=>this.sync()),this.sync()}disconnectedCallback(){this.ownerDocument.removeEventListener("keydown",this.keydown),this.ownerDocument.removeEventListener("fullscreenchange",this.fullscreenChanged),this.observer?.disconnect(),this.unsubscribe?.()}attributeChangedCallback(t,e,i){this.isConnected&&("target-id"===t&&this.bindTarget(),"value"!==t||e===i||this.writingValue||(this.fitting=!1),this.sync(),this.fitting&&["content-width","content-height","fit-padding","min","max"].includes(t)&&this.fit())}bindTarget(){this.observer?.disconnect(),this.target=(this.getAttribute("target-id")?this.ownerDocument.getElementById(this.getAttribute("target-id")):this.parentElement)||void 0,this.target&&(this.observer=new ResizeObserver(()=>{this.fitting&&this.fit()}),this.observer.observe(this.target))}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}change(t,e){const i=this.value,s=Math.round(100*Math.min(this.max,Math.max(this.min,t)))/100;this.writingValue=!0,this.value=s,this.writingValue=!1,this.fitting="fit"===e,this.sync(),i!==s&&this.emit("db-change",{value:s,previousValue:i,source:e})}zoomIn(t="button"){this.disabled||this.change(this.value+Math.max(1,this.number("step",25)),t)}zoomOut(t="button"){this.disabled||this.change(this.value-Math.max(1,this.number("step",25)),t)}reset(t="button"){this.disabled||this.change(100,t)}fit(){if(this.disabled)return;const t=this.number("content-width",0),e=this.number("content-height",0);if(this.target&&t>0&&e>0){const i=2*Math.max(0,this.number("fit-padding",24)),s=this.target.clientWidth-i,n=this.target.clientHeight-i;s>0&&n>0&&(this.change(100*Math.min(s/t,n/e),"fit"),this.fitting=!0,this.sync())}this.emit("db-fit",{value:this.value})}async toggleFullscreen(){if(!(this.disabled||this.fullscreenPending||this.hasAttribute("hide-fullscreen"))&&this.target){this.fullscreenPending=!0,this.sync();try{this.ownerDocument.fullscreenElement===this.target?await this.ownerDocument.exitFullscreen():await this.target.requestFullscreen()}catch{this.emit("db-error",{action:"fullscreen",message:e("zoom.fullscreenError")})}finally{this.fullscreenPending=!1,this.sync()}}}sync(){if(!this.toolbar)return;this.toolbar.setAttribute("size",["xs","s"].includes(this.getAttribute("size")||"")?"s":"m"),this.toolbar.setAttribute("tone","dark"===this.getAttribute("tone")?"dark":"default"),this.toolbar.setAttribute("aria-label",this.getAttribute("aria-label")||e("zoom.label"));const t=this.ownerDocument.fullscreenElement===this.target,shortcut=t=>this.shortcuts?t:void 0,i=[{id:"out",icon:"plain"===this.getAttribute("zoom-style")?"minus":"zoom-out",label:e("zoom.out"),shortcut:shortcut("−"),disabled:this.disabled||this.value<=this.min},{id:"value",label:e("zoom.value"),text:`${this.value}%`,value:this.fitting?"fit":String(this.value),disabled:this.disabled,options:[...[25,50,75,100,125,150,200,400].filter(t=>t>=this.min&&t<=this.max).map(t=>({id:String(t),label:`${t}%`,shortcut:100===t?shortcut("Shift+1"):void 0})),...this.hasControl("fit")?[{id:"fit",label:e("zoom.fit"),shortcut:shortcut("Shift+0")}]:[]]},{id:"in",icon:"plain"===this.getAttribute("zoom-style")?"plus":"zoom-in",label:e("zoom.in"),shortcut:shortcut("+"),disabled:this.disabled||this.value>=this.max},{id:"fit",text:e("zoom.fitShort"),label:e("zoom.fit"),shortcut:shortcut("Shift+0"),disabled:this.disabled}];this.hasControl("fullscreen")&&i.push({id:"fullscreen",icon:t?"shrink":"expand",label:e(t?"zoom.exitFullscreen":"zoom.fullscreen"),shortcut:shortcut("Shift+F"),active:t,disabled:this.disabled||this.fullscreenPending||!this.target||!this.ownerDocument.fullscreenEnabled}),this.toolbar.items=i.filter(t=>this.hasControl(t.id)),this.toolbar.hidden=!this.toolbar.items.length;const s={out:"-",in:"+",fit:"Shift+0",fullscreen:"Shift+F"};for(const t of this.toolbar.querySelectorAll("db-button[data-tool-id]")){const e=s[t.dataset.toolId||""];e&&this.shortcuts?t.setAttribute("aria-keyshortcuts",e):t.removeAttribute("aria-keyshortcuts")}const n=`${this.value}%`;this.status&&this.status.textContent!==n&&(this.status.textContent=n)}}customElements.get("db-zoom-control")||customElements.define("db-zoom-control",DbZoomControl);export{DbZoomControl};
@@ -1 +1 @@
1
- import{dbText as e}from"../core/i18n.js";const t={weak:"약함",fair:"보통",good:"강함","very-strong":"아주 강함"};function getPasswordScore(e){switch(e){case"weak":return 1;case"fair":return 2;case"good":return 3;case"very-strong":return 4;default:return 0}}function getPasswordValidation(r,o=8){const s=[{id:"length",label:`최소 ${o}자 이상`,met:r.length>=o},{id:"lowercase",label:e("소문자 포함"),met:/[a-z]/.test(r)},{id:"uppercase",label:e("대문자 포함"),met:/[A-Z]/.test(r)},{id:"number",label:e("숫자 포함"),met:/\d/.test(r)},{id:"special",label:e("특수문자 포함"),met:/[!@#$%^&*(),.?":{}|<>]/.test(r)}],a=function getPasswordStrengthLevel(e,t){return!t||e<=1?"weak":2===e?"fair":3===e?"good":"very-strong"}(s.filter(e=>e.met).length,r.length>0);return{requirements:s,score:getPasswordScore(a),level:a,levelLabel:t[a],isValid:s.every(e=>e.met),isStrongEnough:"good"===a||"very-strong"===a}}export{getPasswordValidation};
1
+ import{dbFormat as e,dbText as r,t}from"../core/i18n.js";const o={weak:"password.weak",fair:"password.fair",good:"password.good","very-strong":"password.veryStrong"};function getPasswordScore(e){switch(e){case"weak":return 1;case"fair":return 2;case"good":return 3;case"very-strong":return 4;default:return 0}}function getPasswordValidation(s,a=8){const n=[{id:"length",label:e("최소 {0}자 이상",[a]),met:s.length>=a},{id:"lowercase",label:r("소문자 포함"),met:/[a-z]/.test(s)},{id:"uppercase",label:r("대문자 포함"),met:/[A-Z]/.test(s)},{id:"number",label:r("숫자 포함"),met:/\d/.test(s)},{id:"special",label:r("특수문자 포함"),met:/[!@#$%^&*(),.?":{}|<>]/.test(s)}],l=function getPasswordStrengthLevel(e,r){return!r||e<=1?"weak":2===e?"fair":3===e?"good":"very-strong"}(n.filter(e=>e.met).length,s.length>0);return{requirements:n,score:getPasswordScore(l),level:l,levelLabel:t(o[l]),isValid:n.every(e=>e.met),isStrongEnough:"good"===l||"very-strong"===l}}export{getPasswordValidation};
@@ -12,7 +12,9 @@ interface DbPaperAction {
12
12
  declare abstract class DbPaperCard extends DbElement {
13
13
  protected view: HTMLDivElement;
14
14
  private ready;
15
+ private unsubscribeLocale?;
15
16
  connectedCallback(): void;
17
+ disconnectedCallback(): void;
16
18
  protected upgradeNames(): string[];
17
19
  attributeChangedCallback(): void;
18
20
  protected abstract render(): void;
@@ -1 +1 @@
1
- import{t as e}from"../core/i18n.js";import{DbElement as t}from"../core/base-element.js";import"./db-button.js";function paperNode(e,t,r=""){const a=document.createElement(e);return a.className=t,a.textContent=r,a}function paperFields(e){return Array.isArray(e)?e.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.label&&"string"==typeof e.value).map(({label:e,value:t})=>({label:e,value:t})):[]}function paperJson(e){try{return null==e?[]:JSON.parse(e)}catch{return[]}}function paperImageSrc(e){if(!e?.trim())return null;try{const t=new URL(e,document.baseURI);return["http:","https:","blob:"].includes(t.protocol)||"data:"===t.protocol&&/^data:image\/(png|jpeg|webp|gif|svg\+xml)[;,]/i.test(e)?e:null}catch{return null}}function paperDetails(e){const t=paperNode("dl","db-paper__fields");for(const{label:r,value:a}of e){const e=paperNode("div","db-paper__field");e.append(paperNode("dt","",r),paperNode("dd","",a)),t.append(e)}return t}class DbPaperCard extends t{constructor(){super(...arguments),this.view=paperNode("div","db-paper__view"),this.ready=!1}connectedCallback(){this.view.parentNode||this.prepend(this.view),this.classList.add("db-paper"),this.upgradeProperties(this.upgradeNames()),this.ready=!0,this.render()}upgradeNames(){return[]}attributeChangedCallback(){this.ready&&this.render()}attr(e){return this.getAttribute(e)||""}action(e){const t=this.attr("action-label");if(!t)return null;const r=document.createElement("db-button");return r.textContent=t,r.setAttribute("variant","secondary"),r.setAttribute("size","m"),r.dataset.paperAction="",r.toggleAttribute("disabled",this.hasAttribute("disabled")),r.addEventListener("db-click",()=>{this.hasAttribute("disabled")||this.dbEmit("action",{reference:e})}),r}replace(...e){const t=this.view.contains(document.activeElement)&&document.activeElement?.hasAttribute("data-paper-action");this.view.replaceChildren(...e),t&&this.view.querySelector("[data-paper-action]:not([disabled])")?.focus()}code(t){const r=paperNode("div","db-paper__code"),a=paperImageSrc(this.attr("code-image-src"));if(a){const p=paperNode("img","db-paper__code-image");p.src=a,p.alt=this.attr("code-alt")||t||e("paper.codeImage"),p.addEventListener("error",()=>{p.hidden=!0}),r.append(p)}return t&&r.append(paperNode("span","db-paper__reference",t)),r}}export{DbPaperCard,paperDetails,paperFields,paperImageSrc,paperJson,paperNode};
1
+ import{subscribeDbLocale as e,t}from"../core/i18n.js";import{DbElement as r}from"../core/base-element.js";import"./db-button.js";function paperNode(e,t,r=""){const a=document.createElement(e);return a.className=t,a.textContent=r,a}function paperFields(e){return Array.isArray(e)?e.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.label&&"string"==typeof e.value).map(({label:e,value:t})=>({label:e,value:t})):[]}function paperJson(e){try{return null==e?[]:JSON.parse(e)}catch{return[]}}function paperImageSrc(e){if(!e?.trim())return null;try{const t=new URL(e,document.baseURI);return["http:","https:","blob:"].includes(t.protocol)||"data:"===t.protocol&&/^data:image\/(png|jpeg|webp|gif|svg\+xml)[;,]/i.test(e)?e:null}catch{return null}}function paperDetails(e){const t=paperNode("dl","db-paper__fields");for(const{label:r,value:a}of e){const e=paperNode("div","db-paper__field");e.append(paperNode("dt","",r),paperNode("dd","",a)),t.append(e)}return t}class DbPaperCard extends r{constructor(){super(...arguments),this.view=paperNode("div","db-paper__view"),this.ready=!1}connectedCallback(){this.view.parentNode||this.prepend(this.view),this.classList.add("db-paper"),this.upgradeProperties(this.upgradeNames()),this.ready=!0,this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.render()),this.render()}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,this.ready=!1,super.disconnectedCallback()}upgradeNames(){return[]}attributeChangedCallback(){this.ready&&this.render()}attr(e){return this.getAttribute(e)||""}action(e){const t=this.attr("action-label");if(!t)return null;const r=document.createElement("db-button");return r.textContent=t,r.setAttribute("variant","secondary"),r.setAttribute("size","m"),r.dataset.paperAction="",r.toggleAttribute("disabled",this.hasAttribute("disabled")),r.addEventListener("db-click",()=>{this.hasAttribute("disabled")||this.dbEmit("action",{reference:e})}),r}replace(...e){const t=this.view.contains(document.activeElement)&&document.activeElement?.hasAttribute("data-paper-action");this.view.replaceChildren(...e),t&&this.view.querySelector("[data-paper-action]:not([disabled])")?.focus()}code(e){const r=paperNode("div","db-paper__code"),a=paperImageSrc(this.attr("code-image-src"));if(a){const i=paperNode("img","db-paper__code-image");i.src=a,i.alt=this.attr("code-alt")||e||t("paper.codeImage"),i.addEventListener("error",()=>{i.hidden=!0}),r.append(i)}return e&&r.append(paperNode("span","db-paper__reference",e)),r}}export{DbPaperCard,paperDetails,paperFields,paperImageSrc,paperJson,paperNode};
@@ -0,0 +1 @@
1
+ class ControlLabel{constructor(t){this.host=t}connect(t,e){this.disconnect(),this.target!==t&&(this.target=t,this.slot=t.querySelector(":scope > [data-db-children]"),this.text=void 0),this.slot&&(this.observer=new MutationObserver(e),this.observer.observe(this.slot,{childList:!0,characterData:!0,subtree:!0})),this.sync()}sync(){if(!this.target)return;const t=this.host.getAttribute("label");if(null===t)return this.text?.remove(),void(this.slot&&(this.slot.style.display="contents"));this.text??(this.text=this.target.ownerDocument.createTextNode(""));for(const t of Array.from(this.target.childNodes))t!==this.slot&&t!==this.text&&t.remove();this.text.parentNode!==this.target&&this.target.append(this.text),this.text.data!==t&&(this.text.data=t),this.slot&&(this.slot.style.display="none")}get value(){return this.host.getAttribute("label")??this.target?.textContent??""}disconnect(){this.observer?.disconnect(),this.observer=void 0}}export{ControlLabel};