@m3ui-vue/m3ui-vue 0.2.0 → 0.2.2

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 (92) hide show
  1. package/dist/{MMenuItem-CIDblhtb.js → MMenuItem-DpoEsH91.js} +195 -114
  2. package/dist/MMenuItem-DpoEsH91.js.map +1 -0
  3. package/dist/components/MAlert.vue.d.ts +1 -0
  4. package/dist/components/MButton.vue.d.ts +1 -1
  5. package/dist/components/MCalendar.vue.d.ts +3 -0
  6. package/dist/components/MChip.vue.d.ts +3 -3
  7. package/dist/components/MCommandPalette.vue.d.ts +3 -2
  8. package/dist/components/MConfirmDialog.vue.d.ts +0 -2
  9. package/dist/components/MDataTable.vue.d.ts +8 -2
  10. package/dist/components/MDatePicker.vue.d.ts +2 -0
  11. package/dist/components/MDateRangePicker.vue.d.ts +5 -0
  12. package/dist/components/MDialog.vue.d.ts +1 -0
  13. package/dist/components/MFileUpload.vue.d.ts +15 -1
  14. package/dist/components/MInfiniteScroll.vue.d.ts +7 -5
  15. package/dist/components/MMultiSelect.vue.d.ts +13 -1
  16. package/dist/components/MRichTextEditor.vue.d.ts +28 -1
  17. package/dist/components/MScheduler.vue.d.ts +5 -0
  18. package/dist/components/MSnackbar.vue.d.ts +4 -1
  19. package/dist/components/MSpinner.vue.d.ts +1 -0
  20. package/dist/components/MSpotlightSearch.vue.d.ts +3 -2
  21. package/dist/components/MTable.vue.d.ts +2 -1
  22. package/dist/components/MTimePicker.vue.d.ts +1 -0
  23. package/dist/components/MTour.vue.d.ts +3 -0
  24. package/dist/components/MTransferList.vue.d.ts +19 -3
  25. package/dist/components/MTree.vue.d.ts +1 -1
  26. package/dist/composables/useLocale.d.ts +74 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/locales/de.d.ts +2 -0
  29. package/dist/locales/es.d.ts +2 -0
  30. package/dist/locales/fr.d.ts +2 -0
  31. package/dist/locales/index.d.ts +8 -0
  32. package/dist/locales/index.js +489 -0
  33. package/dist/locales/index.js.map +1 -0
  34. package/dist/locales/ja.d.ts +2 -0
  35. package/dist/locales/ko.d.ts +2 -0
  36. package/dist/locales/pt.d.ts +2 -0
  37. package/dist/locales/zh.d.ts +2 -0
  38. package/dist/m3ui-vue.css +1 -1
  39. package/dist/m3ui.js +2734 -2647
  40. package/dist/m3ui.js.map +1 -1
  41. package/dist/plugin.d.ts +3 -1
  42. package/dist/rich-text-editor.d.ts +1 -0
  43. package/dist/rich-text-editor.js +141 -113
  44. package/dist/rich-text-editor.js.map +1 -1
  45. package/dist/styles/palettes.css +660 -0
  46. package/dist/styles.css +1 -1
  47. package/package.json +11 -2
  48. package/src/__tests__/MAlert.test.ts +59 -0
  49. package/src/__tests__/MConfirmDialog.test.ts +51 -0
  50. package/src/__tests__/MDialog.test.ts +58 -0
  51. package/src/__tests__/MSpinner.test.ts +37 -0
  52. package/src/__tests__/localePresets.test.ts +45 -0
  53. package/src/__tests__/plugin.test.ts +27 -0
  54. package/src/__tests__/setup.ts +11 -0
  55. package/src/__tests__/useColorPalette.test.ts +71 -0
  56. package/src/__tests__/useLocale.test.ts +50 -0
  57. package/src/__tests__/useTheme.test.ts +61 -0
  58. package/src/components/MAlert.vue +5 -1
  59. package/src/components/MCalendar.vue +14 -5
  60. package/src/components/MCommandPalette.vue +11 -7
  61. package/src/components/MConfirmDialog.vue +6 -5
  62. package/src/components/MDataTable.vue +18 -9
  63. package/src/components/MDatePicker.vue +11 -4
  64. package/src/components/MDateRangePicker.vue +15 -5
  65. package/src/components/MDialog.vue +5 -1
  66. package/src/components/MFileUpload.vue +22 -10
  67. package/src/components/MInfiniteScroll.vue +14 -9
  68. package/src/components/MMultiSelect.vue +11 -7
  69. package/src/components/MRichTextEditor.vue +95 -34
  70. package/src/components/MScheduler.vue +12 -4
  71. package/src/components/MSnackbar.vue +8 -1
  72. package/src/components/MSpinner.vue +5 -1
  73. package/src/components/MSpotlightSearch.vue +11 -7
  74. package/src/components/MTable.vue +8 -4
  75. package/src/components/MTimePicker.vue +5 -1
  76. package/src/components/MTour.vue +12 -9
  77. package/src/components/MTransferList.vue +30 -15
  78. package/src/composables/useColorPalette.ts +30 -20
  79. package/src/composables/useLocale.ts +181 -0
  80. package/src/index.ts +2 -0
  81. package/src/locales/de.ts +93 -0
  82. package/src/locales/es.ts +93 -0
  83. package/src/locales/fr.ts +93 -0
  84. package/src/locales/index.ts +8 -0
  85. package/src/locales/ja.ts +93 -0
  86. package/src/locales/ko.ts +93 -0
  87. package/src/locales/pt.ts +93 -0
  88. package/src/locales/zh.ts +93 -0
  89. package/src/plugin.ts +7 -1
  90. package/src/rich-text-editor.ts +1 -0
  91. package/src/styles/palettes.css +660 -0
  92. package/dist/MMenuItem-CIDblhtb.js.map +0 -1
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- .code-editor-container[data-v-b0a66a4b] .cm-editor{height:100%;min-height:inherit;font-family:Roboto Mono,Fira Code,Consolas,monospace;font-size:.8125rem;line-height:1.6}.code-editor-container[data-v-b0a66a4b] .cm-editor.cm-focused{outline:none}.code-editor-container[data-v-b0a66a4b] .cm-scroller{min-height:inherit}.code-editor-container[data-v-b0a66a4b] .cm-content{padding:12px 0}.code-editor-container[data-v-b0a66a4b] .cm-line{padding:0 16px}.code-editor-container[data-v-b0a66a4b] .cm-gutters{background:var(--color-surface-container);border-right:1px solid var(--color-outline-variant);color:var(--color-outline);padding:0 4px;font-size:.75rem}.code-editor-container[data-v-b0a66a4b] .cm-activeLineGutter{background:var(--color-surface-container-high);color:var(--color-on-surface-variant)}.code-editor-container[data-v-b0a66a4b] .cm-activeLine{background:var(--color-surface-container-lowest)}.code-editor-container[data-v-b0a66a4b] .cm-selectionBackground{background:var(--color-primary-container)!important}.code-editor-container[data-v-b0a66a4b] .cm-cursor{border-left-color:var(--color-primary);border-left-width:2px}.code-editor-container[data-v-b0a66a4b] .cm-matchingBracket{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px}.code-editor-container[data-v-b0a66a4b] .cm-foldGutter span{color:var(--color-on-surface-variant)}@keyframes m3-wavy-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes m3-wavy-travel{0%{stroke-dashoffset:0}to{stroke-dashoffset:calc(var(--m3-wave-len) * -1px)}}@media (prefers-reduced-motion:reduce){.animate-\[m3-wavy-spin_2\.8s_linear_infinite\]{animation:2.8s linear infinite m3-wavy-spin}.animate-\[m3-wavy-travel_2s_linear_infinite\]{animation:none!important}}.m3-dialog-enter-active[data-v-e7dfca29],.m3-dialog-leave-active[data-v-e7dfca29]{transition:opacity .15s}.m3-dialog-enter-from[data-v-e7dfca29],.m3-dialog-leave-to[data-v-e7dfca29]{opacity:0}.m3-dialog-enter-active .dialog-box[data-v-e7dfca29],.m3-dialog-leave-active .dialog-box[data-v-e7dfca29]{transition:transform .15s}.m3-dialog-enter-from .dialog-box[data-v-e7dfca29],.m3-dialog-leave-to .dialog-box[data-v-e7dfca29]{transform:scale(.95)}.bs-scrim[data-v-5b1fb748]{transition:opacity .3s}.bs-enter-from .bs-scrim[data-v-5b1fb748],.bs-leave-to .bs-scrim[data-v-5b1fb748]{opacity:0}.bs-panel[data-v-5b1fb748]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.bs-enter-from .bs-panel[data-v-5b1fb748]{opacity:0;transform:translateY(40%)}.bs-leave-to .bs-panel[data-v-5b1fb748]{opacity:0;transform:translateY(100%)!important}.m3-carousel-right-enter-active,.m3-carousel-right-leave-active,.m3-carousel-left-enter-active,.m3-carousel-left-leave-active{transition:transform .5s cubic-bezier(.2,0,0,1),opacity .5s cubic-bezier(.2,0,0,1)}.m3-carousel-right-enter-from{opacity:0;transform:translate(100%)}.m3-carousel-right-leave-to,.m3-carousel-left-enter-from{opacity:0;transform:translate(-100%)}.m3-carousel-left-leave-to{opacity:0;transform:translate(100%)}.hue-slider[data-v-872ab758]{background:linear-gradient(90deg,red 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.hue-slider[data-v-872ab758]::-webkit-slider-thumb{-webkit-appearance:none;cursor:pointer;background:#fff;border-radius:50%;width:16px;height:16px;box-shadow:0 1px 3px #0006}.hue-slider[data-v-872ab758]::-moz-range-thumb{cursor:pointer;background:#fff;border:none;border-radius:50%;width:16px;height:16px;box-shadow:0 1px 3px #0006}.m3-cmd-enter-active[data-v-da578f14],.m3-cmd-leave-active[data-v-da578f14]{transition:opacity .15s}.m3-cmd-enter-from[data-v-da578f14],.m3-cmd-leave-to[data-v-da578f14]{opacity:0}.m3-cmd-enter-active .cmd-box[data-v-da578f14],.m3-cmd-leave-active .cmd-box[data-v-da578f14]{transition:transform .15s}.m3-cmd-enter-from .cmd-box[data-v-da578f14],.m3-cmd-leave-to .cmd-box[data-v-da578f14]{transform:scale(.95)translateY(-10px)}.dt-expand-grid[data-v-891083a3]{grid-template-rows:0fr;transition:grid-template-rows .25s cubic-bezier(.2,0,0,1);display:grid}.dt-expand-grid.dt-expand-open[data-v-891083a3]{grid-template-rows:1fr}.dt-expand-body[data-v-891083a3]{min-height:0;overflow:hidden}.expand-grid[data-v-89e4475b]{grid-template-rows:1fr;display:grid}.expand-body[data-v-89e4475b]{min-height:0;overflow:hidden}.expand-enter-active[data-v-89e4475b]{transition:grid-template-rows .28s cubic-bezier(.2,0,0,1)}.expand-enter-active>.expand-body[data-v-89e4475b]{transition:opacity .22s}.expand-enter-from[data-v-89e4475b]{grid-template-rows:0fr}.expand-enter-from>.expand-body[data-v-89e4475b]{opacity:0}.expand-leave-active[data-v-89e4475b]{transition:grid-template-rows .22s cubic-bezier(.4,0,1,1)}.expand-leave-active>.expand-body[data-v-89e4475b]{transition:opacity .15s}.expand-leave-to[data-v-89e4475b]{grid-template-rows:0fr}.expand-leave-to>.expand-body[data-v-89e4475b]{opacity:0}.m3-file-enter-active[data-v-34a862f0],.m3-file-leave-active[data-v-34a862f0]{transition:all .2s}.m3-file-enter-from[data-v-34a862f0],.m3-file-leave-to[data-v-34a862f0]{opacity:0;transform:translateY(-8px)}.mli-expand-grid{grid-template-rows:1fr;display:grid}.mli-expand-body{min-height:0;padding-left:2rem;overflow:hidden}.mli-expand-enter-active{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1)}.mli-expand-enter-active>.mli-expand-body{transition:opacity .2s cubic-bezier(.2,0,0,1) 80ms}.mli-expand-enter-from{grid-template-rows:0fr}.mli-expand-enter-from>.mli-expand-body{opacity:0}.mli-expand-leave-active{transition:grid-template-rows .25s cubic-bezier(.4,0,1,1)}.mli-expand-leave-active>.mli-expand-body{transition:opacity .12s cubic-bezier(.4,0,1,1)}.mli-expand-leave-to{grid-template-rows:0fr}.mli-expand-leave-to>.mli-expand-body{opacity:0}input[type=number][data-v-9068433a]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number][data-v-9068433a]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number][data-v-9068433a]{-moz-appearance:textfield}.nd-scrim[data-v-51e30965]{transition:opacity .28s}.nd-enter-from .nd-scrim[data-v-51e30965],.nd-leave-to .nd-scrim[data-v-51e30965]{opacity:0}.nd-panel[data-v-51e30965]{transition:transform .3s cubic-bezier(.2,0,0,1)}.nd-enter-from .nd-panel[data-v-51e30965],.nd-leave-to .nd-panel[data-v-51e30965]{transform:translate(-100%)}.nd-section-grid[data-v-51e30965]{grid-template-rows:1fr;display:grid}.nd-section-body[data-v-51e30965]{min-height:0;overflow:hidden}.nd-section-enter-active[data-v-51e30965]{transition:grid-template-rows .35s cubic-bezier(.2,0,0,1)}.nd-section-enter-active>.nd-section-body[data-v-51e30965]{transition:opacity .25s cubic-bezier(.2,0,0,1) 80ms}.nd-section-enter-from[data-v-51e30965]{grid-template-rows:0fr}.nd-section-enter-from>.nd-section-body[data-v-51e30965]{opacity:0}.nd-section-leave-active[data-v-51e30965]{transition:grid-template-rows .28s cubic-bezier(.4,0,1,1)}.nd-section-leave-active>.nd-section-body[data-v-51e30965]{transition:opacity .15s cubic-bezier(.4,0,1,1)}.nd-section-leave-to[data-v-51e30965]{grid-template-rows:0fr}.nd-section-leave-to>.nd-section-body[data-v-51e30965]{opacity:0}.nd-inline[data-v-51e30965]{transition:width .3s cubic-bezier(.2,0,0,1);overflow:hidden}.nd-inline[data-v-51e30965]>*{min-width:18rem}.nd-inline.nd-collapsed[data-v-51e30965]>*{min-width:0}.nd-label[data-v-51e30965]{transition:opacity .2s cubic-bezier(.2,0,0,1) 80ms}.nd-inline.nd-collapsed .nd-label[data-v-51e30965],.nd-inline.nd-collapsed[data-v-51e30965] .nd-label{opacity:0;transition:opacity .1s cubic-bezier(.2,0,0,1)}.nd-inline .nd-collapse-h[data-v-51e30965]{max-height:80px;transition:max-height .3s cubic-bezier(.2,0,0,1)}.nd-inline.nd-collapsed .nd-collapse-h[data-v-51e30965]{max-height:0}.nd-inline .nd-section-body[data-v-51e30965]>*,.nd-inline[data-v-51e30965] .nd-section-body>*{transition:padding .3s cubic-bezier(.2,0,0,1)}.nd-children-divider[data-v-51e30965]{border-top:1px solid #0000;height:0;margin:0 .75rem;transition:border-color .1s,margin .1s}.nd-inline.nd-collapsed .nd-children-divider[data-v-51e30965]{border-top-color:var(--color-outline-variant);margin:.25rem .75rem;transition:border-color .2s .3s,margin .2s .3s}@keyframes m3-wave-flow{0%{transform:translate(0)}to{transform:translate(-20px)}}@keyframes m3-progress-indeterminate{0%{left:-40%}to{left:100%}}@media (prefers-reduced-motion:reduce){.animate-\[m3-wave-flow_1\.2s_linear_infinite\],.animate-\[m3-wave-flow_0\.9s_linear_infinite\],.animate-\[m3-progress-indeterminate_1\.6s_ease-in-out_infinite\]{animation:none!important}}.m3-radio-dot[data-v-cdb650b5]{transform-box:fill-box;transform-origin:50%;transition:transform .15s;transform:scale(0)}.m3-radio-dot.is-checked[data-v-cdb650b5]{transform:scale(1)}.ss-scrim[data-v-ba5b6f4b]{transition:opacity .28s}.ss-enter-from .ss-scrim[data-v-ba5b6f4b],.ss-leave-to .ss-scrim[data-v-ba5b6f4b]{opacity:0}.ss-panel[data-v-ba5b6f4b]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.ss-enter-from .ss-panel[data-v-ba5b6f4b]{opacity:0;transform:translate(40%)}.ss-leave-to .ss-panel[data-v-ba5b6f4b]{opacity:0;transform:translate(100%)!important}@keyframes skeleton-wave-move-32ecf05b{0%{transform:translate(-100%)}60%{transform:translate(100%)}to{transform:translate(100%)}}.skeleton-wave[data-v-32ecf05b]{position:relative;overflow:hidden}.skeleton-wave[data-v-32ecf05b]:after{content:"";background:linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);opacity:.06;animation:1.8s ease-in-out infinite skeleton-wave-move-32ecf05b;position:absolute;inset:0}.toast-row[data-v-746fa345]{grid-template-rows:1fr;padding-bottom:8px;display:grid}.toast-row>.toast-inner[data-v-746fa345]{min-height:0}.m3-toast-bot-enter-active[data-v-746fa345]{transition:grid-template-rows .22s cubic-bezier(.2,0,0,1),padding-bottom .22s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-bot-enter-active>.toast-inner[data-v-746fa345]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-bot-enter-from[data-v-746fa345]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-enter-from>.toast-inner[data-v-746fa345]{opacity:0;transform:translateY(20px)scale(.94)}.m3-toast-bot-leave-active[data-v-746fa345]{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1),padding-bottom .3s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-bot-leave-active>.toast-inner[data-v-746fa345]{transition:opacity .18s,transform .18s}.m3-toast-bot-leave-to[data-v-746fa345]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-leave-to>.toast-inner[data-v-746fa345]{opacity:0;transform:scale(.92)}.m3-toast-top-enter-active[data-v-746fa345]{transition:grid-template-rows .22s cubic-bezier(.2,0,0,1),padding-bottom .22s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-top-enter-active>.toast-inner[data-v-746fa345]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-top-enter-from[data-v-746fa345]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-enter-from>.toast-inner[data-v-746fa345]{opacity:0;transform:translateY(-20px)scale(.94)}.m3-toast-top-leave-active[data-v-746fa345]{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1),padding-bottom .3s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-top-leave-active>.toast-inner[data-v-746fa345]{transition:opacity .18s,transform .18s}.m3-toast-top-leave-to[data-v-746fa345]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-leave-to>.toast-inner[data-v-746fa345]{opacity:0;transform:scale(.92)}@keyframes m3-toast-progress-746fa345{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.m3-spot-enter-active[data-v-51b103ff],.m3-spot-leave-active[data-v-51b103ff]{transition:opacity .15s}.m3-spot-enter-from[data-v-51b103ff],.m3-spot-leave-to[data-v-51b103ff]{opacity:0}.m3-spot-enter-active .spot-box[data-v-51b103ff],.m3-spot-leave-active .spot-box[data-v-51b103ff]{transition:transform .15s,opacity .15s}.m3-spot-enter-from .spot-box[data-v-51b103ff]{opacity:0;transform:scale(.96)translateY(-8px)}.m3-spot-leave-to .spot-box[data-v-51b103ff]{opacity:0;transform:scale(.98)}.m3-tour-highlight{box-shadow:0 0 0 4px var(--color-primary);border-radius:8px;position:relative;z-index:201!important}.m3-tour-enter-active,.m3-tour-leave-active{transition:opacity .2s}.m3-tour-enter-from,.m3-tour-leave-to{opacity:0}.m3-markdown[data-v-23db2c2c] h1{font-size:var(--text-headline-large);line-height:var(--text-headline-large--line-height);color:var(--color-on-surface);margin:1em 0 .5em;font-weight:600}.m3-markdown[data-v-23db2c2c] h2{font-size:var(--text-headline-medium);line-height:var(--text-headline-medium--line-height);color:var(--color-on-surface);margin:1em 0 .5em;font-weight:600}.m3-markdown[data-v-23db2c2c] h3{font-size:var(--text-headline-small);line-height:var(--text-headline-small--line-height);color:var(--color-on-surface);margin:.75em 0 .25em;font-weight:600}.m3-markdown[data-v-23db2c2c] h4{font-size:var(--text-title-large);line-height:var(--text-title-large--line-height);color:var(--color-on-surface);margin:.75em 0 .25em;font-weight:600}.m3-markdown[data-v-23db2c2c] p{margin:.5em 0}.m3-markdown[data-v-23db2c2c] a{color:var(--color-primary);text-underline-offset:2px;text-decoration:underline}.m3-markdown[data-v-23db2c2c] a:hover{opacity:.8}.m3-markdown[data-v-23db2c2c] strong{color:var(--color-on-surface);font-weight:600}.m3-markdown[data-v-23db2c2c] em{font-style:italic}.m3-markdown[data-v-23db2c2c] ul{margin:.5em 0;padding-left:1.5em;list-style-type:disc}.m3-markdown[data-v-23db2c2c] ol{margin:.5em 0;padding-left:1.5em;list-style-type:decimal}.m3-markdown[data-v-23db2c2c] li{margin:.25em 0}.m3-markdown[data-v-23db2c2c] li::marker{color:var(--color-on-surface-variant)}.m3-markdown[data-v-23db2c2c] blockquote{border-left:3px solid var(--color-primary);background:var(--color-surface-container);color:var(--color-on-surface-variant);border-radius:0 8px 8px 0;margin:.75em 0;padding:.5em 1em}.m3-markdown[data-v-23db2c2c] code{background:var(--color-surface-container-highest);color:var(--color-primary);border-radius:4px;padding:.15em .4em;font-family:Roboto Mono,Fira Code,Consolas,monospace;font-size:.875em}.m3-markdown[data-v-23db2c2c] pre{background:var(--color-surface-container-highest);border:1px solid var(--color-outline-variant);border-radius:12px;margin:.75em 0;padding:1em;overflow-x:auto}.m3-markdown[data-v-23db2c2c] pre code{color:var(--color-on-surface);background:0 0;padding:0;font-size:.8125rem;line-height:1.6}.m3-markdown[data-v-23db2c2c] .hljs-keyword,.m3-markdown[data-v-23db2c2c] .hljs-selector-tag,.m3-markdown[data-v-23db2c2c] .hljs-built_in,.m3-markdown[data-v-23db2c2c] .hljs-tag .hljs-keyword{color:var(--color-primary)}.m3-markdown[data-v-23db2c2c] .hljs-string,.m3-markdown[data-v-23db2c2c] .hljs-regexp,.m3-markdown[data-v-23db2c2c] .hljs-template-variable{color:var(--color-tertiary)}.m3-markdown[data-v-23db2c2c] .hljs-number,.m3-markdown[data-v-23db2c2c] .hljs-literal{color:var(--color-error)}.m3-markdown[data-v-23db2c2c] .hljs-title.function_,.m3-markdown[data-v-23db2c2c] .hljs-title.function_>.hljs-keyword{color:var(--color-secondary);font-weight:500}.m3-markdown[data-v-23db2c2c] .hljs-type,.m3-markdown[data-v-23db2c2c] .hljs-title.class_{color:var(--color-primary);font-style:italic}.m3-markdown[data-v-23db2c2c] .hljs-comment,.m3-markdown[data-v-23db2c2c] .hljs-quote{color:var(--color-outline);font-style:italic}.m3-markdown[data-v-23db2c2c] .hljs-attr,.m3-markdown[data-v-23db2c2c] .hljs-attribute{color:var(--color-tertiary)}.m3-markdown[data-v-23db2c2c] .hljs-tag{color:var(--color-on-surface-variant)}.m3-markdown[data-v-23db2c2c] .hljs-name{color:var(--color-primary)}.m3-markdown[data-v-23db2c2c] .hljs-punctuation,.m3-markdown[data-v-23db2c2c] .hljs-operator,.m3-markdown[data-v-23db2c2c] .hljs-meta,.m3-markdown[data-v-23db2c2c] .hljs-meta .hljs-keyword{color:var(--color-on-surface-variant)}.m3-markdown[data-v-23db2c2c] .hljs-variable,.m3-markdown[data-v-23db2c2c] .hljs-property,.m3-markdown[data-v-23db2c2c] .hljs-symbol,.m3-markdown[data-v-23db2c2c] .hljs-params{color:var(--color-on-surface)}.m3-markdown[data-v-23db2c2c] .m3-code-block{position:relative}.m3-markdown[data-v-23db2c2c] .m3-code-copy{background:var(--color-surface-container);width:28px;height:28px;color:var(--color-on-surface-variant);cursor:pointer;opacity:0;z-index:1;border:none;border-radius:6px;justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:absolute;top:8px;right:8px}.m3-markdown[data-v-23db2c2c] .m3-code-block:hover .m3-code-copy{opacity:1}.m3-markdown[data-v-23db2c2c] .m3-code-copy:hover{background:var(--color-surface-container-high);color:var(--color-on-surface)}.m3-markdown[data-v-23db2c2c] .m3-code-line{display:block}.m3-markdown[data-v-23db2c2c] .m3-line-hl{background:color-mix(in srgb, var(--color-primary) 8%, transparent);border-left:2px solid color-mix(in srgb, var(--color-primary) 40%, transparent);margin:0 -1em;padding:0 1em 0 calc(1em - 2px)}.m3-markdown[data-v-23db2c2c] hr{border:none;border-top:1px solid var(--color-outline-variant);margin:1.5em 0}.m3-markdown[data-v-23db2c2c] table{border-collapse:collapse;width:100%;margin:.75em 0}.m3-markdown[data-v-23db2c2c] th{background:var(--color-surface-container);text-align:left;border-bottom:2px solid var(--color-outline-variant);font-weight:600;font-size:var(--text-label-large);color:var(--color-on-surface);padding:.5em .75em}.m3-markdown[data-v-23db2c2c] td{border-bottom:1px solid var(--color-outline-variant);padding:.5em .75em}.m3-markdown[data-v-23db2c2c] img{border-radius:12px;max-width:100%;height:auto;margin:.5em 0}.rte-content[data-v-c59a60e3] .tiptap{min-height:inherit;outline:none}.rte-content[data-v-c59a60e3] .tiptap p.is-editor-empty:first-child:before{content:attr(data-placeholder);float:left;color:var(--color-on-surface-variant);opacity:.5;pointer-events:none;height:0}.rte-content[data-v-c59a60e3] h1{font-size:var(--text-headline-large);line-height:var(--text-headline-large--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-c59a60e3] h2{font-size:var(--text-headline-medium);line-height:var(--text-headline-medium--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-c59a60e3] h3{font-size:var(--text-headline-small);line-height:var(--text-headline-small--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-c59a60e3] p{margin:.5em 0}.rte-content[data-v-c59a60e3] ul{margin:.5em 0;padding-left:1.5em;list-style-type:disc}.rte-content[data-v-c59a60e3] ol{margin:.5em 0;padding-left:1.5em;list-style-type:decimal}.rte-content[data-v-c59a60e3] blockquote{border-left:3px solid var(--color-primary);color:var(--color-on-surface-variant);margin:.5em 0;padding-left:1em}.rte-content[data-v-c59a60e3] code{background:var(--color-surface-container-highest);border-radius:4px;padding:.15em .4em;font-size:.875em}.rte-content[data-v-c59a60e3] pre{background:var(--color-surface-container-highest);border-radius:8px;margin:.5em 0;padding:1em;overflow-x:auto}.rte-content[data-v-c59a60e3] pre code{background:0 0;padding:0}.rte-content[data-v-c59a60e3] a{color:var(--color-primary);text-decoration:underline}.rte-content[data-v-c59a60e3] mark{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px;padding:.1em .2em}.rte-content[data-v-c59a60e3] img{border-radius:8px;max-width:100%;height:auto;margin:.5em 0}
1
+ .code-editor-container[data-v-b0a66a4b] .cm-editor{height:100%;min-height:inherit;font-family:Roboto Mono,Fira Code,Consolas,monospace;font-size:.8125rem;line-height:1.6}.code-editor-container[data-v-b0a66a4b] .cm-editor.cm-focused{outline:none}.code-editor-container[data-v-b0a66a4b] .cm-scroller{min-height:inherit}.code-editor-container[data-v-b0a66a4b] .cm-content{padding:12px 0}.code-editor-container[data-v-b0a66a4b] .cm-line{padding:0 16px}.code-editor-container[data-v-b0a66a4b] .cm-gutters{background:var(--color-surface-container);border-right:1px solid var(--color-outline-variant);color:var(--color-outline);padding:0 4px;font-size:.75rem}.code-editor-container[data-v-b0a66a4b] .cm-activeLineGutter{background:var(--color-surface-container-high);color:var(--color-on-surface-variant)}.code-editor-container[data-v-b0a66a4b] .cm-activeLine{background:var(--color-surface-container-lowest)}.code-editor-container[data-v-b0a66a4b] .cm-selectionBackground{background:var(--color-primary-container)!important}.code-editor-container[data-v-b0a66a4b] .cm-cursor{border-left-color:var(--color-primary);border-left-width:2px}.code-editor-container[data-v-b0a66a4b] .cm-matchingBracket{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px}.code-editor-container[data-v-b0a66a4b] .cm-foldGutter span{color:var(--color-on-surface-variant)}@keyframes m3-wavy-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes m3-wavy-travel{0%{stroke-dashoffset:0}to{stroke-dashoffset:calc(var(--m3-wave-len) * -1px)}}@media (prefers-reduced-motion:reduce){.animate-\[m3-wavy-spin_2\.8s_linear_infinite\]{animation:2.8s linear infinite m3-wavy-spin}.animate-\[m3-wavy-travel_2s_linear_infinite\]{animation:none!important}}.m3-dialog-enter-active[data-v-4ccfbc3c],.m3-dialog-leave-active[data-v-4ccfbc3c]{transition:opacity .15s}.m3-dialog-enter-from[data-v-4ccfbc3c],.m3-dialog-leave-to[data-v-4ccfbc3c]{opacity:0}.m3-dialog-enter-active .dialog-box[data-v-4ccfbc3c],.m3-dialog-leave-active .dialog-box[data-v-4ccfbc3c]{transition:transform .15s}.m3-dialog-enter-from .dialog-box[data-v-4ccfbc3c],.m3-dialog-leave-to .dialog-box[data-v-4ccfbc3c]{transform:scale(.95)}.bs-scrim[data-v-5b1fb748]{transition:opacity .3s}.bs-enter-from .bs-scrim[data-v-5b1fb748],.bs-leave-to .bs-scrim[data-v-5b1fb748]{opacity:0}.bs-panel[data-v-5b1fb748]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.bs-enter-from .bs-panel[data-v-5b1fb748]{opacity:0;transform:translateY(40%)}.bs-leave-to .bs-panel[data-v-5b1fb748]{opacity:0;transform:translateY(100%)!important}.m3-carousel-right-enter-active,.m3-carousel-right-leave-active,.m3-carousel-left-enter-active,.m3-carousel-left-leave-active{transition:transform .5s cubic-bezier(.2,0,0,1),opacity .5s cubic-bezier(.2,0,0,1)}.m3-carousel-right-enter-from{opacity:0;transform:translate(100%)}.m3-carousel-right-leave-to,.m3-carousel-left-enter-from{opacity:0;transform:translate(-100%)}.m3-carousel-left-leave-to{opacity:0;transform:translate(100%)}.hue-slider[data-v-872ab758]{background:linear-gradient(90deg,red 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.hue-slider[data-v-872ab758]::-webkit-slider-thumb{-webkit-appearance:none;cursor:pointer;background:#fff;border-radius:50%;width:16px;height:16px;box-shadow:0 1px 3px #0006}.hue-slider[data-v-872ab758]::-moz-range-thumb{cursor:pointer;background:#fff;border:none;border-radius:50%;width:16px;height:16px;box-shadow:0 1px 3px #0006}.m3-cmd-enter-active[data-v-4d6cca63],.m3-cmd-leave-active[data-v-4d6cca63]{transition:opacity .15s}.m3-cmd-enter-from[data-v-4d6cca63],.m3-cmd-leave-to[data-v-4d6cca63]{opacity:0}.m3-cmd-enter-active .cmd-box[data-v-4d6cca63],.m3-cmd-leave-active .cmd-box[data-v-4d6cca63]{transition:transform .15s}.m3-cmd-enter-from .cmd-box[data-v-4d6cca63],.m3-cmd-leave-to .cmd-box[data-v-4d6cca63]{transform:scale(.95)translateY(-10px)}.dt-expand-grid[data-v-857139c7]{grid-template-rows:0fr;transition:grid-template-rows .25s cubic-bezier(.2,0,0,1);display:grid}.dt-expand-grid.dt-expand-open[data-v-857139c7]{grid-template-rows:1fr}.dt-expand-body[data-v-857139c7]{min-height:0;overflow:hidden}.expand-grid[data-v-89e4475b]{grid-template-rows:1fr;display:grid}.expand-body[data-v-89e4475b]{min-height:0;overflow:hidden}.expand-enter-active[data-v-89e4475b]{transition:grid-template-rows .28s cubic-bezier(.2,0,0,1)}.expand-enter-active>.expand-body[data-v-89e4475b]{transition:opacity .22s}.expand-enter-from[data-v-89e4475b]{grid-template-rows:0fr}.expand-enter-from>.expand-body[data-v-89e4475b]{opacity:0}.expand-leave-active[data-v-89e4475b]{transition:grid-template-rows .22s cubic-bezier(.4,0,1,1)}.expand-leave-active>.expand-body[data-v-89e4475b]{transition:opacity .15s}.expand-leave-to[data-v-89e4475b]{grid-template-rows:0fr}.expand-leave-to>.expand-body[data-v-89e4475b]{opacity:0}.m3-file-enter-active[data-v-6b150427],.m3-file-leave-active[data-v-6b150427]{transition:all .2s}.m3-file-enter-from[data-v-6b150427],.m3-file-leave-to[data-v-6b150427]{opacity:0;transform:translateY(-8px)}.mli-expand-grid{grid-template-rows:1fr;display:grid}.mli-expand-body{min-height:0;padding-left:2rem;overflow:hidden}.mli-expand-enter-active{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1)}.mli-expand-enter-active>.mli-expand-body{transition:opacity .2s cubic-bezier(.2,0,0,1) 80ms}.mli-expand-enter-from{grid-template-rows:0fr}.mli-expand-enter-from>.mli-expand-body{opacity:0}.mli-expand-leave-active{transition:grid-template-rows .25s cubic-bezier(.4,0,1,1)}.mli-expand-leave-active>.mli-expand-body{transition:opacity .12s cubic-bezier(.4,0,1,1)}.mli-expand-leave-to{grid-template-rows:0fr}.mli-expand-leave-to>.mli-expand-body{opacity:0}input[type=number][data-v-9068433a]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number][data-v-9068433a]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number][data-v-9068433a]{-moz-appearance:textfield}.nd-scrim[data-v-51e30965]{transition:opacity .28s}.nd-enter-from .nd-scrim[data-v-51e30965],.nd-leave-to .nd-scrim[data-v-51e30965]{opacity:0}.nd-panel[data-v-51e30965]{transition:transform .3s cubic-bezier(.2,0,0,1)}.nd-enter-from .nd-panel[data-v-51e30965],.nd-leave-to .nd-panel[data-v-51e30965]{transform:translate(-100%)}.nd-section-grid[data-v-51e30965]{grid-template-rows:1fr;display:grid}.nd-section-body[data-v-51e30965]{min-height:0;overflow:hidden}.nd-section-enter-active[data-v-51e30965]{transition:grid-template-rows .35s cubic-bezier(.2,0,0,1)}.nd-section-enter-active>.nd-section-body[data-v-51e30965]{transition:opacity .25s cubic-bezier(.2,0,0,1) 80ms}.nd-section-enter-from[data-v-51e30965]{grid-template-rows:0fr}.nd-section-enter-from>.nd-section-body[data-v-51e30965]{opacity:0}.nd-section-leave-active[data-v-51e30965]{transition:grid-template-rows .28s cubic-bezier(.4,0,1,1)}.nd-section-leave-active>.nd-section-body[data-v-51e30965]{transition:opacity .15s cubic-bezier(.4,0,1,1)}.nd-section-leave-to[data-v-51e30965]{grid-template-rows:0fr}.nd-section-leave-to>.nd-section-body[data-v-51e30965]{opacity:0}.nd-inline[data-v-51e30965]{transition:width .3s cubic-bezier(.2,0,0,1);overflow:hidden}.nd-inline[data-v-51e30965]>*{min-width:18rem}.nd-inline.nd-collapsed[data-v-51e30965]>*{min-width:0}.nd-label[data-v-51e30965]{transition:opacity .2s cubic-bezier(.2,0,0,1) 80ms}.nd-inline.nd-collapsed .nd-label[data-v-51e30965],.nd-inline.nd-collapsed[data-v-51e30965] .nd-label{opacity:0;transition:opacity .1s cubic-bezier(.2,0,0,1)}.nd-inline .nd-collapse-h[data-v-51e30965]{max-height:80px;transition:max-height .3s cubic-bezier(.2,0,0,1)}.nd-inline.nd-collapsed .nd-collapse-h[data-v-51e30965]{max-height:0}.nd-inline .nd-section-body[data-v-51e30965]>*,.nd-inline[data-v-51e30965] .nd-section-body>*{transition:padding .3s cubic-bezier(.2,0,0,1)}.nd-children-divider[data-v-51e30965]{border-top:1px solid #0000;height:0;margin:0 .75rem;transition:border-color .1s,margin .1s}.nd-inline.nd-collapsed .nd-children-divider[data-v-51e30965]{border-top-color:var(--color-outline-variant);margin:.25rem .75rem;transition:border-color .2s .3s,margin .2s .3s}@keyframes m3-wave-flow{0%{transform:translate(0)}to{transform:translate(-20px)}}@keyframes m3-progress-indeterminate{0%{left:-40%}to{left:100%}}@media (prefers-reduced-motion:reduce){.animate-\[m3-wave-flow_1\.2s_linear_infinite\],.animate-\[m3-wave-flow_0\.9s_linear_infinite\],.animate-\[m3-progress-indeterminate_1\.6s_ease-in-out_infinite\]{animation:none!important}}.m3-radio-dot[data-v-cdb650b5]{transform-box:fill-box;transform-origin:50%;transition:transform .15s;transform:scale(0)}.m3-radio-dot.is-checked[data-v-cdb650b5]{transform:scale(1)}.ss-scrim[data-v-ba5b6f4b]{transition:opacity .28s}.ss-enter-from .ss-scrim[data-v-ba5b6f4b],.ss-leave-to .ss-scrim[data-v-ba5b6f4b]{opacity:0}.ss-panel[data-v-ba5b6f4b]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.ss-enter-from .ss-panel[data-v-ba5b6f4b]{opacity:0;transform:translate(40%)}.ss-leave-to .ss-panel[data-v-ba5b6f4b]{opacity:0;transform:translate(100%)!important}@keyframes skeleton-wave-move-32ecf05b{0%{transform:translate(-100%)}60%{transform:translate(100%)}to{transform:translate(100%)}}.skeleton-wave[data-v-32ecf05b]{position:relative;overflow:hidden}.skeleton-wave[data-v-32ecf05b]:after{content:"";background:linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);opacity:.06;animation:1.8s ease-in-out infinite skeleton-wave-move-32ecf05b;position:absolute;inset:0}.toast-row[data-v-64ce9e7c]{grid-template-rows:1fr;padding-bottom:8px;display:grid}.toast-row>.toast-inner[data-v-64ce9e7c]{min-height:0}.m3-toast-bot-enter-active[data-v-64ce9e7c]{transition:grid-template-rows .22s cubic-bezier(.2,0,0,1),padding-bottom .22s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-bot-enter-active>.toast-inner[data-v-64ce9e7c]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-bot-enter-from[data-v-64ce9e7c]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-enter-from>.toast-inner[data-v-64ce9e7c]{opacity:0;transform:translateY(20px)scale(.94)}.m3-toast-bot-leave-active[data-v-64ce9e7c]{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1),padding-bottom .3s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-bot-leave-active>.toast-inner[data-v-64ce9e7c]{transition:opacity .18s,transform .18s}.m3-toast-bot-leave-to[data-v-64ce9e7c]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-leave-to>.toast-inner[data-v-64ce9e7c]{opacity:0;transform:scale(.92)}.m3-toast-top-enter-active[data-v-64ce9e7c]{transition:grid-template-rows .22s cubic-bezier(.2,0,0,1),padding-bottom .22s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-top-enter-active>.toast-inner[data-v-64ce9e7c]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-top-enter-from[data-v-64ce9e7c]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-enter-from>.toast-inner[data-v-64ce9e7c]{opacity:0;transform:translateY(-20px)scale(.94)}.m3-toast-top-leave-active[data-v-64ce9e7c]{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1),padding-bottom .3s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-top-leave-active>.toast-inner[data-v-64ce9e7c]{transition:opacity .18s,transform .18s}.m3-toast-top-leave-to[data-v-64ce9e7c]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-leave-to>.toast-inner[data-v-64ce9e7c]{opacity:0;transform:scale(.92)}@keyframes m3-toast-progress-64ce9e7c{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.m3-spot-enter-active[data-v-1fa0da82],.m3-spot-leave-active[data-v-1fa0da82]{transition:opacity .15s}.m3-spot-enter-from[data-v-1fa0da82],.m3-spot-leave-to[data-v-1fa0da82]{opacity:0}.m3-spot-enter-active .spot-box[data-v-1fa0da82],.m3-spot-leave-active .spot-box[data-v-1fa0da82]{transition:transform .15s,opacity .15s}.m3-spot-enter-from .spot-box[data-v-1fa0da82]{opacity:0;transform:scale(.96)translateY(-8px)}.m3-spot-leave-to .spot-box[data-v-1fa0da82]{opacity:0;transform:scale(.98)}.m3-tour-highlight{box-shadow:0 0 0 4px var(--color-primary);border-radius:8px;position:relative;z-index:201!important}.m3-tour-enter-active,.m3-tour-leave-active{transition:opacity .2s}.m3-tour-enter-from,.m3-tour-leave-to{opacity:0}.m3-markdown[data-v-23db2c2c] h1{font-size:var(--text-headline-large);line-height:var(--text-headline-large--line-height);color:var(--color-on-surface);margin:1em 0 .5em;font-weight:600}.m3-markdown[data-v-23db2c2c] h2{font-size:var(--text-headline-medium);line-height:var(--text-headline-medium--line-height);color:var(--color-on-surface);margin:1em 0 .5em;font-weight:600}.m3-markdown[data-v-23db2c2c] h3{font-size:var(--text-headline-small);line-height:var(--text-headline-small--line-height);color:var(--color-on-surface);margin:.75em 0 .25em;font-weight:600}.m3-markdown[data-v-23db2c2c] h4{font-size:var(--text-title-large);line-height:var(--text-title-large--line-height);color:var(--color-on-surface);margin:.75em 0 .25em;font-weight:600}.m3-markdown[data-v-23db2c2c] p{margin:.5em 0}.m3-markdown[data-v-23db2c2c] a{color:var(--color-primary);text-underline-offset:2px;text-decoration:underline}.m3-markdown[data-v-23db2c2c] a:hover{opacity:.8}.m3-markdown[data-v-23db2c2c] strong{color:var(--color-on-surface);font-weight:600}.m3-markdown[data-v-23db2c2c] em{font-style:italic}.m3-markdown[data-v-23db2c2c] ul{margin:.5em 0;padding-left:1.5em;list-style-type:disc}.m3-markdown[data-v-23db2c2c] ol{margin:.5em 0;padding-left:1.5em;list-style-type:decimal}.m3-markdown[data-v-23db2c2c] li{margin:.25em 0}.m3-markdown[data-v-23db2c2c] li::marker{color:var(--color-on-surface-variant)}.m3-markdown[data-v-23db2c2c] blockquote{border-left:3px solid var(--color-primary);background:var(--color-surface-container);color:var(--color-on-surface-variant);border-radius:0 8px 8px 0;margin:.75em 0;padding:.5em 1em}.m3-markdown[data-v-23db2c2c] code{background:var(--color-surface-container-highest);color:var(--color-primary);border-radius:4px;padding:.15em .4em;font-family:Roboto Mono,Fira Code,Consolas,monospace;font-size:.875em}.m3-markdown[data-v-23db2c2c] pre{background:var(--color-surface-container-highest);border:1px solid var(--color-outline-variant);border-radius:12px;margin:.75em 0;padding:1em;overflow-x:auto}.m3-markdown[data-v-23db2c2c] pre code{color:var(--color-on-surface);background:0 0;padding:0;font-size:.8125rem;line-height:1.6}.m3-markdown[data-v-23db2c2c] .hljs-keyword,.m3-markdown[data-v-23db2c2c] .hljs-selector-tag,.m3-markdown[data-v-23db2c2c] .hljs-built_in,.m3-markdown[data-v-23db2c2c] .hljs-tag .hljs-keyword{color:var(--color-primary)}.m3-markdown[data-v-23db2c2c] .hljs-string,.m3-markdown[data-v-23db2c2c] .hljs-regexp,.m3-markdown[data-v-23db2c2c] .hljs-template-variable{color:var(--color-tertiary)}.m3-markdown[data-v-23db2c2c] .hljs-number,.m3-markdown[data-v-23db2c2c] .hljs-literal{color:var(--color-error)}.m3-markdown[data-v-23db2c2c] .hljs-title.function_,.m3-markdown[data-v-23db2c2c] .hljs-title.function_>.hljs-keyword{color:var(--color-secondary);font-weight:500}.m3-markdown[data-v-23db2c2c] .hljs-type,.m3-markdown[data-v-23db2c2c] .hljs-title.class_{color:var(--color-primary);font-style:italic}.m3-markdown[data-v-23db2c2c] .hljs-comment,.m3-markdown[data-v-23db2c2c] .hljs-quote{color:var(--color-outline);font-style:italic}.m3-markdown[data-v-23db2c2c] .hljs-attr,.m3-markdown[data-v-23db2c2c] .hljs-attribute{color:var(--color-tertiary)}.m3-markdown[data-v-23db2c2c] .hljs-tag{color:var(--color-on-surface-variant)}.m3-markdown[data-v-23db2c2c] .hljs-name{color:var(--color-primary)}.m3-markdown[data-v-23db2c2c] .hljs-punctuation,.m3-markdown[data-v-23db2c2c] .hljs-operator,.m3-markdown[data-v-23db2c2c] .hljs-meta,.m3-markdown[data-v-23db2c2c] .hljs-meta .hljs-keyword{color:var(--color-on-surface-variant)}.m3-markdown[data-v-23db2c2c] .hljs-variable,.m3-markdown[data-v-23db2c2c] .hljs-property,.m3-markdown[data-v-23db2c2c] .hljs-symbol,.m3-markdown[data-v-23db2c2c] .hljs-params{color:var(--color-on-surface)}.m3-markdown[data-v-23db2c2c] .m3-code-block{position:relative}.m3-markdown[data-v-23db2c2c] .m3-code-copy{background:var(--color-surface-container);width:28px;height:28px;color:var(--color-on-surface-variant);cursor:pointer;opacity:0;z-index:1;border:none;border-radius:6px;justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:absolute;top:8px;right:8px}.m3-markdown[data-v-23db2c2c] .m3-code-block:hover .m3-code-copy{opacity:1}.m3-markdown[data-v-23db2c2c] .m3-code-copy:hover{background:var(--color-surface-container-high);color:var(--color-on-surface)}.m3-markdown[data-v-23db2c2c] .m3-code-line{display:block}.m3-markdown[data-v-23db2c2c] .m3-line-hl{background:color-mix(in srgb, var(--color-primary) 8%, transparent);border-left:2px solid color-mix(in srgb, var(--color-primary) 40%, transparent);margin:0 -1em;padding:0 1em 0 calc(1em - 2px)}.m3-markdown[data-v-23db2c2c] hr{border:none;border-top:1px solid var(--color-outline-variant);margin:1.5em 0}.m3-markdown[data-v-23db2c2c] table{border-collapse:collapse;width:100%;margin:.75em 0}.m3-markdown[data-v-23db2c2c] th{background:var(--color-surface-container);text-align:left;border-bottom:2px solid var(--color-outline-variant);font-weight:600;font-size:var(--text-label-large);color:var(--color-on-surface);padding:.5em .75em}.m3-markdown[data-v-23db2c2c] td{border-bottom:1px solid var(--color-outline-variant);padding:.5em .75em}.m3-markdown[data-v-23db2c2c] img{border-radius:12px;max-width:100%;height:auto;margin:.5em 0}.rte-content[data-v-e6829e5c] .tiptap{min-height:inherit;outline:none}.rte-content[data-v-e6829e5c] .tiptap p.is-editor-empty:first-child:before{content:attr(data-placeholder);float:left;color:var(--color-on-surface-variant);opacity:.5;pointer-events:none;height:0}.rte-content[data-v-e6829e5c] h1{font-size:var(--text-headline-large);line-height:var(--text-headline-large--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-e6829e5c] h2{font-size:var(--text-headline-medium);line-height:var(--text-headline-medium--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-e6829e5c] h3{font-size:var(--text-headline-small);line-height:var(--text-headline-small--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-e6829e5c] p{margin:.5em 0}.rte-content[data-v-e6829e5c] ul{margin:.5em 0;padding-left:1.5em;list-style-type:disc}.rte-content[data-v-e6829e5c] ol{margin:.5em 0;padding-left:1.5em;list-style-type:decimal}.rte-content[data-v-e6829e5c] blockquote{border-left:3px solid var(--color-primary);color:var(--color-on-surface-variant);margin:.5em 0;padding-left:1em}.rte-content[data-v-e6829e5c] code{background:var(--color-surface-container-highest);border-radius:4px;padding:.15em .4em;font-size:.875em}.rte-content[data-v-e6829e5c] pre{background:var(--color-surface-container-highest);border-radius:8px;margin:.5em 0;padding:1em;overflow-x:auto}.rte-content[data-v-e6829e5c] pre code{background:0 0;padding:0}.rte-content[data-v-e6829e5c] a{color:var(--color-primary);text-decoration:underline}.rte-content[data-v-e6829e5c] mark{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px;padding:.1em .2em}.rte-content[data-v-e6829e5c] img{border-radius:8px;max-width:100%;height:auto;margin:.5em 0}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m3ui-vue/m3ui-vue",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Material 3 component library for Vue 3 + Tailwind CSS v4",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -49,7 +49,11 @@
49
49
  },
50
50
  "./styles": "./dist/styles.css",
51
51
  "./theme": "./src/styles/theme.css",
52
- "./palettes": "./src/styles/palettes.css"
52
+ "./palettes": "./src/styles/palettes.css",
53
+ "./locales": {
54
+ "types": "./dist/locales/index.d.ts",
55
+ "import": "./dist/locales/index.js"
56
+ }
53
57
  },
54
58
  "files": [
55
59
  "dist",
@@ -60,6 +64,8 @@
60
64
  "build:js": "vite build && cp dist/m3ui-vue.css dist/styles.css",
61
65
  "build:types": "vue-tsc -p tsconfig.build.json",
62
66
  "build:css": "cp -r src/styles dist/",
67
+ "test": "vitest run",
68
+ "test:watch": "vitest",
63
69
  "prepublishOnly": "pnpm build"
64
70
  },
65
71
  "peerDependencies": {
@@ -177,15 +183,18 @@
177
183
  "@tiptap/vue-3": "^3.26.1",
178
184
  "@types/markdown-it": "^14.1.2",
179
185
  "@vitejs/plugin-vue": "^6.0.7",
186
+ "@vue/test-utils": "^2.4.11",
180
187
  "@vue/tsconfig": "^0.9.1",
181
188
  "@xterm/addon-fit": "^0.11.0",
182
189
  "@xterm/xterm": "^6.0.0",
183
190
  "chart.js": "^4.5.1",
184
191
  "codemirror": "^6.0.2",
192
+ "happy-dom": "^20.10.6",
185
193
  "highlight.js": "^11.11.1",
186
194
  "markdown-it": "^14.2.0",
187
195
  "typescript": "~6.0.3",
188
196
  "vite": "^8.0.16",
197
+ "vitest": "^4.1.9",
189
198
  "vue": "^3.5.38",
190
199
  "vue-chartjs": "^5.3.3",
191
200
  "vue-tsc": "^3.3.5"
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import MAlert from '../components/MAlert.vue'
4
+
5
+ describe('MAlert', () => {
6
+ it('renders slot content', () => {
7
+ const wrapper = mount(MAlert, {
8
+ slots: { default: 'Alert message' },
9
+ })
10
+ expect(wrapper.text()).toContain('Alert message')
11
+ })
12
+
13
+ it('renders title when provided', () => {
14
+ const wrapper = mount(MAlert, {
15
+ props: { title: 'Warning' },
16
+ slots: { default: 'Content' },
17
+ })
18
+ expect(wrapper.text()).toContain('Warning')
19
+ })
20
+
21
+ it('defaults to info type', () => {
22
+ const wrapper = mount(MAlert, {
23
+ slots: { default: 'Content' },
24
+ })
25
+ expect(wrapper.find('[class*="primary"]').exists()).toBe(true)
26
+ })
27
+
28
+ it('shows close button when closeable', () => {
29
+ const wrapper = mount(MAlert, {
30
+ props: { closeable: true },
31
+ slots: { default: 'Content' },
32
+ })
33
+ expect(wrapper.find('button').exists()).toBe(true)
34
+ })
35
+
36
+ it('hides close button by default', () => {
37
+ const wrapper = mount(MAlert, {
38
+ slots: { default: 'Content' },
39
+ })
40
+ expect(wrapper.find('button').exists()).toBe(false)
41
+ })
42
+
43
+ it('emits close when button clicked', async () => {
44
+ const wrapper = mount(MAlert, {
45
+ props: { closeable: true },
46
+ slots: { default: 'Content' },
47
+ })
48
+ await wrapper.find('button').trigger('click')
49
+ expect(wrapper.emitted('close')).toHaveLength(1)
50
+ })
51
+
52
+ it('uses closeAriaLabel prop', () => {
53
+ const wrapper = mount(MAlert, {
54
+ props: { closeable: true, closeAriaLabel: 'Dismiss' },
55
+ slots: { default: 'Content' },
56
+ })
57
+ expect(wrapper.find('button').attributes('aria-label')).toBe('Dismiss')
58
+ })
59
+ })
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import MConfirmDialog from '../components/MConfirmDialog.vue'
4
+
5
+ describe('MConfirmDialog', () => {
6
+ const baseProps = {
7
+ modelValue: true,
8
+ title: 'Delete item?',
9
+ message: 'This cannot be undone.',
10
+ }
11
+
12
+ beforeEach(() => {
13
+ document.body.innerHTML = ''
14
+ })
15
+
16
+ it('renders title and message', () => {
17
+ mount(MConfirmDialog, { props: baseProps, attachTo: document.body })
18
+ expect(document.body.textContent).toContain('Delete item?')
19
+ expect(document.body.textContent).toContain('This cannot be undone.')
20
+ })
21
+
22
+ it('uses locale defaults for labels', () => {
23
+ mount(MConfirmDialog, { props: baseProps, attachTo: document.body })
24
+ expect(document.body.textContent).toContain('Confirm')
25
+ expect(document.body.textContent).toContain('Cancel')
26
+ })
27
+
28
+ it('uses custom labels when provided', () => {
29
+ mount(MConfirmDialog, {
30
+ props: { ...baseProps, confirmLabel: 'Delete', cancelLabel: 'Keep' },
31
+ attachTo: document.body,
32
+ })
33
+ expect(document.body.textContent).toContain('Delete')
34
+ expect(document.body.textContent).toContain('Keep')
35
+ })
36
+
37
+ it('emits confirm when confirm button clicked', async () => {
38
+ const wrapper = mount(MConfirmDialog, { props: baseProps, attachTo: document.body })
39
+ const buttons = wrapper.findAllComponents({ name: 'MButton' })
40
+ const confirmBtn = buttons[buttons.length - 1]!
41
+ await confirmBtn.trigger('click')
42
+ expect(wrapper.emitted('confirm')).toHaveLength(1)
43
+ })
44
+
45
+ it('emits update:modelValue false when cancel clicked', async () => {
46
+ const wrapper = mount(MConfirmDialog, { props: baseProps, attachTo: document.body })
47
+ const cancelBtn = wrapper.findAllComponents({ name: 'MButton' })[0]!
48
+ await cancelBtn.trigger('click')
49
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([false])
50
+ })
51
+ })
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import MDialog from '../components/MDialog.vue'
4
+
5
+ describe('MDialog', () => {
6
+ beforeEach(() => {
7
+ document.body.innerHTML = ''
8
+ })
9
+
10
+ it('renders nothing when closed', () => {
11
+ mount(MDialog, {
12
+ props: { modelValue: false },
13
+ attachTo: document.body,
14
+ })
15
+ expect(document.body.querySelector('.fixed')).toBeNull()
16
+ })
17
+
18
+ it('renders content when open', () => {
19
+ mount(MDialog, {
20
+ props: { modelValue: true, title: 'Test Dialog' },
21
+ slots: { default: 'Dialog body' },
22
+ attachTo: document.body,
23
+ })
24
+ expect(document.body.textContent).toContain('Test Dialog')
25
+ expect(document.body.textContent).toContain('Dialog body')
26
+ })
27
+
28
+ it('emits update:modelValue false when close button clicked', async () => {
29
+ const wrapper = mount(MDialog, {
30
+ props: { modelValue: true, title: 'Test' },
31
+ attachTo: document.body,
32
+ })
33
+ const closeBtn = wrapper.findComponent({ name: 'MIconButton' })
34
+ await closeBtn.trigger('click')
35
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([false])
36
+ })
37
+
38
+ it('hides close button when persistent', () => {
39
+ const wrapper = mount(MDialog, {
40
+ props: { modelValue: true, persistent: true, title: 'Test' },
41
+ attachTo: document.body,
42
+ })
43
+ const iconBtn = wrapper.findComponent({ name: 'MIconButton' })
44
+ expect(iconBtn.exists()).toBe(false)
45
+ })
46
+
47
+ it('renders actions slot', () => {
48
+ mount(MDialog, {
49
+ props: { modelValue: true, title: 'Test' },
50
+ slots: {
51
+ default: 'Body',
52
+ actions: '<button>OK</button>',
53
+ },
54
+ attachTo: document.body,
55
+ })
56
+ expect(document.body.textContent).toContain('OK')
57
+ })
58
+ })
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import MSpinner from '../components/MSpinner.vue'
4
+
5
+ describe('MSpinner', () => {
6
+ it('renders with default size', () => {
7
+ const wrapper = mount(MSpinner)
8
+ const span = wrapper.find('span')
9
+ expect(span.attributes('style')).toContain('width: 20px')
10
+ })
11
+
12
+ it('respects custom size', () => {
13
+ const wrapper = mount(MSpinner, { props: { size: 48 } })
14
+ const span = wrapper.find('span')
15
+ expect(span.attributes('style')).toContain('width: 48px')
16
+ })
17
+
18
+ it('uses locale default for aria-label', () => {
19
+ const wrapper = mount(MSpinner)
20
+ expect(wrapper.find('[role="status"]').attributes('aria-label')).toBe('Loading')
21
+ })
22
+
23
+ it('uses custom label prop', () => {
24
+ const wrapper = mount(MSpinner, { props: { label: 'Cargando' } })
25
+ expect(wrapper.find('[role="status"]').attributes('aria-label')).toBe('Cargando')
26
+ })
27
+
28
+ it('renders standard spinner by default', () => {
29
+ const wrapper = mount(MSpinner)
30
+ expect(wrapper.find('svg').exists()).toBe(false)
31
+ })
32
+
33
+ it('renders wavy spinner when wavy=true', () => {
34
+ const wrapper = mount(MSpinner, { props: { wavy: true } })
35
+ expect(wrapper.find('svg').exists()).toBe(true)
36
+ })
37
+ })
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { defaultLocale } from '../composables/useLocale'
3
+ import type { M3Locale } from '../composables/useLocale'
4
+ import { esLocale } from '../locales/es'
5
+ import { frLocale } from '../locales/fr'
6
+ import { ptLocale } from '../locales/pt'
7
+ import { deLocale } from '../locales/de'
8
+ import { jaLocale } from '../locales/ja'
9
+ import { zhLocale } from '../locales/zh'
10
+ import { koLocale } from '../locales/ko'
11
+
12
+ const allLocales: [string, M3Locale][] = [
13
+ ['es', esLocale],
14
+ ['fr', frLocale],
15
+ ['pt', ptLocale],
16
+ ['de', deLocale],
17
+ ['ja', jaLocale],
18
+ ['zh', zhLocale],
19
+ ['ko', koLocale],
20
+ ]
21
+
22
+ const defaultKeys = Object.keys(defaultLocale) as (keyof M3Locale)[]
23
+
24
+ describe('locale presets', () => {
25
+ it.each(allLocales)('%s has all keys from defaultLocale', (_name, locale) => {
26
+ for (const key of defaultKeys) {
27
+ expect(locale[key], `missing key: ${key}`).toBeTruthy()
28
+ }
29
+ })
30
+
31
+ it.each(allLocales)('%s has no extra keys', (_name, locale) => {
32
+ const localeKeys = Object.keys(locale)
33
+ for (const key of localeKeys) {
34
+ expect(defaultKeys).toContain(key)
35
+ }
36
+ })
37
+
38
+ it.each(allLocales)('%s differs from English defaults', (_name, locale) => {
39
+ let diffs = 0
40
+ for (const key of defaultKeys) {
41
+ if (locale[key] !== defaultLocale[key]) diffs++
42
+ }
43
+ expect(diffs).toBeGreaterThan(defaultKeys.length / 2)
44
+ })
45
+ })
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { createApp, defineComponent, h } from 'vue'
3
+ import { createM3UI } from '../plugin'
4
+
5
+ describe('createM3UI', () => {
6
+ beforeEach(() => {
7
+ document.documentElement.removeAttribute('data-palette')
8
+ localStorage.removeItem('m3-palette')
9
+ })
10
+
11
+ it('installs without options', () => {
12
+ const app = createApp(defineComponent({ render: () => h('div') }))
13
+ expect(() => app.use(createM3UI())).not.toThrow()
14
+ })
15
+
16
+ it('sets palette on install', () => {
17
+ const app = createApp(defineComponent({ render: () => h('div') }))
18
+ app.use(createM3UI({ palette: 'teal' }))
19
+ expect(document.documentElement.getAttribute('data-palette')).toBe('teal')
20
+ })
21
+
22
+ it('does not set attribute for purple (default)', () => {
23
+ const app = createApp(defineComponent({ render: () => h('div') }))
24
+ app.use(createM3UI({ palette: 'purple' }))
25
+ expect(document.documentElement.hasAttribute('data-palette')).toBe(false)
26
+ })
27
+ })
@@ -0,0 +1,11 @@
1
+ if (typeof globalThis.localStorage === 'undefined') {
2
+ const store: Record<string, string> = {}
3
+ globalThis.localStorage = {
4
+ getItem: (key: string) => store[key] ?? null,
5
+ setItem: (key: string, value: string) => { store[key] = value },
6
+ removeItem: (key: string) => { delete store[key] },
7
+ clear: () => { for (const k of Object.keys(store)) delete store[k] },
8
+ get length() { return Object.keys(store).length },
9
+ key: (i: number) => Object.keys(store)[i] ?? null,
10
+ }
11
+ }
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { defineComponent, h, nextTick } from 'vue'
4
+ import { useColorPalette, palettes } from '../composables/useColorPalette'
5
+
6
+ function withSetup() {
7
+ let result!: ReturnType<typeof useColorPalette>
8
+ mount(defineComponent({
9
+ setup() {
10
+ result = useColorPalette()
11
+ return () => h('div')
12
+ },
13
+ }))
14
+ return result
15
+ }
16
+
17
+ describe('useColorPalette', () => {
18
+ beforeEach(() => {
19
+ document.documentElement.removeAttribute('data-palette')
20
+ localStorage.setItem('m3-palette', 'purple')
21
+ })
22
+
23
+ it('has 30 palettes', () => {
24
+ expect(palettes).toHaveLength(30)
25
+ })
26
+
27
+ it('each palette has id, label, and seed', () => {
28
+ for (const p of palettes) {
29
+ expect(p.id).toBeTruthy()
30
+ expect(p.label).toBeTruthy()
31
+ expect(p.seed).toMatch(/^#[0-9A-Fa-f]{6}$/)
32
+ }
33
+ })
34
+
35
+ it('includes monochromatic palettes', () => {
36
+ const ids = palettes.map(p => p.id)
37
+ expect(ids).toContain('graphite')
38
+ expect(ids).toContain('charcoal')
39
+ expect(ids).toContain('steel')
40
+ expect(ids).toContain('ash')
41
+ expect(ids).toContain('iron')
42
+ expect(ids).toContain('fog')
43
+ expect(ids).toContain('pewter')
44
+ expect(ids).toContain('smoke')
45
+ expect(ids).toContain('stone')
46
+ expect(ids).toContain('zinc')
47
+ })
48
+
49
+ it('set() applies data-palette attribute', async () => {
50
+ const { set } = withSetup()
51
+ set('teal')
52
+ await nextTick()
53
+ expect(document.documentElement.getAttribute('data-palette')).toBe('teal')
54
+ })
55
+
56
+ it('set(purple) removes data-palette', async () => {
57
+ const { set } = withSetup()
58
+ set('teal')
59
+ await nextTick()
60
+ set('purple')
61
+ await nextTick()
62
+ expect(document.documentElement.hasAttribute('data-palette')).toBe(false)
63
+ })
64
+
65
+ it('persists to localStorage', async () => {
66
+ const { set } = withSetup()
67
+ set('blue')
68
+ await nextTick()
69
+ expect(localStorage.getItem('m3-palette')).toBe('blue')
70
+ })
71
+ })
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { defineComponent, h } from 'vue'
4
+ import { useLocale, defaultLocale, M3_LOCALE_KEY } from '../composables/useLocale'
5
+
6
+ function withSetup<T>(fn: () => T, provide?: Record<symbol, unknown>): T {
7
+ let result!: T
8
+ mount(defineComponent({
9
+ setup() {
10
+ result = fn()
11
+ return () => h('div')
12
+ },
13
+ }), {
14
+ global: provide ? { provide } : undefined,
15
+ })
16
+ return result
17
+ }
18
+
19
+ describe('useLocale', () => {
20
+ it('returns English defaults without provider', () => {
21
+ const locale = withSetup(() => useLocale())
22
+ expect(locale.search).toBe('Search...')
23
+ expect(locale.close).toBe('Close')
24
+ expect(locale.cancel).toBe('Cancel')
25
+ expect(locale.confirm).toBe('Confirm')
26
+ expect(locale.previous).toBe('Previous')
27
+ expect(locale.next).toBe('Next')
28
+ expect(locale.finish).toBe('Finish')
29
+ expect(locale.noResults).toBe('No results')
30
+ })
31
+
32
+ it('merges partial overrides from provider', () => {
33
+ const locale = withSetup(
34
+ () => useLocale(),
35
+ { [M3_LOCALE_KEY as symbol]: { search: 'Buscar...', close: 'Cerrar' } },
36
+ )
37
+ expect(locale.search).toBe('Buscar...')
38
+ expect(locale.close).toBe('Cerrar')
39
+ expect(locale.cancel).toBe('Cancel')
40
+ expect(locale.next).toBe('Next')
41
+ })
42
+
43
+ it('defaultLocale has all keys defined', () => {
44
+ const keys = Object.keys(defaultLocale)
45
+ expect(keys.length).toBeGreaterThan(50)
46
+ for (const key of keys) {
47
+ expect((defaultLocale as unknown as Record<string, string>)[key]).toBeTruthy()
48
+ }
49
+ })
50
+ })
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { mount, flushPromises } from '@vue/test-utils'
3
+ import { defineComponent, h, nextTick } from 'vue'
4
+ import { useTheme } from '../composables/useTheme'
5
+
6
+ function withSetup() {
7
+ let result!: ReturnType<typeof useTheme>
8
+ mount(defineComponent({
9
+ setup() {
10
+ result = useTheme()
11
+ return () => h('div')
12
+ },
13
+ }))
14
+ return result
15
+ }
16
+
17
+ describe('useTheme', () => {
18
+ beforeEach(async () => {
19
+ document.documentElement.classList.remove('dark')
20
+ localStorage.setItem('m3-theme', 'system')
21
+ })
22
+
23
+ it('returns theme ref', () => {
24
+ const { theme } = withSetup()
25
+ expect(['light', 'dark', 'system']).toContain(theme.value)
26
+ })
27
+
28
+ it('can set to dark and applies class', async () => {
29
+ const { theme } = withSetup()
30
+ theme.value = 'dark'
31
+ await nextTick()
32
+ expect(document.documentElement.classList.contains('dark')).toBe(true)
33
+ })
34
+
35
+ it('can set to light and removes dark class', async () => {
36
+ document.documentElement.classList.add('dark')
37
+ const { theme } = withSetup()
38
+ theme.value = 'light'
39
+ await nextTick()
40
+ expect(document.documentElement.classList.contains('dark')).toBe(false)
41
+ })
42
+
43
+ it('cycle rotates through modes', async () => {
44
+ const { theme, cycle } = withSetup()
45
+ theme.value = 'light'
46
+ await nextTick()
47
+ cycle()
48
+ expect(theme.value).toBe('dark')
49
+ cycle()
50
+ expect(theme.value).toBe('system')
51
+ cycle()
52
+ expect(theme.value).toBe('light')
53
+ })
54
+
55
+ it('persists to localStorage', async () => {
56
+ const { theme } = withSetup()
57
+ theme.value = 'dark'
58
+ await nextTick()
59
+ expect(localStorage.getItem('m3-theme')).toBe('dark')
60
+ })
61
+ })
@@ -1,11 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import MIcon from './MIcon.vue'
3
+ import { useLocale } from '../composables/useLocale'
3
4
 
4
5
  const props = withDefaults(
5
6
  defineProps<{
6
7
  type?: 'info' | 'success' | 'warning' | 'error'
7
8
  title?: string
8
9
  closeable?: boolean
10
+ closeAriaLabel?: string
9
11
  }>(),
10
12
  {
11
13
  type: 'info',
@@ -13,6 +15,8 @@ const props = withDefaults(
13
15
  },
14
16
  )
15
17
 
18
+ const locale = useLocale()
19
+
16
20
  const emit = defineEmits<{ close: [] }>()
17
21
 
18
22
  const config = {
@@ -60,7 +64,7 @@ const config = {
60
64
  v-if="closeable"
61
65
  type="button"
62
66
  class="-mr-1 flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-on-surface/8"
63
- aria-label="Cerrar"
67
+ :aria-label="closeAriaLabel ?? locale.close"
64
68
  @click="emit('close')"
65
69
  >
66
70
  <MIcon name="close" :size="18" />