@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,909 @@
1
- const t="designbase-wc-grid-background";class DbGridBackground extends HTMLElement{constructor(){super(...arguments),this.lastTime=0,this.lastPointerMove=0,this.bandAngle=Math.random()*Math.PI*2,this.pointer={x:0,y:0,strength:0,inside:!1},this.cssW=0,this.cssH=0,this.tracePulses=[],this.traceSpawnAccumulator=0,this.onThemeMediaChange=()=>this.scheduleDraw(),this.onPointerMove=t=>this.handlePointerMove(t)}static get observedAttributes(){return["type","size","thickness","color","theme","opacity","fade","fade-size","background","overlay","interactive","interaction","interaction-color","interaction-radius","cross-size","cross-style","lines-layout","guide-inset","guide-edges","guide-x","guide-y","guide-span-x","guide-span-y","animated","animation-style","animation-speed","trace-length","trace-direction","width","height","border-radius"]}connectedCallback(){this.ensureCanvas(),this.applyHostStyles(),this.startObserving(),this.observeDocumentTheme(),this.bindPointer(),this.measure(),this.scheduleDraw()}disconnectedCallback(){this.ro?.disconnect(),this.ro=void 0,this.themeObserver?.disconnect(),this.themeObserver=void 0,window.removeEventListener("pointermove",this.onPointerMove),this.themeMedia?.removeEventListener("change",this.onThemeMediaChange),this.themeMedia=void 0,null!=this.rafId&&cancelAnimationFrame(this.rafId),this.rafId=void 0}attributeChangedCallback(){this.isConnected&&(this.applyHostStyles(),this.bindPointer(),this.measure(),this.scheduleDraw())}getBool(t){return this.hasAttribute(t)&&"false"!==this.getAttribute(t)}getNumber(t,e){const i=this.getAttribute(t);if(null===i||""===i)return e;const s=Number(i);return Number.isFinite(s)?s:e}getSizeStyle(t){if(!t)return;const e=Number(t);return Number.isFinite(e)?`${e}px`:t}getColorProbe(){if(!this.colorProbe){const t=document.createElement("span");t.setAttribute("aria-hidden","true"),Object.assign(t.style,{position:"absolute",width:"0",height:"0",overflow:"hidden",visibility:"hidden",pointerEvents:"none"}),this.appendChild(t),this.colorProbe=t}return this.colorProbe}resolveCssColor(t){if(!t.includes("var(")&&!t.startsWith("color-mix"))return t;const e=this.getColorProbe();e.style.color=t;const i=getComputedStyle(e).color;return i&&"rgba(0, 0, 0, 0)"!==i?i:t}isDarkContext(){const t=this.getAttribute("theme");if("dark"===t)return!0;if("light"===t)return!1;const e=document.documentElement,i=e.getAttribute("data-color-mode");if("dark"===i)return!0;if("light"===i)return!1;const s=e.getAttribute("data-theme");return"dark"===s||"light"!==s&&window.matchMedia("(prefers-color-scheme: dark)").matches}resolveThemeColor(t,e,i,s){if(t)return this.resolveCssColor(t);const r=this.getColorProbe(),a=this.isDarkContext();r.style.color=`var(${e}, ${a?s:i})`;const o=getComputedStyle(r).color;return o&&"rgba(0, 0, 0, 0)"!==o?o:a?s:i}effectiveOpacity(){const t=this.getAttribute("opacity");return null!==t&&""!==t?this.getNumber("opacity",.05):this.isDarkContext()?.1:.05}effectiveColor(){const t=this.getAttribute("color");return t?this.resolveCssColor(t):this.isDarkContext()?"#ffffff":"#000000"}effectiveInterColor(){const t=this.getAttribute("interaction-color");return t?this.resolveCssColor(t):this.effectiveColor()}traceDrawColor(t){return this.hasAttribute("interaction-color")?t.interColor:t.color}traceDrawOpacity(t){const e=t.opacity;return this.hasAttribute("interaction-color")?Math.min(1,e+.2):Math.min(1,3*e)}parseColorRgb(t){const e=this.resolveCssColor(t).match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/);return e?[Number(e[1]),Number(e[2]),Number(e[3])]:this.isDarkContext()?[255,255,255]:[0,0,0]}crossArm(t,e,i){return i>0?i<=1?t*i:i:Math.min(.32*t,8+1.5*e)}interactionMode(){const t=this.getAttribute("interaction");if("glow"===t||"magnet"===t||"rotate"===t||"none"===t)return t;if(!this.getBool("interactive"))return"none";const e=this.getAttribute("type")||"lines";return"dots"===e?"magnet":"cross"===e?"rotate":"glow"}animationStyle(){const t=this.getAttribute("animation-style")||"band";return"trace"===t||"both"===t?t:"band"}traceDirection(){const t=this.getAttribute("trace-direction")||"both";return"vertical"===t||"horizontal"===t?t:"both"}linesLayout(){const t=this.getAttribute("lines-layout")||"grid";return"frame"===t||"center"===t?t:"grid"}crossStyle(){return"line"===(this.getAttribute("cross-style")||"plus")?"line":"plus"}parseGuideEdges(t){const e=(t||"top,right,bottom,left").split(",").map(t=>t.trim().toLowerCase()).filter(Boolean);return new Set(e)}guideFraction(t,e){const i=this.getAttribute(t);if(null===i||""===i)return e;const s=Number(i);return Number.isFinite(s)?Math.min(1,Math.max(0,s)):e}isGridLines(t){return"lines"===t.type&&"grid"===t.linesLayout}isTraceableLines(t){return"lines"===t.type&&("grid"===t.linesLayout||"frame"===t.linesLayout)}get cfg(){return{type:this.getAttribute("type")||"lines",linesLayout:this.linesLayout(),crossStyle:this.crossStyle(),size:Math.max(4,this.getNumber("size",32)),thickness:Math.max(.5,this.getNumber("thickness",1)),color:this.effectiveColor(),opacity:this.effectiveOpacity(),fade:this.getAttribute("fade")||"none",fadeSize:this.getNumber("fade-size",.22),interaction:this.interactionMode(),interColor:this.effectiveInterColor(),interRadius:Math.max(20,this.getNumber("interaction-radius",130)),crossSize:this.getNumber("cross-size",0),guideInset:Math.max(0,this.getNumber("guide-inset",16)),guideEdges:this.parseGuideEdges(this.getAttribute("guide-edges")),guideX:this.guideFraction("guide-x",.5),guideY:this.guideFraction("guide-y",.5),guideSpanX:Math.max(0,this.getNumber("guide-span-x",0)),guideSpanY:Math.max(0,this.getNumber("guide-span-y",0)),animate:this.getBool("animated"),animSpeed:this.getNumber("animation-speed",1),animationStyle:this.animationStyle(),traceLength:this.getNumber("trace-length",0),traceDirection:this.traceDirection()}}ensureCanvas(){if(this.canvas)return;const t=document.createElement("canvas");Object.assign(t.style,{display:"block",width:"100%",height:"100%",pointerEvents:"none"}),this.appendChild(t),this.canvas=t,this.ctx=t.getContext("2d")??void 0,this.hlCanvas=document.createElement("canvas"),this.maskCanvas=document.createElement("canvas")}applyHostStyles(){const e=this.getBool("overlay");this.className=[t,`${t}--${this.getAttribute("type")||"lines"}`,e&&`${t}--overlay`].filter(Boolean).join(" "),this.style.display="block",this.style.boxSizing="border-box",this.style.pointerEvents="none";const i=this.getAttribute("background");if(this.style.backgroundColor=i?this.resolveCssColor(i):"transparent",e)this.style.position="absolute",this.style.inset="0",this.style.width="100%",this.style.height="100%",this.style.zIndex||(this.style.zIndex="1");else{"absolute"!==this.style.position&&this.style.position||(this.style.position="relative");const t=this.getSizeStyle(this.getAttribute("width")),e=this.getSizeStyle(this.getAttribute("height"));t&&(this.style.width=t),e&&(this.style.height=e)}const s=this.getSizeStyle(this.getAttribute("border-radius"));this.style.borderRadius=s||"",this.canvas&&(this.canvas.style.borderRadius=s||"")}startObserving(){this.ro||(this.ro=new ResizeObserver(()=>{this.measure(),this.scheduleDraw()}),this.ro.observe(this))}observeDocumentTheme(){this.themeObserver||"undefined"!=typeof MutationObserver&&(this.themeObserver=new MutationObserver(()=>this.scheduleDraw()),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","data-color-mode","class"]}))}bindPointer(){window.removeEventListener("pointermove",this.onPointerMove),this.themeMedia?.removeEventListener("change",this.onThemeMediaChange),this.themeMedia||(this.themeMedia=window.matchMedia("(prefers-color-scheme: dark)")),this.themeMedia.addEventListener("change",this.onThemeMediaChange),"none"!==this.interactionMode()?window.addEventListener("pointermove",this.onPointerMove,{passive:!0}):(this.pointer.strength=0,this.pointer.inside=!1)}handlePointerMove(t){if(!this.canvas)return;const e=this.canvas.getBoundingClientRect(),i=t.clientX-e.left,s=t.clientY-e.top,r=this.cfg.interRadius,a=i>=-r&&i<=e.width+r&&s>=-r&&s<=e.height+r;this.pointer.x=i,this.pointer.y=s,this.pointer.inside=a,a&&(this.pointer.strength=1,this.lastPointerMove=performance.now(),this.ensureLoop())}measure(){if(!this.canvas||!this.ctx)return;const t=this.getBoundingClientRect(),e=Math.max(1,Math.round(t.width)),i=Math.max(1,Math.round(t.height));this.cssW=e,this.cssH=i,this.tracePulses=[],this.traceSpawnAccumulator=0;const s=Math.min(2,window.devicePixelRatio||1);[this.canvas,this.hlCanvas,this.maskCanvas].forEach(t=>{t&&(t.width=Math.round(e*s),t.height=Math.round(i*s))})}axis(t,e){const i=t/2,s=[],r=Math.ceil((0-i)/e),a=Math.floor((t-i)/e);for(let o=r;o<=a;o++){const r=i+o*e;r>.75&&r<t-.75&&s.push(r)}return s}scheduleDraw(){const t=this.cfg;this.drawFrame(performance.now(),0),t.animate||this.pointer.strength>.001?this.ensureLoop():null!=this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=void 0)}ensureLoop(){if(null!=this.rafId)return;this.lastTime=performance.now();const tick=t=>{const e=Math.min(.05,(t-this.lastTime)/1e3);this.lastTime=t,"none"!==this.interactionMode()?t-this.lastPointerMove>90&&(this.pointer.strength=Math.max(0,this.pointer.strength-2.2*e)):this.pointer.strength=0,this.drawFrame(t,e);this.cfg.animate||this.pointer.strength>.001?this.rafId=requestAnimationFrame(tick):(this.rafId=void 0,this.drawFrame(t,e))};this.rafId=requestAnimationFrame(tick)}drawFrame(t,e){const i=this.ctx;if(!i||!this.canvas)return;const{cssW:s,cssH:r}=this;if(s<=0||r<=0)return;const a=this.cfg,o=Math.min(2,window.devicePixelRatio||1),n=t/1e3;i.setTransform(o,0,0,o,0,0),i.clearRect(0,0,s,r),this.drawPattern(i,s,r,a,a.color,a.opacity,n),a.animate&&this.isTraceableLines(a)&&("trace"===a.animationStyle||"both"===a.animationStyle)&&this.drawTraceHighlight(i,s,r,a,e);if(("glow"===a.interaction&&this.pointer.strength>.001||a.animate&&"trace"!==a.animationStyle&&(this.isGridLines(a)||"diagonal"===a.type))&&this.hlCanvas&&this.maskCanvas){const t=this.hlCanvas.getContext("2d"),e=this.maskCanvas.getContext("2d");t&&e&&(t.setTransform(o,0,0,o,0,0),t.clearRect(0,0,s,r),this.drawPattern(t,s,r,a,a.interColor,Math.min(1,a.opacity+.4),n),this.buildEnergyMask(e,s,r,o,a,n),t.setTransform(1,0,0,1,0,0),t.globalCompositeOperation="destination-in",t.drawImage(this.maskCanvas,0,0),t.globalCompositeOperation="source-over",i.setTransform(1,0,0,1,0,0),i.globalCompositeOperation="lighter",i.drawImage(this.hlCanvas,0,0),i.globalCompositeOperation="source-over",i.setTransform(o,0,0,o,0,0))}this.applyFade(i,s,r,a)}easeInOutCubic(t){const e=Math.min(1,Math.max(0,t));return e<.5?4*e*e*e:1-(-2*e+2)**3/2}traceSegmentLength(t){return t.traceLength>0?t.traceLength:28}pulseHead(t,e){const i=this.easeInOutCubic(t.progress);return-t.length+i*(e+2*t.length)}updateTracePulses(t,e,i,s){const r=this.traceSegmentLength(i),a=Math.min(6,Math.max(2,Math.floor(t*e/(i.size*i.size*180)))),o=(.38+.52*Math.random())/Math.max(.25,i.animSpeed);for(this.traceSpawnAccumulator+=s;this.traceSpawnAccumulator>=o&&this.tracePulses.length<a&&(this.traceSpawnAccumulator-=o,this.spawnTracePulse(t,e,i,r),!(Math.random()>.45)););this.tracePulses=this.tracePulses.filter(t=>(t.progress+=s/t.duration,t.progress<.1?t.alpha=t.progress/.1:t.progress>.9?t.alpha=(1-t.progress)/.1:t.alpha=1,t.progress<1&&t.alpha>.02))}traceLinePositions(t,e,i){if("frame"===i.linesLayout){const s=Math.min(i.guideInset,t/2-1,e/2-1),r=i.guideEdges,a=[],o=[];return r.has("left")&&a.push(s),r.has("right")&&a.push(t-s),r.has("top")&&o.push(s),r.has("bottom")&&o.push(e-s),{xs:a,ys:o}}return{xs:this.axis(t,i.size),ys:this.axis(e,i.size)}}spawnTracePulse(t,e,i,s){const{xs:r,ys:a}=this.traceLinePositions(t,e,i),o="horizontal"!==i.traceDirection&&r.length>0,n="vertical"!==i.traceDirection&&a.length>0;if(!o&&!n)return;let h;h=o&&n?Math.random()<.5?"v":"h":o?"v":"h";const l=s,c=(("v"===h?e:t)+2*l)/(88*Math.max(.25,i.animSpeed)),d={axis:h,linePos:"v"===h?r[Math.floor(Math.random()*r.length)]:a[Math.floor(Math.random()*a.length)],progress:0,length:l,duration:c,alpha:0};this.tracePulses.push(d)}drawTracePulseSegment(t,e,i,s,r){const[a,o,n]=this.parseColorRgb(this.traceDrawColor(r)),h=Math.min(1,this.traceDrawOpacity(r)*e.alpha),l="v"===e.axis?s:i,c=this.pulseHead(e,l);let d,u,g,m;if("v"===e.axis?(d=g=e.linePos,m=Math.min(s,c),u=Math.max(0,c-e.length)):(u=m=e.linePos,g=Math.min(i,c),d=Math.max(0,c-e.length)),Math.abs(g-d)<.5&&Math.abs(m-u)<.5)return;const p=t.createLinearGradient(d,u,g,m);p.addColorStop(0,`rgba(${a},${o},${n},0)`),p.addColorStop(.18,`rgba(${a},${o},${n},${.2*h})`),p.addColorStop(.5,`rgba(${a},${o},${n},${h})`),p.addColorStop(.82,`rgba(${a},${o},${n},${.2*h})`),p.addColorStop(1,`rgba(${a},${o},${n},0)`),t.save(),t.strokeStyle=p,t.lineCap="round",t.globalAlpha=e.alpha,t.shadowColor=`rgba(${a},${o},${n},${.9*h})`,t.shadowBlur=12+5*r.thickness,t.lineWidth=Math.max(1.1,r.thickness+.6),t.beginPath(),t.moveTo(d,u),t.lineTo(g,m),t.stroke(),t.shadowBlur=0,t.lineWidth=Math.max(.6,.35*r.thickness),t.globalAlpha=Math.min(1,1.1*e.alpha),t.stroke(),t.restore()}drawTraceHighlight(t,e,i,s,r){this.updateTracePulses(e,i,s,r),t.save(),t.globalCompositeOperation="lighter",this.tracePulses.forEach(r=>this.drawTracePulseSegment(t,r,e,i,s)),t.restore()}drawPattern(t,e,i,s,r,a,o){const{type:n,size:h,thickness:l}=s;if(t.save(),t.globalAlpha=a,t.strokeStyle=r,t.fillStyle=r,t.lineWidth=l,t.lineCap="round","lines"===n)"frame"===s.linesLayout?this.drawFrameGuides(t,e,i,s):"center"===s.linesLayout?this.drawCenterGuides(t,e,i,s):this.drawGridLines(t,e,i,s.size);else if("dots"===n){const r=this.axis(e,h),a=this.axis(i,h),n=Math.max(.75,l),c="magnet"===s.interaction&&this.pointer.strength>.001,d=this.pointer,u=s.interRadius;r.forEach((e,i)=>{a.forEach((r,a)=>{let l=e,g=r,m=n;if(c&&d.inside){const t=d.x-e,i=d.y-r,s=Math.hypot(t,i);if(s<u){const e=(1-s/u)*d.strength;m=n*(1+1.6*e),l+=t/(s||1)*e*h*.28,g+=i/(s||1)*e*h*.28}}if(s.animate){m*=.75+.25*Math.sin(2*o*s.animSpeed+.9*(i+a))}t.beginPath(),t.arc(l,g,m,0,2*Math.PI),t.fill()})})}else if("cross"===n){const r=this.axis(e,h),a=this.axis(i,h),o=this.crossArm(h,l,s.crossSize),n="rotate"===s.interaction&&this.pointer.strength>.001,c=this.pointer;r.forEach(e=>{a.forEach(i=>{if(t.save(),t.translate(e,i),n&&c.inside&&c.strength>.01){const s=c.x-e,r=c.y-i;t.rotate(Math.atan2(r,s))}t.beginPath(),"line"===s.crossStyle?(t.moveTo(-o,0),t.lineTo(o,0)):(t.moveTo(-o,0),t.lineTo(o,0),t.moveTo(0,-o),t.lineTo(0,o)),t.stroke(),t.restore()})})}else{t.save(),t.beginPath(),t.rect(0,0,e,i),t.clip(),t.translate(e/2,i/2),t.rotate(Math.PI/4);const s=Math.hypot(e,i);t.beginPath();for(let e=-s;e<=s;e+=h)t.moveTo(e,-s),t.lineTo(e,s),t.moveTo(-s,e),t.lineTo(s,e);t.stroke(),t.restore()}t.restore()}drawGridLines(t,e,i,s){const r=this.axis(e,s),a=this.axis(i,s);t.beginPath(),r.forEach(e=>{t.moveTo(e,0),t.lineTo(e,i)}),a.forEach(i=>{t.moveTo(0,i),t.lineTo(e,i)}),t.stroke()}drawFrameGuides(t,e,i,s){const r=Math.min(s.guideInset,e/2-1,i/2-1),a=s.guideEdges;t.beginPath(),a.has("top")&&(t.moveTo(0,r),t.lineTo(e,r)),a.has("bottom")&&(t.moveTo(0,i-r),t.lineTo(e,i-r)),a.has("left")&&(t.moveTo(r,0),t.lineTo(r,i)),a.has("right")&&(t.moveTo(e-r,0),t.lineTo(e-r,i)),t.stroke()}drawCenterGuides(t,e,i,s){const r=e*s.guideX,a=i*s.guideY,o=s.guideSpanX>0?s.guideSpanX:e,n=s.guideSpanY>0?s.guideSpanY:i;t.beginPath(),t.moveTo(r-o/2,a),t.lineTo(r+o/2,a),t.moveTo(r,a-n/2),t.lineTo(r,a+n/2),t.stroke()}buildEnergyMask(t,e,i,s,r,a){if(t.setTransform(s,0,0,s,0,0),t.clearRect(0,0,e,i),t.globalCompositeOperation="lighter","glow"===r.interaction&&this.pointer.strength>.001&&this.pointer.inside){const s=r.interRadius,a=t.createRadialGradient(this.pointer.x,this.pointer.y,0,this.pointer.x,this.pointer.y,s);a.addColorStop(0,`rgba(255,255,255,${.9*this.pointer.strength})`),a.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=a,t.fillRect(0,0,e,i)}if(r.animate&&"trace"!==r.animationStyle){t.save(),t.translate(e/2,i/2),t.rotate(this.bandAngle);const s=Math.hypot(e,i),o=Math.max(60,.18*s),n=s+o,h=90*a*r.animSpeed%(2*n)-n,l=t.createLinearGradient(h-o,0,h+o,0);l.addColorStop(0,"rgba(255,255,255,0)"),l.addColorStop(.5,"rgba(255,255,255,0.55)"),l.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=l,t.fillRect(-s,-s,2*s,2*s),t.restore()}t.globalCompositeOperation="source-over"}applyFade(t,e,i,s){const r=s.fade;if("none"===r)return;const a=s.fadeSize,o=a>1?a:Math.max(8,e*a),n=a>1?a:Math.max(8,i*a);t.save(),t.globalCompositeOperation="destination-in";const edge=s=>{let r;"top"===s?(r=t.createLinearGradient(0,0,0,n),r.addColorStop(0,"rgba(0,0,0,0)"),r.addColorStop(1,"rgba(0,0,0,1)")):"bottom"===s?(r=t.createLinearGradient(0,i-n,0,i),r.addColorStop(0,"rgba(0,0,0,1)"),r.addColorStop(1,"rgba(0,0,0,0)")):"left"===s?(r=t.createLinearGradient(0,0,o,0),r.addColorStop(0,"rgba(0,0,0,0)"),r.addColorStop(1,"rgba(0,0,0,1)")):(r=t.createLinearGradient(e-o,0,e,0),r.addColorStop(0,"rgba(0,0,0,1)"),r.addColorStop(1,"rgba(0,0,0,0)")),t.fillStyle=r,t.fillRect(0,0,e,i)};if("vignette"===r){const s=t.createRadialGradient(e/2,i/2,.2*Math.min(e,i),e/2,i/2,Math.hypot(e,i)/2);s.addColorStop(0,"rgba(0,0,0,1)"),s.addColorStop(1,"rgba(0,0,0,0)"),t.fillStyle=s,t.fillRect(0,0,e,i)}else{("top"===r?["top"]:"bottom"===r?["bottom"]:"left"===r?["left"]:"right"===r?["right"]:"vertical"===r?["top","bottom"]:"horizontal"===r?["left","right"]:["top","bottom","left","right"]).forEach(edge)}t.restore()}}customElements.get("db-grid-background")||customElements.define("db-grid-background",DbGridBackground);export{DbGridBackground};
1
+ const CLASS = 'designbase-wc-grid-background';
2
+ const MAX_DPR = 2;
3
+ /** trace 이동 기준 속도(px/s). animation-speed로 배율 조정 */
4
+ const TRACE_BASE_SPEED = 88;
5
+ /** trace-length 미지정 시 기본 세그먼트 길이(px) */
6
+ const TRACE_DEFAULT_LENGTH = 28;
7
+ /**
8
+ * db-grid-background — 캔버스 기반 그리드 배경.
9
+ *
10
+ * 패턴은 항상 중앙을 기준으로 대칭 배치되며(가장자리 라인 없음),
11
+ * 단독 배경으로도, 다른 배경 위 데코 레이어(overlay)로도 쓸 수 있다.
12
+ * interactive/animate 로 마우스 인터랙션과 앰비언트 애니메이션을 켤 수 있다.
13
+ *
14
+ * @example
15
+ * <db-grid-background type="lines" size="32" fade="all"
16
+ * interactive animate height="240"></db-grid-background>
17
+ */
18
+ class DbGridBackground extends HTMLElement {
19
+ constructor() {
20
+ super(...arguments);
21
+ this.lastTime = 0;
22
+ this.lastPointerMove = 0;
23
+ this.bandAngle = Math.random() * Math.PI * 2;
24
+ this.pointer = { x: 0, y: 0, strength: 0, inside: false };
25
+ this.cssW = 0;
26
+ this.cssH = 0;
27
+ this.tracePulses = [];
28
+ this.traceSpawnAccumulator = 0;
29
+ this.onThemeMediaChange = () => this.scheduleDraw();
30
+ this.onPointerMove = (e) => this.handlePointerMove(e);
31
+ }
32
+ static get observedAttributes() {
33
+ return [
34
+ 'type',
35
+ 'size',
36
+ 'thickness',
37
+ 'color',
38
+ 'theme',
39
+ 'opacity',
40
+ 'fade',
41
+ 'fade-size',
42
+ 'background',
43
+ 'overlay',
44
+ 'interactive',
45
+ 'interaction',
46
+ 'interaction-color',
47
+ 'interaction-radius',
48
+ 'cross-size',
49
+ 'cross-style',
50
+ 'lines-layout',
51
+ 'guide-inset',
52
+ 'guide-edges',
53
+ 'guide-x',
54
+ 'guide-y',
55
+ 'guide-span-x',
56
+ 'guide-span-y',
57
+ 'animated',
58
+ 'animation-style',
59
+ 'animation-speed',
60
+ 'trace-length',
61
+ 'trace-direction',
62
+ 'width',
63
+ 'height',
64
+ 'border-radius',
65
+ ];
66
+ }
67
+ connectedCallback() {
68
+ this.ensureCanvas();
69
+ this.applyHostStyles();
70
+ this.startObserving();
71
+ this.observeDocumentTheme();
72
+ this.bindPointer();
73
+ this.measure();
74
+ this.scheduleDraw();
75
+ }
76
+ disconnectedCallback() {
77
+ this.ro?.disconnect();
78
+ this.ro = undefined;
79
+ this.themeObserver?.disconnect();
80
+ this.themeObserver = undefined;
81
+ window.removeEventListener('pointermove', this.onPointerMove);
82
+ this.themeMedia?.removeEventListener('change', this.onThemeMediaChange);
83
+ this.themeMedia = undefined;
84
+ if (this.rafId != null)
85
+ cancelAnimationFrame(this.rafId);
86
+ this.rafId = undefined;
87
+ }
88
+ attributeChangedCallback() {
89
+ if (!this.isConnected)
90
+ return;
91
+ this.applyHostStyles();
92
+ this.bindPointer();
93
+ this.measure();
94
+ this.scheduleDraw();
95
+ }
96
+ /* --------------------------------------------------------------------- */
97
+ /* Attr helpers */
98
+ /* --------------------------------------------------------------------- */
99
+ getBool(name) {
100
+ return this.hasAttribute(name) && this.getAttribute(name) !== 'false';
101
+ }
102
+ getNumber(name, fallback) {
103
+ const raw = this.getAttribute(name);
104
+ if (raw === null || raw === '')
105
+ return fallback;
106
+ const n = Number(raw);
107
+ return Number.isFinite(n) ? n : fallback;
108
+ }
109
+ getSizeStyle(value) {
110
+ if (!value)
111
+ return undefined;
112
+ const num = Number(value);
113
+ return Number.isFinite(num) ? `${num}px` : value;
114
+ }
115
+ getColorProbe() {
116
+ if (!this.colorProbe) {
117
+ const probe = document.createElement('span');
118
+ probe.setAttribute('aria-hidden', 'true');
119
+ Object.assign(probe.style, {
120
+ position: 'absolute',
121
+ width: '0',
122
+ height: '0',
123
+ overflow: 'hidden',
124
+ visibility: 'hidden',
125
+ pointerEvents: 'none',
126
+ });
127
+ this.appendChild(probe);
128
+ this.colorProbe = probe;
129
+ }
130
+ return this.colorProbe;
131
+ }
132
+ resolveCssColor(input) {
133
+ if (!input.includes('var(') && !input.startsWith('color-mix'))
134
+ return input;
135
+ const probe = this.getColorProbe();
136
+ probe.style.color = input;
137
+ const resolved = getComputedStyle(probe).color;
138
+ return resolved && resolved !== 'rgba(0, 0, 0, 0)' ? resolved : input;
139
+ }
140
+ isDarkContext() {
141
+ const theme = this.getAttribute('theme');
142
+ if (theme === 'dark')
143
+ return true;
144
+ if (theme === 'light')
145
+ return false;
146
+ const root = document.documentElement;
147
+ const colorMode = root.getAttribute('data-color-mode');
148
+ if (colorMode === 'dark')
149
+ return true;
150
+ if (colorMode === 'light')
151
+ return false;
152
+ const rootTheme = root.getAttribute('data-theme');
153
+ if (rootTheme === 'dark')
154
+ return true;
155
+ if (rootTheme === 'light')
156
+ return false;
157
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
158
+ }
159
+ resolveThemeColor(explicit, cssVarName, fallbackLight, fallbackDark) {
160
+ if (explicit)
161
+ return this.resolveCssColor(explicit);
162
+ const probe = this.getColorProbe();
163
+ const dark = this.isDarkContext();
164
+ probe.style.color = `var(${cssVarName}, ${dark ? fallbackDark : fallbackLight})`;
165
+ const resolved = getComputedStyle(probe).color;
166
+ if (resolved && resolved !== 'rgba(0, 0, 0, 0)')
167
+ return resolved;
168
+ return dark ? fallbackDark : fallbackLight;
169
+ }
170
+ effectiveOpacity() {
171
+ const raw = this.getAttribute('opacity');
172
+ if (raw !== null && raw !== '') {
173
+ return this.getNumber('opacity', 0.05);
174
+ }
175
+ return this.isDarkContext() ? 0.1 : 0.05;
176
+ }
177
+ effectiveColor() {
178
+ const explicit = this.getAttribute('color');
179
+ if (explicit)
180
+ return this.resolveCssColor(explicit);
181
+ return this.isDarkContext() ? '#ffffff' : '#000000';
182
+ }
183
+ effectiveInterColor() {
184
+ const explicit = this.getAttribute('interaction-color');
185
+ if (explicit)
186
+ return this.resolveCssColor(explicit);
187
+ return this.effectiveColor();
188
+ }
189
+ traceDrawColor(cfg) {
190
+ return this.hasAttribute('interaction-color') ? cfg.interColor : cfg.color;
191
+ }
192
+ traceDrawOpacity(cfg) {
193
+ const base = cfg.opacity;
194
+ if (this.hasAttribute('interaction-color')) {
195
+ return Math.min(1, base + 0.2);
196
+ }
197
+ return Math.min(1, base * 3);
198
+ }
199
+ parseColorRgb(color) {
200
+ const resolved = this.resolveCssColor(color);
201
+ const rgb = resolved.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/);
202
+ if (rgb)
203
+ return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
204
+ return this.isDarkContext() ? [255, 255, 255] : [0, 0, 0];
205
+ }
206
+ crossArm(size, thickness, crossSize) {
207
+ if (crossSize > 0) {
208
+ if (crossSize <= 1)
209
+ return size * crossSize;
210
+ return crossSize;
211
+ }
212
+ return Math.min(size * 0.32, 8 + thickness * 1.5);
213
+ }
214
+ interactionMode() {
215
+ const explicit = this.getAttribute('interaction');
216
+ if (explicit === 'glow' || explicit === 'magnet' || explicit === 'rotate' || explicit === 'none') {
217
+ return explicit;
218
+ }
219
+ if (!this.getBool('interactive'))
220
+ return 'none';
221
+ const type = this.getAttribute('type') || 'lines';
222
+ if (type === 'dots')
223
+ return 'magnet';
224
+ if (type === 'cross')
225
+ return 'rotate';
226
+ return 'glow';
227
+ }
228
+ animationStyle() {
229
+ const raw = this.getAttribute('animation-style') || 'band';
230
+ if (raw === 'trace' || raw === 'both')
231
+ return raw;
232
+ return 'band';
233
+ }
234
+ traceDirection() {
235
+ const raw = this.getAttribute('trace-direction') || 'both';
236
+ if (raw === 'vertical' || raw === 'horizontal')
237
+ return raw;
238
+ return 'both';
239
+ }
240
+ linesLayout() {
241
+ const raw = this.getAttribute('lines-layout') || 'grid';
242
+ if (raw === 'frame' || raw === 'center')
243
+ return raw;
244
+ return 'grid';
245
+ }
246
+ crossStyle() {
247
+ const raw = this.getAttribute('cross-style') || 'plus';
248
+ return raw === 'line' ? 'line' : 'plus';
249
+ }
250
+ parseGuideEdges(raw) {
251
+ const parts = (raw || 'top,right,bottom,left')
252
+ .split(',')
253
+ .map((part) => part.trim().toLowerCase())
254
+ .filter(Boolean);
255
+ return new Set(parts);
256
+ }
257
+ guideFraction(name, fallback) {
258
+ const raw = this.getAttribute(name);
259
+ if (raw === null || raw === '')
260
+ return fallback;
261
+ const n = Number(raw);
262
+ if (!Number.isFinite(n))
263
+ return fallback;
264
+ return Math.min(1, Math.max(0, n));
265
+ }
266
+ isGridLines(cfg) {
267
+ return cfg.type === 'lines' && cfg.linesLayout === 'grid';
268
+ }
269
+ isTraceableLines(cfg) {
270
+ return cfg.type === 'lines' && (cfg.linesLayout === 'grid' || cfg.linesLayout === 'frame');
271
+ }
272
+ get cfg() {
273
+ return {
274
+ type: (this.getAttribute('type') || 'lines'),
275
+ linesLayout: this.linesLayout(),
276
+ crossStyle: this.crossStyle(),
277
+ size: Math.max(4, this.getNumber('size', 32)),
278
+ thickness: Math.max(0.5, this.getNumber('thickness', 1)),
279
+ color: this.effectiveColor(),
280
+ opacity: this.effectiveOpacity(),
281
+ fade: (this.getAttribute('fade') || 'none'),
282
+ fadeSize: this.getNumber('fade-size', 0.22),
283
+ interaction: this.interactionMode(),
284
+ interColor: this.effectiveInterColor(),
285
+ interRadius: Math.max(20, this.getNumber('interaction-radius', 130)),
286
+ crossSize: this.getNumber('cross-size', 0),
287
+ guideInset: Math.max(0, this.getNumber('guide-inset', 16)),
288
+ guideEdges: this.parseGuideEdges(this.getAttribute('guide-edges')),
289
+ guideX: this.guideFraction('guide-x', 0.5),
290
+ guideY: this.guideFraction('guide-y', 0.5),
291
+ guideSpanX: Math.max(0, this.getNumber('guide-span-x', 0)),
292
+ guideSpanY: Math.max(0, this.getNumber('guide-span-y', 0)),
293
+ animate: this.getBool('animated'),
294
+ animSpeed: this.getNumber('animation-speed', 1),
295
+ animationStyle: this.animationStyle(),
296
+ traceLength: this.getNumber('trace-length', 0),
297
+ traceDirection: this.traceDirection(),
298
+ };
299
+ }
300
+ /* --------------------------------------------------------------------- */
301
+ /* Setup */
302
+ /* --------------------------------------------------------------------- */
303
+ ensureCanvas() {
304
+ if (this.canvas)
305
+ return;
306
+ const canvas = document.createElement('canvas');
307
+ Object.assign(canvas.style, {
308
+ display: 'block',
309
+ width: '100%',
310
+ height: '100%',
311
+ pointerEvents: 'none',
312
+ });
313
+ this.appendChild(canvas);
314
+ this.canvas = canvas;
315
+ this.ctx = canvas.getContext('2d') ?? undefined;
316
+ this.hlCanvas = document.createElement('canvas');
317
+ this.maskCanvas = document.createElement('canvas');
318
+ }
319
+ applyHostStyles() {
320
+ const overlay = this.getBool('overlay');
321
+ this.className = [CLASS, `${CLASS}--${this.getAttribute('type') || 'lines'}`, overlay && `${CLASS}--overlay`]
322
+ .filter(Boolean)
323
+ .join(' ');
324
+ this.style.display = 'block';
325
+ this.style.boxSizing = 'border-box';
326
+ // interactive 여도 canvas 는 pointer-events:none, 좌표는 window 에서 계산 → 하위 상호작용 방해 없음
327
+ this.style.pointerEvents = 'none';
328
+ const bg = this.getAttribute('background');
329
+ this.style.backgroundColor = bg ? this.resolveCssColor(bg) : 'transparent';
330
+ if (overlay) {
331
+ this.style.position = 'absolute';
332
+ this.style.inset = '0';
333
+ this.style.width = '100%';
334
+ this.style.height = '100%';
335
+ if (!this.style.zIndex)
336
+ this.style.zIndex = '1';
337
+ }
338
+ else {
339
+ if (this.style.position === 'absolute' || !this.style.position)
340
+ this.style.position = 'relative';
341
+ const width = this.getSizeStyle(this.getAttribute('width'));
342
+ const height = this.getSizeStyle(this.getAttribute('height'));
343
+ if (width)
344
+ this.style.width = width;
345
+ if (height)
346
+ this.style.height = height;
347
+ }
348
+ const borderRadius = this.getSizeStyle(this.getAttribute('border-radius'));
349
+ this.style.borderRadius = borderRadius || '';
350
+ if (this.canvas)
351
+ this.canvas.style.borderRadius = borderRadius || '';
352
+ }
353
+ startObserving() {
354
+ if (this.ro)
355
+ return;
356
+ this.ro = new ResizeObserver(() => {
357
+ this.measure();
358
+ this.scheduleDraw();
359
+ });
360
+ this.ro.observe(this);
361
+ }
362
+ observeDocumentTheme() {
363
+ if (this.themeObserver)
364
+ return;
365
+ if (typeof MutationObserver === 'undefined')
366
+ return;
367
+ this.themeObserver = new MutationObserver(() => this.scheduleDraw());
368
+ this.themeObserver.observe(document.documentElement, {
369
+ attributes: true,
370
+ attributeFilter: ['data-theme', 'data-color-mode', 'class'],
371
+ });
372
+ }
373
+ bindPointer() {
374
+ window.removeEventListener('pointermove', this.onPointerMove);
375
+ this.themeMedia?.removeEventListener('change', this.onThemeMediaChange);
376
+ if (!this.themeMedia) {
377
+ this.themeMedia = window.matchMedia('(prefers-color-scheme: dark)');
378
+ }
379
+ this.themeMedia.addEventListener('change', this.onThemeMediaChange);
380
+ if (this.interactionMode() !== 'none') {
381
+ window.addEventListener('pointermove', this.onPointerMove, { passive: true });
382
+ }
383
+ else {
384
+ this.pointer.strength = 0;
385
+ this.pointer.inside = false;
386
+ }
387
+ }
388
+ handlePointerMove(e) {
389
+ if (!this.canvas)
390
+ return;
391
+ const rect = this.canvas.getBoundingClientRect();
392
+ const x = e.clientX - rect.left;
393
+ const y = e.clientY - rect.top;
394
+ const pad = this.cfg.interRadius;
395
+ const inside = x >= -pad && x <= rect.width + pad && y >= -pad && y <= rect.height + pad;
396
+ this.pointer.x = x;
397
+ this.pointer.y = y;
398
+ this.pointer.inside = inside;
399
+ if (inside) {
400
+ this.pointer.strength = 1;
401
+ this.lastPointerMove = performance.now();
402
+ this.ensureLoop();
403
+ }
404
+ }
405
+ measure() {
406
+ if (!this.canvas || !this.ctx)
407
+ return;
408
+ const rect = this.getBoundingClientRect();
409
+ const w = Math.max(1, Math.round(rect.width));
410
+ const h = Math.max(1, Math.round(rect.height));
411
+ this.cssW = w;
412
+ this.cssH = h;
413
+ this.tracePulses = [];
414
+ this.traceSpawnAccumulator = 0;
415
+ const dpr = Math.min(MAX_DPR, window.devicePixelRatio || 1);
416
+ [this.canvas, this.hlCanvas, this.maskCanvas].forEach((c) => {
417
+ if (!c)
418
+ return;
419
+ c.width = Math.round(w * dpr);
420
+ c.height = Math.round(h * dpr);
421
+ });
422
+ }
423
+ /* --------------------------------------------------------------------- */
424
+ /* Geometry */
425
+ /* --------------------------------------------------------------------- */
426
+ /** 중앙을 기준으로 대칭 배치되며 (0, extent) 내부 좌표만 반환 → 가장자리 라인 없음 */
427
+ axis(extent, step) {
428
+ const center = extent / 2;
429
+ const positions = [];
430
+ const kMin = Math.ceil((0 - center) / step);
431
+ const kMax = Math.floor((extent - center) / step);
432
+ for (let k = kMin; k <= kMax; k++) {
433
+ const p = center + k * step;
434
+ if (p > 0.75 && p < extent - 0.75)
435
+ positions.push(p);
436
+ }
437
+ return positions;
438
+ }
439
+ /* --------------------------------------------------------------------- */
440
+ /* Draw */
441
+ /* --------------------------------------------------------------------- */
442
+ scheduleDraw() {
443
+ const cfg = this.cfg;
444
+ // 즉시 1회 동기 렌더 → rAF 지연(백그라운드 탭 등)에도 첫 프레임 보장
445
+ this.drawFrame(performance.now(), 0);
446
+ if (cfg.animate || this.pointer.strength > 0.001) {
447
+ this.ensureLoop();
448
+ }
449
+ else if (this.rafId != null) {
450
+ cancelAnimationFrame(this.rafId);
451
+ this.rafId = undefined;
452
+ }
453
+ }
454
+ ensureLoop() {
455
+ if (this.rafId != null)
456
+ return;
457
+ this.lastTime = performance.now();
458
+ const tick = (now) => {
459
+ const dt = Math.min(0.05, (now - this.lastTime) / 1000);
460
+ this.lastTime = now;
461
+ // 포인터 비활성 시 강도 감쇠
462
+ if (this.interactionMode() !== 'none') {
463
+ if (now - this.lastPointerMove > 90) {
464
+ this.pointer.strength = Math.max(0, this.pointer.strength - dt * 2.2);
465
+ }
466
+ }
467
+ else {
468
+ this.pointer.strength = 0;
469
+ }
470
+ this.drawFrame(now, dt);
471
+ const cfg = this.cfg;
472
+ if (cfg.animate || this.pointer.strength > 0.001) {
473
+ this.rafId = requestAnimationFrame(tick);
474
+ }
475
+ else {
476
+ this.rafId = undefined;
477
+ this.drawFrame(now, dt); // 최종 정적 프레임
478
+ }
479
+ };
480
+ this.rafId = requestAnimationFrame(tick);
481
+ }
482
+ drawFrame(now, dt) {
483
+ const ctx = this.ctx;
484
+ if (!ctx || !this.canvas)
485
+ return;
486
+ const { cssW: W, cssH: H } = this;
487
+ if (W <= 0 || H <= 0)
488
+ return;
489
+ const cfg = this.cfg;
490
+ const dpr = Math.min(MAX_DPR, window.devicePixelRatio || 1);
491
+ const time = now / 1000;
492
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
493
+ ctx.clearRect(0, 0, W, H);
494
+ // 1) 베이스 그리드
495
+ this.drawPattern(ctx, W, H, cfg, cfg.color, cfg.opacity, time);
496
+ // 2) trace 애니메이션 — 그리드/프레임 선을 따라 짧은 세그먼트 이동
497
+ if (cfg.animate && this.isTraceableLines(cfg) && (cfg.animationStyle === 'trace' || cfg.animationStyle === 'both')) {
498
+ this.drawTraceHighlight(ctx, W, H, cfg, dt);
499
+ }
500
+ // 3) 하이라이트(글로우/밴드) 레이어
501
+ const wantBandHighlight = (cfg.interaction === 'glow' && this.pointer.strength > 0.001) ||
502
+ (cfg.animate &&
503
+ cfg.animationStyle !== 'trace' &&
504
+ (this.isGridLines(cfg) || cfg.type === 'diagonal'));
505
+ if (wantBandHighlight && this.hlCanvas && this.maskCanvas) {
506
+ const hl = this.hlCanvas.getContext('2d');
507
+ const mask = this.maskCanvas.getContext('2d');
508
+ if (hl && mask) {
509
+ // 2a) 하이라이트 색으로 동일 패턴
510
+ hl.setTransform(dpr, 0, 0, dpr, 0, 0);
511
+ hl.clearRect(0, 0, W, H);
512
+ this.drawPattern(hl, W, H, cfg, cfg.interColor, Math.min(1, cfg.opacity + 0.4), time);
513
+ // 2b) 에너지 필드 마스크
514
+ this.buildEnergyMask(mask, W, H, dpr, cfg, time);
515
+ // 2c) 하이라이트 ∩ 에너지
516
+ hl.setTransform(1, 0, 0, 1, 0, 0);
517
+ hl.globalCompositeOperation = 'destination-in';
518
+ hl.drawImage(this.maskCanvas, 0, 0);
519
+ hl.globalCompositeOperation = 'source-over';
520
+ // 2d) 가산 합성
521
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
522
+ ctx.globalCompositeOperation = 'lighter';
523
+ ctx.drawImage(this.hlCanvas, 0, 0);
524
+ ctx.globalCompositeOperation = 'source-over';
525
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
526
+ }
527
+ }
528
+ // 4) 페이드 마스크
529
+ this.applyFade(ctx, W, H, cfg);
530
+ }
531
+ easeInOutCubic(t) {
532
+ const x = Math.min(1, Math.max(0, t));
533
+ return x < 0.5 ? 4 * x * x * x : 1 - (-2 * x + 2) ** 3 / 2;
534
+ }
535
+ traceSegmentLength(cfg) {
536
+ if (cfg.traceLength > 0)
537
+ return cfg.traceLength;
538
+ return TRACE_DEFAULT_LENGTH;
539
+ }
540
+ pulseHead(pulse, travel) {
541
+ const eased = this.easeInOutCubic(pulse.progress);
542
+ return -pulse.length + eased * (travel + pulse.length * 2);
543
+ }
544
+ updateTracePulses(W, H, cfg, dt) {
545
+ const segLen = this.traceSegmentLength(cfg);
546
+ const maxActive = Math.min(6, Math.max(2, Math.floor((W * H) / (cfg.size * cfg.size * 180))));
547
+ const spawnGap = (0.38 + Math.random() * 0.52) / Math.max(0.25, cfg.animSpeed);
548
+ this.traceSpawnAccumulator += dt;
549
+ while (this.traceSpawnAccumulator >= spawnGap && this.tracePulses.length < maxActive) {
550
+ this.traceSpawnAccumulator -= spawnGap;
551
+ this.spawnTracePulse(W, H, cfg, segLen);
552
+ if (Math.random() > 0.45)
553
+ break;
554
+ }
555
+ this.tracePulses = this.tracePulses.filter((pulse) => {
556
+ pulse.progress += dt / pulse.duration;
557
+ if (pulse.progress < 0.1)
558
+ pulse.alpha = pulse.progress / 0.1;
559
+ else if (pulse.progress > 0.9)
560
+ pulse.alpha = (1 - pulse.progress) / 0.1;
561
+ else
562
+ pulse.alpha = 1;
563
+ return pulse.progress < 1 && pulse.alpha > 0.02;
564
+ });
565
+ }
566
+ traceLinePositions(W, H, cfg) {
567
+ if (cfg.linesLayout === 'frame') {
568
+ const inset = Math.min(cfg.guideInset, W / 2 - 1, H / 2 - 1);
569
+ const edges = cfg.guideEdges;
570
+ const xs = [];
571
+ const ys = [];
572
+ if (edges.has('left'))
573
+ xs.push(inset);
574
+ if (edges.has('right'))
575
+ xs.push(W - inset);
576
+ if (edges.has('top'))
577
+ ys.push(inset);
578
+ if (edges.has('bottom'))
579
+ ys.push(H - inset);
580
+ return { xs, ys };
581
+ }
582
+ return { xs: this.axis(W, cfg.size), ys: this.axis(H, cfg.size) };
583
+ }
584
+ spawnTracePulse(W, H, cfg, segLen) {
585
+ const { xs, ys } = this.traceLinePositions(W, H, cfg);
586
+ const allowV = cfg.traceDirection !== 'horizontal' && xs.length > 0;
587
+ const allowH = cfg.traceDirection !== 'vertical' && ys.length > 0;
588
+ if (!allowV && !allowH)
589
+ return;
590
+ let axis;
591
+ if (allowV && allowH)
592
+ axis = Math.random() < 0.5 ? 'v' : 'h';
593
+ else
594
+ axis = allowV ? 'v' : 'h';
595
+ const travel = axis === 'v' ? H : W;
596
+ const length = segLen;
597
+ const distance = travel + length * 2;
598
+ const duration = distance / (TRACE_BASE_SPEED * Math.max(0.25, cfg.animSpeed));
599
+ const pulse = {
600
+ axis,
601
+ linePos: axis === 'v' ? xs[Math.floor(Math.random() * xs.length)] : ys[Math.floor(Math.random() * ys.length)],
602
+ progress: 0,
603
+ length,
604
+ duration,
605
+ alpha: 0,
606
+ };
607
+ this.tracePulses.push(pulse);
608
+ }
609
+ drawTracePulseSegment(ctx, pulse, W, H, cfg) {
610
+ const [r, g, b] = this.parseColorRgb(this.traceDrawColor(cfg));
611
+ const coreAlpha = Math.min(1, this.traceDrawOpacity(cfg) * pulse.alpha);
612
+ const travel = pulse.axis === 'v' ? H : W;
613
+ const head = this.pulseHead(pulse, travel);
614
+ let x0;
615
+ let y0;
616
+ let x1;
617
+ let y1;
618
+ if (pulse.axis === 'v') {
619
+ x0 = x1 = pulse.linePos;
620
+ y1 = Math.min(H, head);
621
+ y0 = Math.max(0, head - pulse.length);
622
+ }
623
+ else {
624
+ y0 = y1 = pulse.linePos;
625
+ x1 = Math.min(W, head);
626
+ x0 = Math.max(0, head - pulse.length);
627
+ }
628
+ if (Math.abs(x1 - x0) < 0.5 && Math.abs(y1 - y0) < 0.5)
629
+ return;
630
+ const grad = ctx.createLinearGradient(x0, y0, x1, y1);
631
+ grad.addColorStop(0, `rgba(${r},${g},${b},0)`);
632
+ grad.addColorStop(0.18, `rgba(${r},${g},${b},${coreAlpha * 0.2})`);
633
+ grad.addColorStop(0.5, `rgba(${r},${g},${b},${coreAlpha})`);
634
+ grad.addColorStop(0.82, `rgba(${r},${g},${b},${coreAlpha * 0.2})`);
635
+ grad.addColorStop(1, `rgba(${r},${g},${b},0)`);
636
+ ctx.save();
637
+ ctx.strokeStyle = grad;
638
+ ctx.lineCap = 'round';
639
+ ctx.globalAlpha = pulse.alpha;
640
+ ctx.shadowColor = `rgba(${r},${g},${b},${coreAlpha * 0.9})`;
641
+ ctx.shadowBlur = 12 + cfg.thickness * 5;
642
+ ctx.lineWidth = Math.max(1.1, cfg.thickness + 0.6);
643
+ ctx.beginPath();
644
+ ctx.moveTo(x0, y0);
645
+ ctx.lineTo(x1, y1);
646
+ ctx.stroke();
647
+ ctx.shadowBlur = 0;
648
+ ctx.lineWidth = Math.max(0.6, cfg.thickness * 0.35);
649
+ ctx.globalAlpha = Math.min(1, pulse.alpha * 1.1);
650
+ ctx.stroke();
651
+ ctx.restore();
652
+ }
653
+ drawTraceHighlight(ctx, W, H, cfg, dt) {
654
+ this.updateTracePulses(W, H, cfg, dt);
655
+ ctx.save();
656
+ ctx.globalCompositeOperation = 'lighter';
657
+ this.tracePulses.forEach((pulse) => this.drawTracePulseSegment(ctx, pulse, W, H, cfg));
658
+ ctx.restore();
659
+ }
660
+ drawPattern(ctx, W, H, cfg, color, alpha, time) {
661
+ const { type, size, thickness } = cfg;
662
+ ctx.save();
663
+ ctx.globalAlpha = alpha;
664
+ ctx.strokeStyle = color;
665
+ ctx.fillStyle = color;
666
+ ctx.lineWidth = thickness;
667
+ ctx.lineCap = 'round';
668
+ if (type === 'lines') {
669
+ if (cfg.linesLayout === 'frame') {
670
+ this.drawFrameGuides(ctx, W, H, cfg);
671
+ }
672
+ else if (cfg.linesLayout === 'center') {
673
+ this.drawCenterGuides(ctx, W, H, cfg);
674
+ }
675
+ else {
676
+ this.drawGridLines(ctx, W, H, cfg.size);
677
+ }
678
+ }
679
+ else if (type === 'dots') {
680
+ const xs = this.axis(W, size);
681
+ const ys = this.axis(H, size);
682
+ const baseR = Math.max(0.75, thickness);
683
+ const magnet = cfg.interaction === 'magnet' && this.pointer.strength > 0.001;
684
+ const pr = this.pointer;
685
+ const rInf = cfg.interRadius;
686
+ xs.forEach((x, ix) => {
687
+ ys.forEach((y, iy) => {
688
+ let cx = x;
689
+ let cy = y;
690
+ let r = baseR;
691
+ if (magnet && pr.inside) {
692
+ const dx = pr.x - x;
693
+ const dy = pr.y - y;
694
+ const d = Math.hypot(dx, dy);
695
+ if (d < rInf) {
696
+ const t = (1 - d / rInf) * pr.strength;
697
+ r = baseR * (1 + t * 1.6);
698
+ cx += (dx / (d || 1)) * t * size * 0.28;
699
+ cy += (dy / (d || 1)) * t * size * 0.28;
700
+ }
701
+ }
702
+ if (cfg.animate) {
703
+ const tw = 0.75 + 0.25 * Math.sin(time * 2 * cfg.animSpeed + (ix + iy) * 0.9);
704
+ r *= tw;
705
+ }
706
+ ctx.beginPath();
707
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
708
+ ctx.fill();
709
+ });
710
+ });
711
+ }
712
+ else if (type === 'cross') {
713
+ const xs = this.axis(W, size);
714
+ const ys = this.axis(H, size);
715
+ const arm = this.crossArm(size, thickness, cfg.crossSize);
716
+ const rotate = cfg.interaction === 'rotate' && this.pointer.strength > 0.001;
717
+ const pr = this.pointer;
718
+ xs.forEach((x) => {
719
+ ys.forEach((y) => {
720
+ ctx.save();
721
+ ctx.translate(x, y);
722
+ if (rotate && pr.inside && pr.strength > 0.01) {
723
+ const dx = pr.x - x;
724
+ const dy = pr.y - y;
725
+ ctx.rotate(Math.atan2(dy, dx));
726
+ }
727
+ ctx.beginPath();
728
+ if (cfg.crossStyle === 'line') {
729
+ ctx.moveTo(-arm, 0);
730
+ ctx.lineTo(arm, 0);
731
+ }
732
+ else {
733
+ ctx.moveTo(-arm, 0);
734
+ ctx.lineTo(arm, 0);
735
+ ctx.moveTo(0, -arm);
736
+ ctx.lineTo(0, arm);
737
+ }
738
+ ctx.stroke();
739
+ ctx.restore();
740
+ });
741
+ });
742
+ }
743
+ else {
744
+ // diagonal — 중앙 기준 45° 크로스해치
745
+ ctx.save();
746
+ ctx.beginPath();
747
+ ctx.rect(0, 0, W, H);
748
+ ctx.clip();
749
+ ctx.translate(W / 2, H / 2);
750
+ ctx.rotate(Math.PI / 4);
751
+ const R = Math.hypot(W, H);
752
+ ctx.beginPath();
753
+ for (let p = -R; p <= R; p += size) {
754
+ ctx.moveTo(p, -R);
755
+ ctx.lineTo(p, R);
756
+ ctx.moveTo(-R, p);
757
+ ctx.lineTo(R, p);
758
+ }
759
+ ctx.stroke();
760
+ ctx.restore();
761
+ }
762
+ ctx.restore();
763
+ }
764
+ drawGridLines(ctx, W, H, size) {
765
+ const xs = this.axis(W, size);
766
+ const ys = this.axis(H, size);
767
+ ctx.beginPath();
768
+ xs.forEach((x) => {
769
+ ctx.moveTo(x, 0);
770
+ ctx.lineTo(x, H);
771
+ });
772
+ ys.forEach((y) => {
773
+ ctx.moveTo(0, y);
774
+ ctx.lineTo(W, y);
775
+ });
776
+ ctx.stroke();
777
+ }
778
+ drawFrameGuides(ctx, W, H, cfg) {
779
+ const inset = Math.min(cfg.guideInset, W / 2 - 1, H / 2 - 1);
780
+ const edges = cfg.guideEdges;
781
+ ctx.beginPath();
782
+ if (edges.has('top')) {
783
+ ctx.moveTo(0, inset);
784
+ ctx.lineTo(W, inset);
785
+ }
786
+ if (edges.has('bottom')) {
787
+ ctx.moveTo(0, H - inset);
788
+ ctx.lineTo(W, H - inset);
789
+ }
790
+ if (edges.has('left')) {
791
+ ctx.moveTo(inset, 0);
792
+ ctx.lineTo(inset, H);
793
+ }
794
+ if (edges.has('right')) {
795
+ ctx.moveTo(W - inset, 0);
796
+ ctx.lineTo(W - inset, H);
797
+ }
798
+ ctx.stroke();
799
+ }
800
+ drawCenterGuides(ctx, W, H, cfg) {
801
+ const cx = W * cfg.guideX;
802
+ const cy = H * cfg.guideY;
803
+ const spanX = cfg.guideSpanX > 0 ? cfg.guideSpanX : W;
804
+ const spanY = cfg.guideSpanY > 0 ? cfg.guideSpanY : H;
805
+ ctx.beginPath();
806
+ ctx.moveTo(cx - spanX / 2, cy);
807
+ ctx.lineTo(cx + spanX / 2, cy);
808
+ ctx.moveTo(cx, cy - spanY / 2);
809
+ ctx.lineTo(cx, cy + spanY / 2);
810
+ ctx.stroke();
811
+ }
812
+ buildEnergyMask(mask, W, H, dpr, cfg, time) {
813
+ mask.setTransform(dpr, 0, 0, dpr, 0, 0);
814
+ mask.clearRect(0, 0, W, H);
815
+ mask.globalCompositeOperation = 'lighter';
816
+ // 포인터 방사형
817
+ if (cfg.interaction === 'glow' && this.pointer.strength > 0.001 && this.pointer.inside) {
818
+ const r = cfg.interRadius;
819
+ const g = mask.createRadialGradient(this.pointer.x, this.pointer.y, 0, this.pointer.x, this.pointer.y, r);
820
+ g.addColorStop(0, `rgba(255,255,255,${0.9 * this.pointer.strength})`);
821
+ g.addColorStop(1, 'rgba(255,255,255,0)');
822
+ mask.fillStyle = g;
823
+ mask.fillRect(0, 0, W, H);
824
+ }
825
+ // 이동하는 밝은 밴드
826
+ if (cfg.animate && cfg.animationStyle !== 'trace') {
827
+ mask.save();
828
+ mask.translate(W / 2, H / 2);
829
+ mask.rotate(this.bandAngle);
830
+ const span = Math.hypot(W, H);
831
+ const bandW = Math.max(60, span * 0.18);
832
+ const travel = span + bandW;
833
+ const pos = ((time * 90 * cfg.animSpeed) % (travel * 2)) - travel;
834
+ const grad = mask.createLinearGradient(pos - bandW, 0, pos + bandW, 0);
835
+ grad.addColorStop(0, 'rgba(255,255,255,0)');
836
+ grad.addColorStop(0.5, 'rgba(255,255,255,0.55)');
837
+ grad.addColorStop(1, 'rgba(255,255,255,0)');
838
+ mask.fillStyle = grad;
839
+ mask.fillRect(-span, -span, span * 2, span * 2);
840
+ mask.restore();
841
+ }
842
+ mask.globalCompositeOperation = 'source-over';
843
+ }
844
+ applyFade(ctx, W, H, cfg) {
845
+ const fade = cfg.fade;
846
+ if (fade === 'none')
847
+ return;
848
+ const frac = cfg.fadeSize;
849
+ const pxX = frac > 1 ? frac : Math.max(8, W * frac);
850
+ const pxY = frac > 1 ? frac : Math.max(8, H * frac);
851
+ ctx.save();
852
+ ctx.globalCompositeOperation = 'destination-in';
853
+ const edge = (dir) => {
854
+ let g;
855
+ if (dir === 'top') {
856
+ g = ctx.createLinearGradient(0, 0, 0, pxY);
857
+ g.addColorStop(0, 'rgba(0,0,0,0)');
858
+ g.addColorStop(1, 'rgba(0,0,0,1)');
859
+ }
860
+ else if (dir === 'bottom') {
861
+ g = ctx.createLinearGradient(0, H - pxY, 0, H);
862
+ g.addColorStop(0, 'rgba(0,0,0,1)');
863
+ g.addColorStop(1, 'rgba(0,0,0,0)');
864
+ }
865
+ else if (dir === 'left') {
866
+ g = ctx.createLinearGradient(0, 0, pxX, 0);
867
+ g.addColorStop(0, 'rgba(0,0,0,0)');
868
+ g.addColorStop(1, 'rgba(0,0,0,1)');
869
+ }
870
+ else {
871
+ g = ctx.createLinearGradient(W - pxX, 0, W, 0);
872
+ g.addColorStop(0, 'rgba(0,0,0,1)');
873
+ g.addColorStop(1, 'rgba(0,0,0,0)');
874
+ }
875
+ // 여러 방향은 교집합(destination-in 연쇄) → 각 변이 개별적으로 페이드
876
+ ctx.fillStyle = g;
877
+ ctx.fillRect(0, 0, W, H);
878
+ };
879
+ if (fade === 'vignette') {
880
+ const g = ctx.createRadialGradient(W / 2, H / 2, Math.min(W, H) * 0.2, W / 2, H / 2, Math.hypot(W, H) / 2);
881
+ g.addColorStop(0, 'rgba(0,0,0,1)');
882
+ g.addColorStop(1, 'rgba(0,0,0,0)');
883
+ ctx.fillStyle = g;
884
+ ctx.fillRect(0, 0, W, H);
885
+ }
886
+ else {
887
+ const edges = fade === 'top'
888
+ ? ['top']
889
+ : fade === 'bottom'
890
+ ? ['bottom']
891
+ : fade === 'left'
892
+ ? ['left']
893
+ : fade === 'right'
894
+ ? ['right']
895
+ : fade === 'vertical'
896
+ ? ['top', 'bottom']
897
+ : fade === 'horizontal'
898
+ ? ['left', 'right']
899
+ : ['top', 'bottom', 'left', 'right']; // all
900
+ edges.forEach(edge);
901
+ }
902
+ ctx.restore();
903
+ }
904
+ }
905
+ if (!customElements.get('db-grid-background')) {
906
+ customElements.define('db-grid-background', DbGridBackground);
907
+ }
908
+
909
+ export { DbGridBackground };