@designbasekorea/ui-wc 0.5.5 → 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 (155) 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.esm.js +39373 -1
  153. package/package.json +1 -1
  154. package/dist/chunks/lottie-2637aa0f.js +0 -1
  155. package/dist/react/chunks/lottie-14e7addb.js +0 -1
@@ -1 +1,402 @@
1
- const t="designbase-wc-marquee";function isImageItem(t){return"object"==typeof t&&null!==t&&"string"==typeof t.src}class DbMarquee extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.rows=[],this.rowCount=0,this.containerWidth=0,this.gap=24,this.scrollVelocityEnabled=!1,this.rafId=0,this.lastFrameTime=0,this.lastScrollY=0,this.smoothedScrollV=0,this.baseDurationSec=12,this.reduceMotion=!1,this.boundMeasure=()=>this.measure(),this.boundFrame=t=>this.frame(t)}static get observedAttributes(){return["items","direction","duration","speed","size","variant","bordered","pause-on-hover","fade-edges","fade-width","edge-padding","gap","align-y","row-count","row-gap","scroll-velocity","scroll-boost","aria-label"]}connectedCallback(){this.style.display="block",this.built||(this.built=!0,this.reduceMotion="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,this.build(),this.bindResize()),this.updateUI()}disconnectedCallback(){this.resizeObserver?.disconnect(),this.stopScrollEngine(),this.unbindImageListeners()}attributeChangedCallback(){this.built&&this.updateUI()}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const r=this.getAttribute(t);return""===r||"true"===r}parseItems(){try{const t=this.getAttribute("items"),e=t?JSON.parse(t):[];if(Array.isArray(e)&&e.length>0)return e.filter(t=>"string"==typeof t||isImageItem(t))}catch{}const t=Array.from(this.children).filter(t=>!t.hasAttribute("data-wc-internal"));return t.length>0?t.map(t=>t.textContent?.trim()||""):[]}itemToText(t){return isImageItem(t)?t.alt||"":t}build(){this.containerEl=document.createElement("div"),this.srOnlyEl=document.createElement("span"),this.srOnlyEl.className=`${t}__sr-only`,this.rowsEl=document.createElement("div"),this.rowsEl.className=`${t}__rows`,this.containerEl.append(this.srOnlyEl,this.rowsEl),this.append(this.containerEl)}bindResize(){this.resizeObserver=new ResizeObserver(()=>this.measure()),this.containerEl&&this.resizeObserver.observe(this.containerEl)}reconcileRows(e,r){if(this.rowsEl){for(this.rowCount=e;this.rows.length<e;){const e=document.createElement("div");e.className=`${t}__row`;const r=document.createElement("div");r.className=`${t}__track`;const s=document.createElement("div");s.className=`${t}__group`,s.setAttribute("aria-hidden","true"),r.append(s),e.append(r),this.rowsEl.append(e),this.rows.push({rowEl:e,trackEl:r,baseGroupEl:s,baseGroupWidth:0,duplicateCount:0,baseDir:1,x:0,dirState:1})}for(;this.rows.length>e;){const t=this.rows.pop();t?.rowEl.remove()}this.rows.forEach((e,s)=>{e.baseDir=s%2==0?r:-r;const i=e.baseDir<0;e.rowEl.classList.toggle(`${t}__row--right`,i),e.rowEl.style.setProperty("--db-marquee-row-index",String(s))}),this.resizeObserver&&this.rows.forEach(t=>this.resizeObserver?.observe(t.baseGroupEl))}}measure(){this.containerWidth=Math.ceil(this.containerEl?.clientWidth??0),this.rows.forEach(t=>{t.baseGroupWidth=Math.ceil(t.baseGroupEl.scrollWidth??0),t.rowEl.style.setProperty("--db-marquee-shift",`${t.baseGroupWidth+this.gap}px`),this.reconcileDuplicates(t)}),this.syncReadyState()}unbindImageListeners(){this.rows.forEach(t=>{t.baseGroupEl.querySelectorAll("img").forEach(t=>{t.removeEventListener("load",this.boundMeasure),t.removeEventListener("error",this.boundMeasure)})})}bindImageListeners(){this.rows.forEach(t=>{t.baseGroupEl.querySelectorAll("img").forEach(t=>{t.complete||(t.addEventListener("load",this.boundMeasure),t.addEventListener("error",this.boundMeasure))})})}renderItem(e){const r=document.createElement("div");if(r.className=`${t}__item`,isImageItem(e)){r.classList.add(`${t}__item--image`);const s=document.createElement("img");if(s.className=`${t}__image`,s.src=e.src,s.alt=e.alt||"",s.loading="lazy",s.decoding="async",e.height&&(s.style.height=`${e.height}px`),e.href){const i=document.createElement("a");i.className=`${t}__link`,i.href=e.href,i.setAttribute("tabindex","-1"),i.append(s),r.append(i)}else r.append(s);return r}return r.textContent=e,r}renderGroup(e,r){const s=r??document.createElement("div");return s.className=`${t}__group`,s.setAttribute("aria-hidden","true"),s.replaceChildren(...e.map(t=>this.renderItem(t))),s}buildBase(t){this.rows.forEach(e=>{for(this.renderGroup(t,e.baseGroupEl);e.trackEl.children.length>1;)e.trackEl.lastElementChild?.remove();e.duplicateCount=0})}baseImagesReady(t){return Array.from(t.baseGroupEl.querySelectorAll("img")).every(t=>t.complete)}reconcileDuplicates(t){const e=t.baseGroupWidth>0&&this.baseImagesReady(t)?Math.max(1,Math.ceil(2*this.containerWidth/t.baseGroupWidth)):1;let r=t.trackEl.children.length-1;if(r===e)return void(t.duplicateCount=e);const s=this.parseItems();for(;r<e;)t.trackEl.append(this.renderGroup(s)),r+=1;for(;r>e;)t.trackEl.lastElementChild?.remove(),r-=1;t.duplicateCount=e}syncReadyState(){if(!this.containerEl)return;const e=this.rows.length>0&&this.rows.every(t=>t.baseGroupWidth>0);this.containerEl.classList.toggle(`${t}--ready`,e)}startScrollEngine(){this.rafId||this.reduceMotion||(this.lastScrollY="undefined"!=typeof window?window.scrollY:0,this.lastFrameTime=0,this.smoothedScrollV=0,this.rows.forEach(t=>{t.x=0,t.dirState=1}),this.rafId=requestAnimationFrame(this.boundFrame))}stopScrollEngine(){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),this.rows.forEach(t=>{t.trackEl.style.transform=""})}frame(t){if(!this.scrollVelocityEnabled)return void(this.rafId=0);this.lastFrameTime||(this.lastFrameTime=t);const e=Math.min((t-this.lastFrameTime)/1e3,.05);this.lastFrameTime=t;const r="undefined"!=typeof window?window.scrollY:0,s=e>0?(r-this.lastScrollY)/e:0;this.lastScrollY=r;const i=1-Math.exp(-e/.12);this.smoothedScrollV+=(s-this.smoothedScrollV)*i;const a=Math.max(Number(this.getAttribute("scroll-boost")||6),0),o=this.smoothedScrollV/800*(a/6);this.rows.forEach(t=>{const r=t.baseGroupWidth+this.gap;if(r<=0)return;const s=r/this.baseDurationSec;let i=-t.baseDir*s*e;o<0?t.dirState=-1:o>0&&(t.dirState=1),i*=t.dirState,i+=i*Math.abs(o);let a=(t.x+i)%r;a>0&&(a-=r),t.x=a,t.trackEl.style.transform=`translate3d(${a}px, 0, 0)`}),this.rafId=requestAnimationFrame(this.boundFrame)}updateUI(){const e=this.parseItems();if(0===e.length)return this.stopScrollEngine(),void(this.hidden=!0);this.hidden=!1;const r=this.getAttribute("direction")||"left",s="right"===r?-1:1,i=Math.max(Number(this.getAttribute("duration")||12),1),a=Number(this.getAttribute("speed")||1),o=i/(Number.isFinite(a)&&a>0?a:1);this.baseDurationSec=o;const n=this.getAttribute("size")||"m",l=this.getAttribute("variant")||"default",h=this.getAttribute("align-y")||"center",c=this.getBool("fade-edges"),d=Math.max(Number(this.getAttribute("fade-width")||48),0),u=this.getAttribute("edge-padding"),m=null!==u&&""!==u?Math.max(Number(u),0):c?d:0,b=Math.max(Number(this.getAttribute("gap")||24),0);this.gap=b;const p=Math.round(Number(this.getAttribute("row-count")||1)),g=Number.isFinite(p)?Math.min(Math.max(p,1),8):1,E=Math.max(Number(this.getAttribute("row-gap")||12),0),f=this.getBool("scroll-velocity");this.scrollVelocityEnabled=f;const w=this.getBool("pause-on-hover",!0),y=this.hasAttribute("bordered")?this.getBool("bordered"):"outlined"===l,v=this.getAttribute("aria-label")||"Scrolling content";if(this.containerEl&&(this.containerEl.className=[t,`${t}--size-${n}`,`${t}--variant-${l}`,`${t}--align-${h}`,w&&!f&&`${t}--pause-on-hover`,c&&`${t}--fade-edges`,"right"===r&&`${t}--right`,g>1&&`${t}--multi-row`,f&&`${t}--scroll-velocity`,y&&`${t}--bordered`].filter(Boolean).join(" "),this.containerEl.style.setProperty("--db-marquee-duration",`${o}s`),this.containerEl.style.setProperty("--db-marquee-gap",`${b}px`),this.containerEl.style.setProperty("--db-marquee-row-gap",`${E}px`),this.containerEl.style.setProperty("--db-marquee-fade-width",`${d}px`),this.containerEl.style.setProperty("--db-marquee-edge-padding",`${m}px`),this.containerEl.setAttribute("role","region"),this.containerEl.setAttribute("aria-label",v)),this.srOnlyEl){const t=e.map(t=>this.itemToText(t)).filter(Boolean).join(" • ");this.srOnlyEl.textContent=t||v}this.reconcileRows(g,s),this.buildBase(e),this.bindImageListeners(),this.measure(),f?this.startScrollEngine():this.stopScrollEngine()}}customElements.get("db-marquee")||customElements.define("db-marquee",DbMarquee);export{DbMarquee};
1
+ const CLASS = 'designbase-wc-marquee';
2
+ function isImageItem(item) {
3
+ return typeof item === 'object' && item !== null && typeof item.src === 'string';
4
+ }
5
+ class DbMarquee extends HTMLElement {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.built = false;
9
+ this.rows = [];
10
+ this.rowCount = 0;
11
+ this.containerWidth = 0;
12
+ this.gap = 24;
13
+ // scroll-velocity 상태
14
+ this.scrollVelocityEnabled = false;
15
+ this.rafId = 0;
16
+ this.lastFrameTime = 0;
17
+ this.lastScrollY = 0;
18
+ this.smoothedScrollV = 0;
19
+ this.baseDurationSec = 12;
20
+ this.reduceMotion = false;
21
+ this.boundMeasure = () => this.measure();
22
+ this.boundFrame = (t) => this.frame(t);
23
+ }
24
+ static get observedAttributes() {
25
+ return [
26
+ 'items',
27
+ 'direction',
28
+ 'duration',
29
+ 'speed',
30
+ 'size',
31
+ 'variant',
32
+ 'bordered',
33
+ 'pause-on-hover',
34
+ 'fade-edges',
35
+ 'fade-width',
36
+ 'edge-padding',
37
+ 'gap',
38
+ 'align-y',
39
+ 'row-count',
40
+ 'row-gap',
41
+ 'scroll-velocity',
42
+ 'scroll-boost',
43
+ 'aria-label',
44
+ ];
45
+ }
46
+ connectedCallback() {
47
+ this.style.display = 'block';
48
+ if (!this.built) {
49
+ this.built = true;
50
+ this.reduceMotion =
51
+ typeof window !== 'undefined' &&
52
+ typeof window.matchMedia === 'function' &&
53
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches;
54
+ this.build();
55
+ this.bindResize();
56
+ }
57
+ this.updateUI();
58
+ }
59
+ disconnectedCallback() {
60
+ this.resizeObserver?.disconnect();
61
+ this.stopScrollEngine();
62
+ this.unbindImageListeners();
63
+ }
64
+ attributeChangedCallback() {
65
+ if (!this.built)
66
+ return;
67
+ this.updateUI();
68
+ }
69
+ getBool(name, fallback = false) {
70
+ if (!this.hasAttribute(name))
71
+ return fallback;
72
+ const value = this.getAttribute(name);
73
+ return value === '' || value === 'true';
74
+ }
75
+ parseItems() {
76
+ try {
77
+ const raw = this.getAttribute('items');
78
+ const parsed = raw ? JSON.parse(raw) : [];
79
+ if (Array.isArray(parsed) && parsed.length > 0) {
80
+ return parsed.filter((item) => typeof item === 'string' || isImageItem(item));
81
+ }
82
+ }
83
+ catch {
84
+ /* fallback to slot */
85
+ }
86
+ const slotted = Array.from(this.children).filter((child) => !child.hasAttribute('data-wc-internal'));
87
+ if (slotted.length > 0)
88
+ return slotted.map((node) => node.textContent?.trim() || '');
89
+ return [];
90
+ }
91
+ itemToText(item) {
92
+ return isImageItem(item) ? item.alt || '' : item;
93
+ }
94
+ build() {
95
+ this.containerEl = document.createElement('div');
96
+ this.srOnlyEl = document.createElement('span');
97
+ this.srOnlyEl.className = `${CLASS}__sr-only`;
98
+ this.rowsEl = document.createElement('div');
99
+ this.rowsEl.className = `${CLASS}__rows`;
100
+ this.containerEl.append(this.srOnlyEl, this.rowsEl);
101
+ this.append(this.containerEl);
102
+ }
103
+ bindResize() {
104
+ this.resizeObserver = new ResizeObserver(() => this.measure());
105
+ if (this.containerEl)
106
+ this.resizeObserver.observe(this.containerEl);
107
+ }
108
+ /** rows 개수에 맞춰 행 DOM을 만들거나 제거한다. */
109
+ reconcileRows(count, baseDir) {
110
+ if (!this.rowsEl)
111
+ return;
112
+ this.rowCount = count;
113
+ while (this.rows.length < count) {
114
+ const rowEl = document.createElement('div');
115
+ rowEl.className = `${CLASS}__row`;
116
+ const trackEl = document.createElement('div');
117
+ trackEl.className = `${CLASS}__track`;
118
+ const baseGroupEl = document.createElement('div');
119
+ baseGroupEl.className = `${CLASS}__group`;
120
+ baseGroupEl.setAttribute('aria-hidden', 'true');
121
+ trackEl.append(baseGroupEl);
122
+ rowEl.append(trackEl);
123
+ this.rowsEl.append(rowEl);
124
+ this.rows.push({
125
+ rowEl,
126
+ trackEl,
127
+ baseGroupEl,
128
+ baseGroupWidth: 0,
129
+ duplicateCount: 0,
130
+ baseDir: 1,
131
+ x: 0,
132
+ dirState: 1,
133
+ });
134
+ }
135
+ while (this.rows.length > count) {
136
+ const removed = this.rows.pop();
137
+ removed?.rowEl.remove();
138
+ }
139
+ this.rows.forEach((row, index) => {
140
+ // 홀수 행은 반대 방향으로 흘려 좌/우 교차 효과를 만든다.
141
+ row.baseDir = index % 2 === 0 ? baseDir : -baseDir;
142
+ const goesRight = row.baseDir < 0;
143
+ row.rowEl.classList.toggle(`${CLASS}__row--right`, goesRight);
144
+ row.rowEl.style.setProperty('--db-marquee-row-index', String(index));
145
+ });
146
+ if (this.resizeObserver) {
147
+ this.rows.forEach((row) => this.resizeObserver?.observe(row.baseGroupEl));
148
+ }
149
+ }
150
+ measure() {
151
+ this.containerWidth = Math.ceil(this.containerEl?.clientWidth ?? 0);
152
+ this.rows.forEach((row) => {
153
+ row.baseGroupWidth = Math.ceil(row.baseGroupEl.scrollWidth ?? 0);
154
+ row.rowEl.style.setProperty('--db-marquee-shift', `${row.baseGroupWidth + this.gap}px`);
155
+ this.reconcileDuplicates(row);
156
+ });
157
+ this.syncReadyState();
158
+ }
159
+ unbindImageListeners() {
160
+ this.rows.forEach((row) => {
161
+ row.baseGroupEl.querySelectorAll('img').forEach((img) => {
162
+ img.removeEventListener('load', this.boundMeasure);
163
+ img.removeEventListener('error', this.boundMeasure);
164
+ });
165
+ });
166
+ }
167
+ bindImageListeners() {
168
+ this.rows.forEach((row) => {
169
+ row.baseGroupEl.querySelectorAll('img').forEach((img) => {
170
+ if (img.complete)
171
+ return;
172
+ img.addEventListener('load', this.boundMeasure);
173
+ img.addEventListener('error', this.boundMeasure);
174
+ });
175
+ });
176
+ }
177
+ renderItem(entry) {
178
+ const item = document.createElement('div');
179
+ item.className = `${CLASS}__item`;
180
+ if (isImageItem(entry)) {
181
+ item.classList.add(`${CLASS}__item--image`);
182
+ const img = document.createElement('img');
183
+ img.className = `${CLASS}__image`;
184
+ img.src = entry.src;
185
+ img.alt = entry.alt || '';
186
+ img.loading = 'lazy';
187
+ img.decoding = 'async';
188
+ if (entry.height)
189
+ img.style.height = `${entry.height}px`;
190
+ if (entry.href) {
191
+ const link = document.createElement('a');
192
+ link.className = `${CLASS}__link`;
193
+ link.href = entry.href;
194
+ link.setAttribute('tabindex', '-1');
195
+ link.append(img);
196
+ item.append(link);
197
+ }
198
+ else {
199
+ item.append(img);
200
+ }
201
+ return item;
202
+ }
203
+ item.textContent = entry;
204
+ return item;
205
+ }
206
+ renderGroup(items, ref) {
207
+ const group = ref ?? document.createElement('div');
208
+ group.className = `${CLASS}__group`;
209
+ group.setAttribute('aria-hidden', 'true');
210
+ group.replaceChildren(...items.map((text) => this.renderItem(text)));
211
+ return group;
212
+ }
213
+ /**
214
+ * 각 행의 base 그룹을 items로 다시 만든다 — items/attributes 변경 시에만 실행.
215
+ * 이미지 노드 재생성으로 인한 재로딩을 막기 위해 measure() 중에는 건드리지 않는다.
216
+ */
217
+ buildBase(items) {
218
+ this.rows.forEach((row) => {
219
+ this.renderGroup(items, row.baseGroupEl);
220
+ while (row.trackEl.children.length > 1) {
221
+ row.trackEl.lastElementChild?.remove();
222
+ }
223
+ row.duplicateCount = 0;
224
+ });
225
+ }
226
+ baseImagesReady(row) {
227
+ return Array.from(row.baseGroupEl.querySelectorAll('img')).every((img) => img.complete);
228
+ }
229
+ /** 트랙을 채울 만큼의 복제 그룹 개수를 델타만 조정한다. */
230
+ reconcileDuplicates(row) {
231
+ const desired = row.baseGroupWidth > 0 && this.baseImagesReady(row)
232
+ ? Math.max(1, Math.ceil((this.containerWidth * 2) / row.baseGroupWidth))
233
+ : 1;
234
+ let current = row.trackEl.children.length - 1;
235
+ if (current === desired) {
236
+ row.duplicateCount = desired;
237
+ return;
238
+ }
239
+ const items = this.parseItems();
240
+ while (current < desired) {
241
+ row.trackEl.append(this.renderGroup(items));
242
+ current += 1;
243
+ }
244
+ while (current > desired) {
245
+ row.trackEl.lastElementChild?.remove();
246
+ current -= 1;
247
+ }
248
+ row.duplicateCount = desired;
249
+ }
250
+ syncReadyState() {
251
+ if (!this.containerEl)
252
+ return;
253
+ const ready = this.rows.length > 0 && this.rows.every((row) => row.baseGroupWidth > 0);
254
+ this.containerEl.classList.toggle(`${CLASS}--ready`, ready);
255
+ }
256
+ // ── scroll-velocity 엔진 ──────────────────────────────────────────────
257
+ startScrollEngine() {
258
+ if (this.rafId || this.reduceMotion)
259
+ return;
260
+ this.lastScrollY = typeof window !== 'undefined' ? window.scrollY : 0;
261
+ this.lastFrameTime = 0;
262
+ this.smoothedScrollV = 0;
263
+ this.rows.forEach((row) => {
264
+ row.x = 0;
265
+ row.dirState = 1;
266
+ });
267
+ this.rafId = requestAnimationFrame(this.boundFrame);
268
+ }
269
+ stopScrollEngine() {
270
+ if (this.rafId) {
271
+ cancelAnimationFrame(this.rafId);
272
+ this.rafId = 0;
273
+ }
274
+ // CSS 애니메이션으로 복귀할 수 있도록 인라인 transform 제거
275
+ this.rows.forEach((row) => {
276
+ row.trackEl.style.transform = '';
277
+ });
278
+ }
279
+ frame(now) {
280
+ if (!this.scrollVelocityEnabled) {
281
+ this.rafId = 0;
282
+ return;
283
+ }
284
+ if (!this.lastFrameTime)
285
+ this.lastFrameTime = now;
286
+ const dt = Math.min((now - this.lastFrameTime) / 1000, 0.05);
287
+ this.lastFrameTime = now;
288
+ // 스크롤 속도(px/s)를 측정하고 지수 감쇠로 부드럽게 만든다.
289
+ const scrollY = typeof window !== 'undefined' ? window.scrollY : 0;
290
+ const rawV = dt > 0 ? (scrollY - this.lastScrollY) / dt : 0;
291
+ this.lastScrollY = scrollY;
292
+ const smoothing = 1 - Math.exp(-dt / 0.12);
293
+ this.smoothedScrollV += (rawV - this.smoothedScrollV) * smoothing;
294
+ const boost = Math.max(Number(this.getAttribute('scroll-boost') || 6), 0);
295
+ // 스크롤 속도를 배율로 환산(부호 유지). 800px/s ≈ 1.0
296
+ const velocityFactor = (this.smoothedScrollV / 800) * (boost / 6);
297
+ this.rows.forEach((row) => {
298
+ const shift = row.baseGroupWidth + this.gap;
299
+ if (shift <= 0)
300
+ return;
301
+ const pxPerSec = shift / this.baseDurationSec;
302
+ // 기본 진행: baseDir(+1=왼쪽) → x 감소
303
+ let moveBy = -row.baseDir * pxPerSec * dt;
304
+ // 아래로 스크롤(velocityFactor>0)은 기본 방향 유지·가속,
305
+ // 위로 스크롤(velocityFactor<0)은 진행 방향 반전.
306
+ if (velocityFactor < 0)
307
+ row.dirState = -1;
308
+ else if (velocityFactor > 0)
309
+ row.dirState = 1;
310
+ moveBy *= row.dirState;
311
+ moveBy += moveBy * Math.abs(velocityFactor);
312
+ // 이음새 없는 루프를 위해 위치를 (-shift, 0] 범위로 정규화한다.
313
+ let x = (row.x + moveBy) % shift;
314
+ if (x > 0)
315
+ x -= shift;
316
+ row.x = x;
317
+ row.trackEl.style.transform = `translate3d(${x}px, 0, 0)`;
318
+ });
319
+ this.rafId = requestAnimationFrame(this.boundFrame);
320
+ }
321
+ updateUI() {
322
+ const items = this.parseItems();
323
+ if (items.length === 0) {
324
+ this.stopScrollEngine();
325
+ this.hidden = true;
326
+ return;
327
+ }
328
+ this.hidden = false;
329
+ const direction = this.getAttribute('direction') || 'left';
330
+ const baseDir = direction === 'right' ? -1 : 1;
331
+ const duration = Math.max(Number(this.getAttribute('duration') || 12), 1);
332
+ const speed = Number(this.getAttribute('speed') || 1);
333
+ const resolvedSpeed = Number.isFinite(speed) && speed > 0 ? speed : 1;
334
+ const resolvedDuration = duration / resolvedSpeed;
335
+ this.baseDurationSec = resolvedDuration;
336
+ const size = this.getAttribute('size') || 'm';
337
+ const variant = this.getAttribute('variant') || 'default';
338
+ const alignY = this.getAttribute('align-y') || 'center';
339
+ const fadeEdges = this.getBool('fade-edges');
340
+ const fadeWidth = Math.max(Number(this.getAttribute('fade-width') || 48), 0);
341
+ const edgePaddingRaw = this.getAttribute('edge-padding');
342
+ const edgePadding = edgePaddingRaw !== null && edgePaddingRaw !== ''
343
+ ? Math.max(Number(edgePaddingRaw), 0)
344
+ : fadeEdges
345
+ ? fadeWidth
346
+ : 0;
347
+ const gap = Math.max(Number(this.getAttribute('gap') || 24), 0);
348
+ this.gap = gap;
349
+ const rowCountRaw = Math.round(Number(this.getAttribute('row-count') || 1));
350
+ const rows = Number.isFinite(rowCountRaw) ? Math.min(Math.max(rowCountRaw, 1), 8) : 1;
351
+ const rowGap = Math.max(Number(this.getAttribute('row-gap') || 12), 0);
352
+ const scrollVelocity = this.getBool('scroll-velocity');
353
+ this.scrollVelocityEnabled = scrollVelocity;
354
+ const pauseOnHover = this.getBool('pause-on-hover', true);
355
+ const bordered = this.hasAttribute('bordered') ? this.getBool('bordered') : variant === 'outlined';
356
+ const ariaLabel = this.getAttribute('aria-label') || 'Scrolling content';
357
+ if (this.containerEl) {
358
+ this.containerEl.className = [
359
+ CLASS,
360
+ `${CLASS}--size-${size}`,
361
+ `${CLASS}--variant-${variant}`,
362
+ `${CLASS}--align-${alignY}`,
363
+ pauseOnHover && !scrollVelocity && `${CLASS}--pause-on-hover`,
364
+ fadeEdges && `${CLASS}--fade-edges`,
365
+ direction === 'right' && `${CLASS}--right`,
366
+ rows > 1 && `${CLASS}--multi-row`,
367
+ scrollVelocity && `${CLASS}--scroll-velocity`,
368
+ bordered && `${CLASS}--bordered`,
369
+ ]
370
+ .filter(Boolean)
371
+ .join(' ');
372
+ this.containerEl.style.setProperty('--db-marquee-duration', `${resolvedDuration}s`);
373
+ this.containerEl.style.setProperty('--db-marquee-gap', `${gap}px`);
374
+ this.containerEl.style.setProperty('--db-marquee-row-gap', `${rowGap}px`);
375
+ this.containerEl.style.setProperty('--db-marquee-fade-width', `${fadeWidth}px`);
376
+ this.containerEl.style.setProperty('--db-marquee-edge-padding', `${edgePadding}px`);
377
+ this.containerEl.setAttribute('role', 'region');
378
+ this.containerEl.setAttribute('aria-label', ariaLabel);
379
+ }
380
+ if (this.srOnlyEl) {
381
+ const srText = items
382
+ .map((item) => this.itemToText(item))
383
+ .filter(Boolean)
384
+ .join(' • ');
385
+ this.srOnlyEl.textContent = srText || ariaLabel;
386
+ }
387
+ this.reconcileRows(rows, baseDir);
388
+ this.buildBase(items);
389
+ this.bindImageListeners();
390
+ this.measure();
391
+ if (scrollVelocity) {
392
+ this.startScrollEngine();
393
+ }
394
+ else {
395
+ this.stopScrollEngine();
396
+ }
397
+ }
398
+ }
399
+ if (!customElements.get('db-marquee'))
400
+ customElements.define('db-marquee', DbMarquee);
401
+
402
+ export { DbMarquee };
@@ -1 +1,94 @@
1
- const e=[1,2,3,4,5,6],t=["xs","s","m","l","xl"];class DbMasonry extends HTMLElement{static get observedAttributes(){return["images","columns","spacing","hover","clickable"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}get images(){try{const e=JSON.parse(this.getAttribute("images")||"[]");return Array.isArray(e)?e.filter(e=>e&&"string"==typeof e.src):[]}catch{return[]}}set images(e){this.setAttribute("images","string"==typeof e?e:JSON.stringify(e))}boolAttr(e,t=!0){if(!this.hasAttribute(e))return t;const s=this.getAttribute(e);return""===s||"true"===s}render(){const s=Number(this.getAttribute("columns")||3),n=e.includes(s)?s:3,i=this.getAttribute("spacing")||"m",a=t.includes(i)?i:"m",r=this.boolAttr("hover",!0),c=this.boolAttr("clickable",!0);this.className=["designbase-wc-masonry",`designbase-wc-masonry--columns-${n}`,`designbase-wc-masonry--spacing-${a}`,r&&"designbase-wc-masonry--hover",c&&"designbase-wc-masonry--clickable"].filter(Boolean).join(" ");const o=document.createDocumentFragment();this.images.forEach((e,t)=>{const s=document.createElement("figure");s.className="designbase-wc-masonry__item";const n=document.createElement("img");if(n.className="designbase-wc-masonry__image",n.src=e.thumbnail||e.src,n.alt=e.alt||e.title||`image-${t+1}`,n.loading="lazy",n.addEventListener("load",()=>{this.dispatchEvent(new CustomEvent("db-image-load",{detail:{image:e,index:t},bubbles:!0,composed:!0}))}),n.addEventListener("error",()=>{this.dispatchEvent(new CustomEvent("db-image-error",{detail:{image:e,index:t},bubbles:!0,composed:!0}))}),s.append(n),e.title||e.description){const t=document.createElement("figcaption");if(t.className="designbase-wc-masonry__overlay",e.title){const s=document.createElement("h3");s.className="designbase-wc-masonry__title",s.textContent=e.title,t.append(s)}if(e.description){const s=document.createElement("p");s.className="designbase-wc-masonry__description",s.textContent=e.description,t.append(s)}s.append(t)}c&&(s.tabIndex=0,s.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-image-click",{detail:{image:e,index:t},bubbles:!0,composed:!0}))})),o.append(s)}),this.replaceChildren(o)}}customElements.get("db-masonry")||customElements.define("db-masonry",DbMasonry);export{DbMasonry};
1
+ const COLUMNS = [1, 2, 3, 4, 5, 6];
2
+ const SPACINGS = ['xs', 's', 'm', 'l', 'xl'];
3
+ class DbMasonry extends HTMLElement {
4
+ static get observedAttributes() {
5
+ return ['images', 'columns', 'spacing', 'hover', 'clickable'];
6
+ }
7
+ connectedCallback() {
8
+ this.render();
9
+ }
10
+ attributeChangedCallback() {
11
+ this.render();
12
+ }
13
+ get images() {
14
+ try {
15
+ const parsed = JSON.parse(this.getAttribute('images') || '[]');
16
+ return Array.isArray(parsed) ? parsed.filter((item) => item && typeof item.src === 'string') : [];
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ }
22
+ set images(value) {
23
+ this.setAttribute('images', typeof value === 'string' ? value : JSON.stringify(value));
24
+ }
25
+ boolAttr(name, fallback = true) {
26
+ if (!this.hasAttribute(name))
27
+ return fallback;
28
+ const value = this.getAttribute(name);
29
+ return value === '' || value === 'true';
30
+ }
31
+ render() {
32
+ const columnsRaw = Number(this.getAttribute('columns') || 3);
33
+ const columns = COLUMNS.includes(columnsRaw) ? columnsRaw : 3;
34
+ const spacing = this.getAttribute('spacing') || 'm';
35
+ const normalizedSpacing = SPACINGS.includes(spacing) ? spacing : 'm';
36
+ const hover = this.boolAttr('hover', true);
37
+ const clickable = this.boolAttr('clickable', true);
38
+ this.className = [
39
+ 'designbase-wc-masonry',
40
+ `designbase-wc-masonry--columns-${columns}`,
41
+ `designbase-wc-masonry--spacing-${normalizedSpacing}`,
42
+ hover && 'designbase-wc-masonry--hover',
43
+ clickable && 'designbase-wc-masonry--clickable',
44
+ ]
45
+ .filter(Boolean)
46
+ .join(' ');
47
+ const fragment = document.createDocumentFragment();
48
+ this.images.forEach((item, index) => {
49
+ const el = document.createElement('figure');
50
+ el.className = 'designbase-wc-masonry__item';
51
+ const img = document.createElement('img');
52
+ img.className = 'designbase-wc-masonry__image';
53
+ img.src = item.thumbnail || item.src;
54
+ img.alt = item.alt || item.title || `image-${index + 1}`;
55
+ img.loading = 'lazy';
56
+ img.addEventListener('load', () => {
57
+ this.dispatchEvent(new CustomEvent('db-image-load', { detail: { image: item, index }, bubbles: true, composed: true }));
58
+ });
59
+ img.addEventListener('error', () => {
60
+ this.dispatchEvent(new CustomEvent('db-image-error', { detail: { image: item, index }, bubbles: true, composed: true }));
61
+ });
62
+ el.append(img);
63
+ if (item.title || item.description) {
64
+ const overlay = document.createElement('figcaption');
65
+ overlay.className = 'designbase-wc-masonry__overlay';
66
+ if (item.title) {
67
+ const title = document.createElement('h3');
68
+ title.className = 'designbase-wc-masonry__title';
69
+ title.textContent = item.title;
70
+ overlay.append(title);
71
+ }
72
+ if (item.description) {
73
+ const desc = document.createElement('p');
74
+ desc.className = 'designbase-wc-masonry__description';
75
+ desc.textContent = item.description;
76
+ overlay.append(desc);
77
+ }
78
+ el.append(overlay);
79
+ }
80
+ if (clickable) {
81
+ el.tabIndex = 0;
82
+ el.addEventListener('click', () => {
83
+ this.dispatchEvent(new CustomEvent('db-image-click', { detail: { image: item, index }, bubbles: true, composed: true }));
84
+ });
85
+ }
86
+ fragment.append(el);
87
+ });
88
+ this.replaceChildren(fragment);
89
+ }
90
+ }
91
+ if (!customElements.get('db-masonry'))
92
+ customElements.define('db-masonry', DbMasonry);
93
+
94
+ export { DbMasonry };