11<script setup lang="ts">
22import { ref , computed , nextTick , watch , onMounted } from ' vue'
33import { useI18n } from ' vue-i18n'
4- import { Music , Play , Pause , Zap , Minus , Plus , ChevronLeft , ChevronRight , LayoutGrid , AlignJustify } from ' lucide-vue-next'
4+ import { Music , Play , Pause , Zap , Minus , Plus , ChevronLeft , ChevronRight , LayoutGrid , AlignJustify , RotateCcw } from ' lucide-vue-next'
55import { useTranspose } from ' ~/composables/useTranspose'
66import IconButton from ' ~/components/base/IconButton.vue'
77import Stepper from ' ~/components/base/Stepper.vue'
@@ -34,6 +34,10 @@ const emit = defineEmits<{
3434 (e : ' update:fontSize' , value : number ): void
3535 (e : ' update:gridMode' , value : boolean ): void
3636 (e : ' update:gridColumns' , value : GridColumns ): void
37+ (e : ' reset:transpose' ): void
38+ (e : ' reset:scroll' ): void
39+ (e : ' reset:fontSize' ): void
40+ (e : ' reset:layout' ): void
3741}>()
3842
3943// Column options for grid
@@ -178,8 +182,13 @@ const closeDrawer = () => {
178182 <div class =" hidden lg:flex flex-col gap-6 sticky top-24" >
179183 <!-- Transpose Card -->
180184 <Card variant =" section" :title =" t('toolbox.transpose')" >
185+ <template #header-action >
186+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
187+ :title =" t('toolbox.reset')" @click =" emit('reset:transpose')" />
188+ </template >
181189 <!-- +/- Controls -->
182- <div class =" mb-3" >
190+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of +/- controls regardless of app direction -->
191+ <div class =" mb-3" dir =" ltr" >
183192 <Stepper :model-value =" currentTableIndex" :min =" 0" :max =" 11" :step =" 1" :format-value =" formatKeyName"
184193 :decrease-aria-label =" t('toolbox.ariaLabels.decreaseTranspose')"
185194 :increase-aria-label =" t('toolbox.ariaLabels.increaseTranspose')" size =" md" variant =" compact"
@@ -192,7 +201,8 @@ const closeDrawer = () => {
192201 @click =" scrollCarousel(carouselRef, -1)" />
193202
194203 <!-- Carousel Container -->
195- <div ref =" carouselRef" class =" flex gap-1 overflow-x-auto scrollbar-hide scroll-smooth px-7 py-1"
204+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of keys regardless of app direction -->
205+ <div ref =" carouselRef" dir =" ltr" class =" flex gap-1 overflow-x-auto scrollbar-hide scroll-smooth px-7 py-1"
196206 style =" scrollbar-width : none ; -ms-overflow-style : none ;" >
197207 <button v-for =" key in keySignatures" :key =" key.index" :data-key-index =" key.index"
198208 class =" flex-shrink-0 w-9 h-9 rounded-lg font-mono text-sm font-bold transition-all cursor-pointer" :class =" [
@@ -218,6 +228,10 @@ const closeDrawer = () => {
218228
219229 <!-- Auto Scroll Card -->
220230 <Card variant =" section" :title =" t('toolbox.autoScroll')" >
231+ <template #header-action >
232+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
233+ :title =" t('toolbox.reset')" @click =" emit('reset:scroll')" />
234+ </template >
221235 <div class =" relative group" >
222236 <button
223237 class =" w-full px-4 py-2 rounded-lg font-bold transition-colors flex items-center justify-center mb-3 cursor-pointer"
@@ -233,22 +247,36 @@ const closeDrawer = () => {
233247 </div >
234248
235249 <div class =" text-xs text-text-muted mb-2" >{{ t('toolbox.speed') }}</div >
236- <Stepper :model-value =" scrollSpeed" :min =" MIN_SPEED" :max =" MAX_SPEED" :step =" SPEED_STEP"
237- :format-value =" formatSpeed" :decrease-aria-label =" t('toolbox.ariaLabels.decreaseSpeed')"
238- :increase-aria-label =" t('toolbox.ariaLabels.increaseSpeed')" size =" md" variant =" compact"
239- @update:model-value =" (val) => emit('update:speed', val)" />
250+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of +/- controls regardless of app direction -->
251+ <div dir =" ltr" >
252+ <Stepper :model-value =" scrollSpeed" :min =" MIN_SPEED" :max =" MAX_SPEED" :step =" SPEED_STEP"
253+ :format-value =" formatSpeed" :decrease-aria-label =" t('toolbox.ariaLabels.decreaseSpeed')"
254+ :increase-aria-label =" t('toolbox.ariaLabels.increaseSpeed')" size =" md" variant =" compact"
255+ @update:model-value =" (val) => emit('update:speed', val)" />
256+ </div >
240257 </Card >
241258
242259 <!-- Font Size Card -->
243260 <Card variant =" section" :title =" t('toolbox.fontSize')" >
244- <Stepper :model-value =" fontSize" :min =" 0.8" :max =" 2.0" :step =" 0.1" :format-value =" formatFontSize"
245- :decrease-aria-label =" t('toolbox.ariaLabels.decreaseFontSize')"
246- :increase-aria-label =" t('toolbox.ariaLabels.increaseFontSize')" size =" md" variant =" compact"
247- @update:model-value =" (val) => emit('update:fontSize', val)" />
261+ <template #header-action >
262+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
263+ :title =" t('toolbox.reset')" @click =" emit('reset:fontSize')" />
264+ </template >
265+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of +/- controls regardless of app direction -->
266+ <div dir =" ltr" >
267+ <Stepper :model-value =" fontSize" :min =" 0.8" :max =" 2.0" :step =" 0.1" :format-value =" formatFontSize"
268+ :decrease-aria-label =" t('toolbox.ariaLabels.decreaseFontSize')"
269+ :increase-aria-label =" t('toolbox.ariaLabels.increaseFontSize')" size =" md" variant =" compact"
270+ @update:model-value =" (val) => emit('update:fontSize', val)" />
271+ </div >
248272 </Card >
249273
250274 <!-- Layout Card (Grid Toggle) -->
251275 <Card variant =" section" :title =" t('toolbox.layout')" >
276+ <template #header-action >
277+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
278+ :title =" t('toolbox.reset')" @click =" emit('reset:layout')" />
279+ </template >
252280 <div class =" mb-3" >
253281 <SegmentedControl :model-value =" gridMode" :options =" gridModeOptions" size =" md" variant =" default"
254282 @update:model-value =" (val) => emit('update:gridMode', val)" />
@@ -303,10 +331,17 @@ const closeDrawer = () => {
303331
304332 <!-- Transpose Tab -->
305333 <div v-if =" activeTab === 'transpose'" class =" space-y-6" >
306- <h3 class =" text-lg font-bold text-center" >{{ t('toolbox.transposeKey') }}</h3 >
334+ <div class =" flex items-center justify-center relative" >
335+ <h3 class =" text-lg font-bold text-center" >{{ t('toolbox.transposeKey') }}</h3 >
336+ <div class =" absolute right-0" >
337+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
338+ :title =" t('toolbox.reset')" @click =" emit('reset:transpose')" />
339+ </div >
340+ </div >
307341
308342 <!-- +/- Controls with Current Key -->
309- <div class =" flex items-center justify-center gap-6" >
343+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of +/- controls regardless of app direction -->
344+ <div class =" flex items-center justify-center gap-6" dir =" ltr" >
310345 <IconButton :icon =" Minus" variant =" secondary" size =" sm" :ariaLabel =" t('toolbox.ariaLabels.decreaseTranspose')"
311346 @click =" stepKey(-1)" />
312347
@@ -325,7 +360,9 @@ const closeDrawer = () => {
325360 @click =" scrollCarousel(mobileCarouselRef, -1)" />
326361
327362 <!-- Carousel Container -->
328- <div ref =" mobileCarouselRef" class =" flex gap-2 overflow-x-auto scrollbar-hide scroll-smooth px-10 py-2"
363+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of keys regardless of app direction -->
364+ <div ref =" mobileCarouselRef" dir =" ltr"
365+ class =" flex gap-2 overflow-x-auto scrollbar-hide scroll-smooth px-10 py-2"
329366 style =" scrollbar-width : none ; -ms-overflow-style : none ;" >
330367 <button v-for =" key in keySignatures" :key =" key.index" :data-key-index =" key.index"
331368 class =" flex-shrink-0 w-12 h-12 rounded-xl font-mono text-base font-bold transition-all cursor-pointer"
@@ -353,9 +390,16 @@ const closeDrawer = () => {
353390
354391 <!-- Scroll/Speed Tab -->
355392 <div v-else-if =" activeTab === 'scroll'" class =" space-y-6" >
356- <h3 class =" text-lg font-bold text-center" >{{ t('toolbox.autoScrollSpeed') }}</h3 >
393+ <div class =" flex items-center justify-center relative" >
394+ <h3 class =" text-lg font-bold text-center" >{{ t('toolbox.autoScrollSpeed') }}</h3 >
395+ <div class =" absolute right-0" >
396+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
397+ :title =" t('toolbox.reset')" @click =" emit('reset:scroll')" />
398+ </div >
399+ </div >
357400
358- <div class =" flex items-center justify-center gap-6" >
401+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of +/- controls regardless of app direction -->
402+ <div class =" flex items-center justify-center gap-6" dir =" ltr" >
359403 <IconButton :icon =" Minus" variant =" secondary" size =" sm" :disabled =" scrollSpeed <= MIN_SPEED"
360404 :ariaLabel =" t('toolbox.ariaLabels.decreaseSpeed')" @click =" stepSpeed(-1)" />
361405
@@ -368,18 +412,29 @@ const closeDrawer = () => {
368412 </div >
369413
370414 <div class =" flex items-center justify-between bg-surface-muted rounded-xl p-4" >
371- <span class =" text-sm font-bold" >{{ t('toolbox.fontSize') }}</span >
372- <Stepper :model-value =" fontSize" :min =" 0.8" :max =" 2.0" :step =" 0.1" :format-value =" formatFontSize"
373- :decrease-aria-label =" t('toolbox.ariaLabels.decreaseFontSize')"
374- :increase-aria-label =" t('toolbox.ariaLabels.increaseFontSize')" size =" sm" variant =" compact"
375- @update:model-value =" (val) => emit('update:fontSize', val)" />
415+ <div class =" flex items-center gap-2" >
416+ <span class =" text-sm font-bold" >{{ t('toolbox.fontSize') }}</span >
417+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
418+ :title =" t('toolbox.reset')" @click =" emit('reset:fontSize')" />
419+ </div >
420+ <!-- Note: dir="ltr" is mandatory here to ensure correct order of +/- controls regardless of app direction -->
421+ <div dir =" ltr" >
422+ <Stepper :model-value =" fontSize" :min =" 0.8" :max =" 2.0" :step =" 0.1" :format-value =" formatFontSize"
423+ :decrease-aria-label =" t('toolbox.ariaLabels.decreaseFontSize')"
424+ :increase-aria-label =" t('toolbox.ariaLabels.increaseFontSize')" size =" sm" variant =" compact"
425+ @update:model-value =" (val) => emit('update:fontSize', val)" />
426+ </div >
376427 </div >
377428
378429 <!-- Grid Layout & Columns Combined (Mobile) -->
379430 <div class =" bg-surface-muted rounded-xl p-4 space-y-3" >
380431 <!-- Layout Toggle Row -->
381432 <div class =" flex items-center justify-between" >
382- <span class =" text-sm font-bold" >{{ t('toolbox.layout') }}</span >
433+ <div class =" flex items-center gap-2" >
434+ <span class =" text-sm font-bold" >{{ t('toolbox.layout') }}</span >
435+ <IconButton :icon =" RotateCcw" variant =" ghost" size =" xs" :ariaLabel =" t('toolbox.reset')"
436+ :title =" t('toolbox.reset')" @click =" emit('reset:layout')" />
437+ </div >
383438 <SegmentedControl :model-value =" gridMode" :options =" gridModeOptions" size =" lg" variant =" compact"
384439 @update:model-value =" (val) => emit('update:gridMode', val)" />
385440 </div >
0 commit comments