@designbasekorea/ui-wc 0.5.3 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/chunks/lottie-e273b936.js +17032 -0
  2. package/dist/esm/components/animation-background/canvas-controller.js +121 -1
  3. package/dist/esm/components/animation-background/controls.js +180 -1
  4. package/dist/esm/components/animation-background/graphite-controller.js +196 -1
  5. package/dist/esm/components/animation-background/graphite-presets.js +42 -1
  6. package/dist/esm/components/animation-background/graphite-shader.js +111 -1
  7. package/dist/esm/components/animation-background/layers.js +369 -1
  8. package/dist/esm/components/animation-background/lumina-presets.js +53 -1
  9. package/dist/esm/components/animation-background/lumina-shaders.js +181 -1
  10. package/dist/esm/components/animation-background/lumina-utils.js +60 -1
  11. package/dist/esm/components/animation-background/particle-defaults.js +36 -1
  12. package/dist/esm/components/animation-background/renderers.js +167 -1
  13. package/dist/esm/components/animation-background/shader-controller.js +154 -1
  14. package/dist/esm/components/color-picker-utils.js +134 -1
  15. package/dist/esm/components/date-picker-utils.js +140 -1
  16. package/dist/esm/components/db-accordion.js +304 -1
  17. package/dist/esm/components/db-ad-banner.js +455 -1
  18. package/dist/esm/components/db-alert.js +149 -1
  19. package/dist/esm/components/db-animation-background.js +486 -1
  20. package/dist/esm/components/db-animation-text.js +601 -1
  21. package/dist/esm/components/db-app-badge.js +88 -1
  22. package/dist/esm/components/db-audio-player.js +752 -1
  23. package/dist/esm/components/db-avatar.js +188 -1
  24. package/dist/esm/components/db-backdrop.js +139 -1
  25. package/dist/esm/components/db-badge.js +91 -1
  26. package/dist/esm/components/db-banner.js +188 -1
  27. package/dist/esm/components/db-bottom-navigation.js +146 -1
  28. package/dist/esm/components/db-bottom-sheet.js +481 -1
  29. package/dist/esm/components/db-breadcrumbs.js +329 -1
  30. package/dist/esm/components/db-button.js +316 -1
  31. package/dist/esm/components/db-calendar.js +675 -1
  32. package/dist/esm/components/db-card.js +265 -1
  33. package/dist/esm/components/db-carousel.js +264 -1
  34. package/dist/esm/components/db-checkbox.js +202 -1
  35. package/dist/esm/components/db-chip.js +104 -1
  36. package/dist/esm/components/db-code-block.js +304 -1
  37. package/dist/esm/components/db-color-picker.js +746 -1
  38. package/dist/esm/components/db-confirm.js +191 -1
  39. package/dist/esm/components/db-container.js +39 -1
  40. package/dist/esm/components/db-context-menu.js +265 -1
  41. package/dist/esm/components/db-countdown.js +146 -1
  42. package/dist/esm/components/db-cursor-follower.js +173 -1
  43. package/dist/esm/components/db-date-picker.js +837 -1
  44. package/dist/esm/components/db-divider.js +66 -1
  45. package/dist/esm/components/db-drawer.js +245 -1
  46. package/dist/esm/components/db-dropdown.js +493 -1
  47. package/dist/esm/components/db-dropzone.js +266 -1
  48. package/dist/esm/components/db-empty-state.js +97 -1
  49. package/dist/esm/components/db-file-uploader.js +256 -1
  50. package/dist/esm/components/db-floating-action-button.js +139 -1
  51. package/dist/esm/components/db-footer.js +385 -1
  52. package/dist/esm/components/db-form.js +566 -1
  53. package/dist/esm/components/db-gantt.js +882 -1
  54. package/dist/esm/components/db-gradient.js +133 -1
  55. package/dist/esm/components/db-grid-background.js +909 -1
  56. package/dist/esm/components/db-grid.js +73 -1
  57. package/dist/esm/components/db-icons.js +82 -1
  58. package/dist/esm/components/db-image-list.js +214 -1
  59. package/dist/esm/components/db-image-placeholder.js +96 -1
  60. package/dist/esm/components/db-image.js +289 -1
  61. package/dist/esm/components/db-indicator.js +517 -1
  62. package/dist/esm/components/db-input.js +420 -1
  63. package/dist/esm/components/db-kanban.js +538 -1
  64. package/dist/esm/components/db-label.js +66 -1
  65. package/dist/esm/components/db-lightbox.js +372 -1
  66. package/dist/esm/components/db-list.js +340 -1
  67. package/dist/esm/components/db-logo-svg.js +72 -1
  68. package/dist/esm/components/db-logo.js +106 -1
  69. package/dist/esm/components/db-lottie.js +109 -1
  70. package/dist/esm/components/db-markdown-editor.js +458 -1
  71. package/dist/esm/components/db-marquee.js +402 -1
  72. package/dist/esm/components/db-masonry.js +94 -1
  73. package/dist/esm/components/db-menu-item.js +211 -1
  74. package/dist/esm/components/db-modal.js +522 -1
  75. package/dist/esm/components/db-navbar.js +680 -1
  76. package/dist/esm/components/db-onboarding-modal.js +322 -1
  77. package/dist/esm/components/db-page-header.js +312 -1
  78. package/dist/esm/components/db-pagination.js +271 -1
  79. package/dist/esm/components/db-popover.js +580 -1
  80. package/dist/esm/components/db-progress-step.js +166 -1
  81. package/dist/esm/components/db-progress.js +171 -1
  82. package/dist/esm/components/db-progressbar.js +72 -1
  83. package/dist/esm/components/db-radio.js +172 -1
  84. package/dist/esm/components/db-random-gradient.js +121 -1
  85. package/dist/esm/components/db-range-slider.js +811 -1
  86. package/dist/esm/components/db-rating.js +325 -1
  87. package/dist/esm/components/db-reorder.js +286 -1
  88. package/dist/esm/components/db-resizable-panels.js +282 -1
  89. package/dist/esm/components/db-scroll-area.js +126 -1
  90. package/dist/esm/components/db-search-bar.js +668 -1
  91. package/dist/esm/components/db-section-app-download.js +101 -1
  92. package/dist/esm/components/db-section-contact.js +102 -1
  93. package/dist/esm/components/db-section-cta.js +150 -1
  94. package/dist/esm/components/db-section-faq.js +120 -1
  95. package/dist/esm/components/db-section-feature-grid.js +137 -1
  96. package/dist/esm/components/db-section-feature.js +179 -1
  97. package/dist/esm/components/db-section-hero.js +243 -1
  98. package/dist/esm/components/db-section-logo-cloud.js +138 -1
  99. package/dist/esm/components/db-section-pricing.js +166 -1
  100. package/dist/esm/components/db-section-stats.js +129 -1
  101. package/dist/esm/components/db-section-testimonials.js +252 -1
  102. package/dist/esm/components/db-section.js +385 -1
  103. package/dist/esm/components/db-segment-control.js +231 -1
  104. package/dist/esm/components/db-select.js +571 -1
  105. package/dist/esm/components/db-share-icons.js +40 -1
  106. package/dist/esm/components/db-share.js +489 -1
  107. package/dist/esm/components/db-sidebar.js +1068 -1
  108. package/dist/esm/components/db-skeleton.js +94 -1
  109. package/dist/esm/components/db-spinner.js +102 -1
  110. package/dist/esm/components/db-split-view.js +297 -1
  111. package/dist/esm/components/db-stack.js +46 -1
  112. package/dist/esm/components/db-stat.js +218 -1
  113. package/dist/esm/components/db-stepper.js +398 -1
  114. package/dist/esm/components/db-table.js +412 -1
  115. package/dist/esm/components/db-tabs.js +335 -1
  116. package/dist/esm/components/db-testimonial.js +162 -1
  117. package/dist/esm/components/db-textarea.js +233 -1
  118. package/dist/esm/components/db-time-picker.js +523 -1
  119. package/dist/esm/components/db-timeline.js +174 -1
  120. package/dist/esm/components/db-toast.js +158 -1
  121. package/dist/esm/components/db-toggle.js +162 -1
  122. package/dist/esm/components/db-toolbar.js +94 -1
  123. package/dist/esm/components/db-tooltip.js +440 -1
  124. package/dist/esm/components/db-top-banner.js +121 -1
  125. package/dist/esm/components/db-tutorial.js +324 -1
  126. package/dist/esm/components/db-video-player.js +503 -1
  127. package/dist/esm/components/db-youtube-player.js +194 -1
  128. package/dist/esm/components/highlight-code.js +65 -1
  129. package/dist/esm/components/input-validation.js +50 -1
  130. package/dist/esm/components/marketing-block-host-utils.js +9 -1
  131. package/dist/esm/components/number-scrub.js +117 -1
  132. package/dist/esm/components/random-gradient-palettes.js +67 -1
  133. package/dist/esm/components/section-block-header-utils.js +48 -1
  134. package/dist/esm/helpers/confirm.js +52 -1
  135. package/dist/esm/helpers/toast.js +73 -1
  136. package/dist/esm/index.js +111 -1
  137. package/dist/esm/node_modules/prismjs/components/prism-bash.js +235 -1
  138. package/dist/esm/node_modules/prismjs/components/prism-clike.js +31 -1
  139. package/dist/esm/node_modules/prismjs/components/prism-css.js +64 -1
  140. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +172 -1
  141. package/dist/esm/node_modules/prismjs/components/prism-json.js +27 -1
  142. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +143 -1
  143. package/dist/esm/node_modules/prismjs/components/prism-markup.js +186 -1
  144. package/dist/esm/node_modules/prismjs/components/prism-scss.js +81 -1
  145. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +15 -1
  146. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +60 -1
  147. package/dist/index.cjs +55414 -1
  148. package/dist/index.css +1 -1
  149. package/dist/index.esm.js +38284 -1
  150. package/dist/react/chunks/lottie-72a4c6d4.js +17032 -0
  151. package/dist/react/index.cjs +56503 -1
  152. package/dist/react/index.d.ts +1 -1
  153. package/dist/react/index.esm.js +39373 -1
  154. package/package.json +1 -1
  155. package/dist/chunks/lottie-2d1f6278.js +0 -1
  156. package/dist/react/chunks/lottie-14e7addb.js +0 -1
@@ -1 +1,486 @@
1
- import{parseAnimationBackgroundConfig as t,mountAnimationLayers as e,applyContainerStyles as s,syncLuminaOverlays as n,ANIMATION_BACKGROUND_CLASS as i}from"./animation-background/layers.js";import{getControlsForType as o,resetControlsToDefaults as l,getControlSliderBounds as r,controlSliderToAttr as a,writeControlAttribute as c,readControlValue as d,controlAttrToSlider as h}from"./animation-background/controls.js";import{isGraphitePresetId as u,getGraphitePreset as p}from"./animation-background/graphite-presets.js";import{createDbIcon as m}from"./db-icons.js";const b=i,g=["particles","stars","snow","flowers","confetti","mesh-flow","fluid","radiant","ethereal","graphite"],y=["mesh-flow","fluid","radiant","ethereal"];class DbAnimationBackground extends HTMLElement{constructor(){super(...arguments),this.panelOpen=!1,this.controlInputs=new Map,this.syncingControls=!1}static get observedAttributes(){return["type","theme","intensity","blur","speed","direction","colors","width","height","border-radius","opacity","blend-mode","particle-count","particle-size","star-count","star-size","wind","clickable","disabled","noise","show-vignette","preset","zoom","show-controls","shader-speed","shader-intensity","warp"]}connectedCallback(){this.style.display="block",this.render()}disconnectedCallback(){this.teardownLayers(),this.clickHandler&&(this.removeEventListener("click",this.clickHandler),this.clickHandler=void 0)}attributeChangedCallback(t,e,s){this.isConnected&&e!==s&&("show-controls"!==t?this.syncingControls||("type"===t&&this.getBool("show-controls",!1)&&this.rebuildControlInputs(),this.applyConfig(),this.getBool("show-controls",!1)&&this.syncControlInputs()):this.syncControlsVisibility())}getBool(t,e=!1){return this.hasAttribute(t)?"false"!==this.getAttribute(t):e}getSizeStyle(t){if(!t)return;const e=Number(t);return Number.isFinite(e)?`${e}px`:t}getConfig(){return t(this)}teardownLayers(){this.layerHandle?.destroy?.(),this.layerHandle=void 0,this.mountedType=void 0,this.layersEl&&this.layersEl.replaceChildren()}remountLayers(t){this.layersEl&&(this.teardownLayers(),this.layerHandle=e(this.layersEl,t),this.mountedType=t.type)}canLiveUpdate(t){return this.mountedType===t.type&&g.includes(t.type)&&"function"==typeof this.layerHandle?.update}applyConfig(){const t=this.getConfig();this.className=[b,`${b}--${t.type}`,t.clickable&&!t.disabled&&`${b}--clickable`,t.disabled&&`${b}--disabled`,this.getBool("show-controls",!1)&&`${b}--show-controls`].filter(Boolean).join(" "),s(this,t);const e=this.getSizeStyle(this.getAttribute("width")),i=this.getSizeStyle(this.getAttribute("height")),o=this.getSizeStyle(this.getAttribute("border-radius"));e&&(this.style.width=e),i&&(this.style.height=i),o&&(this.style.borderRadius=o),this.style.opacity="",this.layersEl&&(this.layersEl.style.opacity=String(t.opacity));const l=this.getAttribute("blend-mode");if(l&&(this.style.mixBlendMode=l),t.disabled)return this.teardownLayers(),void this.syncClickable(t);this.canLiveUpdate(t)?(this.layerHandle?.update?.(t),this.layersEl&&y.includes(t.type)&&n(this.layersEl,t.noise,t.showVignette)):this.remountLayers(t),this.syncClickable(t)}syncClickable(t){this.clickHandler&&(this.removeEventListener("click",this.clickHandler),this.clickHandler=void 0),t.clickable&&!t.disabled&&(this.clickHandler=()=>{this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0}))},this.addEventListener("click",this.clickHandler))}render(){if(!this.layersEl){this.layersEl=document.createElement("div"),this.layersEl.className=`${b}__layers`,Object.assign(this.layersEl.style,{position:"absolute",inset:"0",width:"100%",height:"100%",zIndex:"0"}),this.contentEl=document.createElement("div"),this.contentEl.className=`${b}__content`,Object.assign(this.contentEl.style,{position:"relative",zIndex:"10",width:"100%",height:"100%"});const t=document.createElement("slot");this.contentEl.appendChild(t),this.append(this.layersEl,this.contentEl)}this.ensureControlsShell(),this.applyConfig(),this.syncControlsVisibility()}ensureControlsShell(){if(this.controlsRootEl)return;const t=document.createElement("div");t.className=`${b}__controls-root`,t.hidden=!0;const e=this.createIconButton("settings","컨트롤 열기",()=>{this.panelOpen=!this.panelOpen,this.syncPanelState()});e.setAttribute("variant","secondary"),e.className=`${b}__controls-gear`;const s=document.createElement("div");s.className=`${b}__controls-panel`,s.setAttribute("role","dialog"),s.hidden=!0,s.addEventListener("click",t=>t.stopPropagation());const n=document.createElement("div");n.className=`${b}__controls-head`;const i=document.createElement("span");i.className=`${b}__controls-title`,i.append(m("settings",14),document.createTextNode(" Controls"));const r=this.createIconButton("refresh","기본값으로 초기화",()=>{const t=this.getConfig(),e=o(t.type);if(this.syncingControls=!0,l(this,e),"graphite"===t.type){const t=p("graphite");this.setAttribute("zoom",String(t.scale)),this.setAttribute("colors",JSON.stringify(t.colors))}this.syncingControls=!1,this.applyConfig(),this.syncControlInputs()});r.className=`${b}__controls-icon-btn`,n.append(i,r),s.append(n),t.append(e,s),this.appendChild(t),this.controlsRootEl=t,this.panelEl=s}syncControlsVisibility(){if(!this.controlsRootEl)return;const t=this.getBool("show-controls",!1);this.controlsRootEl.hidden=!t,t?this.rebuildControlInputs():(this.panelOpen=!1,this.syncPanelState())}syncPanelState(){if(!this.controlsRootEl||!this.panelEl)return;const t=this.controlsRootEl.querySelector(`.${b}__controls-gear`);t&&(t.dataset.open=this.panelOpen?"true":"false",t.setAttribute("aria-expanded",this.panelOpen?"true":"false")),this.panelEl.hidden=!this.panelOpen}createIconButton(t,e,s){const n=document.createElement("db-button");return n.setAttribute("variant","tertiary"),n.setAttribute("size","s"),n.setAttribute("icon-only",""),n.setAttribute("start-icon",t),n.setAttribute("aria-label",e),n.addEventListener("click",t=>{t.stopPropagation(),s()}),n}rebuildControlInputs(){if(!this.panelEl)return;this.panelEl.querySelectorAll(`.${b}__controls-field`).forEach(t=>t.remove()),this.controlInputs.clear();const t=this.getConfig(),e=o(t.type),s=this.panelEl.querySelector(`.${b}__controls-title`);s&&(s.lastChild?.remove(),s.append(document.createTextNode(` ${t.type}`))),e.forEach(t=>{const e=this.createControlField(t);this.panelEl?.appendChild(e)}),this.syncControlInputs()}createControlField(t){const e=document.createElement("div");if(e.className=`${b}__controls-field`,e.dataset.attr=t.attr,"range"===t.kind){const s=r(t),n=document.createElement("db-range-slider");return n.className=`${b}__controls-range`,n.setAttribute("size","s"),n.setAttribute("full-width",""),n.setAttribute("show-label",""),n.setAttribute("label",t.label),n.setAttribute("show-value",""),n.setAttribute("value-position","top"),n.setAttribute("show-thumb-tooltip",""),n.setAttribute("min",String(s.min)),n.setAttribute("max",String(s.max)),n.setAttribute("step",String(s.step)),null!=s.fixed&&n.setAttribute("value-fixed",String(s.fixed)),n.addEventListener("db-change",e=>{const s=e.detail;null!=s.value&&this.commitControlValue(t,a(t,s.value))}),e.appendChild(n),this.controlInputs.set(t.attr,n),e}if("select"===t.kind){const s=document.createElement("div");s.className=`${b}__controls-label`,s.textContent=t.label;const n=t.options??[];if(n.length>4){const i=document.createElement("db-select");i.className=`${b}__controls-select`,i.setAttribute("size","s"),i.setAttribute("full-width",""),i.setAttribute("options",JSON.stringify(n.map(t=>({value:t,label:t})))),i.addEventListener("db-change",e=>{const s=e.detail;null!=s.value&&this.commitControlValue(t,s.value)}),e.append(s,i),this.controlInputs.set(t.attr,i)}else{const i=document.createElement("db-segment-control");i.className=`${b}__controls-seg`,i.setAttribute("size","s"),i.setAttribute("full-width",""),i.setAttribute("options",JSON.stringify(n.map(t=>({value:t,label:t})))),i.addEventListener("db-change",e=>{const s=e.detail;null!=s.value&&this.commitControlValue(t,s.value)}),e.append(s,i),this.controlInputs.set(t.attr,i)}return e}if("toggle"===t.kind){const s=document.createElement("div");s.className=`${b}__controls-toggle`;const n=document.createElement("span");n.textContent=t.label;const i=document.createElement("db-toggle");return i.setAttribute("size","s"),i.addEventListener("db-change",e=>{const s=e.detail;this.commitControlValue(t,Boolean(s.checked))}),s.append(n,i),e.appendChild(s),this.controlInputs.set(t.attr,i),e}const s=document.createElement("div");s.className=`${b}__controls-label`,s.textContent=t.label;const n=document.createElement("div");return n.className=`${b}__controls-colors`,e.append(s,n),this.controlInputs.set(t.attr,n),e}commitControlValue(t,e){if(this.syncingControls=!0,c(this,t,e),"preset"===t.attr&&"graphite"===this.getConfig().type&&"string"==typeof e&&u(e)){const t=p(e);this.setAttribute("zoom",String(t.scale)),this.setAttribute("colors",JSON.stringify(t.colors))}this.syncingControls=!1,this.applyConfig()}syncControlInputs(){o(this.getConfig().type).forEach(t=>{const e=d(this,t),s=this.controlInputs.get(t.attr);if(s)if("range"===t.kind&&function isRangeSlider(t){return"DB-RANGE-SLIDER"===t.tagName}(s)){const n=Number(e);s.setAttribute("value",String(h(t,n)))}else if("select"===t.kind)s.setAttribute("value",String(e));else if("toggle"===t.kind){Boolean(e)?s.setAttribute("checked",""):s.removeAttribute("checked")}else"colors"===t.kind&&this.renderColorInputs(s,t,e)})}renderColorInputs(t,e,s){const n=[...s];t.replaceChildren();const commitStructuralChange=s=>{this.commitControlValue(e,s),this.renderColorInputs(t,e,s)};if(n.forEach((s,i)=>{const o=document.createElement("div");o.className=`${b}__controls-color`;const l=document.createElement("db-color-picker");if(l.className=`${b}__controls-color-picker`,l.setAttribute("size","s"),l.setAttribute("full-width",""),l.setAttribute("show-alpha","false"),l.setAttribute("show-eyedropper","false"),l.setAttribute("value",s),l.addEventListener("db-change",t=>{const s=t.detail,o=s?.value;"string"==typeof o&&o&&(n[i]=o,this.commitControlValue(e,[...n]))}),o.appendChild(l),n.length>1){const t=this.createIconButton("trash","색상 제거",()=>{commitStructuralChange(n.filter((t,e)=>e!==i))});t.className=`${b}__controls-color-remove`,o.appendChild(t)}t.appendChild(o)}),n.length<8){const e=this.createIconButton("plus","색상 추가",()=>{commitStructuralChange([...n,n[n.length-1]??"#ffffff"])});e.className=`${b}__controls-color-add`,t.appendChild(e)}}}customElements.get("db-animation-background")||customElements.define("db-animation-background",DbAnimationBackground);export{DbAnimationBackground};
1
+ import { parseAnimationBackgroundConfig, mountAnimationLayers, applyContainerStyles, syncLuminaOverlays, ANIMATION_BACKGROUND_CLASS as CLASS$1 } from './animation-background/layers.js';
2
+ import { getControlsForType, resetControlsToDefaults, getControlSliderBounds, controlSliderToAttr, writeControlAttribute, readControlValue, controlAttrToSlider } from './animation-background/controls.js';
3
+ import { isGraphitePresetId, getGraphitePreset } from './animation-background/graphite-presets.js';
4
+ import { createDbIcon } from './db-icons.js';
5
+
6
+ const CLASS = CLASS$1;
7
+ const LIVE_UPDATE_TYPES = [
8
+ 'particles',
9
+ 'stars',
10
+ 'snow',
11
+ 'flowers',
12
+ 'confetti',
13
+ 'mesh-flow',
14
+ 'fluid',
15
+ 'radiant',
16
+ 'ethereal',
17
+ 'graphite',
18
+ ];
19
+ const LUMINA_SHADER_TYPES = ['mesh-flow', 'fluid', 'radiant', 'ethereal'];
20
+ function isRangeSlider(el) {
21
+ return el.tagName === 'DB-RANGE-SLIDER';
22
+ }
23
+ class DbAnimationBackground extends HTMLElement {
24
+ constructor() {
25
+ super(...arguments);
26
+ this.panelOpen = false;
27
+ this.controlInputs = new Map();
28
+ this.syncingControls = false;
29
+ }
30
+ static get observedAttributes() {
31
+ return [
32
+ 'type',
33
+ 'theme',
34
+ 'intensity',
35
+ 'blur',
36
+ 'speed',
37
+ 'direction',
38
+ 'colors',
39
+ 'width',
40
+ 'height',
41
+ 'border-radius',
42
+ 'opacity',
43
+ 'blend-mode',
44
+ 'particle-count',
45
+ 'particle-size',
46
+ 'star-count',
47
+ 'star-size',
48
+ 'wind',
49
+ 'clickable',
50
+ 'disabled',
51
+ 'noise',
52
+ 'show-vignette',
53
+ 'preset',
54
+ 'zoom',
55
+ 'show-controls',
56
+ 'shader-speed',
57
+ 'shader-intensity',
58
+ 'warp',
59
+ ];
60
+ }
61
+ connectedCallback() {
62
+ this.style.display = 'block';
63
+ this.render();
64
+ }
65
+ disconnectedCallback() {
66
+ this.teardownLayers();
67
+ if (this.clickHandler) {
68
+ this.removeEventListener('click', this.clickHandler);
69
+ this.clickHandler = undefined;
70
+ }
71
+ }
72
+ attributeChangedCallback(name, oldValue, newValue) {
73
+ if (!this.isConnected || oldValue === newValue)
74
+ return;
75
+ if (name === 'show-controls') {
76
+ this.syncControlsVisibility();
77
+ return;
78
+ }
79
+ if (this.syncingControls)
80
+ return;
81
+ if (name === 'type' && this.getBool('show-controls', false)) {
82
+ this.rebuildControlInputs();
83
+ }
84
+ this.applyConfig();
85
+ if (this.getBool('show-controls', false)) {
86
+ this.syncControlInputs();
87
+ }
88
+ }
89
+ getBool(name, fallback = false) {
90
+ if (!this.hasAttribute(name))
91
+ return fallback;
92
+ return this.getAttribute(name) !== 'false';
93
+ }
94
+ getSizeStyle(value) {
95
+ if (!value)
96
+ return undefined;
97
+ const num = Number(value);
98
+ return Number.isFinite(num) ? `${num}px` : value;
99
+ }
100
+ getConfig() {
101
+ return parseAnimationBackgroundConfig(this);
102
+ }
103
+ teardownLayers() {
104
+ this.layerHandle?.destroy?.();
105
+ this.layerHandle = undefined;
106
+ this.mountedType = undefined;
107
+ if (this.layersEl) {
108
+ this.layersEl.replaceChildren();
109
+ }
110
+ }
111
+ remountLayers(config) {
112
+ if (!this.layersEl)
113
+ return;
114
+ this.teardownLayers();
115
+ this.layerHandle = mountAnimationLayers(this.layersEl, config);
116
+ this.mountedType = config.type;
117
+ }
118
+ canLiveUpdate(config) {
119
+ return (this.mountedType === config.type &&
120
+ LIVE_UPDATE_TYPES.includes(config.type) &&
121
+ typeof this.layerHandle?.update === 'function');
122
+ }
123
+ applyConfig() {
124
+ const config = this.getConfig();
125
+ this.className = [
126
+ CLASS,
127
+ `${CLASS}--${config.type}`,
128
+ config.clickable && !config.disabled && `${CLASS}--clickable`,
129
+ config.disabled && `${CLASS}--disabled`,
130
+ this.getBool('show-controls', false) && `${CLASS}--show-controls`,
131
+ ]
132
+ .filter(Boolean)
133
+ .join(' ');
134
+ applyContainerStyles(this, config);
135
+ const width = this.getSizeStyle(this.getAttribute('width'));
136
+ const height = this.getSizeStyle(this.getAttribute('height'));
137
+ const borderRadius = this.getSizeStyle(this.getAttribute('border-radius'));
138
+ if (width)
139
+ this.style.width = width;
140
+ if (height)
141
+ this.style.height = height;
142
+ this.style.borderRadius = borderRadius || '0';
143
+ this.style.border = 'none';
144
+ this.style.opacity = '';
145
+ if (this.layersEl) {
146
+ this.layersEl.style.opacity = String(config.opacity);
147
+ }
148
+ const blendMode = this.getAttribute('blend-mode');
149
+ if (blendMode)
150
+ this.style.mixBlendMode = blendMode;
151
+ if (config.disabled) {
152
+ this.teardownLayers();
153
+ this.syncClickable(config);
154
+ return;
155
+ }
156
+ if (this.canLiveUpdate(config)) {
157
+ this.layerHandle?.update?.(config);
158
+ if (this.layersEl && LUMINA_SHADER_TYPES.includes(config.type)) {
159
+ syncLuminaOverlays(this.layersEl, config.noise, config.showVignette);
160
+ }
161
+ }
162
+ else {
163
+ this.remountLayers(config);
164
+ }
165
+ this.syncClickable(config);
166
+ }
167
+ syncClickable(config) {
168
+ if (this.clickHandler) {
169
+ this.removeEventListener('click', this.clickHandler);
170
+ this.clickHandler = undefined;
171
+ }
172
+ if (config.clickable && !config.disabled) {
173
+ this.clickHandler = () => {
174
+ this.dispatchEvent(new CustomEvent('db-click', { bubbles: true, composed: true }));
175
+ };
176
+ this.addEventListener('click', this.clickHandler);
177
+ }
178
+ }
179
+ render() {
180
+ if (!this.layersEl) {
181
+ this.layersEl = document.createElement('div');
182
+ this.layersEl.className = `${CLASS}__layers`;
183
+ Object.assign(this.layersEl.style, {
184
+ position: 'absolute',
185
+ inset: '0',
186
+ width: '100%',
187
+ height: '100%',
188
+ zIndex: '0',
189
+ });
190
+ this.contentEl = document.createElement('div');
191
+ this.contentEl.className = `${CLASS}__content`;
192
+ Object.assign(this.contentEl.style, {
193
+ position: 'relative',
194
+ zIndex: '10',
195
+ width: '100%',
196
+ height: '100%',
197
+ });
198
+ const slot = document.createElement('slot');
199
+ this.contentEl.appendChild(slot);
200
+ this.append(this.layersEl, this.contentEl);
201
+ }
202
+ this.ensureControlsShell();
203
+ this.applyConfig();
204
+ this.syncControlsVisibility();
205
+ }
206
+ ensureControlsShell() {
207
+ if (this.controlsRootEl)
208
+ return;
209
+ const root = document.createElement('div');
210
+ root.className = `${CLASS}__controls-root`;
211
+ root.hidden = true;
212
+ const gear = this.createIconButton('settings', '컨트롤 열기', () => {
213
+ this.panelOpen = !this.panelOpen;
214
+ this.syncPanelState();
215
+ });
216
+ gear.setAttribute('variant', 'secondary');
217
+ gear.className = `${CLASS}__controls-gear`;
218
+ const panel = document.createElement('div');
219
+ panel.className = `${CLASS}__controls-panel`;
220
+ panel.setAttribute('role', 'dialog');
221
+ panel.hidden = true;
222
+ panel.addEventListener('click', (event) => event.stopPropagation());
223
+ const head = document.createElement('div');
224
+ head.className = `${CLASS}__controls-head`;
225
+ const title = document.createElement('span');
226
+ title.className = `${CLASS}__controls-title`;
227
+ title.append(createDbIcon('settings', 14), document.createTextNode(' Controls'));
228
+ const resetBtn = this.createIconButton('refresh', '기본값으로 초기화', () => {
229
+ const config = this.getConfig();
230
+ const controls = getControlsForType(config.type);
231
+ this.syncingControls = true;
232
+ resetControlsToDefaults(this, controls);
233
+ if (config.type === 'graphite') {
234
+ const preset = getGraphitePreset('graphite');
235
+ this.setAttribute('zoom', String(preset.scale));
236
+ this.setAttribute('colors', JSON.stringify(preset.colors));
237
+ }
238
+ this.syncingControls = false;
239
+ this.applyConfig();
240
+ this.syncControlInputs();
241
+ });
242
+ resetBtn.className = `${CLASS}__controls-icon-btn`;
243
+ head.append(title, resetBtn);
244
+ panel.append(head);
245
+ root.append(gear, panel);
246
+ this.appendChild(root);
247
+ this.controlsRootEl = root;
248
+ this.panelEl = panel;
249
+ }
250
+ syncControlsVisibility() {
251
+ if (!this.controlsRootEl)
252
+ return;
253
+ const visible = this.getBool('show-controls', false);
254
+ this.controlsRootEl.hidden = !visible;
255
+ if (!visible) {
256
+ this.panelOpen = false;
257
+ this.syncPanelState();
258
+ }
259
+ else {
260
+ this.rebuildControlInputs();
261
+ }
262
+ }
263
+ syncPanelState() {
264
+ if (!this.controlsRootEl || !this.panelEl)
265
+ return;
266
+ const gear = this.controlsRootEl.querySelector(`.${CLASS}__controls-gear`);
267
+ if (gear) {
268
+ gear.dataset.open = this.panelOpen ? 'true' : 'false';
269
+ gear.setAttribute('aria-expanded', this.panelOpen ? 'true' : 'false');
270
+ }
271
+ this.panelEl.hidden = !this.panelOpen;
272
+ }
273
+ createIconButton(icon, label, onClick) {
274
+ const btn = document.createElement('db-button');
275
+ btn.setAttribute('variant', 'tertiary');
276
+ btn.setAttribute('size', 's');
277
+ btn.setAttribute('icon-only', '');
278
+ btn.setAttribute('start-icon', icon);
279
+ btn.setAttribute('aria-label', label);
280
+ btn.addEventListener('click', (event) => {
281
+ event.stopPropagation();
282
+ onClick();
283
+ });
284
+ return btn;
285
+ }
286
+ rebuildControlInputs() {
287
+ if (!this.panelEl)
288
+ return;
289
+ this.panelEl.querySelectorAll(`.${CLASS}__controls-field`).forEach((node) => node.remove());
290
+ this.controlInputs.clear();
291
+ const config = this.getConfig();
292
+ const controls = getControlsForType(config.type);
293
+ const title = this.panelEl.querySelector(`.${CLASS}__controls-title`);
294
+ if (title) {
295
+ title.lastChild?.remove();
296
+ title.append(document.createTextNode(` ${config.type}`));
297
+ }
298
+ controls.forEach((control) => {
299
+ const field = this.createControlField(control);
300
+ this.panelEl?.appendChild(field);
301
+ });
302
+ this.syncControlInputs();
303
+ }
304
+ createControlField(control) {
305
+ const field = document.createElement('div');
306
+ field.className = `${CLASS}__controls-field`;
307
+ field.dataset.attr = control.attr;
308
+ if (control.kind === 'range') {
309
+ const bounds = getControlSliderBounds(control);
310
+ const slider = document.createElement('db-range-slider');
311
+ slider.className = `${CLASS}__controls-range`;
312
+ slider.setAttribute('size', 's');
313
+ slider.setAttribute('full-width', '');
314
+ slider.setAttribute('show-label', '');
315
+ slider.setAttribute('label', control.label);
316
+ slider.setAttribute('show-value', '');
317
+ slider.setAttribute('value-position', 'top');
318
+ slider.setAttribute('show-thumb-tooltip', '');
319
+ slider.setAttribute('min', String(bounds.min));
320
+ slider.setAttribute('max', String(bounds.max));
321
+ slider.setAttribute('step', String(bounds.step));
322
+ if (bounds.fixed != null)
323
+ slider.setAttribute('value-fixed', String(bounds.fixed));
324
+ slider.addEventListener('db-change', (event) => {
325
+ const detail = event.detail;
326
+ if (detail.value == null)
327
+ return;
328
+ this.commitControlValue(control, controlSliderToAttr(control, detail.value));
329
+ });
330
+ field.appendChild(slider);
331
+ this.controlInputs.set(control.attr, slider);
332
+ return field;
333
+ }
334
+ if (control.kind === 'select') {
335
+ const labelRow = document.createElement('div');
336
+ labelRow.className = `${CLASS}__controls-label`;
337
+ labelRow.textContent = control.label;
338
+ const options = control.options ?? [];
339
+ if (options.length > 4) {
340
+ const select = document.createElement('db-select');
341
+ select.className = `${CLASS}__controls-select`;
342
+ select.setAttribute('size', 's');
343
+ select.setAttribute('full-width', '');
344
+ select.setAttribute('options', JSON.stringify(options.map((opt) => ({ value: opt, label: opt }))));
345
+ select.addEventListener('db-change', (event) => {
346
+ const detail = event.detail;
347
+ if (detail.value == null)
348
+ return;
349
+ this.commitControlValue(control, detail.value);
350
+ });
351
+ field.append(labelRow, select);
352
+ this.controlInputs.set(control.attr, select);
353
+ }
354
+ else {
355
+ const seg = document.createElement('db-segment-control');
356
+ seg.className = `${CLASS}__controls-seg`;
357
+ seg.setAttribute('size', 's');
358
+ seg.setAttribute('full-width', '');
359
+ seg.setAttribute('options', JSON.stringify(options.map((opt) => ({ value: opt, label: opt }))));
360
+ seg.addEventListener('db-change', (event) => {
361
+ const detail = event.detail;
362
+ if (detail.value == null)
363
+ return;
364
+ this.commitControlValue(control, detail.value);
365
+ });
366
+ field.append(labelRow, seg);
367
+ this.controlInputs.set(control.attr, seg);
368
+ }
369
+ return field;
370
+ }
371
+ if (control.kind === 'toggle') {
372
+ const row = document.createElement('div');
373
+ row.className = `${CLASS}__controls-toggle`;
374
+ const name = document.createElement('span');
375
+ name.textContent = control.label;
376
+ const toggle = document.createElement('db-toggle');
377
+ toggle.setAttribute('size', 's');
378
+ toggle.addEventListener('db-change', (event) => {
379
+ const detail = event.detail;
380
+ this.commitControlValue(control, Boolean(detail.checked));
381
+ });
382
+ row.append(name, toggle);
383
+ field.appendChild(row);
384
+ this.controlInputs.set(control.attr, toggle);
385
+ return field;
386
+ }
387
+ const labelRow = document.createElement('div');
388
+ labelRow.className = `${CLASS}__controls-label`;
389
+ labelRow.textContent = control.label;
390
+ const colorsWrap = document.createElement('div');
391
+ colorsWrap.className = `${CLASS}__controls-colors`;
392
+ field.append(labelRow, colorsWrap);
393
+ this.controlInputs.set(control.attr, colorsWrap);
394
+ return field;
395
+ }
396
+ commitControlValue(control, value) {
397
+ this.syncingControls = true;
398
+ writeControlAttribute(this, control, value);
399
+ if (control.attr === 'preset' &&
400
+ this.getConfig().type === 'graphite' &&
401
+ typeof value === 'string' &&
402
+ isGraphitePresetId(value)) {
403
+ const preset = getGraphitePreset(value);
404
+ this.setAttribute('zoom', String(preset.scale));
405
+ this.setAttribute('colors', JSON.stringify(preset.colors));
406
+ }
407
+ this.syncingControls = false;
408
+ this.applyConfig();
409
+ }
410
+ syncControlInputs() {
411
+ const controls = getControlsForType(this.getConfig().type);
412
+ controls.forEach((control) => {
413
+ const value = readControlValue(this, control);
414
+ const input = this.controlInputs.get(control.attr);
415
+ if (!input)
416
+ return;
417
+ if (control.kind === 'range' && isRangeSlider(input)) {
418
+ const num = Number(value);
419
+ input.setAttribute('value', String(controlAttrToSlider(control, num)));
420
+ }
421
+ else if (control.kind === 'select') {
422
+ input.setAttribute('value', String(value));
423
+ }
424
+ else if (control.kind === 'toggle') {
425
+ const on = Boolean(value);
426
+ if (on)
427
+ input.setAttribute('checked', '');
428
+ else
429
+ input.removeAttribute('checked');
430
+ }
431
+ else if (control.kind === 'colors') {
432
+ this.renderColorInputs(input, control, value);
433
+ }
434
+ });
435
+ }
436
+ renderColorInputs(container, control, list) {
437
+ // 로컬 사본으로 관리: 색상 값 변경은 in-place 커밋(드롭다운 유지),
438
+ // 추가/삭제 등 구조 변경 시에만 다시 렌더링한다.
439
+ const colors = [...list];
440
+ container.replaceChildren();
441
+ const commitStructuralChange = (next) => {
442
+ this.commitControlValue(control, next);
443
+ this.renderColorInputs(container, control, next);
444
+ };
445
+ colors.forEach((hex, index) => {
446
+ const row = document.createElement('div');
447
+ row.className = `${CLASS}__controls-color`;
448
+ const picker = document.createElement('db-color-picker');
449
+ picker.className = `${CLASS}__controls-color-picker`;
450
+ picker.setAttribute('size', 's');
451
+ picker.setAttribute('full-width', '');
452
+ picker.setAttribute('show-alpha', 'false');
453
+ picker.setAttribute('show-eyedropper', 'false');
454
+ picker.setAttribute('value', hex);
455
+ picker.addEventListener('db-change', (event) => {
456
+ const detail = event.detail;
457
+ const value = detail?.value;
458
+ if (typeof value !== 'string' || !value)
459
+ return;
460
+ colors[index] = value;
461
+ this.commitControlValue(control, [...colors]);
462
+ });
463
+ row.appendChild(picker);
464
+ if (colors.length > 1) {
465
+ const remove = this.createIconButton('trash', '색상 제거', () => {
466
+ commitStructuralChange(colors.filter((_, idx) => idx !== index));
467
+ });
468
+ remove.className = `${CLASS}__controls-color-remove`;
469
+ row.appendChild(remove);
470
+ }
471
+ container.appendChild(row);
472
+ });
473
+ if (colors.length < 8) {
474
+ const add = this.createIconButton('plus', '색상 추가', () => {
475
+ commitStructuralChange([...colors, colors[colors.length - 1] ?? '#ffffff']);
476
+ });
477
+ add.className = `${CLASS}__controls-color-add`;
478
+ container.appendChild(add);
479
+ }
480
+ }
481
+ }
482
+ if (!customElements.get('db-animation-background')) {
483
+ customElements.define('db-animation-background', DbAnimationBackground);
484
+ }
485
+
486
+ export { DbAnimationBackground };