@designbasekorea/ui-wc 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/chunks/lottie-0e23d630.js +1 -0
  2. package/dist/esm/components/animation-background/canvas-controller.js +1 -121
  3. package/dist/esm/components/animation-background/controls.js +1 -180
  4. package/dist/esm/components/animation-background/graphite-controller.js +1 -196
  5. package/dist/esm/components/animation-background/graphite-presets.js +1 -42
  6. package/dist/esm/components/animation-background/graphite-shader.js +1 -111
  7. package/dist/esm/components/animation-background/layers.js +1 -369
  8. package/dist/esm/components/animation-background/lumina-presets.js +1 -53
  9. package/dist/esm/components/animation-background/lumina-shaders.js +1 -181
  10. package/dist/esm/components/animation-background/lumina-utils.js +1 -60
  11. package/dist/esm/components/animation-background/particle-defaults.js +1 -36
  12. package/dist/esm/components/animation-background/renderers.js +1 -167
  13. package/dist/esm/components/animation-background/shader-controller.js +1 -154
  14. package/dist/esm/components/color-picker-utils.js +1 -134
  15. package/dist/esm/components/date-picker-utils.js +1 -140
  16. package/dist/esm/components/db-accordion.js +1 -304
  17. package/dist/esm/components/db-ad-banner.js +1 -455
  18. package/dist/esm/components/db-ai-chat-list.d.ts +43 -0
  19. package/dist/esm/components/db-ai-chat-list.js +1 -0
  20. package/dist/esm/components/db-ai-chat.d.ts +159 -0
  21. package/dist/esm/components/db-ai-chat.js +1 -0
  22. package/dist/esm/components/db-alert.js +1 -149
  23. package/dist/esm/components/db-animation-background.js +1 -486
  24. package/dist/esm/components/db-animation-text.js +1 -601
  25. package/dist/esm/components/db-app-badge.js +1 -88
  26. package/dist/esm/components/db-audio-player.js +1 -752
  27. package/dist/esm/components/db-avatar.js +1 -188
  28. package/dist/esm/components/db-backdrop.js +1 -139
  29. package/dist/esm/components/db-badge.js +1 -91
  30. package/dist/esm/components/db-banner.js +1 -188
  31. package/dist/esm/components/db-bottom-navigation.js +1 -146
  32. package/dist/esm/components/db-bottom-sheet.js +1 -481
  33. package/dist/esm/components/db-breadcrumbs.js +1 -329
  34. package/dist/esm/components/db-button.js +1 -316
  35. package/dist/esm/components/db-calendar.js +1 -675
  36. package/dist/esm/components/db-card.js +1 -265
  37. package/dist/esm/components/db-carousel.js +1 -264
  38. package/dist/esm/components/db-chat-composer.d.ts +134 -0
  39. package/dist/esm/components/db-chat-composer.js +1 -0
  40. package/dist/esm/components/db-chat-list.d.ts +47 -0
  41. package/dist/esm/components/db-chat-list.js +1 -0
  42. package/dist/esm/components/db-chat-message.d.ts +39 -0
  43. package/dist/esm/components/db-chat-message.js +1 -0
  44. package/dist/esm/components/db-chat-orb.d.ts +99 -0
  45. package/dist/esm/components/db-chat-orb.js +1 -0
  46. package/dist/esm/components/db-chat-room.d.ts +71 -0
  47. package/dist/esm/components/db-chat-room.js +1 -0
  48. package/dist/esm/components/db-chat-suggestions.d.ts +44 -0
  49. package/dist/esm/components/db-chat-suggestions.js +1 -0
  50. package/dist/esm/components/db-chat-typing.d.ts +22 -0
  51. package/dist/esm/components/db-chat-typing.js +1 -0
  52. package/dist/esm/components/db-chat-welcome.d.ts +32 -0
  53. package/dist/esm/components/db-chat-welcome.js +1 -0
  54. package/dist/esm/components/db-chat-widget.d.ts +146 -0
  55. package/dist/esm/components/db-chat-widget.js +1 -0
  56. package/dist/esm/components/db-checkbox.js +1 -202
  57. package/dist/esm/components/db-chip.js +1 -104
  58. package/dist/esm/components/db-code-block.js +1 -304
  59. package/dist/esm/components/db-color-picker.js +1 -746
  60. package/dist/esm/components/db-confirm.js +1 -191
  61. package/dist/esm/components/db-container.js +1 -39
  62. package/dist/esm/components/db-context-menu.js +1 -265
  63. package/dist/esm/components/db-countdown.js +1 -146
  64. package/dist/esm/components/db-cursor-follower.js +1 -173
  65. package/dist/esm/components/db-date-picker.js +1 -837
  66. package/dist/esm/components/db-divider.js +1 -66
  67. package/dist/esm/components/db-download-progress.d.ts +89 -0
  68. package/dist/esm/components/db-download-progress.js +1 -0
  69. package/dist/esm/components/db-drawer.js +1 -245
  70. package/dist/esm/components/db-dropdown.js +1 -493
  71. package/dist/esm/components/db-dropzone.js +1 -266
  72. package/dist/esm/components/db-email-verify.d.ts +62 -0
  73. package/dist/esm/components/db-email-verify.js +1 -0
  74. package/dist/esm/components/db-empty-state.js +1 -97
  75. package/dist/esm/components/db-file-uploader.js +1 -256
  76. package/dist/esm/components/db-floating-action-button.js +1 -139
  77. package/dist/esm/components/db-footer.js +1 -385
  78. package/dist/esm/components/db-form.js +1 -566
  79. package/dist/esm/components/db-gantt.js +1 -882
  80. package/dist/esm/components/db-gradient.js +1 -133
  81. package/dist/esm/components/db-grid-background.js +1 -909
  82. package/dist/esm/components/db-grid.js +1 -73
  83. package/dist/esm/components/db-icons.d.ts +3 -0
  84. package/dist/esm/components/db-icons.js +1 -82
  85. package/dist/esm/components/db-image-list.js +1 -214
  86. package/dist/esm/components/db-image-placeholder.js +1 -96
  87. package/dist/esm/components/db-image.js +1 -289
  88. package/dist/esm/components/db-indicator.js +1 -517
  89. package/dist/esm/components/db-input.js +1 -420
  90. package/dist/esm/components/db-kanban.js +1 -538
  91. package/dist/esm/components/db-label.js +1 -66
  92. package/dist/esm/components/db-lightbox.js +1 -372
  93. package/dist/esm/components/db-list.js +1 -340
  94. package/dist/esm/components/db-logo-svg.js +1 -72
  95. package/dist/esm/components/db-logo.js +1 -106
  96. package/dist/esm/components/db-lottie.js +1 -109
  97. package/dist/esm/components/db-markdown-editor.js +1 -458
  98. package/dist/esm/components/db-marquee.js +1 -402
  99. package/dist/esm/components/db-masonry.js +1 -94
  100. package/dist/esm/components/db-menu-item.js +1 -211
  101. package/dist/esm/components/db-modal.js +1 -522
  102. package/dist/esm/components/db-navbar.js +1 -680
  103. package/dist/esm/components/db-onboarding-modal.js +1 -322
  104. package/dist/esm/components/db-otp-input.d.ts +53 -0
  105. package/dist/esm/components/db-otp-input.js +1 -0
  106. package/dist/esm/components/db-page-header.js +1 -312
  107. package/dist/esm/components/db-pagination.js +1 -271
  108. package/dist/esm/components/db-phone-verify.d.ts +61 -0
  109. package/dist/esm/components/db-phone-verify.js +1 -0
  110. package/dist/esm/components/db-popover.js +1 -580
  111. package/dist/esm/components/db-progress-step.js +1 -166
  112. package/dist/esm/components/db-progress.js +1 -171
  113. package/dist/esm/components/db-progressbar.js +1 -72
  114. package/dist/esm/components/db-radio.js +1 -172
  115. package/dist/esm/components/db-random-gradient.js +1 -121
  116. package/dist/esm/components/db-range-slider.js +1 -811
  117. package/dist/esm/components/db-rating.js +1 -325
  118. package/dist/esm/components/db-reorder.js +1 -286
  119. package/dist/esm/components/db-resizable-panels.js +1 -282
  120. package/dist/esm/components/db-scroll-area.js +1 -126
  121. package/dist/esm/components/db-search-bar.js +1 -668
  122. package/dist/esm/components/db-section-app-download.js +1 -101
  123. package/dist/esm/components/db-section-contact.js +1 -102
  124. package/dist/esm/components/db-section-cta.js +1 -150
  125. package/dist/esm/components/db-section-faq.js +1 -120
  126. package/dist/esm/components/db-section-feature-grid.js +1 -137
  127. package/dist/esm/components/db-section-feature.js +1 -179
  128. package/dist/esm/components/db-section-hero.js +1 -243
  129. package/dist/esm/components/db-section-logo-cloud.js +1 -138
  130. package/dist/esm/components/db-section-pricing.js +1 -166
  131. package/dist/esm/components/db-section-stats.js +1 -129
  132. package/dist/esm/components/db-section-testimonials.js +1 -252
  133. package/dist/esm/components/db-section.js +1 -385
  134. package/dist/esm/components/db-segment-control.js +1 -231
  135. package/dist/esm/components/db-select.js +1 -571
  136. package/dist/esm/components/db-share-icons.js +1 -40
  137. package/dist/esm/components/db-share.js +1 -489
  138. package/dist/esm/components/db-sidebar.js +1 -1068
  139. package/dist/esm/components/db-skeleton.js +1 -94
  140. package/dist/esm/components/db-spinner.js +1 -102
  141. package/dist/esm/components/db-split-view.js +1 -297
  142. package/dist/esm/components/db-stack.js +1 -46
  143. package/dist/esm/components/db-stat.js +1 -218
  144. package/dist/esm/components/db-stepper.js +1 -398
  145. package/dist/esm/components/db-table.js +1 -412
  146. package/dist/esm/components/db-tabs.js +1 -335
  147. package/dist/esm/components/db-testimonial.d.ts +2 -0
  148. package/dist/esm/components/db-testimonial.js +1 -162
  149. package/dist/esm/components/db-textarea.js +1 -233
  150. package/dist/esm/components/db-time-picker.js +1 -523
  151. package/dist/esm/components/db-timeline.js +1 -174
  152. package/dist/esm/components/db-toast.js +1 -158
  153. package/dist/esm/components/db-toggle.js +1 -162
  154. package/dist/esm/components/db-toolbar.js +1 -94
  155. package/dist/esm/components/db-tooltip.js +1 -440
  156. package/dist/esm/components/db-top-banner.js +1 -121
  157. package/dist/esm/components/db-tutorial.js +1 -324
  158. package/dist/esm/components/db-two-factor.d.ts +60 -0
  159. package/dist/esm/components/db-two-factor.js +1 -0
  160. package/dist/esm/components/db-video-player.js +1 -503
  161. package/dist/esm/components/db-youtube-player.js +1 -194
  162. package/dist/esm/components/highlight-code.js +1 -65
  163. package/dist/esm/components/input-validation.js +1 -50
  164. package/dist/esm/components/marketing-block-host-utils.js +1 -9
  165. package/dist/esm/components/number-scrub.js +1 -117
  166. package/dist/esm/components/random-gradient-palettes.js +1 -67
  167. package/dist/esm/components/section-block-header-utils.js +1 -48
  168. package/dist/esm/helpers/confirm.js +1 -52
  169. package/dist/esm/helpers/toast.js +1 -73
  170. package/dist/esm/index.d.ts +16 -0
  171. package/dist/esm/index.js +1 -111
  172. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-bash.js +1 -0
  173. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-clike.js +1 -0
  174. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-css.js +1 -0
  175. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-javascript.js +1 -0
  176. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-json.js +1 -0
  177. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-jsx.js +1 -0
  178. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-markup.js +1 -0
  179. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-scss.js +1 -0
  180. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-tsx.js +1 -0
  181. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-typescript.js +1 -0
  182. package/dist/index.cjs +1 -55414
  183. package/dist/index.css +1 -1
  184. package/dist/index.d.ts +1091 -2
  185. package/dist/index.esm.js +1 -38284
  186. package/dist/react/chunks/lottie-14e7addb.js +1 -0
  187. package/dist/react/index.cjs +1 -56503
  188. package/dist/react/index.d.ts +1620 -54
  189. package/dist/react/index.esm.js +1 -39373
  190. package/package.json +1 -1
  191. package/dist/chunks/lottie-e273b936.js +0 -17032
  192. package/dist/esm/node_modules/prismjs/components/prism-bash.js +0 -235
  193. package/dist/esm/node_modules/prismjs/components/prism-clike.js +0 -31
  194. package/dist/esm/node_modules/prismjs/components/prism-css.js +0 -64
  195. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +0 -172
  196. package/dist/esm/node_modules/prismjs/components/prism-json.js +0 -27
  197. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +0 -143
  198. package/dist/esm/node_modules/prismjs/components/prism-markup.js +0 -186
  199. package/dist/esm/node_modules/prismjs/components/prism-scss.js +0 -81
  200. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +0 -15
  201. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +0 -60
  202. package/dist/react/chunks/lottie-72a4c6d4.js +0 -17032
@@ -1,324 +1 @@
1
- import { createDbIcon } from './db-icons.js';
2
-
3
- const CLASS = 'designbase-wc-tutorial';
4
- class DbTutorial extends HTMLElement {
5
- constructor() {
6
- super(...arguments);
7
- this.internalStep = 0;
8
- this.targetElement = null;
9
- this.keyHandler = null;
10
- this.resizeHandler = null;
11
- this.isMounted = false;
12
- }
13
- static get observedAttributes() {
14
- return ['open', 'steps', 'current-step', 'indicator-type', 'indicator-size'];
15
- }
16
- connectedCallback() {
17
- this.style.display = 'contents';
18
- this.syncStepFromAttribute();
19
- this.syncOpenState();
20
- }
21
- disconnectedCallback() {
22
- this.detachListeners();
23
- this.clearTargetHighlight();
24
- this.unmount();
25
- }
26
- attributeChangedCallback(name) {
27
- if (!this.isConnected)
28
- return;
29
- if (name === 'open') {
30
- this.syncOpenState();
31
- return;
32
- }
33
- if (name === 'current-step')
34
- this.syncStepFromAttribute();
35
- if (this.isMounted)
36
- this.render();
37
- }
38
- get steps() {
39
- return this.parseSteps();
40
- }
41
- set steps(value) {
42
- this.setAttribute('steps', typeof value === 'string' ? value : JSON.stringify(value));
43
- }
44
- parseSteps() {
45
- try {
46
- const parsed = JSON.parse(this.getAttribute('steps') || '[]');
47
- return Array.isArray(parsed) ? parsed : [];
48
- }
49
- catch {
50
- return [];
51
- }
52
- }
53
- syncStepFromAttribute() {
54
- if (!this.hasAttribute('current-step'))
55
- return;
56
- const next = Number(this.getAttribute('current-step') || 0);
57
- if (Number.isFinite(next))
58
- this.internalStep = Math.max(0, next);
59
- }
60
- isOpenRequested() {
61
- return this.hasAttribute('open') && this.getAttribute('open') !== 'false';
62
- }
63
- getCurrentStepData() {
64
- const steps = this.parseSteps();
65
- if (!steps.length)
66
- return undefined;
67
- const index = Math.min(Math.max(this.internalStep, 0), steps.length - 1);
68
- return { step: steps[index], index, steps };
69
- }
70
- syncOpenState() {
71
- if (this.isOpenRequested()) {
72
- this.mount();
73
- this.attachListeners();
74
- return;
75
- }
76
- this.detachListeners();
77
- this.clearTargetHighlight();
78
- this.unmount();
79
- }
80
- mount() {
81
- if (!this.root) {
82
- this.root = document.createElement('div');
83
- this.root.className = CLASS;
84
- this.root.setAttribute('role', 'dialog');
85
- this.root.setAttribute('aria-modal', 'true');
86
- this.root.tabIndex = -1;
87
- this.ensureStructure();
88
- }
89
- if (!this.root.isConnected)
90
- document.body.append(this.root);
91
- this.isMounted = true;
92
- this.render();
93
- }
94
- unmount() {
95
- this.isMounted = false;
96
- this.root?.remove();
97
- }
98
- ensureStructure() {
99
- if (!this.root || this.popoverEl)
100
- return;
101
- this.overlayEl = document.createElement('div');
102
- this.overlayEl.className = `${CLASS}__overlay`;
103
- this.overlayEl.addEventListener('click', () => this.handleEnd());
104
- this.popoverEl = document.createElement('div');
105
- this.popoverEl.className = `${CLASS}__popover`;
106
- this.closeButtonEl = document.createElement('button');
107
- this.closeButtonEl.type = 'button';
108
- this.closeButtonEl.className = `${CLASS}__close-button`;
109
- this.closeButtonEl.setAttribute('aria-label', '튜토리얼 종료');
110
- this.closeButtonEl.append(createDbIcon('close', 16));
111
- this.closeButtonEl.addEventListener('click', () => this.handleEnd());
112
- const content = document.createElement('div');
113
- content.className = `${CLASS}__content`;
114
- const text = document.createElement('div');
115
- text.className = `${CLASS}__text`;
116
- this.titleEl = document.createElement('h3');
117
- this.titleEl.id = 'tutorial-title';
118
- this.titleEl.className = `${CLASS}__title`;
119
- this.descriptionEl = document.createElement('p');
120
- this.descriptionEl.id = 'tutorial-content';
121
- this.descriptionEl.className = `${CLASS}__description`;
122
- text.append(this.titleEl, this.descriptionEl);
123
- const navigation = document.createElement('div');
124
- navigation.className = `${CLASS}__navigation`;
125
- this.prevButtonEl = document.createElement('db-button');
126
- this.prevButtonEl.setAttribute('variant', 'secondary');
127
- this.prevButtonEl.setAttribute('size', 's');
128
- this.prevButtonEl.setAttribute('start-icon', 'chevron-left');
129
- this.prevButtonEl.textContent = '이전';
130
- this.prevButtonEl.addEventListener('db-click', () => this.handlePrev());
131
- this.indicatorEl = document.createElement('db-indicator');
132
- this.indicatorEl.addEventListener('db-step-click', (event) => {
133
- const detail = event.detail;
134
- if (typeof detail.index === 'number')
135
- this.goToStep(detail.index);
136
- });
137
- this.nextButtonEl = document.createElement('db-button');
138
- this.nextButtonEl.setAttribute('variant', 'primary');
139
- this.nextButtonEl.setAttribute('size', 's');
140
- this.nextButtonEl.addEventListener('db-click', () => this.handleNext());
141
- navigation.append(this.prevButtonEl, this.indicatorEl, this.nextButtonEl);
142
- content.append(text, navigation);
143
- this.popoverEl.append(this.closeButtonEl, content);
144
- this.root.append(this.overlayEl, this.popoverEl);
145
- }
146
- attachListeners() {
147
- if (!this.keyHandler) {
148
- this.keyHandler = (event) => {
149
- if (event.key === 'Escape')
150
- this.handleEnd();
151
- else if (event.key === 'ArrowLeft')
152
- this.handlePrev();
153
- else if (event.key === 'ArrowRight')
154
- this.handleNext();
155
- };
156
- document.addEventListener('keydown', this.keyHandler);
157
- }
158
- if (!this.resizeHandler) {
159
- this.resizeHandler = () => this.updateTargetElement();
160
- window.addEventListener('resize', this.resizeHandler);
161
- }
162
- }
163
- detachListeners() {
164
- if (this.keyHandler) {
165
- document.removeEventListener('keydown', this.keyHandler);
166
- this.keyHandler = null;
167
- }
168
- if (this.resizeHandler) {
169
- window.removeEventListener('resize', this.resizeHandler);
170
- this.resizeHandler = null;
171
- }
172
- }
173
- clearTargetHighlight() {
174
- if (!this.targetElement)
175
- return;
176
- this.targetElement.classList.remove(`${CLASS}__target`);
177
- this.targetElement.style.position = '';
178
- this.targetElement.style.zIndex = '';
179
- this.targetElement = null;
180
- }
181
- updateTargetElement() {
182
- const current = this.getCurrentStepData();
183
- if (!current?.step.target || !this.popoverEl)
184
- return;
185
- this.clearTargetHighlight();
186
- const element = document.querySelector(current.step.target);
187
- if (!element)
188
- return;
189
- this.targetElement = element;
190
- element.style.position = 'relative';
191
- element.style.zIndex = '1000';
192
- element.classList.add(`${CLASS}__target`);
193
- const rect = element.getBoundingClientRect();
194
- const viewportWidth = window.innerWidth;
195
- const viewportHeight = window.innerHeight;
196
- const popoverWidth = 300;
197
- const popoverHeight = 200;
198
- const margin = 16;
199
- let placement = current.step.placement === 'auto' || !current.step.placement
200
- ? 'bottom'
201
- : current.step.placement;
202
- let top = rect.bottom + margin;
203
- let left = rect.left + rect.width / 2 - popoverWidth / 2;
204
- if (current.step.placement === 'auto') {
205
- if (rect.top - popoverHeight - margin > 0) {
206
- placement = 'top';
207
- top = rect.top - popoverHeight - margin;
208
- }
209
- else if (rect.bottom + popoverHeight + margin < viewportHeight) {
210
- placement = 'bottom';
211
- top = rect.bottom + margin;
212
- }
213
- else if (rect.left - popoverWidth - margin > 0) {
214
- placement = 'left';
215
- top = rect.top + rect.height / 2 - popoverHeight / 2;
216
- left = rect.left - popoverWidth - margin;
217
- }
218
- else {
219
- placement = 'right';
220
- top = rect.top + rect.height / 2 - popoverHeight / 2;
221
- left = rect.right + margin;
222
- }
223
- }
224
- else {
225
- switch (placement) {
226
- case 'top':
227
- top = rect.top - popoverHeight - margin;
228
- break;
229
- case 'bottom':
230
- top = rect.bottom + margin;
231
- break;
232
- case 'left':
233
- top = rect.top + rect.height / 2 - popoverHeight / 2;
234
- left = rect.left - popoverWidth - margin;
235
- break;
236
- case 'right':
237
- top = rect.top + rect.height / 2 - popoverHeight / 2;
238
- left = rect.right + margin;
239
- break;
240
- }
241
- }
242
- left = Math.max(margin, Math.min(left, viewportWidth - popoverWidth - margin));
243
- top = Math.max(margin, Math.min(top, viewportHeight - popoverHeight - margin));
244
- this.popoverEl.className = [`${CLASS}__popover`, `${CLASS}__popover--${placement}`].join(' ');
245
- this.popoverEl.style.top = `${top}px`;
246
- this.popoverEl.style.left = `${left}px`;
247
- }
248
- emitStepChange(step) {
249
- this.dispatchEvent(new CustomEvent('db-step-change', {
250
- bubbles: true,
251
- composed: true,
252
- detail: { step },
253
- }));
254
- }
255
- goToStep(step) {
256
- const steps = this.parseSteps();
257
- if (!steps.length)
258
- return;
259
- this.clearTargetHighlight();
260
- this.internalStep = Math.min(Math.max(step, 0), steps.length - 1);
261
- this.setAttribute('current-step', String(this.internalStep));
262
- this.emitStepChange(this.internalStep);
263
- this.render();
264
- }
265
- handlePrev() {
266
- const current = this.getCurrentStepData();
267
- if (!current || current.index <= 0)
268
- return;
269
- this.dispatchEvent(new CustomEvent('db-prev', { bubbles: true, composed: true }));
270
- this.goToStep(current.index - 1);
271
- }
272
- handleNext() {
273
- const current = this.getCurrentStepData();
274
- if (!current)
275
- return;
276
- if (current.index >= current.steps.length - 1) {
277
- this.handleEnd();
278
- return;
279
- }
280
- this.dispatchEvent(new CustomEvent('db-next', { bubbles: true, composed: true }));
281
- this.goToStep(current.index + 1);
282
- }
283
- handleEnd() {
284
- this.dispatchEvent(new CustomEvent('db-end', { bubbles: true, composed: true }));
285
- this.setAttribute('open', 'false');
286
- }
287
- render() {
288
- const current = this.getCurrentStepData();
289
- if (!current || !this.root)
290
- return;
291
- const { step, index, steps } = current;
292
- const isFirst = index === 0;
293
- const isLast = index === steps.length - 1;
294
- if (this.titleEl) {
295
- this.titleEl.textContent = step.title || '';
296
- this.titleEl.hidden = !step.title;
297
- }
298
- if (this.descriptionEl) {
299
- this.descriptionEl.textContent = step.content || '';
300
- this.descriptionEl.hidden = !step.content;
301
- }
302
- if (this.prevButtonEl)
303
- this.prevButtonEl.hidden = isFirst;
304
- if (this.nextButtonEl) {
305
- this.nextButtonEl.textContent = isLast ? '완료' : '다음';
306
- if (isLast)
307
- this.nextButtonEl.removeAttribute('end-icon');
308
- else
309
- this.nextButtonEl.setAttribute('end-icon', 'chevron-right');
310
- }
311
- if (this.indicatorEl) {
312
- this.indicatorEl.setAttribute('current', String(index));
313
- this.indicatorEl.setAttribute('total', String(steps.length));
314
- this.indicatorEl.setAttribute('type', this.getAttribute('indicator-type') || 'numbers');
315
- this.indicatorEl.setAttribute('size', this.getAttribute('indicator-size') || 'm');
316
- }
317
- this.updateTargetElement();
318
- }
319
- }
320
- if (!customElements.get('db-tutorial')) {
321
- customElements.define('db-tutorial', DbTutorial);
322
- }
323
-
324
- export { DbTutorial };
1
+ import{createDbIcon as t}from"./db-icons.js";const e="designbase-wc-tutorial";class DbTutorial extends HTMLElement{constructor(){super(...arguments),this.internalStep=0,this.targetElement=null,this.keyHandler=null,this.resizeHandler=null,this.isMounted=!1}static get observedAttributes(){return["open","steps","current-step","indicator-type","indicator-size"]}connectedCallback(){this.style.display="contents",this.syncStepFromAttribute(),this.syncOpenState()}disconnectedCallback(){this.detachListeners(),this.clearTargetHighlight(),this.unmount()}attributeChangedCallback(t){this.isConnected&&("open"!==t?("current-step"===t&&this.syncStepFromAttribute(),this.isMounted&&this.render()):this.syncOpenState())}get steps(){return this.parseSteps()}set steps(t){this.setAttribute("steps","string"==typeof t?t:JSON.stringify(t))}parseSteps(){try{const t=JSON.parse(this.getAttribute("steps")||"[]");return Array.isArray(t)?t:[]}catch{return[]}}syncStepFromAttribute(){if(!this.hasAttribute("current-step"))return;const t=Number(this.getAttribute("current-step")||0);Number.isFinite(t)&&(this.internalStep=Math.max(0,t))}isOpenRequested(){return this.hasAttribute("open")&&"false"!==this.getAttribute("open")}getCurrentStepData(){const t=this.parseSteps();if(!t.length)return;const e=Math.min(Math.max(this.internalStep,0),t.length-1);return{step:t[e],index:e,steps:t}}syncOpenState(){if(this.isOpenRequested())return this.mount(),void this.attachListeners();this.detachListeners(),this.clearTargetHighlight(),this.unmount()}mount(){this.root||(this.root=document.createElement("div"),this.root.className=e,this.root.setAttribute("role","dialog"),this.root.setAttribute("aria-modal","true"),this.root.tabIndex=-1,this.ensureStructure()),this.root.isConnected||document.body.append(this.root),this.isMounted=!0,this.render()}unmount(){this.isMounted=!1,this.root?.remove()}ensureStructure(){if(!this.root||this.popoverEl)return;this.overlayEl=document.createElement("div"),this.overlayEl.className=`${e}__overlay`,this.overlayEl.addEventListener("click",()=>this.handleEnd()),this.popoverEl=document.createElement("div"),this.popoverEl.className=`${e}__popover`,this.closeButtonEl=document.createElement("button"),this.closeButtonEl.type="button",this.closeButtonEl.className=`${e}__close-button`,this.closeButtonEl.setAttribute("aria-label","튜토리얼 종료"),this.closeButtonEl.append(t("close",16)),this.closeButtonEl.addEventListener("click",()=>this.handleEnd());const i=document.createElement("div");i.className=`${e}__content`;const s=document.createElement("div");s.className=`${e}__text`,this.titleEl=document.createElement("h3"),this.titleEl.id="tutorial-title",this.titleEl.className=`${e}__title`,this.descriptionEl=document.createElement("p"),this.descriptionEl.id="tutorial-content",this.descriptionEl.className=`${e}__description`,s.append(this.titleEl,this.descriptionEl);const n=document.createElement("div");n.className=`${e}__navigation`,this.prevButtonEl=document.createElement("db-button"),this.prevButtonEl.setAttribute("variant","secondary"),this.prevButtonEl.setAttribute("size","s"),this.prevButtonEl.setAttribute("start-icon","chevron-left"),this.prevButtonEl.textContent="이전",this.prevButtonEl.addEventListener("db-click",()=>this.handlePrev()),this.indicatorEl=document.createElement("db-indicator"),this.indicatorEl.addEventListener("db-step-click",t=>{const e=t.detail;"number"==typeof e.index&&this.goToStep(e.index)}),this.nextButtonEl=document.createElement("db-button"),this.nextButtonEl.setAttribute("variant","primary"),this.nextButtonEl.setAttribute("size","s"),this.nextButtonEl.addEventListener("db-click",()=>this.handleNext()),n.append(this.prevButtonEl,this.indicatorEl,this.nextButtonEl),i.append(s,n),this.popoverEl.append(this.closeButtonEl,i),this.root.append(this.overlayEl,this.popoverEl)}attachListeners(){this.keyHandler||(this.keyHandler=t=>{"Escape"===t.key?this.handleEnd():"ArrowLeft"===t.key?this.handlePrev():"ArrowRight"===t.key&&this.handleNext()},document.addEventListener("keydown",this.keyHandler)),this.resizeHandler||(this.resizeHandler=()=>this.updateTargetElement(),window.addEventListener("resize",this.resizeHandler))}detachListeners(){this.keyHandler&&(document.removeEventListener("keydown",this.keyHandler),this.keyHandler=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}clearTargetHighlight(){this.targetElement&&(this.targetElement.classList.remove(`${e}__target`),this.targetElement.style.position="",this.targetElement.style.zIndex="",this.targetElement=null)}updateTargetElement(){const t=this.getCurrentStepData();if(!t?.step.target||!this.popoverEl)return;this.clearTargetHighlight();const i=document.querySelector(t.step.target);if(!i)return;this.targetElement=i,i.style.position="relative",i.style.zIndex="1000",i.classList.add(`${e}__target`);const s=i.getBoundingClientRect(),n=window.innerWidth,r=window.innerHeight,o=300,l=200,h=16;let a="auto"!==t.step.placement&&t.step.placement?t.step.placement:"bottom",d=s.bottom+h,c=s.left+s.width/2-150;if("auto"===t.step.placement)s.top-l-h>0?(a="top",d=s.top-l-h):s.bottom+l+h<r?(a="bottom",d=s.bottom+h):s.left-o-h>0?(a="left",d=s.top+s.height/2-100,c=s.left-o-h):(a="right",d=s.top+s.height/2-100,c=s.right+h);else switch(a){case"top":d=s.top-l-h;break;case"bottom":d=s.bottom+h;break;case"left":d=s.top+s.height/2-100,c=s.left-o-h;break;case"right":d=s.top+s.height/2-100,c=s.right+h}c=Math.max(h,Math.min(c,n-o-h)),d=Math.max(h,Math.min(d,r-l-h)),this.popoverEl.className=[`${e}__popover`,`${e}__popover--${a}`].join(" "),this.popoverEl.style.top=`${d}px`,this.popoverEl.style.left=`${c}px`}emitStepChange(t){this.dispatchEvent(new CustomEvent("db-step-change",{bubbles:!0,composed:!0,detail:{step:t}}))}goToStep(t){const e=this.parseSteps();e.length&&(this.clearTargetHighlight(),this.internalStep=Math.min(Math.max(t,0),e.length-1),this.setAttribute("current-step",String(this.internalStep)),this.emitStepChange(this.internalStep),this.render())}handlePrev(){const t=this.getCurrentStepData();!t||t.index<=0||(this.dispatchEvent(new CustomEvent("db-prev",{bubbles:!0,composed:!0})),this.goToStep(t.index-1))}handleNext(){const t=this.getCurrentStepData();t&&(t.index>=t.steps.length-1?this.handleEnd():(this.dispatchEvent(new CustomEvent("db-next",{bubbles:!0,composed:!0})),this.goToStep(t.index+1)))}handleEnd(){this.dispatchEvent(new CustomEvent("db-end",{bubbles:!0,composed:!0})),this.setAttribute("open","false")}render(){const t=this.getCurrentStepData();if(!t||!this.root)return;const{step:e,index:i,steps:s}=t,n=0===i,r=i===s.length-1;this.titleEl&&(this.titleEl.textContent=e.title||"",this.titleEl.hidden=!e.title),this.descriptionEl&&(this.descriptionEl.textContent=e.content||"",this.descriptionEl.hidden=!e.content),this.prevButtonEl&&(this.prevButtonEl.hidden=n),this.nextButtonEl&&(this.nextButtonEl.textContent=r?"완료":"다음",r?this.nextButtonEl.removeAttribute("end-icon"):this.nextButtonEl.setAttribute("end-icon","chevron-right")),this.indicatorEl&&(this.indicatorEl.setAttribute("current",String(i)),this.indicatorEl.setAttribute("total",String(s.length)),this.indicatorEl.setAttribute("type",this.getAttribute("indicator-type")||"numbers"),this.indicatorEl.setAttribute("size",this.getAttribute("indicator-size")||"m")),this.updateTargetElement()}}customElements.get("db-tutorial")||customElements.define("db-tutorial",DbTutorial);export{DbTutorial};
@@ -0,0 +1,60 @@
1
+ import './db-button.js';
2
+ import './db-otp-input.js';
3
+ import './db-segment-control.js';
4
+
5
+ /**
6
+ * ui-wc — db-two-factor (수동 작성, 자동 동기화 대상 아님)
7
+ *
8
+ * 2단계 인증(2FA): 인증앱 / SMS / 이메일 방식 + OTP.
9
+ */
10
+
11
+ declare const METHODS: readonly [{
12
+ readonly value: "app";
13
+ readonly label: "인증 앱";
14
+ }, {
15
+ readonly value: "sms";
16
+ readonly label: "SMS";
17
+ }, {
18
+ readonly value: "email";
19
+ readonly label: "이메일";
20
+ }];
21
+ declare const SIZES: readonly ["s", "m", "l"];
22
+ type Size = (typeof SIZES)[number];
23
+ type Method = (typeof METHODS)[number]['value'];
24
+ declare class DbTwoFactor extends HTMLElement {
25
+ static get observedAttributes(): string[];
26
+ private built;
27
+ private timerId;
28
+ private remaining;
29
+ private segment;
30
+ private otpInput;
31
+ private hintEl;
32
+ private timerEl;
33
+ private resendBtn;
34
+ private verifyBtn;
35
+ private backupBtn;
36
+ private errorEl;
37
+ private channelActions;
38
+ get method(): Method;
39
+ set method(v: Method);
40
+ get size(): Size;
41
+ get countdown(): number;
42
+ connectedCallback(): void;
43
+ disconnectedCallback(): void;
44
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
45
+ /** 수동 제어 모드에서 코드 발송 성공 후 카운트다운을 시작합니다. */
46
+ restartCountdown(): void;
47
+ private clearTimer;
48
+ private startTimer;
49
+ private renderTimer;
50
+ private build;
51
+ private sync;
52
+ private requestVerify;
53
+ }
54
+ declare global {
55
+ interface HTMLElementTagNameMap {
56
+ 'db-two-factor': DbTwoFactor;
57
+ }
58
+ }
59
+
60
+ export { DbTwoFactor };
@@ -0,0 +1 @@
1
+ import{createDbIcon as t}from"./db-icons.js";import"./db-button.js";import"./db-otp-input.js";import"./db-segment-control.js";const e=[{value:"app",label:"인증 앱"},{value:"sms",label:"SMS"},{value:"email",label:"이메일"}],i=["s","m","l"];function el(t,e){const i=document.createElement(t);return e&&(i.className=e),i}class DbTwoFactor extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.timerId=null,this.remaining=0}static get observedAttributes(){return["method","code","size","disabled","error","error-message","title","description","verify-label","code-length","show-backup","countdown","destination","manual","sending","verifying","auto-submit","resend-label"]}get method(){const t=this.getAttribute("method");return"sms"===t||"email"===t||"app"===t?t:"app"}set method(t){this.setAttribute("method",t)}get size(){return function pick(t,e,i){return t.includes(e)?e:i}(i,this.getAttribute("size"),"m")}get countdown(){const t=Number(this.getAttribute("countdown")||180);return Number.isFinite(t)&&t>0?t:180}connectedCallback(){this.build(),this.sync(),"app"===this.method||this.hasAttribute("manual")||this.startTimer(this.countdown)}disconnectedCallback(){this.clearTimer()}attributeChangedCallback(t,e,i){this.built&&(this.sync(),"method"!==t&&"manual"!==t||e===i||("app"===this.method||this.hasAttribute("manual")?this.clearTimer():this.startTimer(this.countdown)))}restartCountdown(){"app"!==this.method&&this.startTimer(this.countdown)}clearTimer(){this.timerId&&clearInterval(this.timerId),this.timerId=null,this.remaining=0,this.timerEl&&(this.timerEl.textContent="")}startTimer(t){this.clearTimer(),this.remaining=t,this.renderTimer(),this.timerId=setInterval(()=>{this.remaining-=1,this.renderTimer(),this.remaining<=0&&(this.clearTimer(),this.resendBtn.disabled=!1,this.dispatchEvent(new CustomEvent("db-countdown-complete",{bubbles:!0,composed:!0})))},1e3)}renderTimer(){const t=Math.max(0,Math.floor(this.remaining/60)),e=Math.max(0,this.remaining%60);this.timerEl.textContent="app"===this.method?"":`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`,"app"!==this.method?this.timerEl.setAttribute("aria-label",`남은 시간 ${t}분 ${e}초`):this.timerEl.removeAttribute("aria-label"),this.resendBtn.disabled="app"===this.method||this.remaining>0||this.hasAttribute("disabled")}build(){if(this.built)return;this.built=!0;const i="designbase-wc-two-factor";this.replaceChildren();const s=el("div",`${i}__header`),n=el("div",`${i}__icon`);n.append(t("lock",22));const r=el("div",`${i}__titles`),o=el("h3",`${i}__title`);o.dataset.role="title";const h=el("p",`${i}__description`);h.dataset.role="description",r.append(o,h),s.append(n,r),this.segment=document.createElement("db-segment-control"),this.segment.setAttribute("options",JSON.stringify(e.map(t=>({value:t.value,label:t.label})))),this.segment.setAttribute("full-width",""),this.segment.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"app");e!==this.method&&(this.setAttribute("code",""),this.method=e,this.dispatchEvent(new CustomEvent("db-method-change",{detail:{method:e},bubbles:!0,composed:!0})),"app"!==e&&this.dispatchEvent(new CustomEvent("db-send-code",{detail:{method:e,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})))}),this.hintEl=el("p",`${i}__hint`),this.hintEl.dataset.role="hint";const a=el("div",`${i}__code-head`),d=el("span",`${i}__code-label`);d.textContent="인증 코드",this.timerEl=el("span",`${i}__timer`),a.append(d,this.timerEl),this.otpInput=document.createElement("db-otp-input"),this.otpInput.addEventListener("db-change",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.setAttribute("code",e),this.dispatchEvent(new CustomEvent("db-code-change",{detail:{code:e},bubbles:!0,composed:!0}))}),this.otpInput.addEventListener("db-complete",t=>{t.stopPropagation();const e=String(t.detail?.value||"");this.dispatchEvent(new CustomEvent("db-code-complete",{detail:{code:e,method:this.method},bubbles:!0,composed:!0})),this.hasAttribute("auto-submit")&&this.requestVerify()}),this.channelActions=el("div",`${i}__channel`),this.resendBtn=el("button",`${i}__resend`),this.resendBtn.type="button",this.resendBtn.textContent="코드 재전송",this.resendBtn.addEventListener("click",()=>{this.resendBtn.disabled||(this.dispatchEvent(new CustomEvent("db-resend",{detail:{method:this.method,destination:this.getAttribute("destination")||""},bubbles:!0,composed:!0})),this.hasAttribute("manual")||this.startTimer(this.countdown))}),this.channelActions.append(this.resendBtn),this.verifyBtn=document.createElement("db-button"),this.verifyBtn.setAttribute("variant","primary"),this.verifyBtn.setAttribute("full-width",""),this.verifyBtn.addEventListener("db-click",t=>{t.stopPropagation(),this.requestVerify()}),this.backupBtn=el("button",`${i}__backup`),this.backupBtn.type="button",this.backupBtn.textContent="백업 코드로 인증",this.backupBtn.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("db-backup-request",{bubbles:!0,composed:!0}))}),this.errorEl=el("p",`${i}__error`),this.errorEl.hidden=!0,this.append(s,this.segment,this.hintEl,a,this.otpInput,this.channelActions,this.verifyBtn,this.backupBtn,this.errorEl)}sync(){const t="designbase-wc-two-factor",e=this.size,i=this.method,s=this.hasAttribute("disabled"),n=this.getAttribute("code-length")||"6",r=this.getAttribute("destination")||"";this.className=[t,`${t}--${e}`,`${t}--${i}`,s&&`${t}--disabled`,this.hasAttribute("error")&&`${t}--error`].filter(Boolean).join(" ");const o=this.querySelector('[data-role="title"]'),h=this.querySelector('[data-role="description"]');o.textContent=this.getAttribute("title")||"2단계 인증",h.textContent=this.getAttribute("description")||"계속하려면 인증 코드를 입력해 주세요.",this.segment.setAttribute("value",i),this.segment.setAttribute("size",e),this.segment.toggleAttribute("disabled",s);const a=this.querySelector('[data-role="hint"]');a.textContent="app"===i?"인증 앱(Google Authenticator, 1Password 등)에 표시된 6자리 코드를 입력하세요.":"sms"===i?r?`${r} 으로 보낸 SMS 인증 코드를 입력하세요.`:"등록된 휴대폰으로 보낸 SMS 인증 코드를 입력하세요.":r?`${r} 으로 보낸 이메일 인증 코드를 입력하세요.`:"등록된 이메일로 보낸 인증 코드를 입력하세요.",this.otpInput.setAttribute("length",n),this.otpInput.setAttribute("size",e),this.otpInput.setAttribute("value",this.getAttribute("code")||""),this.otpInput.toggleAttribute("disabled",s),this.otpInput.toggleAttribute("error",this.hasAttribute("error")),this.getAttribute("error-message")?this.otpInput.setAttribute("error-message",this.getAttribute("error-message")||""):this.otpInput.removeAttribute("error-message"),this.channelActions.hidden="app"===i,this.resendBtn.textContent=this.hasAttribute("sending")?"전송 중…":this.getAttribute("resend-label")||"코드 재전송",this.renderTimer(),this.hasAttribute("sending")&&(this.resendBtn.disabled=!0),this.verifyBtn.setAttribute("size",e),this.verifyBtn.textContent=this.getAttribute("verify-label")||"인증하기";const d=this.getAttribute("code")||"";this.verifyBtn.toggleAttribute("disabled",s||d.length<Number(n)),this.verifyBtn.toggleAttribute("loading",this.hasAttribute("verifying")),this.backupBtn.hidden=!this.hasAttribute("show-backup");const l=this.getAttribute("error-message")||"";this.errorEl.textContent=l,this.errorEl.hidden=!this.hasAttribute("error")||!l}requestVerify(){const t=(this.getAttribute("code")||this.otpInput.value).replace(/\D/g,""),e=Number(this.getAttribute("code-length")||6);t.length!==e||this.hasAttribute("disabled")||this.dispatchEvent(new CustomEvent("db-verify",{detail:{method:this.method,code:t},bubbles:!0,composed:!0}))}}customElements.get("db-two-factor")||customElements.define("db-two-factor",DbTwoFactor);export{DbTwoFactor};