@designbasekorea/ui-wc 0.9.0 → 0.9.3

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 (118) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +18 -2
  3. package/dist/esm/components/date-picker-utils.js +1 -1
  4. package/dist/esm/components/db-accordion.js +1 -1
  5. package/dist/esm/components/db-achievement-list.js +1 -1
  6. package/dist/esm/components/db-achievement-modal.js +1 -1
  7. package/dist/esm/components/db-activity-heatmap.js +1 -1
  8. package/dist/esm/components/db-ad-banner.js +1 -1
  9. package/dist/esm/components/db-ai-chat-list.js +1 -1
  10. package/dist/esm/components/db-ai-chat.js +1 -1
  11. package/dist/esm/components/db-alert.d.ts +12 -0
  12. package/dist/esm/components/db-alert.js +1 -1
  13. package/dist/esm/components/db-app-badge.js +1 -1
  14. package/dist/esm/components/db-audio-player.js +1 -1
  15. package/dist/esm/components/db-backdrop.js +1 -1
  16. package/dist/esm/components/db-boarding-pass-stack.js +1 -1
  17. package/dist/esm/components/db-boarding-pass.js +1 -1
  18. package/dist/esm/components/db-bottom-navigation.js +1 -1
  19. package/dist/esm/components/db-bottom-sheet.js +1 -1
  20. package/dist/esm/components/db-breadcrumbs.js +1 -1
  21. package/dist/esm/components/db-calendar.js +1 -1
  22. package/dist/esm/components/db-card.js +1 -1
  23. package/dist/esm/components/db-carousel.d.ts +3 -3
  24. package/dist/esm/components/db-carousel.js +1 -1
  25. package/dist/esm/components/db-chat-composer.js +1 -1
  26. package/dist/esm/components/db-chat-room.js +1 -1
  27. package/dist/esm/components/db-chat-widget.js +1 -1
  28. package/dist/esm/components/db-chip.js +1 -1
  29. package/dist/esm/components/db-code-block.js +1 -1
  30. package/dist/esm/components/db-color-picker.js +1 -1
  31. package/dist/esm/components/db-confirm.js +1 -1
  32. package/dist/esm/components/db-context-bar.js +1 -1
  33. package/dist/esm/components/db-context-menu.js +1 -1
  34. package/dist/esm/components/db-credit-card.js +1 -1
  35. package/dist/esm/components/db-cursor-follower.js +1 -1
  36. package/dist/esm/components/db-date-picker.js +1 -1
  37. package/dist/esm/components/db-design-canvas.js +1 -1
  38. package/dist/esm/components/db-design-toolbar.js +1 -1
  39. package/dist/esm/components/db-download-progress.js +1 -1
  40. package/dist/esm/components/db-drawer.d.ts +1 -0
  41. package/dist/esm/components/db-drawer.js +1 -1
  42. package/dist/esm/components/db-dropdown.js +1 -1
  43. package/dist/esm/components/db-dropzone.js +1 -1
  44. package/dist/esm/components/db-email-verify.d.ts +0 -6
  45. package/dist/esm/components/db-email-verify.js +1 -1
  46. package/dist/esm/components/db-file-uploader.js +1 -1
  47. package/dist/esm/components/db-flight-status.js +1 -1
  48. package/dist/esm/components/db-footer.js +1 -1
  49. package/dist/esm/components/db-gantt.js +1 -1
  50. package/dist/esm/components/db-image-list.d.ts +2 -0
  51. package/dist/esm/components/db-image-list.js +1 -1
  52. package/dist/esm/components/db-indicator.js +1 -1
  53. package/dist/esm/components/db-input.js +1 -1
  54. package/dist/esm/components/db-kanban.js +1 -1
  55. package/dist/esm/components/db-lightbox.d.ts +4 -0
  56. package/dist/esm/components/db-lightbox.js +1 -1
  57. package/dist/esm/components/db-markdown-editor.js +1 -1
  58. package/dist/esm/components/db-marquee.d.ts +2 -10
  59. package/dist/esm/components/db-marquee.js +1 -1
  60. package/dist/esm/components/db-modal.js +1 -1
  61. package/dist/esm/components/db-navbar.js +1 -1
  62. package/dist/esm/components/db-node.js +1 -1
  63. package/dist/esm/components/db-onboarding-modal.js +1 -1
  64. package/dist/esm/components/db-otp-input.js +1 -1
  65. package/dist/esm/components/db-page-header.d.ts +8 -0
  66. package/dist/esm/components/db-page-header.js +1 -1
  67. package/dist/esm/components/db-pagination.js +1 -1
  68. package/dist/esm/components/db-phone-verify.js +1 -1
  69. package/dist/esm/components/db-popover.js +1 -1
  70. package/dist/esm/components/db-progress-step.js +1 -1
  71. package/dist/esm/components/db-receipt.js +1 -1
  72. package/dist/esm/components/db-resizable-panels.js +1 -1
  73. package/dist/esm/components/db-search-bar.js +1 -1
  74. package/dist/esm/components/db-section-testimonials.d.ts +1 -4
  75. package/dist/esm/components/db-section-testimonials.js +1 -1
  76. package/dist/esm/components/db-segment-control.d.ts +0 -4
  77. package/dist/esm/components/db-segment-control.js +1 -1
  78. package/dist/esm/components/db-select.js +1 -1
  79. package/dist/esm/components/db-share.js +1 -1
  80. package/dist/esm/components/db-sidebar.js +1 -1
  81. package/dist/esm/components/db-split-view.js +1 -1
  82. package/dist/esm/components/db-stepper.d.ts +0 -3
  83. package/dist/esm/components/db-stepper.js +1 -1
  84. package/dist/esm/components/db-table.js +1 -1
  85. package/dist/esm/components/db-testimonial.js +1 -1
  86. package/dist/esm/components/db-time-picker.js +1 -1
  87. package/dist/esm/components/db-tooltip.js +1 -1
  88. package/dist/esm/components/db-top-banner.js +1 -1
  89. package/dist/esm/components/db-tutorial.js +1 -1
  90. package/dist/esm/components/db-two-factor.d.ts +4 -10
  91. package/dist/esm/components/db-two-factor.js +1 -1
  92. package/dist/esm/components/db-video-player.js +1 -1
  93. package/dist/esm/components/db-voice-control.js +1 -1
  94. package/dist/esm/components/db-youtube-player.js +1 -1
  95. package/dist/esm/components/input-validation.js +1 -1
  96. package/dist/esm/components/paper-card.js +1 -1
  97. package/dist/esm/core/focus-trap.js +1 -1
  98. package/dist/esm/core/host-class.js +1 -0
  99. package/dist/esm/core/i18n-copy.js +1 -0
  100. package/dist/esm/core/i18n-en.js +1 -0
  101. package/dist/esm/core/i18n.d.ts +65 -1
  102. package/dist/esm/core/i18n.js +1 -1
  103. package/dist/esm/core/theme-scope.js +1 -0
  104. package/dist/esm/index.d.ts +1 -1
  105. package/dist/esm/index.js +1 -1
  106. package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-AGE5BHQ4.js +1 -0
  107. package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-GM4TCAP5.js +1 -0
  108. package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-GSN4JZN7.js +1 -0
  109. package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-VJJVQGV4.js +1 -0
  110. package/dist/esm/node_modules/@designbasekorea/carousel/dist/chunks/chunk-YWLMJGDH.js +1 -0
  111. package/dist/index.cjs +1 -1
  112. package/dist/index.css +1 -1
  113. package/dist/index.d.ts +100 -41
  114. package/dist/index.esm.js +1 -1
  115. package/dist/react/index.cjs +1 -1
  116. package/dist/react/index.d.ts +12 -0
  117. package/dist/react/index.esm.js +1 -1
  118. package/package.json +5 -3
@@ -1 +1 @@
1
- import"./db-button.js";import"./db-input.js";import"./db-card.js";import"./db-badge.js";const clamp=(t,e,i)=>Math.min(i,Math.max(e,t)),num=(t,e)=>"number"==typeof t&&Number.isFinite(t)?t:e,clone=t=>t.map(t=>({...t})),same=(t,e)=>t.length===e.length&&t.every((t,i)=>Object.keys({...t,...e[i]}).every(s=>t[s]===e[i][s]));class DbDesignCanvas extends HTMLElement{constructor(){super(...arguments),this.data=[],this.selected=null,this.view={x:0,y:0,zoom:1},this.objectElements=new Map,this.slots=new Map,this.pointers=new Map,this.space=!1,this.hand=!1,this.creationTool="frame",this.shapeTool="rectangle",this.componentTool="button",this.past=[],this.future=[],this.reading=new Set,this.generation=0,this.beginTextEdit=t=>{if(this.hand||this.space)return;const e=t.target.closest("[data-node-id]")||document.elementFromPoint(t.clientX,t.clientY)?.closest("[data-node-id]"),i=this.data.find(t=>t.id===e?.dataset.nodeId);if(!i||"text"!==i.type||i.locked||this.readOnly)return;const s=e.querySelector(".designbase-wc-design-canvas__text");if(s.isContentEditable)return;t.preventDefault(),this.editing={id:i.id,before:this.nodes},s.contentEditable="plaintext-only",s.setAttribute("role","textbox"),s.setAttribute("aria-label","텍스트 편집"),s.tabIndex=0,s.focus();const finish=(t=!1)=>{if(this.editing?.id!==i.id)return;const e=this.editing.before;this.editing=void 0,s.removeEventListener("input",input),s.removeEventListener("keydown",key),s.removeEventListener("blur",blur),s.contentEditable="false",s.removeAttribute("role"),s.removeAttribute("aria-label"),s.tabIndex=-1,t?this.data=e:same(e,this.data)||this.remember(e),this.emit(t?"text-cancel":"text")},input=()=>{const t=(s.innerText||"").slice(0,1e4);this.data=this.data.map(e=>e.id===i.id?{...e,text:t}:e),this.emit("text-input")},key=t=>{("Escape"===t.key||"Enter"===t.key&&(t.ctrlKey||t.metaKey))&&(t.preventDefault(),t.stopPropagation(),finish("Escape"===t.key),s.blur(),this.surface?.focus({preventScroll:!0}))},blur=()=>finish();s.addEventListener("input",input),s.addEventListener("keydown",key),s.addEventListener("blur",blur)},this.toolAction=t=>{t.stopPropagation();const{id:e,optionId:i}=t.detail;if("select"===e||"pan"===e)return this.hand="pan"===e,void this.drawViewport();if("image"===e)return void(this.readOnly||this.input?.click());if("undo"===e)return void this.undo();if("redo"===e)return void this.redo();if("zoom-in"===e)return void this.zoomTo(1.2*this.view.zoom);if("zoom-out"===e)return void this.zoomTo(this.view.zoom/1.2);if("fit"===e||"zoom"===e&&"fit"===i)return void this.fitToContent();if("zoom"===e){const t=Number(i)/100;return void(t>0&&this.zoomTo(t))}if(this.readOnly)return;if(["frame","text","shape","component"].includes(e))return void this.insertNode(e,i);const s=this.data.find(t=>t.id===this.selected);if(s&&(!s.locked||"lock"===e)&&("duplicate"!==e||!(s.slot||this.data.length>=100))){if(this.remember(this.data),"duplicate"===e){const t={...s,id:crypto.randomUUID(),name:`${s.name} 복사`,x:s.x+24,y:s.y+24};this.data=[...this.data,t],this.select(t.id)}else if("delete"===e)this.data=this.data.filter(t=>t.id!==s.id),this.select(null);else if("arrange"===e){const t=this.data.filter(t=>t.id!==s.id);this.data="front"===i?[...t,s]:[s,...t]}else{const t={...s};if("lock"===e&&(t.locked=!s.locked),"rotate"===e&&(t.rotation="reset"===i?0:(s.rotation||0)+("left"===i?-90:90)),"flip-x"===e&&(t.flipX=!s.flipX),"flip-y"===e&&(t.flipY=!s.flipY),("align"===e||e.startsWith("align-"))&&this.surface){const o="align"===e?i:e.slice(6),n=-this.view.x/this.view.zoom,a=-this.view.y/this.view.zoom,r=this.surface.clientWidth/this.view.zoom/2,h=this.surface.clientHeight/this.view.zoom/2,d=(s.rotation||0)*Math.PI/180,l=Math.abs(s.width*Math.cos(d))+Math.abs(s.height*Math.sin(d)),c=Math.abs(s.width*Math.sin(d))+Math.abs(s.height*Math.cos(d));"center"===o&&(t.x=n-s.width/2),"middle"===o&&(t.y=a-s.height/2),"left"===o&&(t.x=n-r+(l-s.width)/2+24),"right"===o&&(t.x=n+r-(l+s.width)/2-24),"top"===o&&(t.y=a-h+(c-s.height)/2+24),"bottom"===o&&(t.y=a+h-(c+s.height)/2-24)}this.data=this.data.map(e=>e.id===s.id?t:e)}this.emit(e)}},this.onPointerDown=t=>{if(t.target.closest("db-tool-button,db-design-toolbar,db-context-bar"))return;if(0!==t.button&&1!==t.button)return;if(this.pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),"touch"===t.pointerType&&2===this.pointers.size){this.cancelGesture();const[e,i]=[...this.pointers.values()],s={clientX:(e.x+i.x)/2,clientY:(e.y+i.y)/2};return this.pinch={distance:Math.hypot(e.x-i.x,e.y-i.y),zoom:this.view.zoom,world:this.point(s)},void this.surface.setPointerCapture(t.pointerId)}if(this.editable(t))return;const e=t.target,i=e.closest("[data-handle]")?.dataset.handle,s=e.closest("[data-node-id]")?.dataset.nodeId;s&&this.select(s);const o=this.data.find(t=>t.id===this.selected),n=this.space||this.hand||1===t.button;if(!n&&!i&&!s)return this.surface.focus({preventScroll:!0}),this.surface.setPointerCapture(t.pointerId),void this.select(null);if((n||o&&!o.locked&&!this.readOnly)&&(n||!e.closest("db-button,button,a,input,textarea,select")||i)){if(t.preventDefault(),this.surface.focus({preventScroll:!0}),this.gesture={kind:n?"pan":"rotate"===i?"rotate":i?"resize":"move",pointer:t.pointerId,start:{x:t.clientX,y:t.clientY},viewport:this.viewport,node:o?{...o}:void 0,handle:i,before:this.nodes},"rotate"===this.gesture.kind&&o){const e=this.point(t);this.gesture.angle=Math.atan2(e.y-o.y-o.height/2,e.x-o.x-o.width/2)}this.surface.setPointerCapture(t.pointerId),this.drawViewport()}},this.onPointerMove=t=>{if(this.pointers.has(t.pointerId)&&this.pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this.pinch&&this.pointers.size>=2){const[t,e]=[...this.pointers.values()],i=this.surface.getBoundingClientRect();return this.view.zoom=clamp(this.pinch.zoom*Math.hypot(t.x-e.x,t.y-e.y)/Math.max(1,this.pinch.distance),this.minZoom,this.maxZoom),this.view.x=(t.x+e.x)/2-i.left-this.surface.clientWidth/2-this.pinch.world.x*this.view.zoom,this.view.y=(t.y+e.y)/2-i.top-this.surface.clientHeight/2-this.pinch.world.y*this.view.zoom,void this.emitViewport()}const e=this.gesture;if(!e||e.pointer!==t.pointerId)return;if("pan"===e.kind)return this.view.x=e.viewport.x+t.clientX-e.start.x,this.view.y=e.viewport.y+t.clientY-e.start.y,void this.emitViewport();const i=e.node;let s={...i};const o=(t.clientX-e.start.x)/e.viewport.zoom,n=(t.clientY-e.start.y)/e.viewport.zoom,a=(i.rotation||0)*Math.PI/180;if("move"===e.kind){const e=Number(this.getAttribute("snap-to-grid"))||0,round=i=>e>0&&!t.altKey?Math.round(i/e)*e:Math.round(i);s.x=round(i.x+o),s.y=round(i.y+n)}if("rotate"===e.kind){const o=this.point(t),n=Math.atan2(o.y-i.y-i.height/2,o.x-i.x-i.width/2),a=(i.rotation||0)+180*(n-e.angle)/Math.PI;s.rotation=t.shiftKey?15*Math.round(a/15):Math.round(a)}if("resize"===e.kind){const r=o*Math.cos(a)+n*Math.sin(a),h=-o*Math.sin(a)+n*Math.cos(a),d=e.handle;let l=clamp(i.width+(d.includes("e")?r:d.includes("w")?-r:0),16,8e3),c=clamp(i.height+(d.includes("s")?h:d.includes("n")?-h:0),16,8e3);if(t.shiftKey&&2===d.length){const t=Math.max(l/i.width,c/i.height),e=clamp(t,Math.max(16/i.width,16/i.height),Math.min(8e3/i.width,8e3/i.height));l=i.width*e,c=i.height*e}const u=(l-i.width)/2*(d.includes("w")?-1:d.includes("e")?1:0),p=(c-i.height)/2*(d.includes("n")?-1:d.includes("s")?1:0);s.width=Math.round(l),s.height=Math.round(c),s.x=i.x+i.width/2+u*Math.cos(a)-p*Math.sin(a)-s.width/2,s.y=i.y+i.height/2+u*Math.sin(a)+p*Math.cos(a)-s.height/2}this.data=this.data.map(t=>t.id===i.id?s:t),this.emit(e.kind)},this.onPointerUp=t=>{this.pointers.delete(t.pointerId),this.pointers.size<2&&(this.pinch=void 0);const e=this.gesture;e?.pointer===t.pointerId&&(this.gesture=void 0,"pan"===e.kind||same(e.before,this.data)||(this.remember(e.before),this.emit("commit"))),this.surface?.hasPointerCapture(t.pointerId)&&this.surface.releasePointerCapture(t.pointerId),this.drawViewport()},this.onPointerCancel=t=>{this.pointers.delete(t.pointerId),this.pinch=void 0,this.cancelGesture(),this.drawViewport()},this.onLostCapture=t=>{this.gesture?.pointer===t.pointerId&&this.cancelGesture()},this.onWheel=t=>{t.target.closest("db-context-bar")||this.gesture||this.pinch||(t.preventDefault(),t.ctrlKey||t.metaKey?this.zoomTo(this.view.zoom*Math.exp(.002*-t.deltaY),{x:t.clientX,y:t.clientY}):(this.view.x-=t.deltaX,this.view.y-=t.deltaY,this.emitViewport()))},this.onKeyUp=t=>{"Space"!==t.code&&" "!==t.key||(this.space=!1,this.drawViewport())},this.onBlur=()=>{this.space=!1,this.cancelGesture(),this.pointers.clear(),this.pinch=void 0,this.drawViewport()},this.onKeyDown=t=>{if(t.defaultPrevented||t.target.closest("db-tool-button,db-design-toolbar,db-context-bar"))return;if(this.editable(t))return;if("Space"===t.code||" "===t.key)return t.preventDefault(),this.space=!0,void this.drawViewport();if("Escape"===t.key)return t.preventDefault(),void(this.gesture?this.cancelGesture():this.select(null));if("h"!==t.key.toLowerCase()||t.ctrlKey||t.metaKey||(this.hand=!0,this.drawViewport()),"v"!==t.key.toLowerCase()||t.ctrlKey||t.metaKey||(this.hand=!1,this.drawViewport()),(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase())return t.preventDefault(),void(t.shiftKey?this.redo():this.undo());if((t.ctrlKey||t.metaKey)&&"y"===t.key.toLowerCase())return t.preventDefault(),void this.redo();if("+"===t.key||"="===t.key)return t.preventDefault(),void this.zoomTo(1.2*this.view.zoom);if("-"===t.key)return t.preventDefault(),void this.zoomTo(this.view.zoom/1.2);if("0"===t.key)return t.preventDefault(),void(t.shiftKey?this.zoomTo(1):this.fitToContent());if(!t.ctrlKey&&!t.metaKey&&!t.altKey){const e=t.key.toLowerCase();if(["t","r","o","l","f"].includes(e))return t.preventDefault(),void("t"===e?this.insertNode("text"):"f"===e?this.insertNode("frame","frame"):this.insertNode("shape","r"===e?"rectangle":"o"===e?"ellipse":"line"))}const e=this.data.find(t=>t.id===this.selected);if(e&&!e.locked&&!this.readOnly){if("Delete"===t.key||"Backspace"===t.key)return t.preventDefault(),this.remember(this.data),this.data=this.data.filter(t=>t.id!==e.id),this.select(null),void this.emit("delete");if(["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(t.key)){t.preventDefault();const i=t.shiftKey?10:1,s={...e},o=t.target.dataset.handle;this.remember(this.data),"rotate"===o?s.rotation=(e.rotation||0)+("ArrowLeft"===t.key||"ArrowDown"===t.key?-i:i):o?"ArrowLeft"===t.key||"ArrowRight"===t.key?s.width=clamp(e.width+("ArrowRight"===t.key?i:-i),16,8e3):s.height=clamp(e.height+("ArrowDown"===t.key?i:-i),16,8e3):(s.x+="ArrowRight"===t.key?i:"ArrowLeft"===t.key?-i:0,s.y+="ArrowDown"===t.key?i:"ArrowUp"===t.key?-i:0),this.data=this.data.map(t=>t.id===e.id?s:t),this.emit("keyboard")}}},this.onDragOver=t=>{!this.readOnly&&t.dataTransfer?.types.includes("Files")&&(t.preventDefault(),t.dataTransfer.dropEffect="copy",this.surface?.classList.add("designbase-wc-design-canvas__surface--drop"))},this.onDragLeave=t=>{this.surface?.contains(t.relatedTarget)||this.surface?.classList.remove("designbase-wc-design-canvas__surface--drop")},this.onDrop=t=>{t.preventDefault(),this.surface?.classList.remove("designbase-wc-design-canvas__surface--drop"),!this.readOnly&&t.dataTransfer&&this.importImages(Array.from(t.dataTransfer.files),this.point(t))}}static get observedAttributes(){return["nodes","selected-id","min-zoom","max-zoom","readonly","snap-to-grid","grid-size","show-grid","height","max-file-size","toolbar-mode"]}get nodes(){return clone(this.data)}set nodes(t){try{const e="string"==typeof t?JSON.parse(t):t;if(!Array.isArray(e))return;const i=new Set,s=e.filter(t=>t&&"string"==typeof t.id&&t.id&&!i.has(t.id)&&!!i.add(t.id)).slice(0,100).map(t=>this.normalizeNode(t));if(same(this.data,s))return;this.data.length&&!this.gesture&&this.remember(this.data),this.data=s,this.data.some(t=>t.id===this.selected)||(this.selected=null),this.draw()}catch{}}get selectedId(){return this.selected}set selectedId(t){this.selected=this.data.some(e=>e.id===t)?t:null,this.drawSelection()}get viewport(){return{...this.view}}set viewport(t){this.view={x:num(t?.x,0),y:num(t?.y,0),zoom:clamp(num(t?.zoom,1),this.minZoom,this.maxZoom)},this.drawViewport()}get minZoom(){return clamp(Number(this.getAttribute("min-zoom"))||.1,.05,4)}get maxZoom(){return Math.max(this.minZoom,Math.min(8,Number(this.getAttribute("max-zoom"))||4))}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",t)}connectedCallback(){this.generation++,this.ensureStructure(),this.collectSlots(),this.observer=new MutationObserver(()=>{this.collectSlots(),this.draw()}),this.observer.observe(this,{childList:!0}),this.resizeObserver=new ResizeObserver(()=>this.drawViewport()),this.resizeObserver.observe(this.surface),window.addEventListener("keyup",this.onKeyUp),window.addEventListener("blur",this.onBlur),this.draw()}disconnectedCallback(){this.generation++,this.editing=void 0,this.observer?.disconnect(),this.resizeObserver?.disconnect(),window.removeEventListener("keyup",this.onKeyUp),window.removeEventListener("blur",this.onBlur),this.cancelGesture(),this.space=!1,this.pointers.clear(),this.pinch=void 0;for(const t of this.reading)t.abort();this.reading.clear()}attributeChangedCallback(t){"nodes"===t?this.nodes=this.getAttribute("nodes")||"[]":"selected-id"===t?this.selectedId=this.getAttribute("selected-id"):("readonly"===t&&this.cancelGesture(),this.view.zoom=clamp(this.view.zoom,this.minZoom,this.maxZoom),this.draw())}normalizeNode(t){return{...t,type:["frame","image","text","shape","component"].includes(t.type)?t.type:"frame",name:"string"==typeof t.name?t.name:"요소",x:num(t.x,0),y:num(t.y,0),width:clamp(num(t.width,320),16,8e3),height:clamp(num(t.height,240),16,8e3),rotation:num(t.rotation,0),flipX:!0===t.flipX,flipY:!0===t.flipY,opacity:clamp(num(t.opacity,1),0,1),radius:clamp(num(t.radius,0),0,4e3),text:"string"==typeof t.text?t.text.slice(0,1e4):"",fontSize:clamp(num(t.fontSize,24),8,240),fontWeight:clamp(num(t.fontWeight,400),100,900),textColor:t.textColor||"#191919",textAlign:["left","center","right"].includes(t.textAlign||"")?t.textAlign:"left",shape:["rectangle","ellipse","triangle","line"].includes(t.shape||"")?t.shape:"rectangle",component:["button","input","card","badge"].includes(t.component||"")?t.component:"button",stroke:t.stroke||"#191919",strokeWidth:clamp(num(t.strokeWidth,1),0,24)}}ensureStructure(){if(this.surface)return;this.classList.add("designbase-wc-design-canvas"),this.setAttribute("role","region"),this.hasAttribute("aria-label")||this.setAttribute("aria-label","디자인 캔버스"),this.toolbar=document.createElement("db-design-toolbar"),this.toolbar.classList.add("designbase-wc-design-canvas__toolbar"),this.toolbar.setAttribute("aria-label","캔버스 도구"),this.toolbar.setAttribute("size","s"),this.toolbar.addEventListener("db-tool-action",this.toolAction),this.input=document.createElement("input"),this.input.type="file",this.input.multiple=!0,this.input.accept="image/png,image/jpeg,image/webp,image/gif",this.input.hidden=!0,this.input.addEventListener("change",()=>{this.importImages(Array.from(this.input.files||[])),this.input.value=""}),this.surface=document.createElement("div"),this.surface.className="designbase-wc-design-canvas__surface",this.surface.tabIndex=0,this.surface.setAttribute("aria-label","캔버스 작업 영역"),this.surface.setAttribute("aria-describedby",this.id?`${this.id}-help`:""),this.world=document.createElement("div"),this.world.className="designbase-wc-design-canvas__world",this.selection=document.createElement("div"),this.selection.className="designbase-wc-design-canvas__selection";for(const t of["nw","n","ne","e","se","s","sw","w","rotate"]){const e=document.createElement("div");e.dataset.handle=t,e.tabIndex=0,e.setAttribute("role","button"),e.setAttribute("aria-label","rotate"===t?"선택 요소 회전":`선택 요소 크기 조정 ${t}`),e.className="designbase-wc-design-canvas__handle",e.title="rotate"===t?"회전 · Shift: 15° 단위":"크기 조정 · Shift: 비율 유지",this.selection.append(e)}const t=document.createElement("span");t.className="designbase-wc-design-canvas__dimensions",this.selection.append(t),this.world.append(this.selection),this.contextBar=document.createElement("db-context-bar"),this.contextBar.classList.add("designbase-wc-design-canvas__context"),this.contextBar.setAttribute("size","s"),this.contextBar.setAttribute("tone","dark"),this.contextBar.setAttribute("placement","top-start"),this.contextBar.addEventListener("db-tool-action",this.toolAction),this.surface.append(this.world,this.contextBar),this.status=document.createElement("div"),this.status.className="designbase-wc-design-canvas__status",this.status.setAttribute("role","status"),this.status.setAttribute("aria-live","polite"),this.status.textContent="Space + 드래그 이동 · Ctrl/⌘ + 휠 확대 · 이미지 드롭",this.surface.addEventListener("pointerdown",this.onPointerDown),this.surface.addEventListener("pointermove",this.onPointerMove),this.surface.addEventListener("pointerup",this.onPointerUp),this.surface.addEventListener("pointercancel",this.onPointerCancel),this.surface.addEventListener("lostpointercapture",this.onLostCapture),this.surface.addEventListener("wheel",this.onWheel,{passive:!1}),this.surface.addEventListener("keydown",this.onKeyDown),this.surface.addEventListener("dblclick",this.beginTextEdit),this.surface.addEventListener("dragover",this.onDragOver),this.surface.addEventListener("dragleave",this.onDragLeave),this.surface.addEventListener("drop",this.onDrop),this.surface.addEventListener("dragstart",t=>t.preventDefault()),this.append(this.toolbar,this.surface,this.status,this.input)}collectSlots(){for(const t of Array.from(this.children))t instanceof HTMLElement&&t.hasAttribute("slot")&&this.slots.set(t.getAttribute("slot"),t)}draw(){if(this.world&&this.surface){if(this.style.height=this.getAttribute("height")||"560px",this.toolbar){const t="floating"===this.getAttribute("toolbar-mode");this.toolbar.toggleAttribute("movable",t),this.toolbar.classList.toggle("designbase-wc-design-canvas__toolbar--floating",t),t&&this.toolbar.parentElement!==this.surface&&this.surface.append(this.toolbar),t||this.toolbar.parentElement===this||this.insertBefore(this.toolbar,this.surface)}this.surface.classList.toggle("designbase-wc-design-canvas__surface--no-grid","false"===this.getAttribute("show-grid"));for(const[t,e]of this.objectElements)if(!this.data.some(e=>e.id===t)){const i=e.querySelector("[slot]");i instanceof HTMLElement&&this.slots.set(i.slot,i),e.remove(),this.objectElements.delete(t)}for(const t of this.data){let e=this.objectElements.get(t.id);if(e||(e=document.createElement("div"),e.className="designbase-wc-design-canvas__node",e.dataset.nodeId=t.id,e.tabIndex=-1,this.world.insertBefore(e,this.selection),this.objectElements.set(t.id,e)),e.style.zIndex=String(this.data.indexOf(t)),e.dataset.nodeType=t.type,e.setAttribute("aria-label",t.name),e.setAttribute("role","group"),Object.assign(e.style,{left:`${t.x}px`,top:`${t.y}px`,width:`${t.width}px`,height:`${t.height}px`,transform:`rotate(${t.rotation||0}deg) scale(${t.flipX?-1:1}, ${t.flipY?-1:1})`,opacity:String(t.opacity??1),borderRadius:`${t.radius||0}px`,background:t.fill||"var(--db-surface-base)"}),"text"===t.type)this.drawText(e,t);else if("shape"===t.type)this.drawShape(e,t);else if("component"===t.type)this.drawComponent(e,t);else if("image"===t.type){let i=e.querySelector("img");i||(i=document.createElement("img"),i.draggable=!1,e.replaceChildren(i)),i.alt=t.name;const s=t.src||"",o=/^(data:image\/(png|jpe?g|webp|gif);base64,|blob:|https?:\/\/|\/[^/])/i.test(s);o&&i.getAttribute("src")!==s&&(i.src=s),o||i.removeAttribute("src")}else if(t.slot&&this.slots.has(t.slot)){const i=this.slots.get(t.slot);i.parentElement!==e&&e.replaceChildren(i)}else t.slot||e.textContent===t.name||(e.textContent=t.name)}this.drawViewport(),this.drawSelection()}}drawText(t,e){let i=t.querySelector(".designbase-wc-design-canvas__text");i||(i=document.createElement("div"),i.className="designbase-wc-design-canvas__text",t.replaceChildren(i)),t.style.background="transparent",t.style.overflow="visible",i.isContentEditable||i.textContent===e.text||(i.textContent=e.text||""),Object.assign(i.style,{fontSize:`${e.fontSize}px`,fontWeight:String(e.fontWeight),color:e.textColor,textAlign:e.textAlign})}drawShape(t,e){t.style.background="transparent";const i="http://www.w3.org/2000/svg",s=e.shape||"rectangle";if(t.dataset.shape!==s||!t.querySelector("svg")){t.dataset.shape=s;const e=document.createElementNS(i,"svg");e.setAttribute("viewBox","0 0 100 100"),e.setAttribute("preserveAspectRatio","none"),e.setAttribute("aria-hidden","true");const o=document.createElementNS(i,"ellipse"===s?"ellipse":"rectangle"===s?"rect":"line"===s?"line":"polygon");e.append(o),t.replaceChildren(e)}const o=t.querySelector("svg").firstElementChild;"rectangle"===s&&(o.setAttribute("x","1"),o.setAttribute("y","1"),o.setAttribute("width","98"),o.setAttribute("height","98"),o.setAttribute("rx",String((e.radius||0)/e.width*100)),o.setAttribute("ry",String((e.radius||0)/e.height*100))),"ellipse"===s&&(o.setAttribute("cx","50"),o.setAttribute("cy","50"),o.setAttribute("rx","49"),o.setAttribute("ry","49")),"triangle"===s&&o.setAttribute("points","50,1 99,99 1,99"),"line"===s&&(o.setAttribute("x1","0"),o.setAttribute("y1","50"),o.setAttribute("x2","100"),o.setAttribute("y2","50")),o.setAttribute("fill","line"===s?"none":e.fill||"#dce8f5"),o.setAttribute("stroke",e.stroke||"#191919"),o.setAttribute("stroke-width",String(e.strokeWidth??1)),o.setAttribute("vector-effect","non-scaling-stroke")}drawComponent(t,e){const i=e.component||"button";let s=t.querySelector(".designbase-wc-design-canvas__component");if(!s||s.dataset.kind!==i){s=document.createElement("div"),s.className="designbase-wc-design-canvas__component",s.inert=!0,s.dataset.kind=i;const e=document.createElement("input"===i?"db-input":"card"===i?"db-card":"badge"===i?"db-badge":"db-button");if("button"===i&&e.setAttribute("variant","primary"),"card"===i){const t=document.createElement("h3");t.dataset.componentText="";const i=document.createElement("p");i.textContent="내용을 선택하고 나만의 화면으로 구성하세요.",e.append(t,i)}s.append(e),t.replaceChildren(s)}const o=s.firstElementChild;if("input"===i){const t=o.querySelector("input");t&&Object.assign(t.style,{color:e.textColor,fontSize:`${e.fontSize}px`})}if("input"===i)o.setAttribute("placeholder",e.text||"내용을 입력하세요");else if("card"===i){const t=o.querySelector("[data-component-text]");t.textContent=e.text||"새 카드",t.style.fontSize=`${e.fontSize}px`}else o.textContent!==e.text&&(o.textContent=e.text||("badge"===i?"Badge":"Button"));Object.assign(o.style,{width:"100%",height:"100%",boxSizing:"border-box",borderRadius:`${e.radius||0}px`,color:e.textColor,fontSize:`${e.fontSize}px`,background:e.fill||"#ffffff"})}drawViewport(){if(!this.world||!this.surface)return;this.world.style.transform=`translate(${this.surface.clientWidth/2+this.view.x}px, ${this.surface.clientHeight/2+this.view.y}px) scale(${this.view.zoom})`,this.style.setProperty("--db-canvas-inverse-zoom",String(1/this.view.zoom));const t=Math.max(4,Number(this.getAttribute("grid-size"))||24)*this.view.zoom;this.surface.style.backgroundSize=`${t}px ${t}px`,this.surface.style.backgroundPosition=`${this.surface.clientWidth/2+this.view.x}px ${this.surface.clientHeight/2+this.view.y}px`,this.surface.classList.toggle("designbase-wc-design-canvas__surface--pan",this.space||this.hand||"pan"===this.gesture?.kind),this.syncToolbar(),this.positionContext()}drawSelection(){if(!this.selection)return;const t=this.data.find(t=>t.id===this.selected);if(this.selection.hidden=!t,this.syncContext(),this.positionContext(),t){Object.assign(this.selection.style,{left:`${t.x}px`,top:`${t.y}px`,width:`${t.width}px`,height:`${t.height}px`,transform:`rotate(${t.rotation||0}deg)`}),this.selection.classList.toggle("designbase-wc-design-canvas__selection--locked",Boolean(t.locked||this.readOnly)),this.selection.querySelector(".designbase-wc-design-canvas__dimensions").textContent=`${Math.round(t.width)} × ${Math.round(t.height)} · ${Math.round(t.rotation||0)}°`;for(const e of Array.from(this.selection.querySelectorAll("[data-handle]")))e.tabIndex=t.locked||this.readOnly?-1:0,e.setAttribute("aria-disabled",String(!!t.locked||this.readOnly))}}syncToolbar(){if(!this.toolbar)return;const t=Math.round(100*this.view.zoom),e=[{id:"select",label:"선택 도구",icon:"navigation",shortcut:"V",active:!this.hand},{id:"pan",label:"화면 이동",icon:"move",shortcut:"H / Space",active:this.hand},{id:"creation-separator",type:"separator"},{id:"frame",label:"프레임 추가",icon:"file-new",disabled:this.readOnly||this.data.length>=100,menuTrigger:"long-press",value:this.creationTool,options:[{id:"frame",label:"빈 프레임 추가",icon:"file-new"},{id:"note",label:"메모 추가",icon:"documentation"}]},{id:"text",label:"텍스트 추가",icon:"text",shortcut:"T",disabled:this.readOnly||this.data.length>=100},{id:"shape",label:"도형 추가",icon:"shapes",value:this.shapeTool,disabled:this.readOnly||this.data.length>=100,options:[{id:"rectangle",label:"사각형",icon:"shape-rectangle",shortcut:"R"},{id:"ellipse",label:"타원",icon:"shape-ellipse",shortcut:"O"},{id:"triangle",label:"삼각형",icon:"shape-triangle"},{id:"line",label:"선",icon:"shape-line",shortcut:"L"}]},{id:"component",label:"컴포넌트 추가",icon:"package",value:this.componentTool,disabled:this.readOnly||this.data.length>=100,options:[{id:"button",label:"버튼",icon:"package"},{id:"input",label:"텍스트 입력",icon:"documentation"},{id:"card",label:"카드",icon:"file-new"},{id:"badge",label:"배지",icon:"package"}]},{id:"image",label:"이미지 추가",icon:"gallery",disabled:this.readOnly||this.data.length>=100},{id:"history-separator",type:"separator"},{id:"undo",label:"실행 취소",icon:"arrow-back-up",shortcut:"⌘/Ctrl+Z",disabled:!this.past.length||this.readOnly},{id:"redo",label:"다시 실행",icon:"arrow-back-up",shortcut:"⌘/Ctrl+Shift+Z",disabled:!this.future.length||this.readOnly},{id:"zoom-separator",type:"separator"},{id:"zoom-out",label:"축소",icon:"zoom-out",shortcut:"−"},{id:"zoom",label:"확대율",text:`${t}%`,value:String(t),options:[...[25,50,100,200,400].filter(t=>t/100>=this.minZoom&&t/100<=this.maxZoom).map(t=>({id:String(t),label:`${t}%`})),{id:"fit",label:"전체 보기",icon:"expand",shortcut:"0"}]},{id:"zoom-in",label:"확대",icon:"zoom-in",shortcut:"+"},{id:"fit",label:"전체 보기",icon:"expand",shortcut:"0"}];this.toolbar.items=e}syncContext(){if(!this.contextBar)return;const t=this.data.find(t=>t.id===this.selected);if(this.contextBar.hidden=!t,!t)return;const e=this.readOnly||!!t.locked;this.contextBar.items=[...t.slot?[]:[{id:"duplicate",label:"요소 복제",icon:"copy",disabled:e||this.data.length>=100}],{id:"lock",label:t.locked?"잠금 해제":"요소 잠그기",icon:t.locked?"unlock":"lock",active:t.locked,disabled:this.readOnly},{id:"transform-separator",type:"separator"},{id:"rotate",label:"회전",icon:"refresh",disabled:e,options:[{id:"reset",label:"회전 초기화"},{id:"left",label:"왼쪽으로 90° 회전"},{id:"right",label:"오른쪽으로 90° 회전"}]},{id:"flip-x",label:"좌우 반전",icon:"filp-horizontal",active:!!t.flipX,disabled:e},{id:"flip-y",label:"상하 반전",icon:"filp-vertical",active:!!t.flipY,disabled:e},{id:"alignment-separator",type:"separator"},...[["left","화면 왼쪽에 정렬","layout-align-left"],["center","가로 중앙 정렬","layout-align-center"],["right","화면 오른쪽에 정렬","layout-align-right"],["top","화면 위쪽에 정렬","layout-align-top"],["middle","세로 중앙 정렬","layout-align-middle"],["bottom","화면 아래쪽에 정렬","layout-align-bottom"]].map(([t,i,s])=>({id:`align-${t}`,label:i,icon:s,disabled:e})),{id:"layer-separator",type:"separator"},{id:"arrange",label:"레이어 순서",icon:"layer",disabled:e,options:[{id:"front",label:"맨 앞으로 가져오기"},{id:"back",label:"맨 뒤로 보내기"}]},{id:"delete",label:"요소 삭제",icon:"trash",shortcut:"Delete",disabled:e}]}insertNode(t,e){if(this.readOnly||this.data.length>=100)return;this.remember(this.data),"frame"===t&&(this.creationTool=e||this.creationTool),"shape"===t&&(this.shapeTool=e||this.shapeTool),"component"===t&&(this.componentTool=e||this.componentTool);const i="frame"===t&&"note"===this.creationTool;let s=320,o=240,n=i?"새로운 메모":"새 프레임",a="";if(i&&(s=220,o=180),"text"===t&&(s=280,o=100,n="텍스트",a="새로운 아이디어"),"shape"===t&&(s=200,o="line"===this.shapeTool?24:160,n={rectangle:"사각형",ellipse:"타원",triangle:"삼각형",line:"선"}[this.shapeTool]),"component"===t){const t={button:[160,44,"Button"],input:[240,44,"내용을 입력하세요"],card:[280,180,"새 카드"],badge:[100,32,"Badge"]};[s,o,a]=t[this.componentTool]||t.button,n=`${a} 컴포넌트`}const r=this.normalizeNode({id:crypto.randomUUID(),type:t,name:n,x:-this.view.x/this.view.zoom-s/2,y:-this.view.y/this.view.zoom-o/2,width:s,height:o,radius:"component"===t?10:i?12:0,fill:"shape"===t?"#dce8f5":"component"===t&&"button"===this.componentTool?"#191919":i?"#edf2e9":"#ffffff",text:a,fontSize:"component"===t?"card"===this.componentTool?22:14:24,textColor:"component"===t&&"button"===this.componentTool?"#ffffff":"#191919",shape:this.shapeTool,component:this.componentTool});this.data=[...this.data,r],this.select(r.id),this.emit("create")}positionContext(){const t=this.data.find(t=>t.id===this.selected);if(!t||!this.surface||!this.contextBar)return;const e=this.contextBar.offsetWidth||200,i=this.contextBar.offsetHeight||40,s=(t.rotation||0)*Math.PI/180,o=(Math.abs(t.width*Math.sin(s))+Math.abs(t.height*Math.cos(s)))/2*this.view.zoom,n=this.surface.clientWidth/2+this.view.x+(t.x+t.width/2)*this.view.zoom,a=this.surface.clientHeight/2+this.view.y+(t.y+t.height/2)*this.view.zoom,r=a-o-i-44,h=a+o+36,d=r>=12?r:h+i<=this.surface.clientHeight-12?h:12;this.contextBar.style.left=`${clamp(n,e/2+12,Math.max(e/2+12,this.surface.clientWidth-e/2-12))}px`,this.contextBar.style.top=`${clamp(d,12,Math.max(12,this.surface.clientHeight-i-12))}px`}select(t){t!==this.selected&&(this.selected=t,this.drawSelection(),this.dispatchEvent(new CustomEvent("db-selection-change",{detail:{selectedId:t,node:this.data.find(e=>e.id===t)||null},bubbles:!0,composed:!0})))}emit(t){this.draw(),this.dispatchEvent(new CustomEvent("db-nodes-change",{detail:{nodes:this.nodes,selectedId:this.selected,reason:t},bubbles:!0,composed:!0}))}emitViewport(){this.drawViewport(),this.dispatchEvent(new CustomEvent("db-viewport-change",{detail:this.viewport,bubbles:!0,composed:!0}))}remember(t){this.past.push(clone(t)),this.past.length>40&&this.past.shift(),this.future=[]}undo(){!this.readOnly&&this.past.length&&(this.cancelGesture(),this.future.push(this.nodes),this.data=this.past.pop(),this.select(this.data.some(t=>t.id===this.selected)?this.selected:this.data[0]?.id||null),this.emit("undo"))}redo(){!this.readOnly&&this.future.length&&(this.cancelGesture(),this.past.push(this.nodes),this.data=this.future.pop(),this.select(this.data.some(t=>t.id===this.selected)?this.selected:this.data[0]?.id||null),this.emit("redo"))}point(t){const e=this.surface.getBoundingClientRect();return{x:(t.clientX-e.left-this.surface.clientWidth/2-this.view.x)/this.view.zoom,y:(t.clientY-e.top-this.surface.clientHeight/2-this.view.y)/this.view.zoom}}zoomTo(t,e){if(!this.surface)return;const i=this.surface.getBoundingClientRect(),s=e||{x:i.left+i.width/2,y:i.top+i.height/2},o=this.point({clientX:s.x,clientY:s.y});this.view.zoom=clamp(t,this.minZoom,this.maxZoom),this.view.x=s.x-i.left-this.surface.clientWidth/2-o.x*this.view.zoom,this.view.y=s.y-i.top-this.surface.clientHeight/2-o.y*this.view.zoom,this.emitViewport(),this.drawSelection()}fitToContent(){if(!this.surface)return;if(!this.data.length)return this.viewport={x:0,y:0,zoom:1},void this.emitViewport();const t=this.data.flatMap(t=>{const e=(t.rotation||0)*Math.PI/180,i=t.x+t.width/2,s=t.y+t.height/2;return[[-1,-1],[1,-1],[1,1],[-1,1]].map(([o,n])=>({x:i+o*t.width/2*Math.cos(e)-n*t.height/2*Math.sin(e),y:s+o*t.width/2*Math.sin(e)+n*t.height/2*Math.cos(e)}))}),e=Math.min(...t.map(t=>t.x)),i=Math.max(...t.map(t=>t.x)),s=Math.min(...t.map(t=>t.y)),o=Math.max(...t.map(t=>t.y)),n=clamp(Math.min((this.surface.clientWidth-96)/Math.max(16,i-e),(this.surface.clientHeight-96)/Math.max(16,o-s)),this.minZoom,Math.min(1,this.maxZoom));this.view={zoom:n,x:-(e+i)/2*n,y:-(s+o)/2*n},this.emitViewport(),this.drawSelection()}editable(t){return t.composedPath().some(t=>t instanceof HTMLElement&&(t.isContentEditable||/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName)))}cancelGesture(){const t=this.gesture;this.gesture=void 0,t&&"pan"!==t.kind&&(this.data=t.before,this.isConnected&&this.emit("cancel")),t&&this.surface?.hasPointerCapture(t.pointer)&&this.surface.releasePointerCapture(t.pointer)}async importImages(t,e){if(this.readOnly)return;const i=e||{x:-this.view.x/this.view.zoom,y:-this.view.y/this.view.zoom},s=this.generation,o=[];let n=0;const a=Math.max(1,Number(this.getAttribute("max-file-size"))||10485760);for(const e of t.slice(0,12))if(!["image/png","image/jpeg","image/webp","image/gif"].includes(e.type)||e.size>a||this.data.length+o.length>=100)n++;else try{const t=await new Promise((t,i)=>{const s=new FileReader;this.reading.add(s),s.onload=()=>{this.reading.delete(s),t(String(s.result))},s.onerror=s.onabort=()=>{this.reading.delete(s),i(new Error("이미지를 읽지 못했습니다."))},s.readAsDataURL(e)}),n=await new Promise((e,i)=>{const s=new Image;s.onload=()=>e({width:s.naturalWidth,height:s.naturalHeight}),s.onerror=i,s.src=t});if(!this.isConnected||s!==this.generation)return;const a=Math.min(1,480/Math.max(n.width,n.height));o.push({id:crypto.randomUUID(),type:"image",name:e.name,src:t,x:i.x+24*o.length-n.width*a/2,y:i.y+24*o.length-n.height*a/2,width:Math.max(16,n.width*a),height:Math.max(16,n.height*a),rotation:0,opacity:1,radius:0})}catch{n++}this.isConnected&&s===this.generation&&(o.length&&(this.remember(this.data),this.data=[...this.data,...o.map(t=>this.normalizeNode(t))],this.select(o[o.length-1].id),this.emit("import")),this.status&&(this.status.textContent=`${o.length}개 이미지 추가${n||t.length>12?` · ${n+Math.max(0,t.length-12)}개 제외 (PNG/JPG/WebP/GIF, 파일당 ${Math.round(a/1024/1024)}MB 이하)`:""}`))}}customElements.get("db-design-canvas")||customElements.define("db-design-canvas",DbDesignCanvas);export{DbDesignCanvas};
1
+ import{dbText as t,dbFormat as e}from"../core/i18n.js";import"./db-button.js";import"./db-input.js";import"./db-card.js";import"./db-badge.js";const clamp=(t,e,i)=>Math.min(i,Math.max(e,t)),num=(t,e)=>"number"==typeof t&&Number.isFinite(t)?t:e,clone=t=>t.map(t=>({...t})),same=(t,e)=>t.length===e.length&&t.every((t,i)=>Object.keys({...t,...e[i]}).every(s=>t[s]===e[i][s]));class DbDesignCanvas extends HTMLElement{constructor(){super(...arguments),this.data=[],this.selected=null,this.view={x:0,y:0,zoom:1},this.objectElements=new Map,this.slots=new Map,this.pointers=new Map,this.space=!1,this.hand=!1,this.creationTool="frame",this.shapeTool="rectangle",this.componentTool="button",this.past=[],this.future=[],this.reading=new Set,this.generation=0,this.beginTextEdit=e=>{if(this.hand||this.space)return;const i=e.target.closest("[data-node-id]")||document.elementFromPoint(e.clientX,e.clientY)?.closest("[data-node-id]"),s=this.data.find(t=>t.id===i?.dataset.nodeId);if(!s||"text"!==s.type||s.locked||this.readOnly)return;const o=i.querySelector(".designbase-wc-design-canvas__text");if(o.isContentEditable)return;e.preventDefault(),this.editing={id:s.id,before:this.nodes},o.contentEditable="plaintext-only",o.setAttribute("role","textbox"),o.setAttribute("aria-label",t("텍스트 편집")),o.tabIndex=0,o.focus();const finish=(t=!1)=>{if(this.editing?.id!==s.id)return;const e=this.editing.before;this.editing=void 0,o.removeEventListener("input",input),o.removeEventListener("keydown",key),o.removeEventListener("blur",blur),o.contentEditable="false",o.removeAttribute("role"),o.removeAttribute("aria-label"),o.tabIndex=-1,t?this.data=e:same(e,this.data)||this.remember(e),this.emit(t?"text-cancel":"text")},input=()=>{const t=(o.innerText||"").slice(0,1e4);this.data=this.data.map(e=>e.id===s.id?{...e,text:t}:e),this.emit("text-input")},key=t=>{("Escape"===t.key||"Enter"===t.key&&(t.ctrlKey||t.metaKey))&&(t.preventDefault(),t.stopPropagation(),finish("Escape"===t.key),o.blur(),this.surface?.focus({preventScroll:!0}))},blur=()=>finish();o.addEventListener("input",input),o.addEventListener("keydown",key),o.addEventListener("blur",blur)},this.toolAction=t=>{t.stopPropagation();const{id:e,optionId:i}=t.detail;if("select"===e||"pan"===e)return this.hand="pan"===e,void this.drawViewport();if("image"===e)return void(this.readOnly||this.input?.click());if("undo"===e)return void this.undo();if("redo"===e)return void this.redo();if("zoom-in"===e)return void this.zoomTo(1.2*this.view.zoom);if("zoom-out"===e)return void this.zoomTo(this.view.zoom/1.2);if("fit"===e||"zoom"===e&&"fit"===i)return void this.fitToContent();if("zoom"===e){const t=Number(i)/100;return void(t>0&&this.zoomTo(t))}if(this.readOnly)return;if(["frame","text","shape","component"].includes(e))return void this.insertNode(e,i);const s=this.data.find(t=>t.id===this.selected);if(s&&(!s.locked||"lock"===e)&&("duplicate"!==e||!(s.slot||this.data.length>=100))){if(this.remember(this.data),"duplicate"===e){const t={...s,id:crypto.randomUUID(),name:`${s.name} 복사`,x:s.x+24,y:s.y+24};this.data=[...this.data,t],this.select(t.id)}else if("delete"===e)this.data=this.data.filter(t=>t.id!==s.id),this.select(null);else if("arrange"===e){const t=this.data.filter(t=>t.id!==s.id);this.data="front"===i?[...t,s]:[s,...t]}else{const t={...s};if("lock"===e&&(t.locked=!s.locked),"rotate"===e&&(t.rotation="reset"===i?0:(s.rotation||0)+("left"===i?-90:90)),"flip-x"===e&&(t.flipX=!s.flipX),"flip-y"===e&&(t.flipY=!s.flipY),("align"===e||e.startsWith("align-"))&&this.surface){const o="align"===e?i:e.slice(6),n=-this.view.x/this.view.zoom,a=-this.view.y/this.view.zoom,r=this.surface.clientWidth/this.view.zoom/2,h=this.surface.clientHeight/this.view.zoom/2,d=(s.rotation||0)*Math.PI/180,l=Math.abs(s.width*Math.cos(d))+Math.abs(s.height*Math.sin(d)),c=Math.abs(s.width*Math.sin(d))+Math.abs(s.height*Math.cos(d));"center"===o&&(t.x=n-s.width/2),"middle"===o&&(t.y=a-s.height/2),"left"===o&&(t.x=n-r+(l-s.width)/2+24),"right"===o&&(t.x=n+r-(l+s.width)/2-24),"top"===o&&(t.y=a-h+(c-s.height)/2+24),"bottom"===o&&(t.y=a+h-(c+s.height)/2-24)}this.data=this.data.map(e=>e.id===s.id?t:e)}this.emit(e)}},this.onPointerDown=t=>{if(t.target.closest("db-tool-button,db-design-toolbar,db-context-bar"))return;if(0!==t.button&&1!==t.button)return;if(this.pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),"touch"===t.pointerType&&2===this.pointers.size){this.cancelGesture();const[e,i]=[...this.pointers.values()],s={clientX:(e.x+i.x)/2,clientY:(e.y+i.y)/2};return this.pinch={distance:Math.hypot(e.x-i.x,e.y-i.y),zoom:this.view.zoom,world:this.point(s)},void this.surface.setPointerCapture(t.pointerId)}if(this.editable(t))return;const e=t.target,i=e.closest("[data-handle]")?.dataset.handle,s=e.closest("[data-node-id]")?.dataset.nodeId;s&&this.select(s);const o=this.data.find(t=>t.id===this.selected),n=this.space||this.hand||1===t.button;if(!n&&!i&&!s)return this.surface.focus({preventScroll:!0}),this.surface.setPointerCapture(t.pointerId),void this.select(null);if((n||o&&!o.locked&&!this.readOnly)&&(n||!e.closest("db-button,button,a,input,textarea,select")||i)){if(t.preventDefault(),this.surface.focus({preventScroll:!0}),this.gesture={kind:n?"pan":"rotate"===i?"rotate":i?"resize":"move",pointer:t.pointerId,start:{x:t.clientX,y:t.clientY},viewport:this.viewport,node:o?{...o}:void 0,handle:i,before:this.nodes},"rotate"===this.gesture.kind&&o){const e=this.point(t);this.gesture.angle=Math.atan2(e.y-o.y-o.height/2,e.x-o.x-o.width/2)}this.surface.setPointerCapture(t.pointerId),this.drawViewport()}},this.onPointerMove=t=>{if(this.pointers.has(t.pointerId)&&this.pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this.pinch&&this.pointers.size>=2){const[t,e]=[...this.pointers.values()],i=this.surface.getBoundingClientRect();return this.view.zoom=clamp(this.pinch.zoom*Math.hypot(t.x-e.x,t.y-e.y)/Math.max(1,this.pinch.distance),this.minZoom,this.maxZoom),this.view.x=(t.x+e.x)/2-i.left-this.surface.clientWidth/2-this.pinch.world.x*this.view.zoom,this.view.y=(t.y+e.y)/2-i.top-this.surface.clientHeight/2-this.pinch.world.y*this.view.zoom,void this.emitViewport()}const e=this.gesture;if(!e||e.pointer!==t.pointerId)return;if("pan"===e.kind)return this.view.x=e.viewport.x+t.clientX-e.start.x,this.view.y=e.viewport.y+t.clientY-e.start.y,void this.emitViewport();const i=e.node;let s={...i};const o=(t.clientX-e.start.x)/e.viewport.zoom,n=(t.clientY-e.start.y)/e.viewport.zoom,a=(i.rotation||0)*Math.PI/180;if("move"===e.kind){const e=Number(this.getAttribute("snap-to-grid"))||0,round=i=>e>0&&!t.altKey?Math.round(i/e)*e:Math.round(i);s.x=round(i.x+o),s.y=round(i.y+n)}if("rotate"===e.kind){const o=this.point(t),n=Math.atan2(o.y-i.y-i.height/2,o.x-i.x-i.width/2),a=(i.rotation||0)+180*(n-e.angle)/Math.PI;s.rotation=t.shiftKey?15*Math.round(a/15):Math.round(a)}if("resize"===e.kind){const r=o*Math.cos(a)+n*Math.sin(a),h=-o*Math.sin(a)+n*Math.cos(a),d=e.handle;let l=clamp(i.width+(d.includes("e")?r:d.includes("w")?-r:0),16,8e3),c=clamp(i.height+(d.includes("s")?h:d.includes("n")?-h:0),16,8e3);if(t.shiftKey&&2===d.length){const t=Math.max(l/i.width,c/i.height),e=clamp(t,Math.max(16/i.width,16/i.height),Math.min(8e3/i.width,8e3/i.height));l=i.width*e,c=i.height*e}const u=(l-i.width)/2*(d.includes("w")?-1:d.includes("e")?1:0),p=(c-i.height)/2*(d.includes("n")?-1:d.includes("s")?1:0);s.width=Math.round(l),s.height=Math.round(c),s.x=i.x+i.width/2+u*Math.cos(a)-p*Math.sin(a)-s.width/2,s.y=i.y+i.height/2+u*Math.sin(a)+p*Math.cos(a)-s.height/2}this.data=this.data.map(t=>t.id===i.id?s:t),this.emit(e.kind)},this.onPointerUp=t=>{this.pointers.delete(t.pointerId),this.pointers.size<2&&(this.pinch=void 0);const e=this.gesture;e?.pointer===t.pointerId&&(this.gesture=void 0,"pan"===e.kind||same(e.before,this.data)||(this.remember(e.before),this.emit("commit"))),this.surface?.hasPointerCapture(t.pointerId)&&this.surface.releasePointerCapture(t.pointerId),this.drawViewport()},this.onPointerCancel=t=>{this.pointers.delete(t.pointerId),this.pinch=void 0,this.cancelGesture(),this.drawViewport()},this.onLostCapture=t=>{this.gesture?.pointer===t.pointerId&&this.cancelGesture()},this.onWheel=t=>{t.target.closest("db-context-bar")||this.gesture||this.pinch||(t.preventDefault(),t.ctrlKey||t.metaKey?this.zoomTo(this.view.zoom*Math.exp(.002*-t.deltaY),{x:t.clientX,y:t.clientY}):(this.view.x-=t.deltaX,this.view.y-=t.deltaY,this.emitViewport()))},this.onKeyUp=t=>{"Space"!==t.code&&" "!==t.key||(this.space=!1,this.drawViewport())},this.onBlur=()=>{this.space=!1,this.cancelGesture(),this.pointers.clear(),this.pinch=void 0,this.drawViewport()},this.onKeyDown=t=>{if(t.defaultPrevented||t.target.closest("db-tool-button,db-design-toolbar,db-context-bar"))return;if(this.editable(t))return;if("Space"===t.code||" "===t.key)return t.preventDefault(),this.space=!0,void this.drawViewport();if("Escape"===t.key)return t.preventDefault(),void(this.gesture?this.cancelGesture():this.select(null));if("h"!==t.key.toLowerCase()||t.ctrlKey||t.metaKey||(this.hand=!0,this.drawViewport()),"v"!==t.key.toLowerCase()||t.ctrlKey||t.metaKey||(this.hand=!1,this.drawViewport()),(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase())return t.preventDefault(),void(t.shiftKey?this.redo():this.undo());if((t.ctrlKey||t.metaKey)&&"y"===t.key.toLowerCase())return t.preventDefault(),void this.redo();if("+"===t.key||"="===t.key)return t.preventDefault(),void this.zoomTo(1.2*this.view.zoom);if("-"===t.key)return t.preventDefault(),void this.zoomTo(this.view.zoom/1.2);if("0"===t.key)return t.preventDefault(),void(t.shiftKey?this.zoomTo(1):this.fitToContent());if(!t.ctrlKey&&!t.metaKey&&!t.altKey){const e=t.key.toLowerCase();if(["t","r","o","l","f"].includes(e))return t.preventDefault(),void("t"===e?this.insertNode("text"):"f"===e?this.insertNode("frame","frame"):this.insertNode("shape","r"===e?"rectangle":"o"===e?"ellipse":"line"))}const e=this.data.find(t=>t.id===this.selected);if(e&&!e.locked&&!this.readOnly){if("Delete"===t.key||"Backspace"===t.key)return t.preventDefault(),this.remember(this.data),this.data=this.data.filter(t=>t.id!==e.id),this.select(null),void this.emit("delete");if(["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(t.key)){t.preventDefault();const i=t.shiftKey?10:1,s={...e},o=t.target.dataset.handle;this.remember(this.data),"rotate"===o?s.rotation=(e.rotation||0)+("ArrowLeft"===t.key||"ArrowDown"===t.key?-i:i):o?"ArrowLeft"===t.key||"ArrowRight"===t.key?s.width=clamp(e.width+("ArrowRight"===t.key?i:-i),16,8e3):s.height=clamp(e.height+("ArrowDown"===t.key?i:-i),16,8e3):(s.x+="ArrowRight"===t.key?i:"ArrowLeft"===t.key?-i:0,s.y+="ArrowDown"===t.key?i:"ArrowUp"===t.key?-i:0),this.data=this.data.map(t=>t.id===e.id?s:t),this.emit("keyboard")}}},this.onDragOver=t=>{!this.readOnly&&t.dataTransfer?.types.includes("Files")&&(t.preventDefault(),t.dataTransfer.dropEffect="copy",this.surface?.classList.add("designbase-wc-design-canvas__surface--drop"))},this.onDragLeave=t=>{this.surface?.contains(t.relatedTarget)||this.surface?.classList.remove("designbase-wc-design-canvas__surface--drop")},this.onDrop=t=>{t.preventDefault(),this.surface?.classList.remove("designbase-wc-design-canvas__surface--drop"),!this.readOnly&&t.dataTransfer&&this.importImages(Array.from(t.dataTransfer.files),this.point(t))}}static get observedAttributes(){return["nodes","selected-id","min-zoom","max-zoom","readonly","snap-to-grid","grid-size","show-grid","height","max-file-size","toolbar-mode"]}get nodes(){return clone(this.data)}set nodes(t){try{const e="string"==typeof t?JSON.parse(t):t;if(!Array.isArray(e))return;const i=new Set,s=e.filter(t=>t&&"string"==typeof t.id&&t.id&&!i.has(t.id)&&!!i.add(t.id)).slice(0,100).map(t=>this.normalizeNode(t));if(same(this.data,s))return;this.data.length&&!this.gesture&&this.remember(this.data),this.data=s,this.data.some(t=>t.id===this.selected)||(this.selected=null),this.draw()}catch{}}get selectedId(){return this.selected}set selectedId(t){this.selected=this.data.some(e=>e.id===t)?t:null,this.drawSelection()}get viewport(){return{...this.view}}set viewport(t){this.view={x:num(t?.x,0),y:num(t?.y,0),zoom:clamp(num(t?.zoom,1),this.minZoom,this.maxZoom)},this.drawViewport()}get minZoom(){return clamp(Number(this.getAttribute("min-zoom"))||.1,.05,4)}get maxZoom(){return Math.max(this.minZoom,Math.min(8,Number(this.getAttribute("max-zoom"))||4))}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){this.toggleAttribute("readonly",t)}connectedCallback(){this.generation++,this.ensureStructure(),this.collectSlots(),this.observer=new MutationObserver(()=>{this.collectSlots(),this.draw()}),this.observer.observe(this,{childList:!0}),this.resizeObserver=new ResizeObserver(()=>this.drawViewport()),this.resizeObserver.observe(this.surface),window.addEventListener("keyup",this.onKeyUp),window.addEventListener("blur",this.onBlur),this.draw()}disconnectedCallback(){this.generation++,this.editing=void 0,this.observer?.disconnect(),this.resizeObserver?.disconnect(),window.removeEventListener("keyup",this.onKeyUp),window.removeEventListener("blur",this.onBlur),this.cancelGesture(),this.space=!1,this.pointers.clear(),this.pinch=void 0;for(const t of this.reading)t.abort();this.reading.clear()}attributeChangedCallback(t){"nodes"===t?this.nodes=this.getAttribute("nodes")||"[]":"selected-id"===t?this.selectedId=this.getAttribute("selected-id"):("readonly"===t&&this.cancelGesture(),this.view.zoom=clamp(this.view.zoom,this.minZoom,this.maxZoom),this.draw())}normalizeNode(t){return{...t,type:["frame","image","text","shape","component"].includes(t.type)?t.type:"frame",name:"string"==typeof t.name?t.name:"요소",x:num(t.x,0),y:num(t.y,0),width:clamp(num(t.width,320),16,8e3),height:clamp(num(t.height,240),16,8e3),rotation:num(t.rotation,0),flipX:!0===t.flipX,flipY:!0===t.flipY,opacity:clamp(num(t.opacity,1),0,1),radius:clamp(num(t.radius,0),0,4e3),text:"string"==typeof t.text?t.text.slice(0,1e4):"",fontSize:clamp(num(t.fontSize,24),8,240),fontWeight:clamp(num(t.fontWeight,400),100,900),textColor:t.textColor||"#191919",textAlign:["left","center","right"].includes(t.textAlign||"")?t.textAlign:"left",shape:["rectangle","ellipse","triangle","line"].includes(t.shape||"")?t.shape:"rectangle",component:["button","input","card","badge"].includes(t.component||"")?t.component:"button",stroke:t.stroke||"#191919",strokeWidth:clamp(num(t.strokeWidth,1),0,24)}}ensureStructure(){if(this.surface)return;this.classList.add("designbase-wc-design-canvas"),this.setAttribute("role","region"),this.hasAttribute("aria-label")||this.setAttribute("aria-label",t("디자인 캔버스")),this.toolbar=document.createElement("db-design-toolbar"),this.toolbar.classList.add("designbase-wc-design-canvas__toolbar"),this.toolbar.setAttribute("aria-label",t("캔버스 도구")),this.toolbar.setAttribute("size","s"),this.toolbar.addEventListener("db-tool-action",this.toolAction),this.input=document.createElement("input"),this.input.type="file",this.input.multiple=!0,this.input.accept="image/png,image/jpeg,image/webp,image/gif",this.input.hidden=!0,this.input.addEventListener("change",()=>{this.importImages(Array.from(this.input.files||[])),this.input.value=""}),this.surface=document.createElement("div"),this.surface.className="designbase-wc-design-canvas__surface",this.surface.tabIndex=0,this.surface.setAttribute("aria-label",t("캔버스 작업 영역")),this.surface.setAttribute("aria-describedby",this.id?`${this.id}-help`:""),this.world=document.createElement("div"),this.world.className="designbase-wc-design-canvas__world",this.selection=document.createElement("div"),this.selection.className="designbase-wc-design-canvas__selection";for(const t of["nw","n","ne","e","se","s","sw","w","rotate"]){const e=document.createElement("div");e.dataset.handle=t,e.tabIndex=0,e.setAttribute("role","button"),e.setAttribute("aria-label","rotate"===t?"선택 요소 회전":`선택 요소 크기 조정 ${t}`),e.className="designbase-wc-design-canvas__handle",e.title="rotate"===t?"회전 · Shift: 15° 단위":"크기 조정 · Shift: 비율 유지",this.selection.append(e)}const e=document.createElement("span");e.className="designbase-wc-design-canvas__dimensions",this.selection.append(e),this.world.append(this.selection),this.contextBar=document.createElement("db-context-bar"),this.contextBar.classList.add("designbase-wc-design-canvas__context"),this.contextBar.setAttribute("size","s"),this.contextBar.setAttribute("tone","dark"),this.contextBar.setAttribute("placement","top-start"),this.contextBar.addEventListener("db-tool-action",this.toolAction),this.surface.append(this.world,this.contextBar),this.status=document.createElement("div"),this.status.className="designbase-wc-design-canvas__status",this.status.setAttribute("role","status"),this.status.setAttribute("aria-live","polite"),this.status.textContent=t("Space + 드래그 이동 · Ctrl/⌘ + 휠 확대 · 이미지 드롭"),this.surface.addEventListener("pointerdown",this.onPointerDown),this.surface.addEventListener("pointermove",this.onPointerMove),this.surface.addEventListener("pointerup",this.onPointerUp),this.surface.addEventListener("pointercancel",this.onPointerCancel),this.surface.addEventListener("lostpointercapture",this.onLostCapture),this.surface.addEventListener("wheel",this.onWheel,{passive:!1}),this.surface.addEventListener("keydown",this.onKeyDown),this.surface.addEventListener("dblclick",this.beginTextEdit),this.surface.addEventListener("dragover",this.onDragOver),this.surface.addEventListener("dragleave",this.onDragLeave),this.surface.addEventListener("drop",this.onDrop),this.surface.addEventListener("dragstart",t=>t.preventDefault()),this.append(this.toolbar,this.surface,this.status,this.input)}collectSlots(){for(const t of Array.from(this.children))t instanceof HTMLElement&&t.hasAttribute("slot")&&this.slots.set(t.getAttribute("slot"),t)}draw(){if(this.world&&this.surface){if(this.style.height=this.getAttribute("height")||"560px",this.toolbar){const t="floating"===this.getAttribute("toolbar-mode");this.toolbar.toggleAttribute("movable",t),this.toolbar.classList.toggle("designbase-wc-design-canvas__toolbar--floating",t),t&&this.toolbar.parentElement!==this.surface&&this.surface.append(this.toolbar),t||this.toolbar.parentElement===this||this.insertBefore(this.toolbar,this.surface)}this.surface.classList.toggle("designbase-wc-design-canvas__surface--no-grid","false"===this.getAttribute("show-grid"));for(const[t,e]of this.objectElements)if(!this.data.some(e=>e.id===t)){const i=e.querySelector("[slot]");i instanceof HTMLElement&&this.slots.set(i.slot,i),e.remove(),this.objectElements.delete(t)}for(const t of this.data){let e=this.objectElements.get(t.id);if(e||(e=document.createElement("div"),e.className="designbase-wc-design-canvas__node",e.dataset.nodeId=t.id,e.tabIndex=-1,this.world.insertBefore(e,this.selection),this.objectElements.set(t.id,e)),e.style.zIndex=String(this.data.indexOf(t)),e.dataset.nodeType=t.type,e.setAttribute("aria-label",t.name),e.setAttribute("role","group"),Object.assign(e.style,{left:`${t.x}px`,top:`${t.y}px`,width:`${t.width}px`,height:`${t.height}px`,transform:`rotate(${t.rotation||0}deg) scale(${t.flipX?-1:1}, ${t.flipY?-1:1})`,opacity:String(t.opacity??1),borderRadius:`${t.radius||0}px`,background:t.fill||"var(--db-surface-base)"}),"text"===t.type)this.drawText(e,t);else if("shape"===t.type)this.drawShape(e,t);else if("component"===t.type)this.drawComponent(e,t);else if("image"===t.type){let i=e.querySelector("img");i||(i=document.createElement("img"),i.draggable=!1,e.replaceChildren(i)),i.alt=t.name;const s=t.src||"",o=/^(data:image\/(png|jpe?g|webp|gif);base64,|blob:|https?:\/\/|\/[^/])/i.test(s);o&&i.getAttribute("src")!==s&&(i.src=s),o||i.removeAttribute("src")}else if(t.slot&&this.slots.has(t.slot)){const i=this.slots.get(t.slot);i.parentElement!==e&&e.replaceChildren(i)}else t.slot||e.textContent===t.name||(e.textContent=t.name)}this.drawViewport(),this.drawSelection()}}drawText(t,e){let i=t.querySelector(".designbase-wc-design-canvas__text");i||(i=document.createElement("div"),i.className="designbase-wc-design-canvas__text",t.replaceChildren(i)),t.style.background="transparent",t.style.overflow="visible",i.isContentEditable||i.textContent===e.text||(i.textContent=e.text||""),Object.assign(i.style,{fontSize:`${e.fontSize}px`,fontWeight:String(e.fontWeight),color:e.textColor,textAlign:e.textAlign})}drawShape(t,e){t.style.background="transparent";const i="http://www.w3.org/2000/svg",s=e.shape||"rectangle";if(t.dataset.shape!==s||!t.querySelector("svg")){t.dataset.shape=s;const e=document.createElementNS(i,"svg");e.setAttribute("viewBox","0 0 100 100"),e.setAttribute("preserveAspectRatio","none"),e.setAttribute("aria-hidden","true");const o=document.createElementNS(i,"ellipse"===s?"ellipse":"rectangle"===s?"rect":"line"===s?"line":"polygon");e.append(o),t.replaceChildren(e)}const o=t.querySelector("svg").firstElementChild;"rectangle"===s&&(o.setAttribute("x","1"),o.setAttribute("y","1"),o.setAttribute("width","98"),o.setAttribute("height","98"),o.setAttribute("rx",String((e.radius||0)/e.width*100)),o.setAttribute("ry",String((e.radius||0)/e.height*100))),"ellipse"===s&&(o.setAttribute("cx","50"),o.setAttribute("cy","50"),o.setAttribute("rx","49"),o.setAttribute("ry","49")),"triangle"===s&&o.setAttribute("points","50,1 99,99 1,99"),"line"===s&&(o.setAttribute("x1","0"),o.setAttribute("y1","50"),o.setAttribute("x2","100"),o.setAttribute("y2","50")),o.setAttribute("fill","line"===s?"none":e.fill||"#dce8f5"),o.setAttribute("stroke",e.stroke||"#191919"),o.setAttribute("stroke-width",String(e.strokeWidth??1)),o.setAttribute("vector-effect","non-scaling-stroke")}drawComponent(e,i){const s=i.component||"button";let o=e.querySelector(".designbase-wc-design-canvas__component");if(!o||o.dataset.kind!==s){o=document.createElement("div"),o.className="designbase-wc-design-canvas__component",o.inert=!0,o.dataset.kind=s;const i=document.createElement("input"===s?"db-input":"card"===s?"db-card":"badge"===s?"db-badge":"db-button");if("button"===s&&i.setAttribute("variant","primary"),"card"===s){const e=document.createElement("h3");e.dataset.componentText="";const s=document.createElement("p");s.textContent=t("내용을 선택하고 나만의 화면으로 구성하세요."),i.append(e,s)}o.append(i),e.replaceChildren(o)}const n=o.firstElementChild;if("input"===s){const t=n.querySelector("input");t&&Object.assign(t.style,{color:i.textColor,fontSize:`${i.fontSize}px`})}if("input"===s)n.setAttribute("placeholder",i.text||"내용을 입력하세요");else if("card"===s){const t=n.querySelector("[data-component-text]");t.textContent=i.text||"새 카드",t.style.fontSize=`${i.fontSize}px`}else n.textContent!==i.text&&(n.textContent=i.text||("badge"===s?"Badge":"Button"));Object.assign(n.style,{width:"100%",height:"100%",boxSizing:"border-box",borderRadius:`${i.radius||0}px`,color:i.textColor,fontSize:`${i.fontSize}px`,background:i.fill||"#ffffff"})}drawViewport(){if(!this.world||!this.surface)return;this.world.style.transform=`translate(${this.surface.clientWidth/2+this.view.x}px, ${this.surface.clientHeight/2+this.view.y}px) scale(${this.view.zoom})`,this.style.setProperty("--db-canvas-inverse-zoom",String(1/this.view.zoom));const t=Math.max(4,Number(this.getAttribute("grid-size"))||24)*this.view.zoom;this.surface.style.backgroundSize=`${t}px ${t}px`,this.surface.style.backgroundPosition=`${this.surface.clientWidth/2+this.view.x}px ${this.surface.clientHeight/2+this.view.y}px`,this.surface.classList.toggle("designbase-wc-design-canvas__surface--pan",this.space||this.hand||"pan"===this.gesture?.kind),this.syncToolbar(),this.positionContext()}drawSelection(){if(!this.selection)return;const t=this.data.find(t=>t.id===this.selected);if(this.selection.hidden=!t,this.syncContext(),this.positionContext(),t){Object.assign(this.selection.style,{left:`${t.x}px`,top:`${t.y}px`,width:`${t.width}px`,height:`${t.height}px`,transform:`rotate(${t.rotation||0}deg)`}),this.selection.classList.toggle("designbase-wc-design-canvas__selection--locked",Boolean(t.locked||this.readOnly)),this.selection.querySelector(".designbase-wc-design-canvas__dimensions").textContent=`${Math.round(t.width)} × ${Math.round(t.height)} · ${Math.round(t.rotation||0)}°`;for(const e of Array.from(this.selection.querySelectorAll("[data-handle]")))e.tabIndex=t.locked||this.readOnly?-1:0,e.setAttribute("aria-disabled",String(!!t.locked||this.readOnly))}}syncToolbar(){if(!this.toolbar)return;const e=Math.round(100*this.view.zoom),i=[{id:"select",label:t("선택 도구"),icon:"navigation",shortcut:"V",active:!this.hand},{id:"pan",label:t("화면 이동"),icon:"move",shortcut:"H / Space",active:this.hand},{id:"creation-separator",type:"separator"},{id:"frame",label:t("프레임 추가"),icon:"file-new",disabled:this.readOnly||this.data.length>=100,menuTrigger:"long-press",value:this.creationTool,options:[{id:"frame",label:t("빈 프레임 추가"),icon:"file-new"},{id:"note",label:t("메모 추가"),icon:"documentation"}]},{id:"text",label:t("텍스트 추가"),icon:"text",shortcut:"T",disabled:this.readOnly||this.data.length>=100},{id:"shape",label:t("도형 추가"),icon:"shapes",value:this.shapeTool,disabled:this.readOnly||this.data.length>=100,options:[{id:"rectangle",label:t("사각형"),icon:"shape-rectangle",shortcut:"R"},{id:"ellipse",label:t("타원"),icon:"shape-ellipse",shortcut:"O"},{id:"triangle",label:t("삼각형"),icon:"shape-triangle"},{id:"line",label:t("선"),icon:"shape-line",shortcut:"L"}]},{id:"component",label:t("컴포넌트 추가"),icon:"package",value:this.componentTool,disabled:this.readOnly||this.data.length>=100,options:[{id:"button",label:t("버튼"),icon:"package"},{id:"input",label:t("텍스트 입력"),icon:"documentation"},{id:"card",label:t("카드"),icon:"file-new"},{id:"badge",label:t("배지"),icon:"package"}]},{id:"image",label:t("이미지 추가"),icon:"gallery",disabled:this.readOnly||this.data.length>=100},{id:"history-separator",type:"separator"},{id:"undo",label:t("실행 취소"),icon:"arrow-back-up",shortcut:"⌘/Ctrl+Z",disabled:!this.past.length||this.readOnly},{id:"redo",label:t("다시 실행"),icon:"arrow-back-up",shortcut:"⌘/Ctrl+Shift+Z",disabled:!this.future.length||this.readOnly},{id:"zoom-separator",type:"separator"},{id:"zoom-out",label:t("축소"),icon:"zoom-out",shortcut:"−"},{id:"zoom",label:t("확대율"),text:`${e}%`,value:String(e),options:[...[25,50,100,200,400].filter(t=>t/100>=this.minZoom&&t/100<=this.maxZoom).map(t=>({id:String(t),label:`${t}%`})),{id:"fit",label:t("전체 보기"),icon:"expand",shortcut:"0"}]},{id:"zoom-in",label:t("확대"),icon:"zoom-in",shortcut:"+"},{id:"fit",label:t("전체 보기"),icon:"expand",shortcut:"0"}];this.toolbar.items=i}syncContext(){if(!this.contextBar)return;const e=this.data.find(t=>t.id===this.selected);if(this.contextBar.hidden=!e,!e)return;const i=this.readOnly||!!e.locked;this.contextBar.items=[...e.slot?[]:[{id:"duplicate",label:t("요소 복제"),icon:"copy",disabled:i||this.data.length>=100}],{id:"lock",label:e.locked?"잠금 해제":"요소 잠그기",icon:e.locked?"unlock":"lock",active:e.locked,disabled:this.readOnly},{id:"transform-separator",type:"separator"},{id:"rotate",label:t("회전"),icon:"refresh",disabled:i,options:[{id:"reset",label:t("회전 초기화")},{id:"left",label:t("왼쪽으로 90° 회전")},{id:"right",label:t("오른쪽으로 90° 회전")}]},{id:"flip-x",label:t("좌우 반전"),icon:"filp-horizontal",active:!!e.flipX,disabled:i},{id:"flip-y",label:t("상하 반전"),icon:"filp-vertical",active:!!e.flipY,disabled:i},{id:"alignment-separator",type:"separator"},...[["left","화면 왼쪽에 정렬","layout-align-left"],["center","가로 중앙 정렬","layout-align-center"],["right","화면 오른쪽에 정렬","layout-align-right"],["top","화면 위쪽에 정렬","layout-align-top"],["middle","세로 중앙 정렬","layout-align-middle"],["bottom","화면 아래쪽에 정렬","layout-align-bottom"]].map(([t,e,s])=>({id:`align-${t}`,label:e,icon:s,disabled:i})),{id:"layer-separator",type:"separator"},{id:"arrange",label:t("레이어 순서"),icon:"layer",disabled:i,options:[{id:"front",label:t("맨 앞으로 가져오기")},{id:"back",label:t("맨 뒤로 보내기")}]},{id:"delete",label:t("요소 삭제"),icon:"trash",shortcut:"Delete",disabled:i}]}insertNode(t,e){if(this.readOnly||this.data.length>=100)return;this.remember(this.data),"frame"===t&&(this.creationTool=e||this.creationTool),"shape"===t&&(this.shapeTool=e||this.shapeTool),"component"===t&&(this.componentTool=e||this.componentTool);const i="frame"===t&&"note"===this.creationTool;let s=320,o=240,n=i?"새로운 메모":"새 프레임",a="";if(i&&(s=220,o=180),"text"===t&&(s=280,o=100,n="텍스트",a="새로운 아이디어"),"shape"===t&&(s=200,o="line"===this.shapeTool?24:160,n={rectangle:"사각형",ellipse:"타원",triangle:"삼각형",line:"선"}[this.shapeTool]),"component"===t){const t={button:[160,44,"Button"],input:[240,44,"내용을 입력하세요"],card:[280,180,"새 카드"],badge:[100,32,"Badge"]};[s,o,a]=t[this.componentTool]||t.button,n=`${a} 컴포넌트`}const r=this.normalizeNode({id:crypto.randomUUID(),type:t,name:n,x:-this.view.x/this.view.zoom-s/2,y:-this.view.y/this.view.zoom-o/2,width:s,height:o,radius:"component"===t?10:i?12:0,fill:"shape"===t?"#dce8f5":"component"===t&&"button"===this.componentTool?"#191919":i?"#edf2e9":"#ffffff",text:a,fontSize:"component"===t?"card"===this.componentTool?22:14:24,textColor:"component"===t&&"button"===this.componentTool?"#ffffff":"#191919",shape:this.shapeTool,component:this.componentTool});this.data=[...this.data,r],this.select(r.id),this.emit("create")}positionContext(){const t=this.data.find(t=>t.id===this.selected);if(!t||!this.surface||!this.contextBar)return;const e=this.contextBar.offsetWidth||200,i=this.contextBar.offsetHeight||40,s=(t.rotation||0)*Math.PI/180,o=(Math.abs(t.width*Math.sin(s))+Math.abs(t.height*Math.cos(s)))/2*this.view.zoom,n=this.surface.clientWidth/2+this.view.x+(t.x+t.width/2)*this.view.zoom,a=this.surface.clientHeight/2+this.view.y+(t.y+t.height/2)*this.view.zoom,r=a-o-i-44,h=a+o+36,d=r>=12?r:h+i<=this.surface.clientHeight-12?h:12;this.contextBar.style.left=`${clamp(n,e/2+12,Math.max(e/2+12,this.surface.clientWidth-e/2-12))}px`,this.contextBar.style.top=`${clamp(d,12,Math.max(12,this.surface.clientHeight-i-12))}px`}select(t){t!==this.selected&&(this.selected=t,this.drawSelection(),this.dispatchEvent(new CustomEvent("db-selection-change",{detail:{selectedId:t,node:this.data.find(e=>e.id===t)||null},bubbles:!0,composed:!0})))}emit(t){this.draw(),this.dispatchEvent(new CustomEvent("db-nodes-change",{detail:{nodes:this.nodes,selectedId:this.selected,reason:t},bubbles:!0,composed:!0}))}emitViewport(){this.drawViewport(),this.dispatchEvent(new CustomEvent("db-viewport-change",{detail:this.viewport,bubbles:!0,composed:!0}))}remember(t){this.past.push(clone(t)),this.past.length>40&&this.past.shift(),this.future=[]}undo(){!this.readOnly&&this.past.length&&(this.cancelGesture(),this.future.push(this.nodes),this.data=this.past.pop(),this.select(this.data.some(t=>t.id===this.selected)?this.selected:this.data[0]?.id||null),this.emit("undo"))}redo(){!this.readOnly&&this.future.length&&(this.cancelGesture(),this.past.push(this.nodes),this.data=this.future.pop(),this.select(this.data.some(t=>t.id===this.selected)?this.selected:this.data[0]?.id||null),this.emit("redo"))}point(t){const e=this.surface.getBoundingClientRect();return{x:(t.clientX-e.left-this.surface.clientWidth/2-this.view.x)/this.view.zoom,y:(t.clientY-e.top-this.surface.clientHeight/2-this.view.y)/this.view.zoom}}zoomTo(t,e){if(!this.surface)return;const i=this.surface.getBoundingClientRect(),s=e||{x:i.left+i.width/2,y:i.top+i.height/2},o=this.point({clientX:s.x,clientY:s.y});this.view.zoom=clamp(t,this.minZoom,this.maxZoom),this.view.x=s.x-i.left-this.surface.clientWidth/2-o.x*this.view.zoom,this.view.y=s.y-i.top-this.surface.clientHeight/2-o.y*this.view.zoom,this.emitViewport(),this.drawSelection()}fitToContent(){if(!this.surface)return;if(!this.data.length)return this.viewport={x:0,y:0,zoom:1},void this.emitViewport();const t=this.data.flatMap(t=>{const e=(t.rotation||0)*Math.PI/180,i=t.x+t.width/2,s=t.y+t.height/2;return[[-1,-1],[1,-1],[1,1],[-1,1]].map(([o,n])=>({x:i+o*t.width/2*Math.cos(e)-n*t.height/2*Math.sin(e),y:s+o*t.width/2*Math.sin(e)+n*t.height/2*Math.cos(e)}))}),e=Math.min(...t.map(t=>t.x)),i=Math.max(...t.map(t=>t.x)),s=Math.min(...t.map(t=>t.y)),o=Math.max(...t.map(t=>t.y)),n=clamp(Math.min((this.surface.clientWidth-96)/Math.max(16,i-e),(this.surface.clientHeight-96)/Math.max(16,o-s)),this.minZoom,Math.min(1,this.maxZoom));this.view={zoom:n,x:-(e+i)/2*n,y:-(s+o)/2*n},this.emitViewport(),this.drawSelection()}editable(t){return t.composedPath().some(t=>t instanceof HTMLElement&&(t.isContentEditable||/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName)))}cancelGesture(){const t=this.gesture;this.gesture=void 0,t&&"pan"!==t.kind&&(this.data=t.before,this.isConnected&&this.emit("cancel")),t&&this.surface?.hasPointerCapture(t.pointer)&&this.surface.releasePointerCapture(t.pointer)}async importImages(t,i){if(this.readOnly)return;const s=i||{x:-this.view.x/this.view.zoom,y:-this.view.y/this.view.zoom},o=this.generation,n=[];let a=0;const r=Math.max(1,Number(this.getAttribute("max-file-size"))||10485760);for(const e of t.slice(0,12))if(!["image/png","image/jpeg","image/webp","image/gif"].includes(e.type)||e.size>r||this.data.length+n.length>=100)a++;else try{const t=await new Promise((t,i)=>{const s=new FileReader;this.reading.add(s),s.onload=()=>{this.reading.delete(s),t(String(s.result))},s.onerror=s.onabort=()=>{this.reading.delete(s),i(new Error("이미지를 읽지 못했습니다."))},s.readAsDataURL(e)}),i=await new Promise((e,i)=>{const s=new Image;s.onload=()=>e({width:s.naturalWidth,height:s.naturalHeight}),s.onerror=i,s.src=t});if(!this.isConnected||o!==this.generation)return;const a=Math.min(1,480/Math.max(i.width,i.height));n.push({id:crypto.randomUUID(),type:"image",name:e.name,src:t,x:s.x+24*n.length-i.width*a/2,y:s.y+24*n.length-i.height*a/2,width:Math.max(16,i.width*a),height:Math.max(16,i.height*a),rotation:0,opacity:1,radius:0})}catch{a++}this.isConnected&&o===this.generation&&(n.length&&(this.remember(this.data),this.data=[...this.data,...n.map(t=>this.normalizeNode(t))],this.select(n[n.length-1].id),this.emit("import")),this.status&&(this.status.textContent=e("{0}개 이미지 추가{1}",[n.length,a||t.length>12?` · ${a+Math.max(0,t.length-12)}개 제외 (PNG/JPG/WebP/GIF, 파일당 ${Math.round(r/1024/1024)}MB 이하)`:""])))}}customElements.get("db-design-canvas")||customElements.define("db-design-canvas",DbDesignCanvas);export{DbDesignCanvas};
@@ -1 +1 @@
1
- import{DbToolButton as t}from"./db-tool-button.js";class DbDesignToolbar extends HTMLElement{constructor(){super(...arguments),this.tools=new Map,this.focusId="",this.connected=!1,this.location={x:12,y:12},this.focusIn=t=>{const e=t.target.closest("db-button");e&&(this.focusId=e.dataset.toolId||"",this.rove())},this.keyDown=t=>{if(!t.target.closest("db-tool-button"))return;const e="vertical"===this.getAttribute("orientation"),i=e?"ArrowDown":"ArrowRight";if(![e?"ArrowUp":"ArrowLeft",i,"Home","End"].includes(t.key))return;t.preventDefault();const o=this.buttons(),s=o.indexOf(document.activeElement),n="Home"===t.key?0:"End"===t.key?o.length-1:(s+(t.key===i?1:-1)+o.length)%o.length;o[n]?.focus()},this.dragStart=t=>{if(0!==t.button||!this.parentElement)return;t.preventDefault(),t.stopPropagation(),this.closeMenus();const e=this.parentElement.getBoundingClientRect();this.dragState={pointer:t.pointerId,clientX:t.clientX,clientY:t.clientY,...this.location,scaleX:e.width/Math.max(1,this.parentElement.offsetWidth),scaleY:e.height/Math.max(1,this.parentElement.offsetHeight)},this.grip?.querySelector("db-button")?.focus({preventScroll:!0}),this.setPointerCapture(t.pointerId),this.setAttribute("data-moving","")},this.dragMove=t=>{const e=this.dragState;e&&t.pointerId===e.pointer&&(t.preventDefault(),t.stopPropagation(),this.location={x:e.x+(t.clientX-e.clientX)/e.scaleX,y:e.y+(t.clientY-e.clientY)/e.scaleY},this.constrainPosition(!0))},this.dragEnd=t=>{this.dragState?.pointer===t.pointerId&&(this.dragState=void 0,this.removeAttribute("data-moving"),this.hasPointerCapture(t.pointerId)&&this.releasePointerCapture(t.pointerId),this.constrainPosition(!0))},this.dragCancel=t=>{this.dragState?.pointer===t.pointerId&&this.dragEnd(t)},this.gripKey=t=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home"].includes(t.key))return;if(t.preventDefault(),t.stopPropagation(),"Home"===t.key)return void this.resetPosition();const e=t.shiftKey?20:4;this.location.x+="ArrowRight"===t.key?e:"ArrowLeft"===t.key?-e:0,this.location.y+="ArrowDown"===t.key?e:"ArrowUp"===t.key?-e:0,this.constrainPosition(!0)}}static get observedAttributes(){return["items","size","tone","orientation","placement","movable","toolbar-position"]}get toolbarPosition(){return{...this.location}}set toolbarPosition(t){try{const e="string"==typeof t?JSON.parse(t):t;this.location={x:Number.isFinite(e.x)?e.x:12,y:Number.isFinite(e.y)?e.y:12},this.constrainPosition()}catch{}}get items(){try{const t=JSON.parse(this.getAttribute("items")||"[]");return Array.isArray(t)?t.filter(t=>t&&"string"==typeof t.id):[]}catch{return[]}}set items(t){const e="string"==typeof t?t:JSON.stringify(t);e!==this.getAttribute("items")&&this.setAttribute("items",e)}connectedCallback(){this.connected=!0,this.classList.add("designbase-wc-design-toolbar"),this.setAttribute("role","toolbar"),this.hasAttribute("aria-label")||this.setAttribute("aria-label","디자인 도구"),this.addEventListener("keydown",this.keyDown),this.addEventListener("focusin",this.focusIn),this.sync(),this.addEventListener("pointermove",this.dragMove),this.addEventListener("pointerup",this.dragEnd),this.addEventListener("pointercancel",this.dragCancel),this.addEventListener("lostpointercapture",this.dragCancel),this.boundaryObserver=new ResizeObserver(()=>this.constrainPosition()),this.parentElement&&this.boundaryObserver.observe(this.parentElement),this.boundaryObserver.observe(this),this.constrainPosition()}disconnectedCallback(){this.connected=!1,this.dragState=void 0,this.boundaryObserver?.disconnect(),this.removeEventListener("pointermove",this.dragMove),this.removeEventListener("pointerup",this.dragEnd),this.removeEventListener("pointercancel",this.dragCancel),this.removeEventListener("lostpointercapture",this.dragCancel),this.removeEventListener("keydown",this.keyDown),this.removeEventListener("focusin",this.focusIn)}attributeChangedCallback(t){"toolbar-position"===t&&(this.toolbarPosition=this.getAttribute("toolbar-position")||"{}"),this.connected&&this.sync()}sync(){const t=this.items,e=new Set,set=(t,e,i)=>{t.getAttribute(e)!==i&&t.setAttribute(e,i)},i=[];this.hasAttribute("movable")?(this.grip||(this.grip=document.createElement("db-tool-button"),this.grip.setAttribute("tool-id","move-toolbar"),this.grip.setAttribute("label","툴바 이동"),this.grip.setAttribute("shortcut","드래그 / 방향키"),this.grip.setAttribute("icon","grip-vertical"),this.grip.setAttribute("data-toolbar-grip",""),this.grip.addEventListener("db-tool-action",t=>t.stopPropagation()),this.grip.addEventListener("pointerdown",this.dragStart),this.grip.addEventListener("keydown",this.gripKey)),set(this.grip,"tone",this.getAttribute("tone")||"default"),set(this.grip,"size",this.getAttribute("size")||"m"),i.push(this.grip)):this.grip?.remove();for(const o of t){if(e.has(o.id))continue;e.add(o.id);let t=this.tools.get(o.id);const s="separator"===o.type;t&&"DB-TOOL-BUTTON"===t.tagName===s&&(t.remove(),this.tools.delete(o.id),t=void 0),t||(t=s?document.createElement("span"):document.createElement("db-tool-button"),this.tools.set(o.id,t)),s?(t.setAttribute("role","separator"),t.className="designbase-wc-design-toolbar__separator"):(set(t,"tool-id",o.id),set(t,"label",o.label||o.id),set(t,"icon",o.icon||""),void 0!==o.text?set(t,"text",o.text):t.removeAttribute("text"),set(t,"shortcut",o.shortcut||""),set(t,"options",JSON.stringify(o.options||[])),set(t,"value",o.value||""),set(t,"menu-trigger",o.menuTrigger||"click"),set(t,"size",this.getAttribute("size")||"m"),set(t,"tone",this.getAttribute("tone")||"default"),set(t,"placement",this.getAttribute("placement")||"bottom-start"),t.toggleAttribute("toggle","boolean"==typeof o.active),t.toggleAttribute("active",!!o.active),t.toggleAttribute("disabled",!!o.disabled)),i.push(t)}for(const[t,i]of this.tools)e.has(t)||(i.remove(),this.tools.delete(t));i.forEach((t,e)=>{this.children[e]!==t&&this.insertBefore(t,this.children[e]||null)}),this.setAttribute("aria-orientation","vertical"===this.getAttribute("orientation")?"vertical":"horizontal"),this.rove(),this.constrainPosition()}buttons(){return Array.from(this.querySelectorAll(":scope > db-tool-button:not([disabled]) db-button"))}rove(){const t=this.buttons(),e=t.find(t=>t.dataset.toolId===this.focusId)||t[0];t.forEach(t=>t.tabIndex=t===e?0:-1)}constrainPosition(t=!1){if(!this.hasAttribute("movable")||!this.parentElement)return;const e=Math.max(0,this.parentElement.clientWidth-this.offsetWidth),i=Math.max(0,this.parentElement.clientHeight-this.offsetHeight),o=Math.min(8,e/2),s=Math.min(8,i/2);this.location={x:Math.max(o,Math.min(e-o,this.location.x)),y:Math.max(s,Math.min(i-s,this.location.y))},this.style.left=`${this.location.x}px`,this.style.top=`${this.location.y}px`,t&&this.dispatchEvent(new CustomEvent("db-position-change",{detail:this.toolbarPosition,bubbles:!0,composed:!0}))}resetPosition(){this.location={x:12,y:12},this.constrainPosition(!0)}closeMenus(){for(const e of this.tools.values())e instanceof t&&e.closeMenu(!1)}}customElements.get("db-design-toolbar")||customElements.define("db-design-toolbar",DbDesignToolbar);export{DbDesignToolbar};
1
+ import{dbText as t}from"../core/i18n.js";import{DbToolButton as e}from"./db-tool-button.js";class DbDesignToolbar extends HTMLElement{constructor(){super(...arguments),this.tools=new Map,this.focusId="",this.connected=!1,this.location={x:12,y:12},this.focusIn=t=>{const e=t.target.closest("db-button");e&&(this.focusId=e.dataset.toolId||"",this.rove())},this.keyDown=t=>{if(!t.target.closest("db-tool-button"))return;const e="vertical"===this.getAttribute("orientation"),i=e?"ArrowDown":"ArrowRight";if(![e?"ArrowUp":"ArrowLeft",i,"Home","End"].includes(t.key))return;t.preventDefault();const o=this.buttons(),s=o.indexOf(document.activeElement),n="Home"===t.key?0:"End"===t.key?o.length-1:(s+(t.key===i?1:-1)+o.length)%o.length;o[n]?.focus()},this.dragStart=t=>{if(0!==t.button||!this.parentElement)return;t.preventDefault(),t.stopPropagation(),this.closeMenus();const e=this.parentElement.getBoundingClientRect();this.dragState={pointer:t.pointerId,clientX:t.clientX,clientY:t.clientY,...this.location,scaleX:e.width/Math.max(1,this.parentElement.offsetWidth),scaleY:e.height/Math.max(1,this.parentElement.offsetHeight)},this.grip?.querySelector("db-button")?.focus({preventScroll:!0}),this.setPointerCapture(t.pointerId),this.setAttribute("data-moving","")},this.dragMove=t=>{const e=this.dragState;e&&t.pointerId===e.pointer&&(t.preventDefault(),t.stopPropagation(),this.location={x:e.x+(t.clientX-e.clientX)/e.scaleX,y:e.y+(t.clientY-e.clientY)/e.scaleY},this.constrainPosition(!0))},this.dragEnd=t=>{this.dragState?.pointer===t.pointerId&&(this.dragState=void 0,this.removeAttribute("data-moving"),this.hasPointerCapture(t.pointerId)&&this.releasePointerCapture(t.pointerId),this.constrainPosition(!0))},this.dragCancel=t=>{this.dragState?.pointer===t.pointerId&&this.dragEnd(t)},this.gripKey=t=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Home"].includes(t.key))return;if(t.preventDefault(),t.stopPropagation(),"Home"===t.key)return void this.resetPosition();const e=t.shiftKey?20:4;this.location.x+="ArrowRight"===t.key?e:"ArrowLeft"===t.key?-e:0,this.location.y+="ArrowDown"===t.key?e:"ArrowUp"===t.key?-e:0,this.constrainPosition(!0)}}static get observedAttributes(){return["items","size","tone","orientation","placement","movable","toolbar-position"]}get toolbarPosition(){return{...this.location}}set toolbarPosition(t){try{const e="string"==typeof t?JSON.parse(t):t;this.location={x:Number.isFinite(e.x)?e.x:12,y:Number.isFinite(e.y)?e.y:12},this.constrainPosition()}catch{}}get items(){try{const t=JSON.parse(this.getAttribute("items")||"[]");return Array.isArray(t)?t.filter(t=>t&&"string"==typeof t.id):[]}catch{return[]}}set items(t){const e="string"==typeof t?t:JSON.stringify(t);e!==this.getAttribute("items")&&this.setAttribute("items",e)}connectedCallback(){this.connected=!0,this.classList.add("designbase-wc-design-toolbar"),this.setAttribute("role","toolbar"),this.hasAttribute("aria-label")||this.setAttribute("aria-label",t("디자인 도구")),this.addEventListener("keydown",this.keyDown),this.addEventListener("focusin",this.focusIn),this.sync(),this.addEventListener("pointermove",this.dragMove),this.addEventListener("pointerup",this.dragEnd),this.addEventListener("pointercancel",this.dragCancel),this.addEventListener("lostpointercapture",this.dragCancel),this.boundaryObserver=new ResizeObserver(()=>this.constrainPosition()),this.parentElement&&this.boundaryObserver.observe(this.parentElement),this.boundaryObserver.observe(this),this.constrainPosition()}disconnectedCallback(){this.connected=!1,this.dragState=void 0,this.boundaryObserver?.disconnect(),this.removeEventListener("pointermove",this.dragMove),this.removeEventListener("pointerup",this.dragEnd),this.removeEventListener("pointercancel",this.dragCancel),this.removeEventListener("lostpointercapture",this.dragCancel),this.removeEventListener("keydown",this.keyDown),this.removeEventListener("focusin",this.focusIn)}attributeChangedCallback(t){"toolbar-position"===t&&(this.toolbarPosition=this.getAttribute("toolbar-position")||"{}"),this.connected&&this.sync()}sync(){const t=this.items,e=new Set,set=(t,e,i)=>{t.getAttribute(e)!==i&&t.setAttribute(e,i)},i=[];this.hasAttribute("movable")?(this.grip||(this.grip=document.createElement("db-tool-button"),this.grip.setAttribute("tool-id","move-toolbar"),this.grip.setAttribute("label","툴바 이동"),this.grip.setAttribute("shortcut","드래그 / 방향키"),this.grip.setAttribute("icon","grip-vertical"),this.grip.setAttribute("data-toolbar-grip",""),this.grip.addEventListener("db-tool-action",t=>t.stopPropagation()),this.grip.addEventListener("pointerdown",this.dragStart),this.grip.addEventListener("keydown",this.gripKey)),set(this.grip,"tone",this.getAttribute("tone")||"default"),set(this.grip,"size",this.getAttribute("size")||"m"),i.push(this.grip)):this.grip?.remove();for(const o of t){if(e.has(o.id))continue;e.add(o.id);let t=this.tools.get(o.id);const s="separator"===o.type;t&&"DB-TOOL-BUTTON"===t.tagName===s&&(t.remove(),this.tools.delete(o.id),t=void 0),t||(t=s?document.createElement("span"):document.createElement("db-tool-button"),this.tools.set(o.id,t)),s?(t.setAttribute("role","separator"),t.className="designbase-wc-design-toolbar__separator"):(set(t,"tool-id",o.id),set(t,"label",o.label||o.id),set(t,"icon",o.icon||""),void 0!==o.text?set(t,"text",o.text):t.removeAttribute("text"),set(t,"shortcut",o.shortcut||""),set(t,"options",JSON.stringify(o.options||[])),set(t,"value",o.value||""),set(t,"menu-trigger",o.menuTrigger||"click"),set(t,"size",this.getAttribute("size")||"m"),set(t,"tone",this.getAttribute("tone")||"default"),set(t,"placement",this.getAttribute("placement")||"bottom-start"),t.toggleAttribute("toggle","boolean"==typeof o.active),t.toggleAttribute("active",!!o.active),t.toggleAttribute("disabled",!!o.disabled)),i.push(t)}for(const[t,i]of this.tools)e.has(t)||(i.remove(),this.tools.delete(t));i.forEach((t,e)=>{this.children[e]!==t&&this.insertBefore(t,this.children[e]||null)}),this.setAttribute("aria-orientation","vertical"===this.getAttribute("orientation")?"vertical":"horizontal"),this.rove(),this.constrainPosition()}buttons(){return Array.from(this.querySelectorAll(":scope > db-tool-button:not([disabled]) db-button"))}rove(){const t=this.buttons(),e=t.find(t=>t.dataset.toolId===this.focusId)||t[0];t.forEach(t=>t.tabIndex=t===e?0:-1)}constrainPosition(t=!1){if(!this.hasAttribute("movable")||!this.parentElement)return;const e=Math.max(0,this.parentElement.clientWidth-this.offsetWidth),i=Math.max(0,this.parentElement.clientHeight-this.offsetHeight),o=Math.min(8,e/2),s=Math.min(8,i/2);this.location={x:Math.max(o,Math.min(e-o,this.location.x)),y:Math.max(s,Math.min(i-s,this.location.y))},this.style.left=`${this.location.x}px`,this.style.top=`${this.location.y}px`,t&&this.dispatchEvent(new CustomEvent("db-position-change",{detail:this.toolbarPosition,bubbles:!0,composed:!0}))}resetPosition(){this.location={x:12,y:12},this.constrainPosition(!0)}closeMenus(){for(const t of this.tools.values())t instanceof e&&t.closeMenu(!1)}}customElements.get("db-design-toolbar")||customElements.define("db-design-toolbar",DbDesignToolbar);export{DbDesignToolbar};
@@ -1 +1 @@
1
- import{createDbIcon as e}from"./db-icons.js";import"./db-progressbar.js";import"./db-button.js";import{DOWNLOAD_PROGRESS_STATUSES as t,DOWNLOAD_PROGRESS_POSITIONS as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const i={downloading:"download",paused:"pause",error:"error-filled",complete:"circle-checked"},o={downloading:"다운로드 중",paused:"일시정지됨",error:"다운로드 실패",complete:"다운로드 완료"},n={downloading:"완료될 때까지 기다려 주세요.",paused:"다운로드가 일시정지되었습니다.",error:"문제가 발생했어요. 다시 시도해 주세요.",complete:"파일 다운로드가 완료되었습니다."},r={downloading:"primary",paused:"default",error:"danger",complete:"success"},a=new Map;function getStackSet(e){let t=a.get(e);return t||(t=new Set,a.set(e,t)),t}function isStackParticipant(e){return!(!e.isConnected||e.closed||e.hasAttribute("no-stack"))&&"fixed"===getComputedStyle(e).position}function relayoutStack(e){const t=a.get(e);if(!t)return;const s=[...t].filter(isStackParticipant),i=e.startsWith("top"),o=s[0]?.stackMode??"stack";s.forEach((e,t)=>{const n=s.length-1-t;if(e.dataset.stackIndex=String(n),e.style.setProperty("--dbdl-stack-index",String(n)),e.style.zIndex=String(9999+t),"deck"===o){const t=Math.max(.88,1-.04*n),s=10*n;e.style.setProperty("--dbdl-stack-offset","0px"),e.style.setProperty("--dbdl-stack-scale",String(t)),e.style.setProperty("--dbdl-stack-shift",`${i?s:-s}px`),e.classList.add("designbase-wc-download-progress--stacked-deck"),e.classList.remove("designbase-wc-download-progress--stacked-list")}else{let i=0;for(let e=s.length-1;e>t;e-=1)i+=s[e].offsetHeight+12;e.style.setProperty("--dbdl-stack-offset",`${i}px`),e.style.setProperty("--dbdl-stack-scale","1"),e.style.setProperty("--dbdl-stack-shift","0px"),e.classList.add("designbase-wc-download-progress--stacked-list"),e.classList.remove("designbase-wc-download-progress--stacked-deck")}}),t.forEach(e=>{s.includes(e)||(e.style.removeProperty("--dbdl-stack-offset"),e.style.removeProperty("--dbdl-stack-scale"),e.style.removeProperty("--dbdl-stack-shift"),e.style.removeProperty("z-index"),e.classList.remove("designbase-wc-download-progress--stacked-list","designbase-wc-download-progress--stacked-deck"),delete e.dataset.stackIndex)})}class DbDownloadProgress extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.renderedStatus=null,this.closeTimerId=null,this.resizeObserver=null,this.registeredPosition=null,this.closed=!1,this.handleMainClick=e=>{e.target.closest(".designbase-wc-download-progress__header-actions")||this.toggle()},this.handleToggleClick=e=>{e.stopPropagation(),this.toggle()},this.handleCloseClick=e=>{e.stopPropagation(),this.close()}}static get observedAttributes(){return["file-name","value","max","status","position","minimized","label","description","remaining","size-label","error-text","closable","stack-mode","no-stack","auto-remove"]}get value(){return Number(this.getAttribute("value")||0)}set value(e){this.setAttribute("value",String(e))}get max(){return Number(this.getAttribute("max")||100)}set max(e){this.setAttribute("max",String(e))}get status(){const e=this.getAttribute("status");return t.includes(e)?e:"downloading"}set status(e){this.setAttribute("status",e)}get fileName(){return this.getAttribute("file-name")||""}set fileName(e){this.setAttribute("file-name",e)}get position(){const e=this.getAttribute("position");return s.includes(e)?e:"bottom-right"}set position(e){this.setAttribute("position",e)}get minimized(){return this.hasAttribute("minimized")}set minimized(e){this.toggleAttribute("minimized",e)}get stackMode(){return"deck"===this.getAttribute("stack-mode")?"deck":"stack"}set stackMode(e){this.setAttribute("stack-mode",e)}connectedCallback(){this.build(),this.sync(),this.registerStack(),requestAnimationFrame(()=>{this.classList.add("designbase-wc-download-progress--visible"),relayoutStack(this.position)}),"undefined"!=typeof ResizeObserver&&(this.resizeObserver=new ResizeObserver(()=>{this.registeredPosition&&relayoutStack(this.registeredPosition)}),this.resizeObserver.observe(this))}disconnectedCallback(){this.closeTimerId&&clearTimeout(this.closeTimerId),this.closeTimerId=null,this.resizeObserver?.disconnect(),this.resizeObserver=null,this.unregisterStack()}attributeChangedCallback(e){this.built&&(this.sync(),"position"!==e&&"stack-mode"!==e&&"no-stack"!==e&&"minimized"!==e||(this.registerStack(),this.registeredPosition&&relayoutStack(this.registeredPosition),s.forEach(e=>{e!==this.registeredPosition&&relayoutStack(e)})))}registerStack(){const e=this.position;this.registeredPosition&&this.registeredPosition!==e&&(getStackSet(this.registeredPosition).delete(this),relayoutStack(this.registeredPosition)),getStackSet(e).add(this),this.registeredPosition=e}unregisterStack(){if(!this.registeredPosition)return;const e=this.registeredPosition;getStackSet(e).delete(this),this.registeredPosition=null,relayoutStack(e)}build(){if(this.built)return;this.built=!0,this.classList.add("designbase-wc-download-progress"),this.replaceChildren();const t=el("div","designbase-wc-download-progress__shell");this.mainRow=el("button","designbase-wc-download-progress__main"),this.mainRow.type="button",this.mainRow.setAttribute("aria-label","다운로드 상세 펼치기/접기"),this.mainRow.addEventListener("click",this.handleMainClick),this.iconEl=el("span","designbase-wc-download-progress__icon"),this.iconEl.setAttribute("aria-hidden","true");const s=el("span","designbase-wc-download-progress__text"),i=el("span","designbase-wc-download-progress__title");this.labelEl=el("span","designbase-wc-download-progress__label"),this.fileEl=el("span","designbase-wc-download-progress__file"),i.append(this.labelEl,this.fileEl),this.subtitleEl=el("span","designbase-wc-download-progress__subtitle"),s.append(i,this.subtitleEl),this.inlinePercentEl=el("span","designbase-wc-download-progress__inline-percent");const o=el("span","designbase-wc-download-progress__header-actions");this.toggleBtn=el("button","designbase-wc-download-progress__icon-button"),this.toggleBtn.type="button",this.toggleBtn.addEventListener("click",this.handleToggleClick),this.closeBtn=el("button","designbase-wc-download-progress__icon-button"),this.closeBtn.type="button",this.closeBtn.setAttribute("aria-label","닫기"),this.closeBtn.append(e("close",18)),this.closeBtn.addEventListener("click",this.handleCloseClick),o.append(this.toggleBtn,this.closeBtn),this.mainRow.append(this.iconEl,s,this.inlinePercentEl,o);const n=el("div","designbase-wc-download-progress__collapsible"),r=el("div","designbase-wc-download-progress__collapse-inner"),a=el("div","designbase-wc-download-progress__body");this.bodyBar=document.createElement("db-progressbar"),this.bodyBar.setAttribute("full-width",""),this.bodyBar.setAttribute("size","s");const d=el("div","designbase-wc-download-progress__meta");this.percentEl=el("span","designbase-wc-download-progress__percent"),this.metaDotEl=el("span","designbase-wc-download-progress__meta-dot"),this.metaDotEl.textContent="•",this.remainingEl=el("span","designbase-wc-download-progress__remaining"),d.append(this.percentEl,this.metaDotEl,this.remainingEl),a.append(this.bodyBar,d);const l=el("div","designbase-wc-download-progress__footer");this.actionsEl=el("div","designbase-wc-download-progress__actions"),l.append(this.actionsEl),r.append(a,l),n.append(r);const c=el("div","designbase-wc-download-progress__edge");this.edgeBar=document.createElement("db-progressbar"),this.edgeBar.setAttribute("full-width",""),this.edgeBar.setAttribute("size","s"),c.append(this.edgeBar),t.append(this.mainRow,n,c),this.append(t)}sync(){const a=this.status,d=this.max,l="complete"===a?d:this.value,c=Math.max(0,Math.min(d,l)),h=0===d?0:Math.round((c-0)/(d-0)*100);this.classList.add("designbase-wc-download-progress"),s.forEach(e=>this.classList.toggle(`designbase-wc-download-progress--${e}`,e===this.position)),t.forEach(e=>this.classList.toggle(`designbase-wc-download-progress--${e}`,e===a)),this.classList.toggle("designbase-wc-download-progress--minimized",this.minimized),this.renderedStatus!==a&&(this.iconEl.className=`designbase-wc-download-progress__icon designbase-wc-download-progress__icon--${a}`,this.iconEl.replaceChildren(e(i[a],20)),this.renderActions(a),"complete"===a&&(this.iconEl.classList.remove("designbase-wc-download-progress__icon--pop"),this.iconEl.offsetWidth,this.iconEl.classList.add("designbase-wc-download-progress__icon--pop"),this.dispatchEvent(new CustomEvent("db-complete",{bubbles:!0,composed:!0}))),this.renderedStatus=a),this.labelEl.textContent=this.getAttribute("label")||o[a],this.fileEl.textContent=this.fileName,this.fileEl.hidden=!this.fileName,this.subtitleEl.textContent="error"===a&&this.getAttribute("error-text")||this.getAttribute("description")||n[a],this.inlinePercentEl.textContent="complete"===a?"100%":`${h}%`,this.percentEl.textContent=`${h}%`;const g=this.getAttribute("remaining")||"",b=this.getAttribute("size-label")||"";"complete"===a?(this.percentEl.textContent=b||"완료됨",this.metaDotEl.hidden=!0,this.remainingEl.hidden=!0):"error"===a?(this.percentEl.textContent=`${h}%`,this.metaDotEl.hidden=!g,this.remainingEl.hidden=!g,this.remainingEl.textContent=g):(this.metaDotEl.hidden=!g,this.remainingEl.hidden=!g,this.remainingEl.textContent=g);const p=r[a],u="downloading"===a?"animated":"solid";[this.bodyBar,this.edgeBar].forEach(e=>{e.setAttribute("value",String(c)),e.setAttribute("max",String(d)),e.setAttribute("variant",p),e.setAttribute("bar-style",u)}),this.toggleBtn.replaceChildren(e(this.minimized?"expand":"chevron-down",18)),this.toggleBtn.setAttribute("aria-label",this.minimized?"펼치기":"최소화"),this.toggleBtn.setAttribute("aria-expanded",String(!this.minimized));const m="true"===this.getAttribute("closable")||this.hasAttribute("closable")&&"false"!==this.getAttribute("closable")||"complete"===a;this.closeBtn.hidden=!m,this.mainRow.setAttribute("aria-label",this.minimized?"다운로드 펼치기":"다운로드 접기"),this.setAttribute("role","status"),this.setAttribute("aria-live","error"===a?"assertive":"polite")}renderActions(e){this.actionsEl.replaceChildren();const make=(e,t,s,i)=>{const o=document.createElement("db-button");return o.setAttribute("variant",t),o.setAttribute("size","s"),i&&o.setAttribute("start-icon",i),o.textContent=e,o.addEventListener("db-click",()=>{this.dispatchEvent(new CustomEvent(s,{bubbles:!0,composed:!0})),"db-cancel"!==s&&"db-dismiss"!==s||this.close()}),o};"downloading"===e?this.actionsEl.append(make("취소","ghost","db-cancel")):"paused"===e?this.actionsEl.append(make("취소","ghost","db-cancel"),make("재개","primary","db-resume","play")):"error"===e?this.actionsEl.append(make("취소","ghost","db-cancel"),make("다시 시도","primary","db-retry","refresh")):"complete"===e&&this.actionsEl.append(make("폴더 열기","secondary","db-open","folder-open"),make("닫기","ghost","db-dismiss"))}minimize(){this.minimized||(this.minimized=!0,this.dispatchEvent(new CustomEvent("db-minimize",{bubbles:!0,composed:!0})))}expand(){this.minimized&&(this.minimized=!1,this.dispatchEvent(new CustomEvent("db-expand",{bubbles:!0,composed:!0})))}toggle(){this.minimized?this.expand():this.minimize()}setProgress(e){this.value=e}setStatus(e){this.status=e}close(){this.closed||(this.closed=!0,this.classList.remove("designbase-wc-download-progress--visible"),this.classList.add("designbase-wc-download-progress--closing"),this.unregisterStack(),this.closeTimerId&&clearTimeout(this.closeTimerId),this.closeTimerId=setTimeout(()=>{this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0})),this.setAttribute("closed",""),this.hidden=!0;("true"===this.getAttribute("auto-remove")||this.hasAttribute("auto-remove")&&"false"!==this.getAttribute("auto-remove"))&&this.remove()},260))}}function el(e,t){const s=document.createElement(e);return s.className=t,s}customElements.get("db-download-progress")||customElements.define("db-download-progress",DbDownloadProgress);export{DbDownloadProgress};
1
+ import{dbText as e}from"../core/i18n.js";import{createDbIcon as t}from"./db-icons.js";import"./db-progressbar.js";import"./db-button.js";import{DOWNLOAD_PROGRESS_STATUSES as s,DOWNLOAD_PROGRESS_POSITIONS as i}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";const o={downloading:"download",paused:"pause",error:"error-filled",complete:"circle-checked"},n={downloading:"다운로드 중",paused:"일시정지됨",error:"다운로드 실패",complete:"다운로드 완료"},r={downloading:"완료될 때까지 기다려 주세요.",paused:"다운로드가 일시정지되었습니다.",error:"문제가 발생했어요. 다시 시도해 주세요.",complete:"파일 다운로드가 완료되었습니다."},a={downloading:"primary",paused:"default",error:"danger",complete:"success"},d=new Map;function getStackSet(e){let t=d.get(e);return t||(t=new Set,d.set(e,t)),t}function isStackParticipant(e){return!(!e.isConnected||e.closed||e.hasAttribute("no-stack"))&&"fixed"===getComputedStyle(e).position}function relayoutStack(e){const t=d.get(e);if(!t)return;const s=[...t].filter(isStackParticipant),i=e.startsWith("top"),o=s[0]?.stackMode??"stack";s.forEach((e,t)=>{const n=s.length-1-t;if(e.dataset.stackIndex=String(n),e.style.setProperty("--dbdl-stack-index",String(n)),e.style.zIndex=String(9999+t),"deck"===o){const t=Math.max(.88,1-.04*n),s=10*n;e.style.setProperty("--dbdl-stack-offset","0px"),e.style.setProperty("--dbdl-stack-scale",String(t)),e.style.setProperty("--dbdl-stack-shift",`${i?s:-s}px`),e.classList.add("designbase-wc-download-progress--stacked-deck"),e.classList.remove("designbase-wc-download-progress--stacked-list")}else{let i=0;for(let e=s.length-1;e>t;e-=1)i+=s[e].offsetHeight+12;e.style.setProperty("--dbdl-stack-offset",`${i}px`),e.style.setProperty("--dbdl-stack-scale","1"),e.style.setProperty("--dbdl-stack-shift","0px"),e.classList.add("designbase-wc-download-progress--stacked-list"),e.classList.remove("designbase-wc-download-progress--stacked-deck")}}),t.forEach(e=>{s.includes(e)||(e.style.removeProperty("--dbdl-stack-offset"),e.style.removeProperty("--dbdl-stack-scale"),e.style.removeProperty("--dbdl-stack-shift"),e.style.removeProperty("z-index"),e.classList.remove("designbase-wc-download-progress--stacked-list","designbase-wc-download-progress--stacked-deck"),delete e.dataset.stackIndex)})}class DbDownloadProgress extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.renderedStatus=null,this.closeTimerId=null,this.resizeObserver=null,this.registeredPosition=null,this.closed=!1,this.handleMainClick=e=>{e.target.closest(".designbase-wc-download-progress__header-actions")||this.toggle()},this.handleToggleClick=e=>{e.stopPropagation(),this.toggle()},this.handleCloseClick=e=>{e.stopPropagation(),this.close()}}static get observedAttributes(){return["file-name","value","max","status","position","minimized","label","description","remaining","size-label","error-text","closable","stack-mode","no-stack","auto-remove"]}get value(){return Number(this.getAttribute("value")||0)}set value(e){this.setAttribute("value",String(e))}get max(){return Number(this.getAttribute("max")||100)}set max(e){this.setAttribute("max",String(e))}get status(){const e=this.getAttribute("status");return s.includes(e)?e:"downloading"}set status(e){this.setAttribute("status",e)}get fileName(){return this.getAttribute("file-name")||""}set fileName(e){this.setAttribute("file-name",e)}get position(){const e=this.getAttribute("position");return i.includes(e)?e:"bottom-right"}set position(e){this.setAttribute("position",e)}get minimized(){return this.hasAttribute("minimized")}set minimized(e){this.toggleAttribute("minimized",e)}get stackMode(){return"deck"===this.getAttribute("stack-mode")?"deck":"stack"}set stackMode(e){this.setAttribute("stack-mode",e)}connectedCallback(){this.build(),this.sync(),this.registerStack(),requestAnimationFrame(()=>{this.classList.add("designbase-wc-download-progress--visible"),relayoutStack(this.position)}),"undefined"!=typeof ResizeObserver&&(this.resizeObserver=new ResizeObserver(()=>{this.registeredPosition&&relayoutStack(this.registeredPosition)}),this.resizeObserver.observe(this))}disconnectedCallback(){this.closeTimerId&&clearTimeout(this.closeTimerId),this.closeTimerId=null,this.resizeObserver?.disconnect(),this.resizeObserver=null,this.unregisterStack()}attributeChangedCallback(e){this.built&&(this.sync(),"position"!==e&&"stack-mode"!==e&&"no-stack"!==e&&"minimized"!==e||(this.registerStack(),this.registeredPosition&&relayoutStack(this.registeredPosition),i.forEach(e=>{e!==this.registeredPosition&&relayoutStack(e)})))}registerStack(){const e=this.position;this.registeredPosition&&this.registeredPosition!==e&&(getStackSet(this.registeredPosition).delete(this),relayoutStack(this.registeredPosition)),getStackSet(e).add(this),this.registeredPosition=e}unregisterStack(){if(!this.registeredPosition)return;const e=this.registeredPosition;getStackSet(e).delete(this),this.registeredPosition=null,relayoutStack(e)}build(){if(this.built)return;this.built=!0,this.classList.add("designbase-wc-download-progress"),this.replaceChildren();const s=el("div","designbase-wc-download-progress__shell");this.mainRow=el("button","designbase-wc-download-progress__main"),this.mainRow.type="button",this.mainRow.setAttribute("aria-label",e("다운로드 상세 펼치기/접기")),this.mainRow.addEventListener("click",this.handleMainClick),this.iconEl=el("span","designbase-wc-download-progress__icon"),this.iconEl.setAttribute("aria-hidden","true");const i=el("span","designbase-wc-download-progress__text"),o=el("span","designbase-wc-download-progress__title");this.labelEl=el("span","designbase-wc-download-progress__label"),this.fileEl=el("span","designbase-wc-download-progress__file"),o.append(this.labelEl,this.fileEl),this.subtitleEl=el("span","designbase-wc-download-progress__subtitle"),i.append(o,this.subtitleEl),this.inlinePercentEl=el("span","designbase-wc-download-progress__inline-percent");const n=el("span","designbase-wc-download-progress__header-actions");this.toggleBtn=el("button","designbase-wc-download-progress__icon-button"),this.toggleBtn.type="button",this.toggleBtn.addEventListener("click",this.handleToggleClick),this.closeBtn=el("button","designbase-wc-download-progress__icon-button"),this.closeBtn.type="button",this.closeBtn.setAttribute("aria-label",e("닫기")),this.closeBtn.append(t("close",18)),this.closeBtn.addEventListener("click",this.handleCloseClick),n.append(this.toggleBtn,this.closeBtn),this.mainRow.append(this.iconEl,i,this.inlinePercentEl,n);const r=el("div","designbase-wc-download-progress__collapsible"),a=el("div","designbase-wc-download-progress__collapse-inner"),d=el("div","designbase-wc-download-progress__body");this.bodyBar=document.createElement("db-progressbar"),this.bodyBar.setAttribute("full-width",""),this.bodyBar.setAttribute("size","s");const l=el("div","designbase-wc-download-progress__meta");this.percentEl=el("span","designbase-wc-download-progress__percent"),this.metaDotEl=el("span","designbase-wc-download-progress__meta-dot"),this.metaDotEl.textContent="•",this.remainingEl=el("span","designbase-wc-download-progress__remaining"),l.append(this.percentEl,this.metaDotEl,this.remainingEl),d.append(this.bodyBar,l);const c=el("div","designbase-wc-download-progress__footer");this.actionsEl=el("div","designbase-wc-download-progress__actions"),c.append(this.actionsEl),a.append(d,c),r.append(a);const h=el("div","designbase-wc-download-progress__edge");this.edgeBar=document.createElement("db-progressbar"),this.edgeBar.setAttribute("full-width",""),this.edgeBar.setAttribute("size","s"),h.append(this.edgeBar),s.append(this.mainRow,r,h),this.append(s)}sync(){const e=this.status,d=this.max,l="complete"===e?d:this.value,c=Math.max(0,Math.min(d,l)),h=0===d?0:Math.round((c-0)/(d-0)*100);this.classList.add("designbase-wc-download-progress"),i.forEach(e=>this.classList.toggle(`designbase-wc-download-progress--${e}`,e===this.position)),s.forEach(t=>this.classList.toggle(`designbase-wc-download-progress--${t}`,t===e)),this.classList.toggle("designbase-wc-download-progress--minimized",this.minimized),this.renderedStatus!==e&&(this.iconEl.className=`designbase-wc-download-progress__icon designbase-wc-download-progress__icon--${e}`,this.iconEl.replaceChildren(t(o[e],20)),this.renderActions(e),"complete"===e&&(this.iconEl.classList.remove("designbase-wc-download-progress__icon--pop"),this.iconEl.offsetWidth,this.iconEl.classList.add("designbase-wc-download-progress__icon--pop"),this.dispatchEvent(new CustomEvent("db-complete",{bubbles:!0,composed:!0}))),this.renderedStatus=e),this.labelEl.textContent=this.getAttribute("label")||n[e],this.fileEl.textContent=this.fileName,this.fileEl.hidden=!this.fileName,this.subtitleEl.textContent="error"===e&&this.getAttribute("error-text")||this.getAttribute("description")||r[e],this.inlinePercentEl.textContent="complete"===e?"100%":`${h}%`,this.percentEl.textContent=`${h}%`;const g=this.getAttribute("remaining")||"",b=this.getAttribute("size-label")||"";"complete"===e?(this.percentEl.textContent=b||"완료됨",this.metaDotEl.hidden=!0,this.remainingEl.hidden=!0):"error"===e?(this.percentEl.textContent=`${h}%`,this.metaDotEl.hidden=!g,this.remainingEl.hidden=!g,this.remainingEl.textContent=g):(this.metaDotEl.hidden=!g,this.remainingEl.hidden=!g,this.remainingEl.textContent=g);const p=a[e],u="downloading"===e?"animated":"solid";[this.bodyBar,this.edgeBar].forEach(e=>{e.setAttribute("value",String(c)),e.setAttribute("max",String(d)),e.setAttribute("variant",p),e.setAttribute("bar-style",u)}),this.toggleBtn.replaceChildren(t(this.minimized?"expand":"chevron-down",18)),this.toggleBtn.setAttribute("aria-label",this.minimized?"펼치기":"최소화"),this.toggleBtn.setAttribute("aria-expanded",String(!this.minimized));const m="true"===this.getAttribute("closable")||this.hasAttribute("closable")&&"false"!==this.getAttribute("closable")||"complete"===e;this.closeBtn.hidden=!m,this.mainRow.setAttribute("aria-label",this.minimized?"다운로드 펼치기":"다운로드 접기"),this.setAttribute("role","status"),this.setAttribute("aria-live","error"===e?"assertive":"polite")}renderActions(e){this.actionsEl.replaceChildren();const make=(e,t,s,i)=>{const o=document.createElement("db-button");return o.setAttribute("variant",t),o.setAttribute("size","s"),i&&o.setAttribute("start-icon",i),o.textContent=e,o.addEventListener("db-click",()=>{this.dispatchEvent(new CustomEvent(s,{bubbles:!0,composed:!0})),"db-cancel"!==s&&"db-dismiss"!==s||this.close()}),o};"downloading"===e?this.actionsEl.append(make("취소","ghost","db-cancel")):"paused"===e?this.actionsEl.append(make("취소","ghost","db-cancel"),make("재개","primary","db-resume","play")):"error"===e?this.actionsEl.append(make("취소","ghost","db-cancel"),make("다시 시도","primary","db-retry","refresh")):"complete"===e&&this.actionsEl.append(make("폴더 열기","secondary","db-open","folder-open"),make("닫기","ghost","db-dismiss"))}minimize(){this.minimized||(this.minimized=!0,this.dispatchEvent(new CustomEvent("db-minimize",{bubbles:!0,composed:!0})))}expand(){this.minimized&&(this.minimized=!1,this.dispatchEvent(new CustomEvent("db-expand",{bubbles:!0,composed:!0})))}toggle(){this.minimized?this.expand():this.minimize()}setProgress(e){this.value=e}setStatus(e){this.status=e}close(){this.closed||(this.closed=!0,this.classList.remove("designbase-wc-download-progress--visible"),this.classList.add("designbase-wc-download-progress--closing"),this.unregisterStack(),this.closeTimerId&&clearTimeout(this.closeTimerId),this.closeTimerId=setTimeout(()=>{this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0})),this.setAttribute("closed",""),this.hidden=!0;("true"===this.getAttribute("auto-remove")||this.hasAttribute("auto-remove")&&"false"!==this.getAttribute("auto-remove"))&&this.remove()},260))}}function el(e,t){const s=document.createElement(e);return s.className=t,s}customElements.get("db-download-progress")||customElements.define("db-download-progress",DbDownloadProgress);export{DbDownloadProgress};
@@ -17,6 +17,7 @@ declare class DbDrawer extends HTMLElement {
17
17
  /** 잠근 적이 있을 때만 푼다 — 카운트가 음수로 새지 않도록 */
18
18
  private releaseScrollLock;
19
19
  attributeChangedCallback(name: string): void;
20
+ private syncInteraction;
20
21
  private isOpenRequested;
21
22
  private clearTimers;
22
23
  private attachEscape;
@@ -1 +1 @@
1
- import{createFocusTrap as t}from"../core/focus-trap.js";import{unlockScroll as e,lockScroll as s}from"../core/scroll-lock.js";import{DRAWER_POSITIONS as i,DRAWER_SIZES as r}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbDrawer extends HTMLElement{constructor(){super(...arguments),this.isMounted=!1,this.isActive=!1,this.scrollLocked=!1,this.keyHandler=null}static get observedAttributes(){return["open","title","position","size","show-close-button","close-on-backdrop-click","close-on-escape","animation-duration","show-overlay","overlay-closable","z-index","trap-focus","drawer-id"]}connectedCallback(){this.style.display="contents",this.render(),this.syncOpenState()}disconnectedCallback(){this.clearTimers(),this.detachEscape(),this.focusTrap?.deactivate(),this.focusTrap=void 0,this.root?.remove(),this.root=void 0,this.releaseScrollLock()}releaseScrollLock(){this.scrollLocked&&(this.scrollLocked=!1,e())}attributeChangedCallback(t){this.isConnected&&("open"!==t?(this.render(),this.isMounted&&this.syncActiveClasses()):this.syncOpenState())}isOpenRequested(){return this.hasAttribute("open")&&"false"!==this.getAttribute("open")}clearTimers(){null!=this.enterFrame&&(cancelAnimationFrame(this.enterFrame),this.enterFrame=void 0),this.exitTimer&&(clearTimeout(this.exitTimer),this.exitTimer=void 0)}attachEscape(){this.keyHandler||(this.keyHandler=t=>{"Escape"===t.key&&"false"!==this.getAttribute("close-on-escape")&&this.close()},document.addEventListener("keydown",this.keyHandler))}detachEscape(){this.keyHandler&&(document.removeEventListener("keydown",this.keyHandler),this.keyHandler=null)}close(){this.setAttribute("open","false"),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))}collectChildren(){return[...new Set([...Array.from(this.contentEl?.children||[]),...Array.from(this.children)])].filter(t=>!t.hasAttribute("data-wc-internal"))}syncOpenState(){const e=this.isOpenRequested(),i=Number(this.getAttribute("animation-duration")||300);if(this.clearTimers(),e)return this.render(),this.isMounted=!0,this.isActive=!1,this.syncActiveClasses(),this.root&&!this.root.isConnected&&document.body.append(this.root),this.enterFrame=requestAnimationFrame(()=>{this.enterFrame=requestAnimationFrame(()=>{this.enterFrame=void 0,this.isActive=!0,this.syncActiveClasses(),"false"!==this.getAttribute("trap-focus")&&this.container&&(this.focusTrap=t(this.container),this.focusTrap.activate())})}),this.scrollLocked||(s(),this.scrollLocked=!0),void this.attachEscape();this.isActive=!1,this.syncActiveClasses(),this.exitTimer=setTimeout(()=>{this.exitTimer=void 0,this.isMounted=!1,this.syncActiveClasses(),this.root?.remove(),this.releaseScrollLock(),this.detachEscape(),this.focusTrap?.deactivate(),this.focusTrap=void 0},i),this.detachEscape()}syncActiveClasses(){this.root&&(this.root.classList.toggle("designbase-wc-drawer--open",this.isActive),this.root.classList.toggle("designbase-wc-drawer--closing",this.isMounted&&!this.isActive),this.overlay?.classList.toggle("designbase-wc-drawer__overlay--open",this.isActive),this.container?.classList.toggle("designbase-wc-drawer__container--open",this.isActive))}render(){const t=i.includes(this.getAttribute("position")||"right")&&this.getAttribute("position")||"right",e=r.includes(this.getAttribute("size")||"m")&&this.getAttribute("size")||"m",s=this.getAttribute("title"),o="false"!==this.getAttribute("show-close-button"),n="false"!==this.getAttribute("show-overlay"),a=this.getAttribute("animation-duration")||"300",c=this.getAttribute("z-index")||"1000",l=this.getAttribute("drawer-id")||"drawer",h=this.collectChildren();this.root||(this.root=document.createElement("div"),this.root.setAttribute("data-wc-internal",""),this.overlay=document.createElement("div"),this.overlay.className="designbase-wc-drawer__overlay",this.overlay.setAttribute("aria-hidden","true"),this.overlay.addEventListener("click",t=>{if(t.target!==this.overlay)return;const e="false"!==this.getAttribute("overlay-closable"),s="false"!==this.getAttribute("close-on-backdrop-click");e&&s&&this.close()}),this.container=document.createElement("div"),this.container.className=`designbase-wc-drawer__container designbase-wc-drawer__container--${t}`,this.container.setAttribute("role","dialog"),this.container.setAttribute("aria-modal","true"),this.container.tabIndex=-1,this.container.addEventListener("click",t=>t.stopPropagation()),this.contentEl=document.createElement("div"),this.contentEl.className="designbase-wc-drawer__content",this.container.append(this.contentEl)),this.root.className=["designbase-wc-drawer",`designbase-wc-drawer--${t}`,`designbase-wc-drawer--${e}`].join(" "),this.root.style.setProperty("--drawer-animation-duration",`${a}ms`),this.root.style.setProperty("--drawer-z-index",c),this.container.className=`designbase-wc-drawer__container designbase-wc-drawer__container--${t}`,this.container.id=l,s?this.container.setAttribute("aria-labelledby",`${l}-title`):this.container.removeAttribute("aria-labelledby"),this.contentEl.id=`${l}-content`,n&&this.overlay?this.overlay.parentElement!==this.root&&this.root.prepend(this.overlay):this.overlay?.remove(),this.container.querySelector(":scope > .designbase-wc-drawer__header")?.remove();if(Boolean(s)||o){const t=document.createElement("div");if(t.className="designbase-wc-drawer__header",s){const e=document.createElement("h2");e.className="designbase-wc-drawer__title",e.id=`${l}-title`,e.textContent=s,t.append(e)}if(o){const e=document.createElement("db-button");e.className="designbase-wc-drawer__close-button",e.setAttribute("variant","ghost"),e.setAttribute("size","s"),e.setAttribute("icon-only",""),e.setAttribute("start-icon","close"),e.setAttribute("aria-label","닫기"),e.addEventListener("db-click",t=>{t.stopPropagation(),this.close()}),t.append(e)}this.container.insertBefore(t,this.contentEl)}const d=Array.from(this.contentEl.children);(d.length!==h.length||d.some((t,e)=>t!==h[e]))&&this.contentEl.replaceChildren(...h),this.container.parentElement!==this.root&&this.root.append(this.container),this.isMounted&&!this.root.isConnected&&document.body.append(this.root)}}customElements.get("db-drawer")||customElements.define("db-drawer",DbDrawer);export{DbDrawer};
1
+ import{inheritThemeScope as t}from"../core/theme-scope.js";import{dbText as e}from"../core/i18n.js";import{createFocusTrap as s}from"../core/focus-trap.js";import{unlockScroll as i,lockScroll as r}from"../core/scroll-lock.js";import{DRAWER_POSITIONS as o,DRAWER_SIZES as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbDrawer extends HTMLElement{constructor(){super(...arguments),this.isMounted=!1,this.isActive=!1,this.scrollLocked=!1,this.keyHandler=null}static get observedAttributes(){return["open","title","position","size","show-close-button","close-on-backdrop-click","close-on-escape","animation-duration","show-overlay","overlay-closable","z-index","trap-focus","drawer-id"]}connectedCallback(){this.style.display="contents",this.render(),this.syncOpenState()}disconnectedCallback(){this.clearTimers(),this.detachEscape(),this.focusTrap?.deactivate(),this.focusTrap=void 0,this.root?.remove(),this.root=void 0,this.releaseScrollLock()}releaseScrollLock(){this.scrollLocked&&(this.scrollLocked=!1,i())}attributeChangedCallback(t){this.isConnected&&("open"!==t?(this.render(),this.isMounted&&(this.syncActiveClasses(),this.syncInteraction())):this.syncOpenState())}syncInteraction(){"false"===this.getAttribute("show-overlay")?this.releaseScrollLock():this.scrollLocked||(r(),this.scrollLocked=!0);"false"!==this.getAttribute("trap-focus")?this.isActive&&this.container&&!this.focusTrap&&(this.focusTrap=s(this.container),this.focusTrap.activate()):(this.focusTrap?.deactivate(),this.focusTrap=void 0)}isOpenRequested(){return this.hasAttribute("open")&&"false"!==this.getAttribute("open")}clearTimers(){null!=this.enterFrame&&(cancelAnimationFrame(this.enterFrame),this.enterFrame=void 0),this.exitTimer&&(clearTimeout(this.exitTimer),this.exitTimer=void 0)}attachEscape(){this.keyHandler||(this.keyHandler=t=>{"Escape"===t.key&&"false"!==this.getAttribute("close-on-escape")&&this.close()},document.addEventListener("keydown",this.keyHandler))}detachEscape(){this.keyHandler&&(document.removeEventListener("keydown",this.keyHandler),this.keyHandler=null)}close(){this.setAttribute("open","false"),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))}collectChildren(){return[...new Set([...Array.from(this.contentEl?.children||[]),...Array.from(this.children)])].filter(t=>!t.hasAttribute("data-wc-internal"))}syncOpenState(){const e=this.isOpenRequested(),s=Number(this.getAttribute("animation-duration")||300);if(this.clearTimers(),e)return this.render(),this.isMounted=!0,this.isActive=!1,this.syncActiveClasses(),this.root&&!this.root.isConnected&&(t(this,this.root),document.body.append(this.root)),this.enterFrame=requestAnimationFrame(()=>{this.enterFrame=requestAnimationFrame(()=>{this.enterFrame=void 0,this.isActive=!0,this.syncActiveClasses(),this.syncInteraction()})}),this.syncInteraction(),void this.attachEscape();this.isActive=!1,this.syncActiveClasses(),this.exitTimer=setTimeout(()=>{this.exitTimer=void 0,this.isMounted=!1,this.syncActiveClasses(),this.root?.remove(),this.releaseScrollLock(),this.detachEscape(),this.focusTrap?.deactivate(),this.focusTrap=void 0},s),this.detachEscape()}syncActiveClasses(){this.root&&(this.root.classList.toggle("designbase-wc-drawer--open",this.isActive),this.root.classList.toggle("designbase-wc-drawer--closing",this.isMounted&&!this.isActive),this.overlay?.classList.toggle("designbase-wc-drawer__overlay--open",this.isActive),this.container?.classList.toggle("designbase-wc-drawer__container--open",this.isActive))}render(){const s=o.includes(this.getAttribute("position")||"right")&&this.getAttribute("position")||"right",i=n.includes(this.getAttribute("size")||"m")&&this.getAttribute("size")||"m",r=this.getAttribute("title"),a="false"!==this.getAttribute("show-close-button"),c="false"!==this.getAttribute("show-overlay"),h=this.getAttribute("animation-duration")||"300",l=this.getAttribute("z-index")||"1000",d=this.getAttribute("drawer-id")||"drawer",u=this.collectChildren();this.root||(this.root=document.createElement("div"),this.root.setAttribute("data-wc-internal",""),this.overlay=document.createElement("div"),this.overlay.className="designbase-wc-drawer__overlay",this.overlay.setAttribute("aria-hidden","true"),this.overlay.addEventListener("click",t=>{if(t.target!==this.overlay)return;const e="false"!==this.getAttribute("overlay-closable"),s="false"!==this.getAttribute("close-on-backdrop-click");e&&s&&this.close()}),this.container=document.createElement("div"),this.container.className=`designbase-wc-drawer__container designbase-wc-drawer__container--${s}`,this.container.setAttribute("role","dialog"),this.container.setAttribute("aria-modal","true"),this.container.tabIndex=-1,this.contentEl=document.createElement("div"),this.contentEl.className="designbase-wc-drawer__content",this.container.append(this.contentEl)),this.root.className=["designbase-wc-drawer",`designbase-wc-drawer--${s}`,`designbase-wc-drawer--${i}`].join(" "),this.root.style.setProperty("--drawer-animation-duration",`${h}ms`),this.root.style.setProperty("--drawer-z-index",l),this.container.className=`designbase-wc-drawer__container designbase-wc-drawer__container--${s}`,this.container.id=d,this.container.setAttribute("aria-modal",String(c)),r?this.container.setAttribute("aria-labelledby",`${d}-title`):this.container.removeAttribute("aria-labelledby"),this.contentEl.id=`${d}-content`,c&&this.overlay?this.overlay.parentElement!==this.root&&this.root.prepend(this.overlay):this.overlay?.remove(),this.container.querySelector(":scope > .designbase-wc-drawer__header")?.remove();if(Boolean(r)||a){const t=document.createElement("div");if(t.className="designbase-wc-drawer__header",r){const e=document.createElement("h2");e.className="designbase-wc-drawer__title",e.id=`${d}-title`,e.textContent=r,t.append(e)}if(a){const s=document.createElement("db-button");s.className="designbase-wc-drawer__close-button",s.setAttribute("variant","ghost"),s.setAttribute("size","s"),s.setAttribute("icon-only",""),s.setAttribute("start-icon","close"),s.setAttribute("aria-label",e("닫기")),s.addEventListener("db-click",t=>{t.stopPropagation(),this.close()}),t.append(s)}this.container.insertBefore(t,this.contentEl)}const m=Array.from(this.contentEl.children);(m.length!==u.length||m.some((t,e)=>t!==u[e]))&&this.contentEl.replaceChildren(...u),this.container.parentElement!==this.root&&this.root.append(this.container),this.isMounted&&!this.root.isConnected&&(t(this,this.root),document.body.append(this.root))}}customElements.get("db-drawer")||customElements.define("db-drawer",DbDrawer);export{DbDrawer};
@@ -1 +1 @@
1
- import{isDbIconName as e,createDbIcon as t}from"./db-icons.js";import{DROPDOWN_SIZES as i,DROPDOWN_PLACEMENTS as r,DROPDOWN_TRIGGER_VARIANTS as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbDropdown extends HTMLElement{constructor(){super(...arguments),this.focusedIndex=-1,this.onTriggerActivate=null,this.lastTriggerSignature="",this.handleTriggerKeyDown=e=>{if(!this.menu?.contains(e.target))return"Enter"!==e.key&&" "!==e.key&&"ArrowDown"!==e.key||this.open?void("Escape"===e.key&&this.open&&(e.preventDefault(),this.open=!1,this.render())):(e.preventDefault(),this.open=!0,requestAnimationFrame(()=>this.focusItem(0)),void this.render())},this.handleDocumentKeyDown=e=>{if(!this.open)return;if("Escape"===e.key)return e.preventDefault(),this.open=!1,this.render(),void this.triggerWrap?.querySelector("[aria-haspopup]")?.focus();if(this.menu?.querySelector(':scope > [slot="content"]'))return;const t=this.enabledItems();if(!t.length)return;const i=t.findIndex(e=>this.parsedItems.indexOf(e)===this.focusedIndex);switch(e.key){case"ArrowDown":e.preventDefault(),this.focusItem(i<0?0:this.parsedItems.indexOf(t[(i+1)%t.length]));break;case"ArrowUp":e.preventDefault(),this.focusItem(i<0?0:this.parsedItems.indexOf(t[(i-1+t.length)%t.length]));break;case"Home":e.preventDefault(),this.focusItem(this.parsedItems.indexOf(t[0]));break;case"End":e.preventDefault(),this.focusItem(this.parsedItems.indexOf(t[t.length-1]));break;case"Enter":case" ":{e.preventDefault();const t=this.parsedItems[this.focusedIndex];!t||t.divider||t.disabled||this.selectItem(t);break}}},this.handleDocumentMouseDown=e=>{this.open&&(this.contains(e.target)||(this.open=!1,this.render()))}}static get observedAttributes(){return["options","items","placeholder","label","value","size","placement","full-width","disabled","open","trigger-variant","trigger-icon","icon-only","show-chevron"]}get showChevron(){return"false"!==this.getAttribute("show-chevron")}set showChevron(e){this.setAttribute("show-chevron",String(e))}get parsedItems(){const e=this.getAttribute("items");if(e)try{const t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return(this.getAttribute("options")||"").split(",").map(e=>e.trim()).filter(Boolean).map((e,t)=>({id:`option-${t}`,label:e}))}get options(){return this.parsedItems.map(e=>e.label||e.id)}set options(e){this.setAttribute("options",Array.isArray(e)?e.join(","):e)}get items(){return this.parsedItems}set items(e){this.setAttribute("items","string"==typeof e?e:JSON.stringify(e))}get value(){return this.getAttribute("value")||""}set value(e){e?this.setAttribute("value",e):this.removeAttribute("value")}get open(){return this.hasAttribute("open")}set open(e){const t=this.open;this.toggleAttribute("open",e),t!==e&&this.dispatchEvent(new CustomEvent("db-open",{detail:{open:e},bubbles:!0,composed:!0}))}get disabled(){return this.hasAttribute("disabled")}set disabled(e){this.toggleAttribute("disabled",e)}get size(){return this.getAttribute("size")||"m"}set size(e){this.setAttribute("size",e)}get placement(){return this.getAttribute("placement")||"bottom-left"}set placement(e){this.setAttribute("placement",e)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(e){e?this.setAttribute("placeholder",e):this.removeAttribute("placeholder")}get label(){return this.getAttribute("label")||""}set label(e){e?this.setAttribute("label",e):this.removeAttribute("label")}get fullWidth(){return this.hasAttribute("full-width")}set fullWidth(e){this.toggleAttribute("full-width",e)}connectedCallback(){requestAnimationFrame(()=>this.render()),document.addEventListener("mousedown",this.handleDocumentMouseDown),document.addEventListener("keydown",this.handleDocumentKeyDown)}disconnectedCallback(){document.removeEventListener("mousedown",this.handleDocumentMouseDown),document.removeEventListener("keydown",this.handleDocumentKeyDown),this.unbindTrigger()}attributeChangedCallback(e){this.isConnected&&("open"!==e||this.open||(this.focusedIndex=-1),this.render())}resolvedSize(){return i.includes(this.size)?this.size:"m"}resolvedPlacement(){return r.includes(this.placement)?this.placement:"bottom-left"}menuItemIconSize(){const e=this.resolvedSize();return"s"===e?14:"l"===e?18:16}triggerLabel(){return this.label?this.label:this.placeholder?this.placeholder:this.value?this.value:"메뉴"}enabledItems(){return this.parsedItems.filter(e=>!e.disabled&&!e.divider)}collectTriggerChildren(){const isInternalNode=e=>"content"===e.getAttribute("slot")||e.hasAttribute("data-wc-internal")||e===this.triggerWrap||e===this.menu||e===this.defaultTrigger,e=Array.from(this.children).filter(e=>!isInternalNode(e));return e.length>0?e:Array.from(this.triggerWrap?.children||[]).filter(e=>!isInternalNode(e))}hasCustomTrigger(){return this.collectTriggerChildren().length>0}bindTrigger(){this.triggerWrap&&(this.unbindTrigger(),this.onTriggerActivate=e=>{this.disabled||this.menu?.contains(e.target)||(e.preventDefault(),e.stopPropagation(),this.toggleOpen())},this.triggerWrap.addEventListener("click",this.onTriggerActivate),this.triggerWrap.addEventListener("keydown",this.handleTriggerKeyDown))}unbindTrigger(){this.triggerWrap&&this.onTriggerActivate&&this.triggerWrap.removeEventListener("click",this.onTriggerActivate),this.triggerWrap&&this.triggerWrap.removeEventListener("keydown",this.handleTriggerKeyDown),this.onTriggerActivate=null}toggleOpen(){this.disabled||(this.open=!this.open,this.open&&requestAnimationFrame(()=>this.focusItem(0)),this.render())}triggerSignature(){return[this.resolvedSize(),this.getAttribute("trigger-variant")||"tertiary",this.hasAttribute("icon-only")?"1":"0",this.getAttribute("trigger-icon")||"",this.fullWidth?"1":"0",this.showChevron?"1":"0"].join("|")}renderDefaultTrigger(){const t=this.resolvedSize(),i=this.getAttribute("trigger-variant")||"tertiary",r=s.includes(i)?i:"tertiary",n=this.hasAttribute("icon-only"),o=this.getAttribute("trigger-icon"),a=document.createElement("db-button");if(a.setAttribute("type","button"),a.setAttribute("variant",r),a.setAttribute("size",t),a.className="designbase-wc-dropdown__trigger",a.setAttribute("aria-haspopup","menu"),a.setAttribute("aria-label",this.triggerLabel()),this.fullWidth&&a.setAttribute("full-width",""),n)a.setAttribute("icon-only",""),a.setAttribute("start-icon",o&&e(o)?o:"edit"),a.removeAttribute("end-icon");else{a.removeAttribute("icon-only"),o&&e(o)?a.setAttribute("start-icon",o):a.removeAttribute("start-icon");const t=document.createElement("span");t.className="designbase-wc-dropdown__trigger-label",t.textContent=this.triggerLabel(),a.append(t),this.showChevron&&a.setAttribute("end-icon","chevron-down")}return a}syncDefaultTriggerState(e){e.setAttribute("aria-expanded",String(this.open)),e.setAttribute("aria-label",this.triggerLabel()),e.classList.toggle("designbase-wc-dropdown__trigger--open",this.open),this.disabled?e.setAttribute("disabled",""):e.removeAttribute("disabled");const t=e.querySelector(".designbase-wc-dropdown__trigger-label");t&&(t.textContent=this.triggerLabel())}ensureStructure(){this.triggerWrap||(this.triggerWrap=document.createElement("div"),this.triggerWrap.className="designbase-wc-dropdown__trigger-wrap",this.triggerWrap.setAttribute("data-wc-internal","")),this.menu||(this.menu=document.createElement("div"),this.menu.className="designbase-wc-dropdown__menu",this.menu.setAttribute("data-wc-internal",""),this.menu.setAttribute("role","menu"));const e=this.triggerWrap,t=this.menu;e&&t&&(this.contains(e)||this.append(e),e.contains(t)||e.append(t),Array.from(this.children).forEach(i=>{i===e||i.hasAttribute("data-wc-internal")||("content"===i.getAttribute("slot")?t.append(i):e.insertBefore(i,t))}))}renderTrigger(){const e=this.triggerWrap,t=this.menu;if(!e||!t)return;const i=this.collectTriggerChildren();if(i.length>0){this.defaultTrigger?.remove(),this.defaultTrigger=void 0;Array.from(e.children).filter(e=>e!==t&&e!==this.defaultTrigger).forEach(e=>e.remove()),i.forEach(i=>{i.setAttribute("aria-haspopup","menu"),i.setAttribute("aria-expanded",String(this.open)),i.classList.toggle("designbase-wc-dropdown__trigger--open",this.open),e.insertBefore(i,t)})}else{Array.from(e.children).filter(e=>e!==t&&e!==this.defaultTrigger).forEach(e=>e.remove()),this.defaultTrigger&&this.triggerSignature()===this.lastTriggerSignature||(this.defaultTrigger?.remove(),this.defaultTrigger=this.renderDefaultTrigger(),this.lastTriggerSignature=this.triggerSignature(),e.insertBefore(this.defaultTrigger,t)),this.defaultTrigger&&this.syncDefaultTriggerState(this.defaultTrigger)}this.bindTrigger()}renderMenu(){const e=this.resolvedPlacement();this.menu.className=`designbase-wc-dropdown__menu designbase-wc-dropdown__menu--${e}${this.open?" designbase-wc-dropdown__menu--open":""}`,this.menu.setAttribute("aria-hidden",String(!this.open)),this.menu.setAttribute("aria-label",this.triggerLabel());const i=this.menu.querySelector(':scope > [slot="content"]');if(this.menu.setAttribute("role",i?"dialog":"menu"),i)return this.menu.inert=!this.open,this.menu.setAttribute("aria-modal","false"),this.menu.classList.add("designbase-wc-dropdown__menu--custom"),void this.triggerWrap?.querySelector("[aria-haspopup]")?.setAttribute("aria-haspopup","dialog");this.menu.inert=!1,this.menu.removeAttribute("aria-modal"),this.menu.replaceChildren();const r=this.menuItemIconSize();this.parsedItems.forEach((e,i)=>{if(e.divider){const e=document.createElement("div");return e.className="designbase-wc-dropdown__divider",e.setAttribute("role","separator"),void this.menu?.append(e)}e.id||e.label||String(i);const s=e.label||e.id,n=Boolean(e.disabled||this.disabled),o=this.focusedIndex===i,a=document.createElement("button");if(a.type="button",a.className=["designbase-wc-dropdown__item",n&&"designbase-wc-dropdown__item--disabled",o&&"designbase-wc-dropdown__item--focused"].filter(Boolean).join(" "),a.setAttribute("role","menuitem"),a.disabled=n,a.tabIndex=o?0:-1,a.dataset.index=String(i),e.icon){const i=t(e.icon,r);i.classList.add("designbase-wc-dropdown__item-icon"),a.append(i)}const d=document.createElement("span");d.className="designbase-wc-dropdown__item-label",d.textContent=s,a.append(d),a.addEventListener("click",t=>{t.stopPropagation(),this.selectItem(e)}),this.menu?.append(a)})}render(){const e=this.resolvedSize();this.className=["designbase-wc-dropdown",`designbase-wc-dropdown--${e}`,this.open&&"designbase-wc-dropdown--open",this.fullWidth&&"designbase-wc-dropdown--full-width",this.disabled&&"designbase-wc-dropdown--disabled"].filter(Boolean).join(" "),this.ensureStructure(),this.renderTrigger(),this.renderMenu()}selectItem(e){e.disabled||e.divider||(this.open=!1,this.focusedIndex=-1,this.dispatchEvent(new CustomEvent("db-change",{detail:{value:e.label||e.id,id:e.id,item:e},bubbles:!0,composed:!0})),this.render(),this.triggerWrap?.querySelector("[aria-haspopup]")?.focus())}focusItem(e){if(this.menu?.querySelector(':scope > [slot="content"]'))return void this.menu.querySelector('input:not([disabled]), button:not([disabled]), [tabindex="0"]')?.focus();const t=this.enabledItems();if(!t.length)return;const i=t[Math.max(0,Math.min(e,t.length-1))],r=this.parsedItems.indexOf(i);this.focusedIndex=r;Array.from(this.menu?.querySelectorAll(".designbase-wc-dropdown__item")||[]).forEach(e=>{const t=Number(e.dataset.index)===r;e.classList.toggle("designbase-wc-dropdown__item--focused",t),e.tabIndex=t?0:-1,t&&e.focus()})}}customElements.get("db-dropdown")||customElements.define("db-dropdown",DbDropdown);export{DbDropdown};
1
+ import{setHostClasses as e}from"../core/host-class.js";import{isDbIconName as t,createDbIcon as i}from"./db-icons.js";import{DROPDOWN_SIZES as r,DROPDOWN_PLACEMENTS as s,DROPDOWN_TRIGGER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbDropdown extends HTMLElement{constructor(){super(...arguments),this.focusedIndex=-1,this.onTriggerActivate=null,this.lastTriggerSignature="",this.handleTriggerKeyDown=e=>{if(!this.menu?.contains(e.target))return"Enter"!==e.key&&" "!==e.key&&"ArrowDown"!==e.key||this.open?void("Escape"===e.key&&this.open&&(e.preventDefault(),this.open=!1,this.render())):(e.preventDefault(),this.open=!0,requestAnimationFrame(()=>this.focusItem(0)),void this.render())},this.handleDocumentKeyDown=e=>{if(!this.open)return;if("Escape"===e.key)return e.preventDefault(),this.open=!1,this.render(),void this.triggerWrap?.querySelector("[aria-haspopup]")?.focus();if(this.menu?.querySelector(':scope > [slot="content"]'))return;const t=this.enabledItems();if(!t.length)return;const i=t.findIndex(e=>this.parsedItems.indexOf(e)===this.focusedIndex);switch(e.key){case"ArrowDown":e.preventDefault(),this.focusItem(i<0?0:this.parsedItems.indexOf(t[(i+1)%t.length]));break;case"ArrowUp":e.preventDefault(),this.focusItem(i<0?0:this.parsedItems.indexOf(t[(i-1+t.length)%t.length]));break;case"Home":e.preventDefault(),this.focusItem(this.parsedItems.indexOf(t[0]));break;case"End":e.preventDefault(),this.focusItem(this.parsedItems.indexOf(t[t.length-1]));break;case"Enter":case" ":{e.preventDefault();const t=this.parsedItems[this.focusedIndex];!t||t.divider||t.disabled||this.selectItem(t);break}}},this.handleDocumentMouseDown=e=>{this.open&&(this.contains(e.target)||(this.open=!1,this.render()))}}static get observedAttributes(){return["options","items","placeholder","label","value","size","placement","full-width","disabled","open","trigger-variant","trigger-icon","icon-only","show-chevron"]}get showChevron(){return"false"!==this.getAttribute("show-chevron")}set showChevron(e){this.setAttribute("show-chevron",String(e))}get parsedItems(){const e=this.getAttribute("items");if(e)try{const t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return(this.getAttribute("options")||"").split(",").map(e=>e.trim()).filter(Boolean).map((e,t)=>({id:`option-${t}`,label:e}))}get options(){return this.parsedItems.map(e=>e.label||e.id)}set options(e){this.setAttribute("options",Array.isArray(e)?e.join(","):e)}get items(){return this.parsedItems}set items(e){this.setAttribute("items","string"==typeof e?e:JSON.stringify(e))}get value(){return this.getAttribute("value")||""}set value(e){e?this.setAttribute("value",e):this.removeAttribute("value")}get open(){return this.hasAttribute("open")}set open(e){const t=this.open;this.toggleAttribute("open",e),t!==e&&this.dispatchEvent(new CustomEvent("db-open",{detail:{open:e},bubbles:!0,composed:!0}))}get disabled(){return this.hasAttribute("disabled")}set disabled(e){this.toggleAttribute("disabled",e)}get size(){return this.getAttribute("size")||"m"}set size(e){this.setAttribute("size",e)}get placement(){return this.getAttribute("placement")||"bottom-left"}set placement(e){this.setAttribute("placement",e)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(e){e?this.setAttribute("placeholder",e):this.removeAttribute("placeholder")}get label(){return this.getAttribute("label")||""}set label(e){e?this.setAttribute("label",e):this.removeAttribute("label")}get fullWidth(){return this.hasAttribute("full-width")}set fullWidth(e){this.toggleAttribute("full-width",e)}connectedCallback(){requestAnimationFrame(()=>this.render()),document.addEventListener("mousedown",this.handleDocumentMouseDown),document.addEventListener("keydown",this.handleDocumentKeyDown)}disconnectedCallback(){document.removeEventListener("mousedown",this.handleDocumentMouseDown),document.removeEventListener("keydown",this.handleDocumentKeyDown),this.unbindTrigger()}attributeChangedCallback(e){this.isConnected&&("open"!==e||this.open||(this.focusedIndex=-1),this.render())}resolvedSize(){return r.includes(this.size)?this.size:"m"}resolvedPlacement(){return s.includes(this.placement)?this.placement:"bottom-left"}menuItemIconSize(){const e=this.resolvedSize();return"s"===e?14:"l"===e?18:16}triggerLabel(){return this.label?this.label:this.placeholder?this.placeholder:this.value?this.value:"메뉴"}enabledItems(){return this.parsedItems.filter(e=>!e.disabled&&!e.divider)}collectTriggerChildren(){const isInternalNode=e=>"content"===e.getAttribute("slot")||e.hasAttribute("data-wc-internal")||e===this.triggerWrap||e===this.menu||e===this.defaultTrigger,e=Array.from(this.children).filter(e=>!isInternalNode(e));return e.length>0?e:Array.from(this.triggerWrap?.children||[]).filter(e=>!isInternalNode(e))}hasCustomTrigger(){return this.collectTriggerChildren().length>0}bindTrigger(){this.triggerWrap&&(this.unbindTrigger(),this.onTriggerActivate=e=>{this.disabled||this.menu?.contains(e.target)||(e.preventDefault(),e.stopPropagation(),this.toggleOpen())},this.triggerWrap.addEventListener("click",this.onTriggerActivate),this.triggerWrap.addEventListener("keydown",this.handleTriggerKeyDown))}unbindTrigger(){this.triggerWrap&&this.onTriggerActivate&&this.triggerWrap.removeEventListener("click",this.onTriggerActivate),this.triggerWrap&&this.triggerWrap.removeEventListener("keydown",this.handleTriggerKeyDown),this.onTriggerActivate=null}toggleOpen(){this.disabled||(this.open=!this.open,this.open&&requestAnimationFrame(()=>this.focusItem(0)),this.render())}triggerSignature(){return[this.resolvedSize(),this.getAttribute("trigger-variant")||"tertiary",this.hasAttribute("icon-only")?"1":"0",this.getAttribute("trigger-icon")||"",this.fullWidth?"1":"0",this.showChevron?"1":"0"].join("|")}renderDefaultTrigger(){const e=this.resolvedSize(),i=this.getAttribute("trigger-variant")||"tertiary",r=n.includes(i)?i:"tertiary",s=this.hasAttribute("icon-only"),o=this.getAttribute("trigger-icon"),a=document.createElement("db-button");if(a.setAttribute("type","button"),a.setAttribute("variant",r),a.setAttribute("size",e),a.className="designbase-wc-dropdown__trigger",a.setAttribute("aria-haspopup","menu"),a.setAttribute("aria-label",this.triggerLabel()),this.fullWidth&&a.setAttribute("full-width",""),s)a.setAttribute("icon-only",""),a.setAttribute("start-icon",o&&t(o)?o:"edit"),a.removeAttribute("end-icon");else{a.removeAttribute("icon-only"),o&&t(o)?a.setAttribute("start-icon",o):a.removeAttribute("start-icon");const e=document.createElement("span");e.className="designbase-wc-dropdown__trigger-label",e.textContent=this.triggerLabel(),a.append(e),this.showChevron&&a.setAttribute("end-icon","chevron-down")}return a}syncDefaultTriggerState(e){e.setAttribute("aria-expanded",String(this.open)),e.setAttribute("aria-label",this.triggerLabel()),e.classList.toggle("designbase-wc-dropdown__trigger--open",this.open),this.disabled?e.setAttribute("disabled",""):e.removeAttribute("disabled");const t=e.querySelector(".designbase-wc-dropdown__trigger-label");t&&(t.textContent=this.triggerLabel())}ensureStructure(){this.triggerWrap||(this.triggerWrap=document.createElement("div"),this.triggerWrap.className="designbase-wc-dropdown__trigger-wrap",this.triggerWrap.setAttribute("data-wc-internal","")),this.menu||(this.menu=document.createElement("div"),this.menu.className="designbase-wc-dropdown__menu",this.menu.setAttribute("data-wc-internal",""),this.menu.setAttribute("role","menu"));const e=this.triggerWrap,t=this.menu;e&&t&&(this.contains(e)||this.append(e),e.contains(t)||e.append(t),Array.from(this.children).forEach(i=>{i===e||i.hasAttribute("data-wc-internal")||("content"===i.getAttribute("slot")?t.append(i):e.insertBefore(i,t))}))}renderTrigger(){const e=this.triggerWrap,t=this.menu;if(!e||!t)return;const i=this.collectTriggerChildren();if(i.length>0){this.defaultTrigger?.remove(),this.defaultTrigger=void 0;Array.from(e.children).filter(e=>e!==t&&e!==this.defaultTrigger).forEach(e=>e.remove()),i.forEach(i=>{i.setAttribute("aria-haspopup","menu"),i.setAttribute("aria-expanded",String(this.open)),i.classList.toggle("designbase-wc-dropdown__trigger--open",this.open),e.insertBefore(i,t)})}else{Array.from(e.children).filter(e=>e!==t&&e!==this.defaultTrigger).forEach(e=>e.remove()),this.defaultTrigger&&this.triggerSignature()===this.lastTriggerSignature||(this.defaultTrigger?.remove(),this.defaultTrigger=this.renderDefaultTrigger(),this.lastTriggerSignature=this.triggerSignature(),e.insertBefore(this.defaultTrigger,t)),this.defaultTrigger&&this.syncDefaultTriggerState(this.defaultTrigger)}this.bindTrigger()}renderMenu(){const e=this.resolvedPlacement();this.menu.className=`designbase-wc-dropdown__menu designbase-wc-dropdown__menu--${e}${this.open?" designbase-wc-dropdown__menu--open":""}`,this.menu.setAttribute("aria-hidden",String(!this.open)),this.menu.setAttribute("aria-label",this.triggerLabel());const t=this.menu.querySelector(':scope > [slot="content"]');if(this.menu.setAttribute("role",t?"dialog":"menu"),t)return this.menu.inert=!this.open,this.menu.setAttribute("aria-modal","false"),this.menu.classList.add("designbase-wc-dropdown__menu--custom"),void this.triggerWrap?.querySelector("[aria-haspopup]")?.setAttribute("aria-haspopup","dialog");this.menu.inert=!1,this.menu.removeAttribute("aria-modal"),this.menu.replaceChildren();const r=this.menuItemIconSize();this.parsedItems.forEach((e,t)=>{if(e.divider){const e=document.createElement("div");return e.className="designbase-wc-dropdown__divider",e.setAttribute("role","separator"),void this.menu?.append(e)}e.id||e.label||String(t);const s=e.label||e.id,n=Boolean(e.disabled||this.disabled),o=this.focusedIndex===t,a=document.createElement("button");if(a.type="button",a.className=["designbase-wc-dropdown__item",n&&"designbase-wc-dropdown__item--disabled",o&&"designbase-wc-dropdown__item--focused"].filter(Boolean).join(" "),a.setAttribute("role","menuitem"),a.disabled=n,a.tabIndex=o?0:-1,a.dataset.index=String(t),e.icon){const t=i(e.icon,r);t.classList.add("designbase-wc-dropdown__item-icon"),a.append(t)}const d=document.createElement("span");d.className="designbase-wc-dropdown__item-label",d.textContent=s,a.append(d),a.addEventListener("click",t=>{t.stopPropagation(),this.selectItem(e)}),this.menu?.append(a)})}render(){const t=this.resolvedSize();e(this,["designbase-wc-dropdown",`designbase-wc-dropdown--${t}`,this.open&&"designbase-wc-dropdown--open",this.fullWidth&&"designbase-wc-dropdown--full-width",this.disabled&&"designbase-wc-dropdown--disabled"]),this.ensureStructure(),this.renderTrigger(),this.renderMenu()}selectItem(e){e.disabled||e.divider||(this.open=!1,this.focusedIndex=-1,this.dispatchEvent(new CustomEvent("db-change",{detail:{value:e.label||e.id,id:e.id,item:e},bubbles:!0,composed:!0})),this.render(),this.triggerWrap?.querySelector("[aria-haspopup]")?.focus())}focusItem(e){if(this.menu?.querySelector(':scope > [slot="content"]'))return void this.menu.querySelector('input:not([disabled]), button:not([disabled]), [tabindex="0"]')?.focus();const t=this.enabledItems();if(!t.length)return;const i=t[Math.max(0,Math.min(e,t.length-1))],r=this.parsedItems.indexOf(i);this.focusedIndex=r;Array.from(this.menu?.querySelectorAll(".designbase-wc-dropdown__item")||[]).forEach(e=>{const t=Number(e.dataset.index)===r;e.classList.toggle("designbase-wc-dropdown__item--focused",t),e.tabIndex=t?0:-1,t&&e.focus()})}}customElements.get("db-dropdown")||customElements.define("db-dropdown",DbDropdown);export{DbDropdown};
@@ -1 +1 @@
1
- import{createDbIcon as e}from"./db-icons.js";import{DROPZONE_SIZES as t,DROPZONE_VARIANTS as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbDropzone extends HTMLElement{constructor(){super(...arguments),this.internalDragOver=!1,this.handleFileInputChange=()=>{this.emitFiles(this.fileInput?.files??null),this.fileInput&&(this.fileInput.value="")},this.handleDragOver=e=>{e.preventDefault(),e.stopPropagation(),this.isDisabled()||(this.internalDragOver=!0,this.syncClasses(),this.dispatchEvent(new CustomEvent("db-drag-over",{bubbles:!0,composed:!0,detail:{event:e}})))},this.handleDragLeave=e=>{e.preventDefault(),e.stopPropagation(),this.isDisabled()||(this.internalDragOver=!1,this.syncClasses(),this.dispatchEvent(new CustomEvent("db-drag-leave",{bubbles:!0,composed:!0,detail:{event:e}})))},this.handleDrop=e=>{e.preventDefault(),e.stopPropagation(),this.isDisabled()||(this.internalDragOver=!1,this.syncClasses(),this.emitFiles(e.dataTransfer?.files??null),this.dispatchEvent(new CustomEvent("db-drop",{bubbles:!0,composed:!0,detail:{event:e}})))},this.handleClick=e=>{this.isDisabled()||e.target.closest(".designbase-wc-dropzone__button")||(this.fileInput?.click(),this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0,detail:{event:e}})))}}static get observedAttributes(){return["size","variant","show-icon","icon","image","title","description","button-text","show-button","accept","max-size","multiple","drag-over","disabled","readonly"]}connectedCallback(){this.ensureInput(),this.sync(),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop),this.addEventListener("click",this.handleClick)}disconnectedCallback(){this.removeEventListener("dragover",this.handleDragOver),this.removeEventListener("dragleave",this.handleDragLeave),this.removeEventListener("drop",this.handleDrop),this.removeEventListener("click",this.handleClick)}attributeChangedCallback(){this.isConnected&&this.sync()}ensureInput(){this.fileInput||(this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.style.display="none",this.fileInput.setAttribute("data-wc-internal",""),this.fileInput.addEventListener("change",this.handleFileInputChange),this.append(this.fileInput))}isDisabled(){return this.hasAttribute("disabled")||this.hasAttribute("readonly")}isDragOver(){return this.hasAttribute("drag-over")?"false"!==this.getAttribute("drag-over"):this.internalDragOver}validateFile(e){const t=this.getAttribute("accept");if(t){const s=t.split(",").map(e=>e.trim()),i=e.type,n=`.${e.name.split(".").pop()?.toLowerCase()}`;if(!s.some(e=>e.startsWith(".")?n===e.toLowerCase():i===e||i.startsWith(e.replace("*",""))))return!1}const s=Number(this.getAttribute("max-size")||0);return!(s>0&&e.size>s)}emitFiles(e){if(!e||0===e.length)return;const t=Array.from(e).filter(e=>this.validateFile(e));t.length&&this.dispatchEvent(new CustomEvent("db-file-select",{bubbles:!0,composed:!0,detail:{files:t}}))}getDescriptionText(){const e=this.getAttribute("description");if(e)return e;const t=this.getAttribute("accept");if(!t)return"모든 파일 형식 지원";let s=t.split(",").map(e=>{const t=e.trim();return t.startsWith(".")?t.toUpperCase():t.includes("*")?t.replace("*","").toUpperCase():t}).join(", ");const i=Number(this.getAttribute("max-size")||0);if(i>0){const e=1024,t=["Bytes","KB","MB","GB"],n=Math.floor(Math.log(i)/Math.log(e));s+=` (최대 ${`${parseFloat((i/e**n).toFixed(2))} ${t[n]}`})`}return s}collectChildren(){return Array.from(this.children).filter(e=>e!==this.fileInput&&!e.hasAttribute("data-wc-internal"))}syncClasses(){const e=this.getAttribute("size")||"m",i=this.getAttribute("variant")||"default",n=t.includes(e)?e:"m",a=s.includes(i)?i:"default";this.className=["designbase-wc-dropzone",`designbase-wc-dropzone--size-${n}`,`designbase-wc-dropzone--variant-${a}`,this.isDragOver()&&"designbase-wc-dropzone--drag-over",this.hasAttribute("disabled")&&"designbase-wc-dropzone--disabled",this.hasAttribute("readonly")&&"designbase-wc-dropzone--readonly"].filter(Boolean).join(" ")}sync(){this.ensureInput(),this.syncClasses();const s=this.getAttribute("accept"),i=this.hasAttribute("multiple");this.fileInput&&(s?this.fileInput.setAttribute("accept",s):this.fileInput.removeAttribute("accept"),this.fileInput.toggleAttribute("multiple",i),this.fileInput.disabled=this.isDisabled());const n=this.collectChildren(),a=this.querySelector(":scope > .designbase-wc-dropzone__content[data-wc-internal]");if(n.length)return void a?.remove();let r=a;r||(r=document.createElement("div"),r.className="designbase-wc-dropzone__content",r.setAttribute("data-wc-internal",""),this.append(r)),r.replaceChildren();const o=this.getAttribute("size")||"m",l=t.includes(o)?o:"m",d=this.getAttribute("image"),c="false"!==this.getAttribute("show-icon"),h=this.getAttribute("icon")||"gallery",p=this.getAttribute("title")||"파일을 드래그하여 업로드하거나 클릭하여 선택하세요",u=this.getDescriptionText(),b=this.hasAttribute("show-button"),g=this.getAttribute("button-text")||"파일 선택";if(d){const e=document.createElement("div");e.className="designbase-wc-dropzone__image";const t=document.createElement("img");t.src=d,t.alt="Upload illustration",e.append(t),r.append(e)}else if(c){const t=document.createElement("div");t.className="designbase-wc-dropzone__icon";const s="s"===l?32:"l"===l?56:48;t.append(e(h,s)),r.append(t)}{const e=document.createElement("div");e.className="designbase-wc-dropzone__title",e.textContent=p,r.append(e)}if(u){const e=document.createElement("div");e.className="designbase-wc-dropzone__description",e.textContent=u,r.append(e)}if(b){const e=document.createElement("button");e.type="button",e.className="designbase-wc-dropzone__button",e.textContent=g,e.disabled=this.isDisabled(),e.addEventListener("click",e=>{e.stopPropagation(),this.isDisabled()||this.fileInput?.click()}),r.append(e)}}}customElements.get("db-dropzone")||customElements.define("db-dropzone",DbDropzone);export{DbDropzone};
1
+ import{t as e}from"../core/i18n.js";import{createDbIcon as t}from"./db-icons.js";import{DROPZONE_SIZES as i,DROPZONE_VARIANTS as s}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbDropzone extends HTMLElement{constructor(){super(...arguments),this.internalDragOver=!1,this.handleFileInputChange=()=>{this.emitFiles(this.fileInput?.files??null),this.fileInput&&(this.fileInput.value="")},this.handleDragOver=e=>{e.preventDefault(),e.stopPropagation(),this.isDisabled()||(this.internalDragOver=!0,this.syncClasses(),this.dispatchEvent(new CustomEvent("db-drag-over",{bubbles:!0,composed:!0,detail:{event:e}})))},this.handleDragLeave=e=>{e.preventDefault(),e.stopPropagation(),this.isDisabled()||(this.internalDragOver=!1,this.syncClasses(),this.dispatchEvent(new CustomEvent("db-drag-leave",{bubbles:!0,composed:!0,detail:{event:e}})))},this.handleDrop=e=>{e.preventDefault(),e.stopPropagation(),this.isDisabled()||(this.internalDragOver=!1,this.syncClasses(),this.emitFiles(e.dataTransfer?.files??null),this.dispatchEvent(new CustomEvent("db-drop",{bubbles:!0,composed:!0,detail:{event:e}})))},this.handleClick=e=>{this.isDisabled()||e.target.closest(".designbase-wc-dropzone__button")||(this.fileInput?.click(),this.dispatchEvent(new CustomEvent("db-click",{bubbles:!0,composed:!0,detail:{event:e}})))}}static get observedAttributes(){return["locale","size","variant","show-icon","icon","image","title","description","button-text","show-button","accept","max-size","multiple","drag-over","disabled","readonly"]}connectedCallback(){this.ensureInput(),this.sync(),this.addEventListener("dragover",this.handleDragOver),this.addEventListener("dragleave",this.handleDragLeave),this.addEventListener("drop",this.handleDrop),this.addEventListener("click",this.handleClick)}disconnectedCallback(){this.removeEventListener("dragover",this.handleDragOver),this.removeEventListener("dragleave",this.handleDragLeave),this.removeEventListener("drop",this.handleDrop),this.removeEventListener("click",this.handleClick)}attributeChangedCallback(){this.isConnected&&this.sync()}ensureInput(){this.fileInput||(this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.style.display="none",this.fileInput.setAttribute("data-wc-internal",""),this.fileInput.addEventListener("change",this.handleFileInputChange),this.append(this.fileInput))}isDisabled(){return this.hasAttribute("disabled")||this.hasAttribute("readonly")}isDragOver(){return this.hasAttribute("drag-over")?"false"!==this.getAttribute("drag-over"):this.internalDragOver}validateFile(e){const t=this.getAttribute("accept");if(t){const i=t.split(",").map(e=>e.trim()),s=e.type,n=`.${e.name.split(".").pop()?.toLowerCase()}`;if(!i.some(e=>e.startsWith(".")?n===e.toLowerCase():s===e||s.startsWith(e.replace("*",""))))return!1}const i=Number(this.getAttribute("max-size")||0);return!(i>0&&e.size>i)}emitFiles(e){if(!e||0===e.length)return;const t=Array.from(e).filter(e=>this.validateFile(e));t.length&&this.dispatchEvent(new CustomEvent("db-file-select",{bubbles:!0,composed:!0,detail:{files:t}}))}getDescriptionText(){const t=this.getAttribute("description");if(t)return t;const i=this.getAttribute("accept");if(!i)return e("upload.allFormats");let s=i.split(",").map(e=>{const t=e.trim();return t.startsWith(".")?t.toUpperCase():t.includes("*")?t.replace("*","").toUpperCase():t}).join(", ");const n=Number(this.getAttribute("max-size")||0);if(n>0){const t=1024,i=["Bytes","KB","MB","GB"],a=Math.floor(Math.log(n)/Math.log(t)),r=`${parseFloat((n/t**a).toFixed(2))} ${i[a]}`;s+=e("upload.maximum").replace("{size}",r)}return s}collectChildren(){return Array.from(this.children).filter(e=>e!==this.fileInput&&!e.hasAttribute("data-wc-internal"))}syncClasses(){const e=this.getAttribute("size")||"m",t=this.getAttribute("variant")||"default",n=i.includes(e)?e:"m",a=s.includes(t)?t:"default";this.className=["designbase-wc-dropzone",`designbase-wc-dropzone--size-${n}`,`designbase-wc-dropzone--variant-${a}`,this.isDragOver()&&"designbase-wc-dropzone--drag-over",this.hasAttribute("disabled")&&"designbase-wc-dropzone--disabled",this.hasAttribute("readonly")&&"designbase-wc-dropzone--readonly"].filter(Boolean).join(" ")}sync(){this.ensureInput(),this.syncClasses();const s=this.getAttribute("accept"),n=this.hasAttribute("multiple");this.fileInput&&(s?this.fileInput.setAttribute("accept",s):this.fileInput.removeAttribute("accept"),this.fileInput.toggleAttribute("multiple",n),this.fileInput.disabled=this.isDisabled());const a=this.collectChildren(),r=this.querySelector(":scope > .designbase-wc-dropzone__content[data-wc-internal]");if(a.length)return void r?.remove();let o=r;o||(o=document.createElement("div"),o.className="designbase-wc-dropzone__content",o.setAttribute("data-wc-internal",""),this.append(o)),o.replaceChildren();const l=this.getAttribute("size")||"m",d=i.includes(l)?l:"m",c=this.getAttribute("image"),h="false"!==this.getAttribute("show-icon"),p=this.getAttribute("icon")||"gallery",u=this.getAttribute("title")||e("upload.title"),b=this.getDescriptionText(),m=this.hasAttribute("show-button"),g=this.getAttribute("button-text")||e("upload.choose");if(c){const t=document.createElement("div");t.className="designbase-wc-dropzone__image";const i=document.createElement("img");i.src=c,i.alt=e("upload.illustration"),t.append(i),o.append(t)}else if(h){const e=document.createElement("div");e.className="designbase-wc-dropzone__icon";const i="s"===d?32:"l"===d?56:48;e.append(t(p,i)),o.append(e)}if(u){const e=document.createElement("div");e.className="designbase-wc-dropzone__title",e.textContent=u,o.append(e)}if(b){const e=document.createElement("div");e.className="designbase-wc-dropzone__description",e.textContent=b,o.append(e)}if(m){const e=document.createElement("button");e.type="button",e.className="designbase-wc-dropzone__button",e.textContent=g,e.disabled=this.isDisabled(),e.addEventListener("click",e=>{e.stopPropagation(),this.isDisabled()||this.fileInput?.click()}),o.append(e)}}}customElements.get("db-dropzone")||customElements.define("db-dropzone",DbDropzone);export{DbDropzone};
@@ -3,12 +3,6 @@ import './db-input.js';
3
3
  import './db-otp-input.js';
4
4
  import { EMAIL_VERIFY_SIZES } from '../ui-contracts/dist/components.d.js';
5
5
 
6
- /**
7
- * ui-wc — db-email-verify (수동 작성, 자동 동기화 대상 아님)
8
- *
9
- * 이메일 인증: code(OTP) 모드 / confirm(메일함 확인) 모드.
10
- */
11
-
12
6
  type Size = (typeof EMAIL_VERIFY_SIZES)[number];
13
7
  declare class DbEmailVerify extends HTMLElement {
14
8
  static get observedAttributes(): string[];
@@ -1 +1 @@
1
- import{createDbIcon as t}from"./db-icons.js";import"./db-button.js";import"./db-input.js";import"./db-otp-input.js";import{coerceToContract as e}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{EMAIL_VERIFY_SIZES as i}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbEmailVerify extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0}static get observedAttributes(){return["mode","email","code","step","size","disabled","error","error-message","countdown","title","description","send-label","resend-label","verify-label","confirm-label","code-length","closable","manual","sending","verifying","auto-submit"]}get mode(){return"confirm"===this.getAttribute("mode")?"confirm":"code"}set mode(t){this.setAttribute("mode",t)}get size(){return e(this.getAttribute("size"),i,"m")}set size(t){this.setAttribute("size",t)}get step(){const t=this.getAttribute("step");return"code"===t||"sent"===t||"verified"===t?t:"email"}set step(t){this.setAttribute("step",t)}get email(){return this.getAttribute("email")||""}set email(t){this.setAttribute("email",t)}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}set countdown(t){this.setAttribute("countdown",String(t))}connectedCallback(){this.build(),this.sync(),"code"===this.mode&&"code"===this.step&&this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"step"!==t&&"mode"!==t||e===i||("code"===this.mode&&"code"===this.step?this.startTimer(this.countdown):this.clearTimer()))}startCodeStep(){"code"!==this.mode&&(this.mode="code"),"code"===this.step?this.startTimer(this.countdown):this.step="code",requestAnimationFrame(()=>this.otpInput?.focus?.())}restartCountdown(){"code"===this.mode&&"code"===this.step&&this.startTimer(this.countdown)}markSent(){this.step="sent",this.clearTimer()}markVerified(){this.step="verified",this.clearTimer()}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0}startTimer(t){this.clearTimer(),this.remaining=t,this.renderTimer(),this.timerId=setInterval(()=>{this.remaining-=1,this.renderTimer(),this.remaining<=0&&(this.clearTimer(),this.resendBtn.disabled=!1,this.dispatchEvent(new CustomEvent("db-countdown-complete",{bubbles:!0,composed:!0})))},1e3)}renderTimer(){const t=Math.max(0,Math.floor(this.remaining/60)),e=Math.max(0,this.remaining%60);this.timerEl.textContent=`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`,this.timerEl.setAttribute("aria-label",`남은 시간 ${t}분 ${e}초`),this.resendBtn.disabled=this.remaining>0||this.hasAttribute("disabled")}build(){if(this.built)return;this.built=!0;const e="designbase-wc-email-verify";this.replaceChildren();const i=el("div",`${e}__shell`),s=el("div",`${e}__top`),n=el("span",`${e}__eyebrow`);n.dataset.role="eyebrow",this.closeBtn=el("button",`${e}__close`),this.closeBtn.type="button",this.closeBtn.setAttribute("aria-label","닫기"),this.closeBtn.append(t("close",18)),this.closeBtn.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))}),s.append(n,this.closeBtn);const r=el("div",`${e}__header`),o=el("h3",`${e}__title`);o.dataset.role="title";const d=el("p",`${e}__description`);d.dataset.role="description",r.append(o,d),this.emailRow=el("div",`${e}__email-row`),this.emailInput=document.createElement("db-input"),this.emailInput.setAttribute("type","email"),this.emailInput.setAttribute("label","이메일"),this.emailInput.setAttribute("placeholder","name@example.com"),this.emailInput.setAttribute("full-width",""),this.emailInput.setAttribute("start-icon","mail"),this.emailInput.addEventListener("db-input",t=>{t.stopPropagation();const e=String(t.detail?.value??this.emailInput.value??"");this.setAttribute("email",e),this.dispatchEvent(new CustomEvent("db-email-change",{detail:{email:e},bubbles:!0,composed:!0}))}),this.sendBtn=document.createElement("db-button"),this.sendBtn.setAttribute("variant","primary"),this.sendBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.isEmailValid()&&(this.dispatchEvent(new CustomEvent("db-send-code",{detail:{email:this.email,mode:this.mode},bubbles:!0,composed:!0})),this.hasAttribute("manual")||("confirm"===this.mode?this.markSent():this.startCodeStep()))}),this.emailRow.append(this.emailInput,this.sendBtn),this.codeBlock=el("div",`${e}__code`);const a=el("div",`${e}__code-head`),l=el("span",`${e}__code-label`);l.textContent="인증 코드",this.timerEl=el("span",`${e}__timer`),a.append(l,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:e},bubbles:!0,composed:!0}))}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.dispatchEvent(new CustomEvent("db-code-complete",{detail:{code:e,email:this.email},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()});const h=el("div",`${e}__code-actions`);this.resendBtn=el("button",`${e}__resend`),this.resendBtn.type="button",this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.dispatchEvent(new CustomEvent("db-resend",{detail:{email:this.email},bubbles:!0,composed:!0})),this.hasAttribute("manual")||this.startTimer(this.countdown))}),this.verifyBtn=document.createElement("db-button"),this.verifyBtn.setAttribute("variant","primary"),this.verifyBtn.setAttribute("full-width",""),this.verifyBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestVerify()}),h.append(this.resendBtn,this.verifyBtn),this.codeBlock.append(a,this.otpInput,h),this.confirmBlock=el("div",`${e}__confirm`);const c=el("div",`${e}__confirm-icon`);c.append(t("mail",40));const m=el("span",`${e}__confirm-badge`);m.append(t("circle-checked",16)),c.append(m);const u=el("h4",`${e}__confirm-title`);u.dataset.role="confirm-title";const b=el("p",`${e}__confirm-desc`);b.dataset.role="confirm-desc",this.confirmBtn=document.createElement("db-button"),this.confirmBtn.setAttribute("variant","primary"),this.confirmBtn.setAttribute("full-width",""),this.confirmBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.dispatchEvent(new CustomEvent("db-confirm",{detail:{email:this.email},bubbles:!0,composed:!0}))});const p=el("button",`${e}__resend`);p.type="button",p.dataset.role="confirm-resend",p.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-resend",{detail:{email:this.email},bubbles:!0,composed:!0}))}),this.confirmBlock.append(c,u,b,this.confirmBtn,p),this.verifiedBlock=el("div",`${e}__confirm`);const f=el("div",`${e}__confirm-icon`);f.append(t("circle-checked",40));const v=el("h4",`${e}__confirm-title`);v.textContent="이메일 인증 완료";const g=el("p",`${e}__confirm-desc`);g.dataset.role="verified-desc",this.verifiedBlock.append(f,v,g),this.errorEl=el("p",`${e}__error`),this.errorEl.hidden=!0,i.append(s,r,this.emailRow,this.codeBlock,this.confirmBlock,this.verifiedBlock,this.errorEl),this.append(i)}sync(){const t="designbase-wc-email-verify",e=this.size,i=this.mode,s=this.step,n=this.hasAttribute("disabled"),r=this.getAttribute("code-length")||"6",o=this.email;this.className=[t,`${t}--${e}`,`${t}--mode-${i}`,`${t}--step-${s}`,n&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" "),this.emailInput?.toggleAttribute("error",this.hasAttribute("error"));this.querySelector('[data-role="eyebrow"]').textContent="confirm"===i?"회원가입":"이메일 인증";const d=this.querySelector('[data-role="title"]'),a=this.querySelector('[data-role="description"]');d.textContent=this.getAttribute("title")||("confirm"===i?"이메일을 확인해 주세요":"이메일 인증");const l=this.getAttribute("description");l?a.textContent=l:"confirm"===i?this.setEmphasizedText(a,"",o||"your@email.com"," 으로 인증 링크를 보냈어요."):this.setEmphasizedText(a,"인증 코드를 ",o||"이메일"," 로 보내 드릴게요."),this.closeBtn&&(this.closeBtn.hidden=!this.hasAttribute("closable")),this.emailInput.value=o,this.emailInput.setAttribute("size",e),this.emailInput.toggleAttribute("disabled",n||"code"===s||"sent"===s||"verified"===s),this.sendBtn.setAttribute("size",e),this.sendBtn.textContent=this.getAttribute("send-label")||("confirm"===i?"인증 메일 보내기":"인증번호 받기"),this.sendBtn.toggleAttribute("disabled",n||!this.isEmailValid()||"email"!==s),this.sendBtn.toggleAttribute("loading",this.hasAttribute("sending")),this.otpInput.setAttribute("length",r),this.otpInput.setAttribute("size",e),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",n||"code"!==s),this.resendBtn.textContent=this.getAttribute("resend-label")||"재전송",this.verifyBtn.setAttribute("size",e),this.verifyBtn.textContent=this.getAttribute("verify-label")||"인증하기",this.verifyBtn.toggleAttribute("disabled",n||"code"!==s||(this.getAttribute("code")||"").length!==Number(r)),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying"));const h=this.querySelector('[data-role="confirm-title"]'),c=this.querySelector('[data-role="confirm-desc"]');h.textContent="메일함을 확인해 주세요!",this.setEmphasizedText(c,"",o||"your@email.com"," 으로 보낸 링크를 클릭하면 인증이 완료됩니다."),this.confirmBtn.setAttribute("size",e),this.confirmBtn.textContent=this.getAttribute("confirm-label")||"확인";const m=this.querySelector('[data-role="confirm-resend"]');m&&(m.textContent="메일 다시 보내기");const u=this.querySelector('[data-role="verified-desc"]');this.setEmphasizedText(u,"",o||"your@email.com"," 주소가 인증되었습니다."),this.emailRow.hidden="confirm"===i&&("sent"===s||"verified"===s)||"code"===i&&"verified"===s,this.codeBlock.hidden=!("code"===i&&"code"===s),this.confirmBlock.hidden=!("confirm"===i&&("sent"===s||"verified"===s)),this.verifiedBlock.hidden=!("code"===i&&"verified"===s);const b=this.getAttribute("error-message")||"";this.errorEl.textContent=b,this.errorEl.hidden=!this.hasAttribute("error")||!b}isEmailValid(){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email.trim())}requestVerify(){const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);"code"!==this.step||t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{email:this.email,code:t},bubbles:!0,composed:!0}))}setEmphasizedText(t,e,i,s){const n=document.createElement("strong");n.textContent=i,t.replaceChildren(document.createTextNode(e),n,document.createTextNode(s))}}customElements.get("db-email-verify")||customElements.define("db-email-verify",DbEmailVerify);export{DbEmailVerify};
1
+ import{dbFormat as t,dbText as e}from"../core/i18n.js";import{createDbIcon as i}from"./db-icons.js";import"./db-button.js";import"./db-input.js";import"./db-otp-input.js";import{coerceToContract as s}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";import{EMAIL_VERIFY_SIZES as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbEmailVerify extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0}static get observedAttributes(){return["mode","email","code","step","size","disabled","error","error-message","countdown","title","description","send-label","resend-label","verify-label","confirm-label","code-length","closable","manual","sending","verifying","auto-submit"]}get mode(){return"confirm"===this.getAttribute("mode")?"confirm":"code"}set mode(t){this.setAttribute("mode",t)}get size(){return s(this.getAttribute("size"),n,"m")}set size(t){this.setAttribute("size",t)}get step(){const t=this.getAttribute("step");return"code"===t||"sent"===t||"verified"===t?t:"email"}set step(t){this.setAttribute("step",t)}get email(){return this.getAttribute("email")||""}set email(t){this.setAttribute("email",t)}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}set countdown(t){this.setAttribute("countdown",String(t))}connectedCallback(){this.build(),this.sync(),"code"===this.mode&&"code"===this.step&&this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"step"!==t&&"mode"!==t||e===i||("code"===this.mode&&"code"===this.step?this.startTimer(this.countdown):this.clearTimer()))}startCodeStep(){"code"!==this.mode&&(this.mode="code"),"code"===this.step?this.startTimer(this.countdown):this.step="code",requestAnimationFrame(()=>this.otpInput?.focus?.())}restartCountdown(){"code"===this.mode&&"code"===this.step&&this.startTimer(this.countdown)}markSent(){this.step="sent",this.clearTimer()}markVerified(){this.step="verified",this.clearTimer()}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0}startTimer(t){this.clearTimer(),this.remaining=t,this.renderTimer(),this.timerId=setInterval(()=>{this.remaining-=1,this.renderTimer(),this.remaining<=0&&(this.clearTimer(),this.resendBtn.disabled=!1,this.dispatchEvent(new CustomEvent("db-countdown-complete",{bubbles:!0,composed:!0})))},1e3)}renderTimer(){const e=Math.max(0,Math.floor(this.remaining/60)),i=Math.max(0,this.remaining%60);this.timerEl.textContent=`${String(e).padStart(2,"0")}:${String(i).padStart(2,"0")}`,this.timerEl.setAttribute("aria-label",t("남은 시간 {0}분 {1}초",[e,i])),this.resendBtn.disabled=this.remaining>0||this.hasAttribute("disabled")}build(){if(this.built)return;this.built=!0;const t="designbase-wc-email-verify";this.replaceChildren();const s=el("div",`${t}__shell`),n=el("div",`${t}__top`),r=el("span",`${t}__eyebrow`);r.dataset.role="eyebrow",this.closeBtn=el("button",`${t}__close`),this.closeBtn.type="button",this.closeBtn.setAttribute("aria-label",e("닫기")),this.closeBtn.append(i("close",18)),this.closeBtn.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0}))}),n.append(r,this.closeBtn);const o=el("div",`${t}__header`),d=el("h3",`${t}__title`);d.dataset.role="title";const a=el("p",`${t}__description`);a.dataset.role="description",o.append(d,a),this.emailRow=el("div",`${t}__email-row`),this.emailInput=document.createElement("db-input"),this.emailInput.setAttribute("type","email"),this.emailInput.setAttribute("label","이메일"),this.emailInput.setAttribute("placeholder","name@example.com"),this.emailInput.setAttribute("full-width",""),this.emailInput.setAttribute("start-icon","mail"),this.emailInput.addEventListener("db-input",t=>{t.stopPropagation();const e=String(t.detail?.value??this.emailInput.value??"");this.setAttribute("email",e),this.dispatchEvent(new CustomEvent("db-email-change",{detail:{email:e},bubbles:!0,composed:!0}))}),this.sendBtn=document.createElement("db-button"),this.sendBtn.setAttribute("variant","primary"),this.sendBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.isEmailValid()&&(this.dispatchEvent(new CustomEvent("db-send-code",{detail:{email:this.email,mode:this.mode},bubbles:!0,composed:!0})),this.hasAttribute("manual")||("confirm"===this.mode?this.markSent():this.startCodeStep()))}),this.emailRow.append(this.emailInput,this.sendBtn),this.codeBlock=el("div",`${t}__code`);const l=el("div",`${t}__code-head`),h=el("span",`${t}__code-label`);h.textContent=e("인증 코드"),this.timerEl=el("span",`${t}__timer`),l.append(h,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:e},bubbles:!0,composed:!0}))}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.dispatchEvent(new CustomEvent("db-code-complete",{detail:{code:e,email:this.email},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()});const c=el("div",`${t}__code-actions`);this.resendBtn=el("button",`${t}__resend`),this.resendBtn.type="button",this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.dispatchEvent(new CustomEvent("db-resend",{detail:{email:this.email},bubbles:!0,composed:!0})),this.hasAttribute("manual")||this.startTimer(this.countdown))}),this.verifyBtn=document.createElement("db-button"),this.verifyBtn.setAttribute("variant","primary"),this.verifyBtn.setAttribute("full-width",""),this.verifyBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestVerify()}),c.append(this.resendBtn,this.verifyBtn),this.codeBlock.append(l,this.otpInput,c),this.confirmBlock=el("div",`${t}__confirm`);const m=el("div",`${t}__confirm-icon`);m.append(i("mail",40));const u=el("span",`${t}__confirm-badge`);u.append(i("circle-checked",16)),m.append(u);const b=el("h4",`${t}__confirm-title`);b.dataset.role="confirm-title";const p=el("p",`${t}__confirm-desc`);p.dataset.role="confirm-desc",this.confirmBtn=document.createElement("db-button"),this.confirmBtn.setAttribute("variant","primary"),this.confirmBtn.setAttribute("full-width",""),this.confirmBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.dispatchEvent(new CustomEvent("db-confirm",{detail:{email:this.email},bubbles:!0,composed:!0}))});const f=el("button",`${t}__resend`);f.type="button",f.dataset.role="confirm-resend",f.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-resend",{detail:{email:this.email},bubbles:!0,composed:!0}))}),this.confirmBlock.append(m,b,p,this.confirmBtn,f),this.verifiedBlock=el("div",`${t}__confirm`);const v=el("div",`${t}__confirm-icon`);v.append(i("circle-checked",40));const g=el("h4",`${t}__confirm-title`);g.textContent=e("이메일 인증 완료");const A=el("p",`${t}__confirm-desc`);A.dataset.role="verified-desc",this.verifiedBlock.append(v,g,A),this.errorEl=el("p",`${t}__error`),this.errorEl.hidden=!0,s.append(n,o,this.emailRow,this.codeBlock,this.confirmBlock,this.verifiedBlock,this.errorEl),this.append(s)}sync(){const t="designbase-wc-email-verify",i=this.size,s=this.mode,n=this.step,r=this.hasAttribute("disabled"),o=this.getAttribute("code-length")||"6",d=this.email;this.className=[t,`${t}--${i}`,`${t}--mode-${s}`,`${t}--step-${n}`,r&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" "),this.emailInput?.toggleAttribute("error",this.hasAttribute("error"));this.querySelector('[data-role="eyebrow"]').textContent="confirm"===s?"회원가입":"이메일 인증";const a=this.querySelector('[data-role="title"]'),l=this.querySelector('[data-role="description"]');a.textContent=this.getAttribute("title")||("confirm"===s?"이메일을 확인해 주세요":"이메일 인증");const h=this.getAttribute("description");h?l.textContent=h:"confirm"===s?this.setEmphasizedText(l,"",d||"your@email.com"," 으로 인증 링크를 보냈어요."):this.setEmphasizedText(l,"인증 코드를 ",d||"이메일"," 로 보내 드릴게요."),this.closeBtn&&(this.closeBtn.hidden=!this.hasAttribute("closable")),this.emailInput.value=d,this.emailInput.setAttribute("size",i),this.emailInput.toggleAttribute("disabled",r||"code"===n||"sent"===n||"verified"===n),this.sendBtn.setAttribute("size",i),this.sendBtn.textContent=this.getAttribute("send-label")||("confirm"===s?"인증 메일 보내기":"인증번호 받기"),this.sendBtn.toggleAttribute("disabled",r||!this.isEmailValid()||"email"!==n),this.sendBtn.toggleAttribute("loading",this.hasAttribute("sending")),this.otpInput.setAttribute("length",o),this.otpInput.setAttribute("size",i),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",r||"code"!==n),this.resendBtn.textContent=this.getAttribute("resend-label")||"재전송",this.verifyBtn.setAttribute("size",i),this.verifyBtn.textContent=this.getAttribute("verify-label")||"인증하기",this.verifyBtn.toggleAttribute("disabled",r||"code"!==n||(this.getAttribute("code")||"").length!==Number(o)),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying"));const c=this.querySelector('[data-role="confirm-title"]'),m=this.querySelector('[data-role="confirm-desc"]');c.textContent=e("메일함을 확인해 주세요!"),this.setEmphasizedText(m,"",d||"your@email.com"," 으로 보낸 링크를 클릭하면 인증이 완료됩니다."),this.confirmBtn.setAttribute("size",i),this.confirmBtn.textContent=this.getAttribute("confirm-label")||"확인";const u=this.querySelector('[data-role="confirm-resend"]');u&&(u.textContent=e("메일 다시 보내기"));const b=this.querySelector('[data-role="verified-desc"]');this.setEmphasizedText(b,"",d||"your@email.com"," 주소가 인증되었습니다."),this.emailRow.hidden="confirm"===s&&("sent"===n||"verified"===n)||"code"===s&&"verified"===n,this.codeBlock.hidden=!("code"===s&&"code"===n),this.confirmBlock.hidden=!("confirm"===s&&("sent"===n||"verified"===n)),this.verifiedBlock.hidden=!("code"===s&&"verified"===n);const p=this.getAttribute("error-message")||"";this.errorEl.textContent=p,this.errorEl.hidden=!this.hasAttribute("error")||!p}isEmailValid(){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email.trim())}requestVerify(){const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);"code"!==this.step||t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{email:this.email,code:t},bubbles:!0,composed:!0}))}setEmphasizedText(t,e,i,s){const n=document.createElement("strong");n.textContent=i,t.replaceChildren(document.createTextNode(e),n,document.createTextNode(s))}}customElements.get("db-email-verify")||customElements.define("db-email-verify",DbEmailVerify);export{DbEmailVerify};