@designbasekorea/ui-wc 0.5.6 → 0.6.1

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 (193) hide show
  1. package/dist/chunks/lottie-35a9b0f5.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 +64 -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 +63 -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.d.ts +20 -0
  139. package/dist/esm/components/db-sidebar.js +1 -1068
  140. package/dist/esm/components/db-skeleton.js +1 -94
  141. package/dist/esm/components/db-spinner.js +1 -102
  142. package/dist/esm/components/db-split-view.js +1 -297
  143. package/dist/esm/components/db-stack.js +1 -46
  144. package/dist/esm/components/db-stat.js +1 -218
  145. package/dist/esm/components/db-stepper.js +1 -398
  146. package/dist/esm/components/db-table.js +1 -412
  147. package/dist/esm/components/db-tabs.js +1 -335
  148. package/dist/esm/components/db-testimonial.d.ts +2 -0
  149. package/dist/esm/components/db-testimonial.js +1 -162
  150. package/dist/esm/components/db-textarea.js +1 -233
  151. package/dist/esm/components/db-time-picker.js +1 -523
  152. package/dist/esm/components/db-timeline.js +1 -174
  153. package/dist/esm/components/db-toast.js +1 -158
  154. package/dist/esm/components/db-toggle.js +1 -162
  155. package/dist/esm/components/db-toolbar.js +1 -94
  156. package/dist/esm/components/db-tooltip.js +1 -440
  157. package/dist/esm/components/db-top-banner.js +1 -121
  158. package/dist/esm/components/db-tutorial.js +1 -324
  159. package/dist/esm/components/db-two-factor.d.ts +71 -0
  160. package/dist/esm/components/db-two-factor.js +1 -0
  161. package/dist/esm/components/db-video-player.js +1 -503
  162. package/dist/esm/components/db-youtube-player.js +1 -194
  163. package/dist/esm/components/highlight-code.js +1 -65
  164. package/dist/esm/components/input-validation.js +1 -50
  165. package/dist/esm/components/marketing-block-host-utils.js +1 -9
  166. package/dist/esm/components/number-scrub.js +1 -117
  167. package/dist/esm/components/random-gradient-palettes.js +1 -67
  168. package/dist/esm/components/section-block-header-utils.js +1 -48
  169. package/dist/esm/helpers/confirm.js +1 -52
  170. package/dist/esm/helpers/toast.js +1 -73
  171. package/dist/esm/index.d.ts +16 -0
  172. package/dist/esm/index.js +1 -111
  173. package/dist/esm/node_modules/prismjs/components/prism-bash.js +1 -235
  174. package/dist/esm/node_modules/prismjs/components/prism-clike.js +1 -31
  175. package/dist/esm/node_modules/prismjs/components/prism-css.js +1 -64
  176. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +1 -172
  177. package/dist/esm/node_modules/prismjs/components/prism-json.js +1 -27
  178. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +1 -143
  179. package/dist/esm/node_modules/prismjs/components/prism-markup.js +1 -186
  180. package/dist/esm/node_modules/prismjs/components/prism-scss.js +1 -81
  181. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +1 -15
  182. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +1 -60
  183. package/dist/index.cjs +1 -55414
  184. package/dist/index.css +1 -1
  185. package/dist/index.d.ts +1125 -2
  186. package/dist/index.esm.js +1 -38284
  187. package/dist/react/chunks/lottie-14e7addb.js +1 -0
  188. package/dist/react/index.cjs +1 -56503
  189. package/dist/react/index.d.ts +1637 -21
  190. package/dist/react/index.esm.js +1 -39373
  191. package/package.json +2 -2
  192. package/dist/chunks/lottie-e273b936.js +0 -17032
  193. package/dist/react/chunks/lottie-72a4c6d4.js +0 -17032
@@ -1,752 +1 @@
1
- import { createDbIcon } from './db-icons.js';
2
-
3
- const CLASS = 'designbase-wc-audio-player';
4
- class DbAudioPlayer extends HTMLElement {
5
- constructor() {
6
- super(...arguments);
7
- this.built = false;
8
- this.isPlaying = false;
9
- this.isMuted = false;
10
- this.volume = 1;
11
- this.currentTime = 0;
12
- this.duration = 0;
13
- this.showSettingsMenu = false;
14
- this.playbackRate = 1;
15
- this.currentRepeatMode = 'none';
16
- this.isShuffle = false;
17
- this.isLoading = false;
18
- this.error = null;
19
- this.playlistIndex = 0;
20
- this.shuffledPlaylist = [];
21
- this.boundKeyDown = (event) => this.handleKeyDown(event);
22
- }
23
- static get observedAttributes() {
24
- return [
25
- 'src',
26
- 'title',
27
- 'artist',
28
- 'album',
29
- 'album-art',
30
- 'size',
31
- 'variant',
32
- 'theme',
33
- 'auto-play',
34
- 'loop',
35
- 'muted',
36
- 'show-controls',
37
- 'enable-keyboard',
38
- 'show-progress',
39
- 'show-time',
40
- 'show-volume',
41
- 'show-settings',
42
- 'playlist',
43
- 'current-index',
44
- 'playback-rates',
45
- 'default-playback-rate',
46
- 'repeat-mode',
47
- 'shuffle',
48
- ];
49
- }
50
- connectedCallback() {
51
- this.style.display = 'block';
52
- if (!this.built) {
53
- this.built = true;
54
- this.build();
55
- this.bindAudioEvents();
56
- }
57
- this.syncFromAttributes();
58
- this.updateUI();
59
- if (this.getBool('enable-keyboard', true)) {
60
- document.addEventListener('keydown', this.boundKeyDown);
61
- }
62
- }
63
- disconnectedCallback() {
64
- document.removeEventListener('keydown', this.boundKeyDown);
65
- }
66
- attributeChangedCallback(name) {
67
- if (!this.built)
68
- return;
69
- if (name === 'current-index') {
70
- this.playlistIndex = Number(this.getAttribute('current-index') || 0);
71
- this.loadCurrentTrack();
72
- return;
73
- }
74
- if (name === 'repeat-mode') {
75
- this.currentRepeatMode = this.getAttribute('repeat-mode') || 'none';
76
- }
77
- if (name === 'shuffle') {
78
- this.isShuffle = this.getBool('shuffle');
79
- this.refreshShuffledPlaylist();
80
- }
81
- if (name === 'default-playback-rate') {
82
- this.playbackRate = Number(this.getAttribute('default-playback-rate') || 1);
83
- if (this.audioEl)
84
- this.audioEl.playbackRate = this.playbackRate;
85
- }
86
- this.updateUI();
87
- }
88
- getBool(name, fallback = false) {
89
- if (!this.hasAttribute(name))
90
- return fallback;
91
- const value = this.getAttribute(name);
92
- return value === '' || value === 'true';
93
- }
94
- getPlaylist() {
95
- try {
96
- const raw = this.getAttribute('playlist');
97
- const parsed = raw ? JSON.parse(raw) : [];
98
- return Array.isArray(parsed) ? parsed : [];
99
- }
100
- catch {
101
- return [];
102
- }
103
- }
104
- getPlaybackRates() {
105
- try {
106
- const raw = this.getAttribute('playback-rates');
107
- const parsed = raw ? JSON.parse(raw) : [0.5, 0.75, 1, 1.25, 1.5, 2];
108
- return Array.isArray(parsed) && parsed.length > 0 ? parsed : [0.5, 0.75, 1, 1.25, 1.5, 2];
109
- }
110
- catch {
111
- return [0.5, 0.75, 1, 1.25, 1.5, 2];
112
- }
113
- }
114
- getIconSize() {
115
- const size = this.getAttribute('size') || 'm';
116
- if (size === 's')
117
- return 16;
118
- if (size === 'l')
119
- return 24;
120
- if (size === 'xl')
121
- return 32;
122
- return 20;
123
- }
124
- getCurrentTrack() {
125
- const playlist = this.getPlaylist();
126
- if (playlist.length > 0) {
127
- const index = this.isShuffle && this.shuffledPlaylist.length > 0
128
- ? this.shuffledPlaylist[this.playlistIndex] ?? this.playlistIndex
129
- : this.playlistIndex;
130
- return playlist[index] || playlist[0];
131
- }
132
- return {
133
- src: this.getAttribute('src') || '',
134
- title: this.getAttribute('title') || undefined,
135
- artist: this.getAttribute('artist') || undefined,
136
- album: this.getAttribute('album') || undefined,
137
- albumArt: this.getAttribute('album-art') || undefined,
138
- };
139
- }
140
- refreshShuffledPlaylist() {
141
- const playlist = this.getPlaylist();
142
- if (playlist.length > 0 && this.isShuffle) {
143
- const indices = Array.from({ length: playlist.length }, (_, i) => i);
144
- this.shuffledPlaylist = [...indices].sort(() => Math.random() - 0.5);
145
- }
146
- else {
147
- this.shuffledPlaylist = [];
148
- }
149
- }
150
- formatTime(time) {
151
- const minutes = Math.floor(time / 60);
152
- const seconds = Math.floor(time % 60);
153
- return `${minutes}:${seconds.toString().padStart(2, '0')}`;
154
- }
155
- syncFromAttributes() {
156
- this.playlistIndex = Number(this.getAttribute('current-index') || 0);
157
- this.currentRepeatMode = this.getAttribute('repeat-mode') || 'none';
158
- this.isShuffle = this.getBool('shuffle');
159
- this.playbackRate = Number(this.getAttribute('default-playback-rate') || 1);
160
- this.isMuted = this.getBool('muted');
161
- this.volume = this.isMuted ? 0 : 1;
162
- this.refreshShuffledPlaylist();
163
- this.loadCurrentTrack();
164
- }
165
- loadCurrentTrack() {
166
- if (!this.audioEl)
167
- return;
168
- const track = this.getCurrentTrack();
169
- const nextSrc = track?.src || this.getAttribute('src') || '';
170
- if (this.audioEl.src !== nextSrc && nextSrc) {
171
- this.audioEl.src = nextSrc;
172
- this.audioEl.load();
173
- }
174
- this.updateTrackInfo();
175
- this.updatePlaylistActive();
176
- }
177
- bindAudioEvents() {
178
- const audio = this.audioEl;
179
- if (!audio)
180
- return;
181
- audio.addEventListener('play', () => {
182
- this.isPlaying = true;
183
- this.dispatchEvent(new CustomEvent('db-play', { bubbles: true, composed: true }));
184
- this.updateUI();
185
- });
186
- audio.addEventListener('pause', () => {
187
- this.isPlaying = false;
188
- this.dispatchEvent(new CustomEvent('db-pause', { bubbles: true, composed: true }));
189
- this.updateUI();
190
- });
191
- audio.addEventListener('timeupdate', () => {
192
- this.currentTime = audio.currentTime;
193
- this.duration = audio.duration || 0;
194
- this.dispatchEvent(new CustomEvent('db-time-update', {
195
- detail: { currentTime: this.currentTime, duration: this.duration },
196
- bubbles: true,
197
- composed: true,
198
- }));
199
- this.updateProgress();
200
- this.updateTime();
201
- });
202
- audio.addEventListener('volumechange', () => {
203
- this.volume = audio.volume;
204
- this.isMuted = audio.muted;
205
- this.dispatchEvent(new CustomEvent('db-volume-change', {
206
- detail: { volume: this.volume },
207
- bubbles: true,
208
- composed: true,
209
- }));
210
- this.updateVolumeUI();
211
- });
212
- audio.addEventListener('ended', () => this.handleEnded());
213
- audio.addEventListener('error', () => {
214
- this.error = '오디오를 재생할 수 없습니다.';
215
- this.dispatchEvent(new CustomEvent('db-error', {
216
- detail: { error: this.error },
217
- bubbles: true,
218
- composed: true,
219
- }));
220
- this.updateUI();
221
- });
222
- audio.addEventListener('loadstart', () => {
223
- this.isLoading = true;
224
- this.error = null;
225
- this.updateUI();
226
- });
227
- audio.addEventListener('canplay', () => {
228
- this.isLoading = false;
229
- this.updateUI();
230
- });
231
- }
232
- handleEnded() {
233
- this.isPlaying = false;
234
- this.dispatchEvent(new CustomEvent('db-ended', { bubbles: true, composed: true }));
235
- const playlist = this.getPlaylist();
236
- if (playlist.length > 0) {
237
- if (this.currentRepeatMode === 'one') {
238
- if (this.audioEl) {
239
- this.audioEl.currentTime = 0;
240
- void this.audioEl.play();
241
- }
242
- }
243
- else if (this.currentRepeatMode === 'all' || this.playlistIndex < playlist.length - 1) {
244
- const nextIndex = this.playlistIndex + 1;
245
- if (nextIndex < playlist.length) {
246
- this.setPlaylistIndex(nextIndex);
247
- }
248
- else if (this.currentRepeatMode === 'all') {
249
- this.setPlaylistIndex(0);
250
- }
251
- }
252
- }
253
- else if (this.getBool('loop') && this.audioEl) {
254
- this.audioEl.currentTime = 0;
255
- void this.audioEl.play();
256
- }
257
- this.updateUI();
258
- }
259
- setPlaylistIndex(index) {
260
- this.playlistIndex = index;
261
- this.dispatchEvent(new CustomEvent('db-playlist-change', {
262
- detail: { index },
263
- bubbles: true,
264
- composed: true,
265
- }));
266
- this.loadCurrentTrack();
267
- if (this.audioEl)
268
- void this.audioEl.play();
269
- }
270
- togglePlay() {
271
- if (!this.audioEl)
272
- return;
273
- if (this.isPlaying)
274
- this.audioEl.pause();
275
- else
276
- void this.audioEl.play();
277
- }
278
- toggleMute() {
279
- if (!this.audioEl)
280
- return;
281
- this.audioEl.muted = !this.isMuted;
282
- }
283
- toggleRepeatMode() {
284
- const modes = ['none', 'one', 'all'];
285
- const next = modes[(modes.indexOf(this.currentRepeatMode) + 1) % modes.length];
286
- this.currentRepeatMode = next;
287
- this.dispatchEvent(new CustomEvent('db-repeat-mode-change', {
288
- detail: { mode: next },
289
- bubbles: true,
290
- composed: true,
291
- }));
292
- this.updateUI();
293
- }
294
- toggleShuffle() {
295
- this.isShuffle = !this.isShuffle;
296
- this.refreshShuffledPlaylist();
297
- this.dispatchEvent(new CustomEvent('db-shuffle-change', {
298
- detail: { shuffle: this.isShuffle },
299
- bubbles: true,
300
- composed: true,
301
- }));
302
- this.updateUI();
303
- }
304
- handlePlaybackRateChange(rate) {
305
- if (!this.audioEl)
306
- return;
307
- this.audioEl.playbackRate = rate;
308
- this.playbackRate = rate;
309
- this.dispatchEvent(new CustomEvent('db-playback-rate-change', {
310
- detail: { rate },
311
- bubbles: true,
312
- composed: true,
313
- }));
314
- }
315
- handleKeyDown(event) {
316
- if (!this.audioEl || !this.getBool('enable-keyboard', true))
317
- return;
318
- switch (event.code) {
319
- case 'Space':
320
- event.preventDefault();
321
- this.togglePlay();
322
- break;
323
- case 'ArrowLeft':
324
- event.preventDefault();
325
- this.audioEl.currentTime = Math.max(0, this.currentTime - 10);
326
- break;
327
- case 'ArrowRight':
328
- event.preventDefault();
329
- this.audioEl.currentTime = Math.min(this.duration, this.currentTime + 10);
330
- break;
331
- case 'ArrowUp':
332
- event.preventDefault();
333
- this.audioEl.volume = Math.min(1, this.volume + 0.1);
334
- this.audioEl.muted = false;
335
- break;
336
- case 'ArrowDown':
337
- event.preventDefault();
338
- this.audioEl.volume = Math.max(0, this.volume - 0.1);
339
- this.audioEl.muted = this.audioEl.volume === 0;
340
- break;
341
- case 'KeyM':
342
- event.preventDefault();
343
- this.toggleMute();
344
- break;
345
- case 'KeyR':
346
- event.preventDefault();
347
- this.toggleRepeatMode();
348
- break;
349
- case 'KeyS':
350
- event.preventDefault();
351
- this.toggleShuffle();
352
- break;
353
- }
354
- }
355
- iconButton(icon, onClick, options) {
356
- const button = document.createElement('button');
357
- button.type = 'button';
358
- button.className = `${CLASS}__control-button`;
359
- button.append(createDbIcon(icon, this.getIconSize()));
360
- if (options?.disabled)
361
- button.disabled = true;
362
- if (options?.data) {
363
- Object.entries(options.data).forEach(([key, value]) => button.setAttribute(`data-${key}`, value));
364
- }
365
- button.addEventListener('click', onClick);
366
- return button;
367
- }
368
- build() {
369
- const size = this.getAttribute('size') || 'm';
370
- const variant = this.getAttribute('variant') || 'default';
371
- const theme = this.getAttribute('theme') || 'auto';
372
- this.className = [
373
- CLASS,
374
- `${CLASS}--size-${size}`,
375
- `${CLASS}--variant-${variant}`,
376
- `${CLASS}--theme-${theme}`,
377
- ].join(' ');
378
- this.audioEl = document.createElement('audio');
379
- this.audioEl.preload = 'metadata';
380
- this.audioEl.hidden = true;
381
- const loading = document.createElement('div');
382
- loading.className = `${CLASS}__loading`;
383
- const spinner = document.createElement('div');
384
- spinner.className = `${CLASS}__spinner`;
385
- loading.append(spinner);
386
- const errorWrap = document.createElement('div');
387
- errorWrap.className = `${CLASS}__error`;
388
- const errorText = document.createElement('p');
389
- errorText.textContent = '오디오를 재생할 수 없습니다.';
390
- const retry = document.createElement('button');
391
- retry.type = 'button';
392
- retry.textContent = '다시 시도';
393
- retry.addEventListener('click', () => window.location.reload());
394
- errorWrap.append(errorText, retry);
395
- const albumArt = document.createElement('div');
396
- albumArt.className = `${CLASS}__album-art`;
397
- this.albumImgEl = document.createElement('img');
398
- this.albumImgEl.addEventListener('error', () => {
399
- if (this.albumImgEl)
400
- this.albumImgEl.style.display = 'none';
401
- this.albumPlaceholderEl?.classList.remove('album-placeholder');
402
- });
403
- this.albumPlaceholderEl = document.createElement('div');
404
- this.albumPlaceholderEl.className = 'album-placeholder';
405
- this.albumPlaceholderEl.textContent = '🎵';
406
- albumArt.append(this.albumImgEl, this.albumPlaceholderEl);
407
- const main = document.createElement('div');
408
- main.className = `${CLASS}__main`;
409
- const info = document.createElement('div');
410
- info.className = `${CLASS}__info`;
411
- this.titleEl = document.createElement('div');
412
- this.titleEl.className = `${CLASS}__title`;
413
- this.artistEl = document.createElement('div');
414
- this.artistEl.className = `${CLASS}__artist`;
415
- this.albumEl = document.createElement('div');
416
- this.albumEl.className = `${CLASS}__album`;
417
- info.append(this.titleEl, this.artistEl, this.albumEl);
418
- this.progressEl = document.createElement('div');
419
- this.progressEl.className = `${CLASS}__progress`;
420
- this.progressBarEl = document.createElement('div');
421
- this.progressBarEl.className = `${CLASS}__progress-bar`;
422
- this.progressHandleEl = document.createElement('div');
423
- this.progressHandleEl.className = `${CLASS}__progress-handle`;
424
- this.progressEl.append(this.progressBarEl, this.progressHandleEl);
425
- this.progressEl.addEventListener('click', (event) => {
426
- if (!this.audioEl || !this.progressEl)
427
- return;
428
- const rect = this.progressEl.getBoundingClientRect();
429
- const percentage = (event.clientX - rect.left) / rect.width;
430
- this.audioEl.currentTime = percentage * (this.duration || 0);
431
- });
432
- const controls = document.createElement('div');
433
- controls.className = `${CLASS}__controls`;
434
- const leftControls = document.createElement('div');
435
- leftControls.className = `${CLASS}__left-controls`;
436
- const centerControls = document.createElement('div');
437
- centerControls.className = `${CLASS}__center-controls`;
438
- const rightControls = document.createElement('div');
439
- rightControls.className = `${CLASS}__right-controls`;
440
- this.playButtonEl = document.createElement('button');
441
- this.playButtonEl.type = 'button';
442
- this.playButtonEl.className = `${CLASS}__play-button`;
443
- this.playButtonEl.addEventListener('click', () => this.togglePlay());
444
- centerControls.append(this.playButtonEl);
445
- controls.append(leftControls, centerControls, rightControls);
446
- const bottomControls = document.createElement('div');
447
- bottomControls.className = `${CLASS}__bottom-controls`;
448
- const leftBottom = document.createElement('div');
449
- leftBottom.className = `${CLASS}__left-bottom`;
450
- const rightBottom = document.createElement('div');
451
- rightBottom.className = `${CLASS}__right-bottom`;
452
- const time = document.createElement('div');
453
- time.className = `${CLASS}__time`;
454
- this.timeCurrentEl = document.createElement('span');
455
- const slash = document.createElement('span');
456
- slash.textContent = '/';
457
- this.timeDurationEl = document.createElement('span');
458
- time.append(this.timeCurrentEl, slash, this.timeDurationEl);
459
- leftBottom.append(time);
460
- const volumeWrap = document.createElement('div');
461
- volumeWrap.className = `${CLASS}__volume`;
462
- const muteButton = this.iconButton(this.isMuted || this.volume === 0 ? 'mute-filled' : 'volume-up', () => this.toggleMute());
463
- muteButton.title = this.isMuted || this.volume === 0 ? '음소거 해제' : '음소거';
464
- const volumeSlider = document.createElement('div');
465
- volumeSlider.className = `${CLASS}__volume-slider`;
466
- const volumeLabel = document.createElement('div');
467
- volumeLabel.className = 'volume-label';
468
- const volumeEmoji = document.createElement('span');
469
- volumeEmoji.textContent = '🔊';
470
- const volumePercent = document.createElement('span');
471
- volumeLabel.append(volumeEmoji, volumePercent);
472
- this.volumeInputEl = document.createElement('input');
473
- this.volumeInputEl.type = 'range';
474
- this.volumeInputEl.min = '0';
475
- this.volumeInputEl.max = '1';
476
- this.volumeInputEl.step = '0.1';
477
- this.volumeInputEl.addEventListener('input', () => {
478
- if (!this.audioEl || !this.volumeInputEl)
479
- return;
480
- const newVolume = parseFloat(this.volumeInputEl.value);
481
- this.audioEl.volume = newVolume;
482
- this.audioEl.muted = newVolume === 0;
483
- });
484
- volumeSlider.append(volumeLabel, this.volumeInputEl);
485
- volumeWrap.append(muteButton, volumeSlider);
486
- const settings = document.createElement('div');
487
- settings.className = `${CLASS}__settings`;
488
- const settingsButton = this.iconButton('settings', () => {
489
- this.showSettingsMenu = !this.showSettingsMenu;
490
- this.updateSettingsMenu();
491
- });
492
- this.settingsMenuEl = document.createElement('div');
493
- this.settingsMenuEl.className = `${CLASS}__settings-menu`;
494
- this.settingsMenuEl.hidden = true;
495
- const settingGroup = document.createElement('div');
496
- settingGroup.className = `${CLASS}__setting-group`;
497
- const label = document.createElement('label');
498
- label.textContent = '재생 속도';
499
- const select = document.createElement('select');
500
- select.addEventListener('change', () => this.handlePlaybackRateChange(parseFloat(select.value)));
501
- settingGroup.append(label, select);
502
- this.settingsMenuEl.append(settingGroup);
503
- settings.append(settingsButton, this.settingsMenuEl);
504
- rightBottom.append(volumeWrap, settings);
505
- bottomControls.append(leftBottom, rightBottom);
506
- main.append(info, this.progressEl, controls, bottomControls);
507
- const playlist = document.createElement('div');
508
- playlist.className = `${CLASS}__playlist`;
509
- playlist.hidden = true;
510
- const playlistTitle = document.createElement('h4');
511
- playlistTitle.textContent = '플레이리스트';
512
- this.playlistTracksEl = document.createElement('div');
513
- this.playlistTracksEl.className = `${CLASS}__playlist-tracks`;
514
- playlist.append(playlistTitle, this.playlistTracksEl);
515
- this.append(this.audioEl, loading, errorWrap, albumArt, main, playlist);
516
- this._leftControls = leftControls;
517
- this._rightControls = rightControls;
518
- this._controls = controls;
519
- this._time = time;
520
- this._volumeWrap = volumeWrap;
521
- this._settings = settings;
522
- this._playlist = playlist;
523
- this._rateSelect = select;
524
- this._volumePercent = volumePercent;
525
- this._muteButton = muteButton;
526
- }
527
- get leftControls() {
528
- return this._leftControls;
529
- }
530
- get rightControls() {
531
- return this._rightControls;
532
- }
533
- get controlsEl() {
534
- return this._controls;
535
- }
536
- get timeEl() {
537
- return this._time;
538
- }
539
- get volumeWrap() {
540
- return this._volumeWrap;
541
- }
542
- get settingsEl() {
543
- return this._settings;
544
- }
545
- get playlistEl() {
546
- return this._playlist;
547
- }
548
- get rateSelect() {
549
- return this._rateSelect;
550
- }
551
- get volumePercent() {
552
- return this._volumePercent;
553
- }
554
- get muteButton() {
555
- return this._muteButton;
556
- }
557
- updateTrackInfo() {
558
- const track = this.getCurrentTrack();
559
- if (this.titleEl)
560
- this.titleEl.textContent = track?.title || 'Unknown Track';
561
- if (this.artistEl)
562
- this.artistEl.textContent = track?.artist || 'Unknown Artist';
563
- if (this.albumEl) {
564
- if (track?.album) {
565
- this.albumEl.textContent = track.album;
566
- this.albumEl.hidden = false;
567
- }
568
- else {
569
- this.albumEl.hidden = true;
570
- }
571
- }
572
- if (this.albumImgEl && this.albumPlaceholderEl) {
573
- if (track?.albumArt) {
574
- this.albumImgEl.src = track.albumArt;
575
- this.albumImgEl.alt = track.title || 'Album Art';
576
- this.albumImgEl.style.display = '';
577
- }
578
- else {
579
- this.albumImgEl.removeAttribute('src');
580
- this.albumImgEl.style.display = 'none';
581
- }
582
- this.albumPlaceholderEl.className = 'album-placeholder';
583
- }
584
- }
585
- updateProgress() {
586
- const ratio = this.duration > 0 ? (this.currentTime / this.duration) * 100 : 0;
587
- if (this.progressBarEl)
588
- this.progressBarEl.style.width = `${ratio}%`;
589
- if (this.progressHandleEl) {
590
- this.progressHandleEl.style.left = `${ratio}%`;
591
- if (ratio <= 0) {
592
- this.progressHandleEl.style.transform = 'translate(0, -50%)';
593
- }
594
- else if (ratio >= 100) {
595
- this.progressHandleEl.style.transform = 'translate(-100%, -50%)';
596
- }
597
- else {
598
- this.progressHandleEl.style.transform = 'translate(-50%, -50%)';
599
- }
600
- }
601
- }
602
- updateTime() {
603
- if (this.timeCurrentEl)
604
- this.timeCurrentEl.textContent = this.formatTime(this.currentTime);
605
- if (this.timeDurationEl)
606
- this.timeDurationEl.textContent = this.formatTime(this.duration);
607
- }
608
- updateVolumeUI() {
609
- if (this.volumeInputEl)
610
- this.volumeInputEl.value = String(this.volume);
611
- if (this.volumePercent)
612
- this.volumePercent.textContent = `${Math.round(this.volume * 100)}%`;
613
- if (this.muteButton) {
614
- this.muteButton.replaceChildren(createDbIcon(this.isMuted || this.volume === 0 ? 'mute-filled' : 'volume-up', this.getIconSize()));
615
- this.muteButton.title = this.isMuted || this.volume === 0 ? '음소거 해제' : '음소거';
616
- }
617
- }
618
- updateSettingsMenu() {
619
- if (!this.settingsMenuEl)
620
- return;
621
- this.settingsMenuEl.hidden = !this.showSettingsMenu;
622
- }
623
- rebuildControlButtons() {
624
- const playlist = this.getPlaylist();
625
- const iconSize = this.getIconSize() * 1.2;
626
- this.leftControls.replaceChildren();
627
- this.rightControls.replaceChildren();
628
- if (playlist.length > 0) {
629
- this.leftControls.append(this.iconButton('refresh', () => this.toggleShuffle(), { data: { shuffle: 'true' } }));
630
- }
631
- this.leftControls.append(this.iconButton('chevron-left', () => {
632
- if (playlist.length > 0) {
633
- const prev = this.playlistIndex - 1;
634
- if (prev >= 0)
635
- this.setPlaylistIndex(prev);
636
- }
637
- else if (this.audioEl) {
638
- this.audioEl.currentTime = 0;
639
- }
640
- }, { disabled: playlist.length > 0 && this.playlistIndex === 0 }));
641
- if (this.playButtonEl) {
642
- this.playButtonEl.replaceChildren(createDbIcon(this.isPlaying ? 'pause' : 'play', iconSize));
643
- }
644
- this.rightControls.append(this.iconButton('chevron-right', () => {
645
- if (playlist.length > 0) {
646
- const next = this.playlistIndex + 1;
647
- if (next < playlist.length)
648
- this.setPlaylistIndex(next);
649
- }
650
- }, { disabled: playlist.length > 0 && this.playlistIndex === playlist.length - 1 }));
651
- if (playlist.length > 0) {
652
- this.rightControls.append(this.iconButton('repeat', () => this.toggleRepeatMode(), { data: { repeat: 'true' } }));
653
- }
654
- }
655
- rebuildPlaylist() {
656
- if (!this.playlistTracksEl)
657
- return;
658
- const playlist = this.getPlaylist();
659
- const variant = this.getAttribute('variant') || 'default';
660
- this.playlistEl.hidden = !(variant === 'full' && playlist.length > 0);
661
- this.playlistTracksEl.replaceChildren();
662
- playlist.forEach((track, index) => {
663
- const row = document.createElement('div');
664
- row.className = `${CLASS}__playlist-track${index === this.playlistIndex ? ` ${CLASS}__playlist-track--active` : ''}`;
665
- row.addEventListener('click', () => this.setPlaylistIndex(index));
666
- if (track.albumArt) {
667
- const img = document.createElement('img');
668
- img.src = track.albumArt;
669
- img.alt = track.title || 'Album Art';
670
- row.append(img);
671
- }
672
- const info = document.createElement('div');
673
- info.className = `${CLASS}__playlist-track-info`;
674
- const title = document.createElement('div');
675
- title.className = `${CLASS}__playlist-track-title`;
676
- title.textContent = track.title || 'Unknown Track';
677
- const artist = document.createElement('div');
678
- artist.className = `${CLASS}__playlist-track-artist`;
679
- artist.textContent = track.artist || 'Unknown Artist';
680
- info.append(title, artist);
681
- row.append(info);
682
- this.playlistTracksEl?.append(row);
683
- });
684
- }
685
- updatePlaylistActive() {
686
- if (!this.playlistTracksEl)
687
- return;
688
- Array.from(this.playlistTracksEl.children).forEach((child, index) => {
689
- child.classList.toggle(`${CLASS}__playlist-track--active`, index === this.playlistIndex);
690
- });
691
- }
692
- updateUI() {
693
- const size = this.getAttribute('size') || 'm';
694
- const variant = this.getAttribute('variant') || 'default';
695
- const theme = this.getAttribute('theme') || 'auto';
696
- const showControls = this.getBool('show-controls', true);
697
- const showProgress = this.getBool('show-progress', true);
698
- const showTime = this.getBool('show-time', true);
699
- const showVolume = this.getBool('show-volume', true);
700
- const showSettings = this.getBool('show-settings', false);
701
- this.className = [
702
- CLASS,
703
- `${CLASS}--size-${size}`,
704
- `${CLASS}--variant-${variant}`,
705
- `${CLASS}--theme-${theme}`,
706
- this.isPlaying && `${CLASS}--playing`,
707
- !this.isPlaying && `${CLASS}--paused`,
708
- this.isMuted && `${CLASS}--muted`,
709
- this.isLoading && `${CLASS}--loading`,
710
- this.error && `${CLASS}--error`,
711
- this.isShuffle && `${CLASS}--shuffle`,
712
- `${CLASS}--repeat-${this.currentRepeatMode}`,
713
- ]
714
- .filter(Boolean)
715
- .join(' ');
716
- if (this.audioEl) {
717
- this.audioEl.autoplay = this.getBool('auto-play');
718
- this.audioEl.loop = this.getBool('loop');
719
- this.audioEl.muted = this.getBool('muted');
720
- this.audioEl.playbackRate = this.playbackRate;
721
- }
722
- if (this.progressEl)
723
- this.progressEl.hidden = !showProgress;
724
- if (this.controlsEl)
725
- this.controlsEl.hidden = !showControls;
726
- if (this.timeEl)
727
- this.timeEl.hidden = !showTime;
728
- if (this.volumeWrap)
729
- this.volumeWrap.hidden = !showVolume;
730
- if (this.settingsEl)
731
- this.settingsEl.hidden = !showSettings;
732
- this.rateSelect.replaceChildren(...this.getPlaybackRates().map((rate) => {
733
- const option = document.createElement('option');
734
- option.value = String(rate);
735
- option.textContent = `${rate}x`;
736
- if (rate === this.playbackRate)
737
- option.selected = true;
738
- return option;
739
- }));
740
- this.rebuildControlButtons();
741
- this.updateTrackInfo();
742
- this.updateProgress();
743
- this.updateTime();
744
- this.updateVolumeUI();
745
- this.updateSettingsMenu();
746
- this.rebuildPlaylist();
747
- }
748
- }
749
- if (!customElements.get('db-audio-player'))
750
- customElements.define('db-audio-player', DbAudioPlayer);
751
-
752
- export { DbAudioPlayer };
1
+ import{createDbIcon as t}from"./db-icons.js";const e="designbase-wc-audio-player";class DbAudioPlayer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.isPlaying=!1,this.isMuted=!1,this.volume=1,this.currentTime=0,this.duration=0,this.showSettingsMenu=!1,this.playbackRate=1,this.currentRepeatMode="none",this.isShuffle=!1,this.isLoading=!1,this.error=null,this.playlistIndex=0,this.shuffledPlaylist=[],this.boundKeyDown=t=>this.handleKeyDown(t)}static get observedAttributes(){return["src","title","artist","album","album-art","size","variant","theme","auto-play","loop","muted","show-controls","enable-keyboard","show-progress","show-time","show-volume","show-settings","playlist","current-index","playback-rates","default-playback-rate","repeat-mode","shuffle"]}connectedCallback(){this.style.display="block",this.built||(this.built=!0,this.build(),this.bindAudioEvents()),this.syncFromAttributes(),this.updateUI(),this.getBool("enable-keyboard",!0)&&document.addEventListener("keydown",this.boundKeyDown)}disconnectedCallback(){document.removeEventListener("keydown",this.boundKeyDown)}attributeChangedCallback(t){if(this.built){if("current-index"===t)return this.playlistIndex=Number(this.getAttribute("current-index")||0),void this.loadCurrentTrack();"repeat-mode"===t&&(this.currentRepeatMode=this.getAttribute("repeat-mode")||"none"),"shuffle"===t&&(this.isShuffle=this.getBool("shuffle"),this.refreshShuffledPlaylist()),"default-playback-rate"===t&&(this.playbackRate=Number(this.getAttribute("default-playback-rate")||1),this.audioEl&&(this.audioEl.playbackRate=this.playbackRate)),this.updateUI()}}getBool(t,e=!1){if(!this.hasAttribute(t))return e;const s=this.getAttribute(t);return""===s||"true"===s}getPlaylist(){try{const t=this.getAttribute("playlist"),e=t?JSON.parse(t):[];return Array.isArray(e)?e:[]}catch{return[]}}getPlaybackRates(){try{const t=this.getAttribute("playback-rates"),e=t?JSON.parse(t):[.5,.75,1,1.25,1.5,2];return Array.isArray(e)&&e.length>0?e:[.5,.75,1,1.25,1.5,2]}catch{return[.5,.75,1,1.25,1.5,2]}}getIconSize(){const t=this.getAttribute("size")||"m";return"s"===t?16:"l"===t?24:"xl"===t?32:20}getCurrentTrack(){const t=this.getPlaylist();if(t.length>0){return t[this.isShuffle&&this.shuffledPlaylist.length>0?this.shuffledPlaylist[this.playlistIndex]??this.playlistIndex:this.playlistIndex]||t[0]}return{src:this.getAttribute("src")||"",title:this.getAttribute("title")||void 0,artist:this.getAttribute("artist")||void 0,album:this.getAttribute("album")||void 0,albumArt:this.getAttribute("album-art")||void 0}}refreshShuffledPlaylist(){const t=this.getPlaylist();if(t.length>0&&this.isShuffle){const e=Array.from({length:t.length},(t,e)=>e);this.shuffledPlaylist=[...e].sort(()=>Math.random()-.5)}else this.shuffledPlaylist=[]}formatTime(t){return`${Math.floor(t/60)}:${Math.floor(t%60).toString().padStart(2,"0")}`}syncFromAttributes(){this.playlistIndex=Number(this.getAttribute("current-index")||0),this.currentRepeatMode=this.getAttribute("repeat-mode")||"none",this.isShuffle=this.getBool("shuffle"),this.playbackRate=Number(this.getAttribute("default-playback-rate")||1),this.isMuted=this.getBool("muted"),this.volume=this.isMuted?0:1,this.refreshShuffledPlaylist(),this.loadCurrentTrack()}loadCurrentTrack(){if(!this.audioEl)return;const t=this.getCurrentTrack(),e=t?.src||this.getAttribute("src")||"";this.audioEl.src!==e&&e&&(this.audioEl.src=e,this.audioEl.load()),this.updateTrackInfo(),this.updatePlaylistActive()}bindAudioEvents(){const t=this.audioEl;t&&(t.addEventListener("play",()=>{this.isPlaying=!0,this.dispatchEvent(new CustomEvent("db-play",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("pause",()=>{this.isPlaying=!1,this.dispatchEvent(new CustomEvent("db-pause",{bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("timeupdate",()=>{this.currentTime=t.currentTime,this.duration=t.duration||0,this.dispatchEvent(new CustomEvent("db-time-update",{detail:{currentTime:this.currentTime,duration:this.duration},bubbles:!0,composed:!0})),this.updateProgress(),this.updateTime()}),t.addEventListener("volumechange",()=>{this.volume=t.volume,this.isMuted=t.muted,this.dispatchEvent(new CustomEvent("db-volume-change",{detail:{volume:this.volume},bubbles:!0,composed:!0})),this.updateVolumeUI()}),t.addEventListener("ended",()=>this.handleEnded()),t.addEventListener("error",()=>{this.error="오디오를 재생할 수 없습니다.",this.dispatchEvent(new CustomEvent("db-error",{detail:{error:this.error},bubbles:!0,composed:!0})),this.updateUI()}),t.addEventListener("loadstart",()=>{this.isLoading=!0,this.error=null,this.updateUI()}),t.addEventListener("canplay",()=>{this.isLoading=!1,this.updateUI()}))}handleEnded(){this.isPlaying=!1,this.dispatchEvent(new CustomEvent("db-ended",{bubbles:!0,composed:!0}));const t=this.getPlaylist();if(t.length>0){if("one"===this.currentRepeatMode)this.audioEl&&(this.audioEl.currentTime=0,this.audioEl.play());else if("all"===this.currentRepeatMode||this.playlistIndex<t.length-1){const e=this.playlistIndex+1;e<t.length?this.setPlaylistIndex(e):"all"===this.currentRepeatMode&&this.setPlaylistIndex(0)}}else this.getBool("loop")&&this.audioEl&&(this.audioEl.currentTime=0,this.audioEl.play());this.updateUI()}setPlaylistIndex(t){this.playlistIndex=t,this.dispatchEvent(new CustomEvent("db-playlist-change",{detail:{index:t},bubbles:!0,composed:!0})),this.loadCurrentTrack(),this.audioEl&&this.audioEl.play()}togglePlay(){this.audioEl&&(this.isPlaying?this.audioEl.pause():this.audioEl.play())}toggleMute(){this.audioEl&&(this.audioEl.muted=!this.isMuted)}toggleRepeatMode(){const t=["none","one","all"],e=t[(t.indexOf(this.currentRepeatMode)+1)%t.length];this.currentRepeatMode=e,this.dispatchEvent(new CustomEvent("db-repeat-mode-change",{detail:{mode:e},bubbles:!0,composed:!0})),this.updateUI()}toggleShuffle(){this.isShuffle=!this.isShuffle,this.refreshShuffledPlaylist(),this.dispatchEvent(new CustomEvent("db-shuffle-change",{detail:{shuffle:this.isShuffle},bubbles:!0,composed:!0})),this.updateUI()}handlePlaybackRateChange(t){this.audioEl&&(this.audioEl.playbackRate=t,this.playbackRate=t,this.dispatchEvent(new CustomEvent("db-playback-rate-change",{detail:{rate:t},bubbles:!0,composed:!0})))}handleKeyDown(t){if(this.audioEl&&this.getBool("enable-keyboard",!0))switch(t.code){case"Space":t.preventDefault(),this.togglePlay();break;case"ArrowLeft":t.preventDefault(),this.audioEl.currentTime=Math.max(0,this.currentTime-10);break;case"ArrowRight":t.preventDefault(),this.audioEl.currentTime=Math.min(this.duration,this.currentTime+10);break;case"ArrowUp":t.preventDefault(),this.audioEl.volume=Math.min(1,this.volume+.1),this.audioEl.muted=!1;break;case"ArrowDown":t.preventDefault(),this.audioEl.volume=Math.max(0,this.volume-.1),this.audioEl.muted=0===this.audioEl.volume;break;case"KeyM":t.preventDefault(),this.toggleMute();break;case"KeyR":t.preventDefault(),this.toggleRepeatMode();break;case"KeyS":t.preventDefault(),this.toggleShuffle()}}iconButton(s,i,l){const a=document.createElement("button");return a.type="button",a.className=`${e}__control-button`,a.append(t(s,this.getIconSize())),l?.disabled&&(a.disabled=!0),l?.data&&Object.entries(l.data).forEach(([t,e])=>a.setAttribute(`data-${t}`,e)),a.addEventListener("click",i),a}build(){const t=this.getAttribute("size")||"m",s=this.getAttribute("variant")||"default",i=this.getAttribute("theme")||"auto";this.className=[e,`${e}--size-${t}`,`${e}--variant-${s}`,`${e}--theme-${i}`].join(" "),this.audioEl=document.createElement("audio"),this.audioEl.preload="metadata",this.audioEl.hidden=!0;const l=document.createElement("div");l.className=`${e}__loading`;const a=document.createElement("div");a.className=`${e}__spinner`,l.append(a);const n=document.createElement("div");n.className=`${e}__error`;const o=document.createElement("p");o.textContent="오디오를 재생할 수 없습니다.";const r=document.createElement("button");r.type="button",r.textContent="다시 시도",r.addEventListener("click",()=>window.location.reload()),n.append(o,r);const h=document.createElement("div");h.className=`${e}__album-art`,this.albumImgEl=document.createElement("img"),this.albumImgEl.addEventListener("error",()=>{this.albumImgEl&&(this.albumImgEl.style.display="none"),this.albumPlaceholderEl?.classList.remove("album-placeholder")}),this.albumPlaceholderEl=document.createElement("div"),this.albumPlaceholderEl.className="album-placeholder",this.albumPlaceholderEl.textContent="🎵",h.append(this.albumImgEl,this.albumPlaceholderEl);const u=document.createElement("div");u.className=`${e}__main`;const d=document.createElement("div");d.className=`${e}__info`,this.titleEl=document.createElement("div"),this.titleEl.className=`${e}__title`,this.artistEl=document.createElement("div"),this.artistEl.className=`${e}__artist`,this.albumEl=document.createElement("div"),this.albumEl.className=`${e}__album`,d.append(this.titleEl,this.artistEl,this.albumEl),this.progressEl=document.createElement("div"),this.progressEl.className=`${e}__progress`,this.progressBarEl=document.createElement("div"),this.progressBarEl.className=`${e}__progress-bar`,this.progressHandleEl=document.createElement("div"),this.progressHandleEl.className=`${e}__progress-handle`,this.progressEl.append(this.progressBarEl,this.progressHandleEl),this.progressEl.addEventListener("click",t=>{if(!this.audioEl||!this.progressEl)return;const e=this.progressEl.getBoundingClientRect(),s=(t.clientX-e.left)/e.width;this.audioEl.currentTime=s*(this.duration||0)});const c=document.createElement("div");c.className=`${e}__controls`;const m=document.createElement("div");m.className=`${e}__left-controls`;const p=document.createElement("div");p.className=`${e}__center-controls`;const E=document.createElement("div");E.className=`${e}__right-controls`,this.playButtonEl=document.createElement("button"),this.playButtonEl.type="button",this.playButtonEl.className=`${e}__play-button`,this.playButtonEl.addEventListener("click",()=>this.togglePlay()),p.append(this.playButtonEl),c.append(m,p,E);const g=document.createElement("div");g.className=`${e}__bottom-controls`;const b=document.createElement("div");b.className=`${e}__left-bottom`;const y=document.createElement("div");y.className=`${e}__right-bottom`;const v=document.createElement("div");v.className=`${e}__time`,this.timeCurrentEl=document.createElement("span");const f=document.createElement("span");f.textContent="/",this.timeDurationEl=document.createElement("span"),v.append(this.timeCurrentEl,f,this.timeDurationEl),b.append(v);const _=document.createElement("div");_.className=`${e}__volume`;const k=this.iconButton(this.isMuted||0===this.volume?"mute-filled":"volume-up",()=>this.toggleMute());k.title=this.isMuted||0===this.volume?"음소거 해제":"음소거";const C=document.createElement("div");C.className=`${e}__volume-slider`;const I=document.createElement("div");I.className="volume-label";const $=document.createElement("span");$.textContent="🔊";const P=document.createElement("span");I.append($,P),this.volumeInputEl=document.createElement("input"),this.volumeInputEl.type="range",this.volumeInputEl.min="0",this.volumeInputEl.max="1",this.volumeInputEl.step="0.1",this.volumeInputEl.addEventListener("input",()=>{if(!this.audioEl||!this.volumeInputEl)return;const t=parseFloat(this.volumeInputEl.value);this.audioEl.volume=t,this.audioEl.muted=0===t}),C.append(I,this.volumeInputEl),_.append(k,C);const A=document.createElement("div");A.className=`${e}__settings`;const x=this.iconButton("settings",()=>{this.showSettingsMenu=!this.showSettingsMenu,this.updateSettingsMenu()});this.settingsMenuEl=document.createElement("div"),this.settingsMenuEl.className=`${e}__settings-menu`,this.settingsMenuEl.hidden=!0;const M=document.createElement("div");M.className=`${e}__setting-group`;const w=document.createElement("label");w.textContent="재생 속도";const B=document.createElement("select");B.addEventListener("change",()=>this.handlePlaybackRateChange(parseFloat(B.value))),M.append(w,B),this.settingsMenuEl.append(M),A.append(x,this.settingsMenuEl),y.append(_,A),g.append(b,y),u.append(d,this.progressEl,c,g);const N=document.createElement("div");N.className=`${e}__playlist`,N.hidden=!0;const T=document.createElement("h4");T.textContent="플레이리스트",this.playlistTracksEl=document.createElement("div"),this.playlistTracksEl.className=`${e}__playlist-tracks`,N.append(T,this.playlistTracksEl),this.append(this.audioEl,l,n,h,u,N),this._leftControls=m,this._rightControls=E,this._controls=c,this._time=v,this._volumeWrap=_,this._settings=A,this._playlist=N,this._rateSelect=B,this._volumePercent=P,this._muteButton=k}get leftControls(){return this._leftControls}get rightControls(){return this._rightControls}get controlsEl(){return this._controls}get timeEl(){return this._time}get volumeWrap(){return this._volumeWrap}get settingsEl(){return this._settings}get playlistEl(){return this._playlist}get rateSelect(){return this._rateSelect}get volumePercent(){return this._volumePercent}get muteButton(){return this._muteButton}updateTrackInfo(){const t=this.getCurrentTrack();this.titleEl&&(this.titleEl.textContent=t?.title||"Unknown Track"),this.artistEl&&(this.artistEl.textContent=t?.artist||"Unknown Artist"),this.albumEl&&(t?.album?(this.albumEl.textContent=t.album,this.albumEl.hidden=!1):this.albumEl.hidden=!0),this.albumImgEl&&this.albumPlaceholderEl&&(t?.albumArt?(this.albumImgEl.src=t.albumArt,this.albumImgEl.alt=t.title||"Album Art",this.albumImgEl.style.display=""):(this.albumImgEl.removeAttribute("src"),this.albumImgEl.style.display="none"),this.albumPlaceholderEl.className="album-placeholder")}updateProgress(){const t=this.duration>0?this.currentTime/this.duration*100:0;this.progressBarEl&&(this.progressBarEl.style.width=`${t}%`),this.progressHandleEl&&(this.progressHandleEl.style.left=`${t}%`,this.progressHandleEl.style.transform=t<=0?"translate(0, -50%)":t>=100?"translate(-100%, -50%)":"translate(-50%, -50%)")}updateTime(){this.timeCurrentEl&&(this.timeCurrentEl.textContent=this.formatTime(this.currentTime)),this.timeDurationEl&&(this.timeDurationEl.textContent=this.formatTime(this.duration))}updateVolumeUI(){this.volumeInputEl&&(this.volumeInputEl.value=String(this.volume)),this.volumePercent&&(this.volumePercent.textContent=`${Math.round(100*this.volume)}%`),this.muteButton&&(this.muteButton.replaceChildren(t(this.isMuted||0===this.volume?"mute-filled":"volume-up",this.getIconSize())),this.muteButton.title=this.isMuted||0===this.volume?"음소거 해제":"음소거")}updateSettingsMenu(){this.settingsMenuEl&&(this.settingsMenuEl.hidden=!this.showSettingsMenu)}rebuildControlButtons(){const e=this.getPlaylist(),s=1.2*this.getIconSize();this.leftControls.replaceChildren(),this.rightControls.replaceChildren(),e.length>0&&this.leftControls.append(this.iconButton("refresh",()=>this.toggleShuffle(),{data:{shuffle:"true"}})),this.leftControls.append(this.iconButton("chevron-left",()=>{if(e.length>0){const t=this.playlistIndex-1;t>=0&&this.setPlaylistIndex(t)}else this.audioEl&&(this.audioEl.currentTime=0)},{disabled:e.length>0&&0===this.playlistIndex})),this.playButtonEl&&this.playButtonEl.replaceChildren(t(this.isPlaying?"pause":"play",s)),this.rightControls.append(this.iconButton("chevron-right",()=>{if(e.length>0){const t=this.playlistIndex+1;t<e.length&&this.setPlaylistIndex(t)}},{disabled:e.length>0&&this.playlistIndex===e.length-1})),e.length>0&&this.rightControls.append(this.iconButton("repeat",()=>this.toggleRepeatMode(),{data:{repeat:"true"}}))}rebuildPlaylist(){if(!this.playlistTracksEl)return;const t=this.getPlaylist(),s=this.getAttribute("variant")||"default";this.playlistEl.hidden=!("full"===s&&t.length>0),this.playlistTracksEl.replaceChildren(),t.forEach((t,s)=>{const i=document.createElement("div");if(i.className=`${e}__playlist-track${s===this.playlistIndex?` ${e}__playlist-track--active`:""}`,i.addEventListener("click",()=>this.setPlaylistIndex(s)),t.albumArt){const e=document.createElement("img");e.src=t.albumArt,e.alt=t.title||"Album Art",i.append(e)}const l=document.createElement("div");l.className=`${e}__playlist-track-info`;const a=document.createElement("div");a.className=`${e}__playlist-track-title`,a.textContent=t.title||"Unknown Track";const n=document.createElement("div");n.className=`${e}__playlist-track-artist`,n.textContent=t.artist||"Unknown Artist",l.append(a,n),i.append(l),this.playlistTracksEl?.append(i)})}updatePlaylistActive(){this.playlistTracksEl&&Array.from(this.playlistTracksEl.children).forEach((t,s)=>{t.classList.toggle(`${e}__playlist-track--active`,s===this.playlistIndex)})}updateUI(){const t=this.getAttribute("size")||"m",s=this.getAttribute("variant")||"default",i=this.getAttribute("theme")||"auto",l=this.getBool("show-controls",!0),a=this.getBool("show-progress",!0),n=this.getBool("show-time",!0),o=this.getBool("show-volume",!0),r=this.getBool("show-settings",!1);this.className=[e,`${e}--size-${t}`,`${e}--variant-${s}`,`${e}--theme-${i}`,this.isPlaying&&`${e}--playing`,!this.isPlaying&&`${e}--paused`,this.isMuted&&`${e}--muted`,this.isLoading&&`${e}--loading`,this.error&&`${e}--error`,this.isShuffle&&`${e}--shuffle`,`${e}--repeat-${this.currentRepeatMode}`].filter(Boolean).join(" "),this.audioEl&&(this.audioEl.autoplay=this.getBool("auto-play"),this.audioEl.loop=this.getBool("loop"),this.audioEl.muted=this.getBool("muted"),this.audioEl.playbackRate=this.playbackRate),this.progressEl&&(this.progressEl.hidden=!a),this.controlsEl&&(this.controlsEl.hidden=!l),this.timeEl&&(this.timeEl.hidden=!n),this.volumeWrap&&(this.volumeWrap.hidden=!o),this.settingsEl&&(this.settingsEl.hidden=!r),this.rateSelect.replaceChildren(...this.getPlaybackRates().map(t=>{const e=document.createElement("option");return e.value=String(t),e.textContent=`${t}x`,t===this.playbackRate&&(e.selected=!0),e})),this.rebuildControlButtons(),this.updateTrackInfo(),this.updateProgress(),this.updateTime(),this.updateVolumeUI(),this.updateSettingsMenu(),this.rebuildPlaylist()}}customElements.get("db-audio-player")||customElements.define("db-audio-player",DbAudioPlayer);export{DbAudioPlayer};