@m3ui-vue/m3ui-vue 0.1.4 → 0.1.6

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.
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)}.bs-scrim[data-v-941a89e7]{transition:opacity .3s}.bs-enter-from .bs-scrim[data-v-941a89e7],.bs-leave-to .bs-scrim[data-v-941a89e7]{opacity:0}.bs-panel[data-v-941a89e7]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.bs-enter-from .bs-panel[data-v-941a89e7]{opacity:0;transform:translateY(40%)}.bs-leave-to .bs-panel[data-v-941a89e7]{opacity:0;transform:translateY(100%)!important}@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}}.hue-slider[data-v-9ee0043f]{background:linear-gradient(90deg,red 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.hue-slider[data-v-9ee0043f]::-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-9ee0043f]::-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)}.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)}.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)}.nd-scrim[data-v-98c11a62]{transition:opacity .28s}.nd-enter-from .nd-scrim[data-v-98c11a62],.nd-leave-to .nd-scrim[data-v-98c11a62]{opacity:0}.nd-panel[data-v-98c11a62]{transition:transform .3s cubic-bezier(.2,0,0,1)}.nd-enter-from .nd-panel[data-v-98c11a62],.nd-leave-to .nd-panel[data-v-98c11a62]{transform:translate(-100%)}@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-f8751672]{transition:opacity .28s}.ss-enter-from .ss-scrim[data-v-f8751672],.ss-leave-to .ss-scrim[data-v-f8751672]{opacity:0}.ss-panel[data-v-f8751672]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.ss-enter-from .ss-panel[data-v-f8751672]{opacity:0;transform:translate(40%)}.ss-leave-to .ss-panel[data-v-f8751672]{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-e83a5c10]{grid-template-rows:1fr;padding-bottom:8px;display:grid}.toast-row>.toast-inner[data-v-e83a5c10]{min-height:0}.m3-toast-bot-enter-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-bot-enter-from[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-enter-from>.toast-inner[data-v-e83a5c10]{opacity:0;transform:translateY(20px)scale(.94)}.m3-toast-bot-leave-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .18s}.m3-toast-bot-leave-to[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-leave-to>.toast-inner[data-v-e83a5c10]{opacity:0;transform:scale(.92)}.m3-toast-top-enter-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-top-enter-from[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-enter-from>.toast-inner[data-v-e83a5c10]{opacity:0;transform:translateY(-20px)scale(.94)}.m3-toast-top-leave-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .18s}.m3-toast-top-leave-to[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-leave-to>.toast-inner[data-v-e83a5c10]{opacity:0;transform:scale(.92)}@keyframes m3-toast-progress-e83a5c10{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-6e4dc2b6] 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-6e4dc2b6] 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-6e4dc2b6] 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-6e4dc2b6] 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-6e4dc2b6] p{margin:.5em 0}.m3-markdown[data-v-6e4dc2b6] a{color:var(--color-primary);text-underline-offset:2px;text-decoration:underline}.m3-markdown[data-v-6e4dc2b6] a:hover{opacity:.8}.m3-markdown[data-v-6e4dc2b6] strong{color:var(--color-on-surface);font-weight:600}.m3-markdown[data-v-6e4dc2b6] em{font-style:italic}.m3-markdown[data-v-6e4dc2b6] ul,.m3-markdown[data-v-6e4dc2b6] ol{margin:.5em 0;padding-left:1.5em}.m3-markdown[data-v-6e4dc2b6] li{margin:.25em 0}.m3-markdown[data-v-6e4dc2b6] li::marker{color:var(--color-on-surface-variant)}.m3-markdown[data-v-6e4dc2b6] 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-6e4dc2b6] code{background:var(--color-surface-container-highest);color:var(--color-primary);border-radius:4px;padding:.15em .4em;font-family:JetBrains Mono,Fira Code,monospace;font-size:.875em}.m3-markdown[data-v-6e4dc2b6] 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-6e4dc2b6] pre code{color:var(--color-on-surface);background:0 0;padding:0}.m3-markdown[data-v-6e4dc2b6] hr{border:none;border-top:1px solid var(--color-outline-variant);margin:1.5em 0}.m3-markdown[data-v-6e4dc2b6] table{border-collapse:collapse;width:100%;margin:.75em 0}.m3-markdown[data-v-6e4dc2b6] 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-6e4dc2b6] td{border-bottom:1px solid var(--color-outline-variant);padding:.5em .75em}.m3-markdown[data-v-6e4dc2b6] img{border-radius:12px;max-width:100%;height:auto;margin:.5em 0}.rte-content[data-v-89c08c83] .tiptap{min-height:inherit;outline:none}.rte-content[data-v-89c08c83] .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-89c08c83] 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-89c08c83] 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-89c08c83] 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-89c08c83] p{margin:.5em 0}.rte-content[data-v-89c08c83] ul,.rte-content[data-v-89c08c83] ol{margin:.5em 0;padding-left:1.5em}.rte-content[data-v-89c08c83] blockquote{border-left:3px solid var(--color-primary);color:var(--color-on-surface-variant);margin:.5em 0;padding-left:1em}.rte-content[data-v-89c08c83] code{background:var(--color-surface-container-highest);border-radius:4px;padding:.15em .4em;font-size:.875em}.rte-content[data-v-89c08c83] pre{background:var(--color-surface-container-highest);border-radius:8px;margin:.5em 0;padding:1em;overflow-x:auto}.rte-content[data-v-89c08c83] pre code{background:0 0;padding:0}.rte-content[data-v-89c08c83] a{color:var(--color-primary);text-decoration:underline}.rte-content[data-v-89c08c83] mark{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px;padding:.1em .2em}.rte-content[data-v-89c08c83] 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)}.bs-scrim[data-v-941a89e7]{transition:opacity .3s}.bs-enter-from .bs-scrim[data-v-941a89e7],.bs-leave-to .bs-scrim[data-v-941a89e7]{opacity:0}.bs-panel[data-v-941a89e7]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.bs-enter-from .bs-panel[data-v-941a89e7]{opacity:0;transform:translateY(40%)}.bs-leave-to .bs-panel[data-v-941a89e7]{opacity:0;transform:translateY(100%)!important}@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}}.hue-slider[data-v-9ee0043f]{background:linear-gradient(90deg,red 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.hue-slider[data-v-9ee0043f]::-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-9ee0043f]::-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)}.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)}.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)}.nd-scrim[data-v-d898f7f1]{transition:opacity .28s}.nd-enter-from .nd-scrim[data-v-d898f7f1],.nd-leave-to .nd-scrim[data-v-d898f7f1]{opacity:0}.nd-panel[data-v-d898f7f1]{transition:transform .3s cubic-bezier(.2,0,0,1)}.nd-enter-from .nd-panel[data-v-d898f7f1],.nd-leave-to .nd-panel[data-v-d898f7f1]{transform:translate(-100%)}.nd-section-grid[data-v-d898f7f1]{grid-template-rows:1fr;display:grid}.nd-section-body[data-v-d898f7f1]{min-height:0;overflow:hidden}.nd-section-enter-active[data-v-d898f7f1]{transition:grid-template-rows .35s cubic-bezier(.2,0,0,1)}.nd-section-enter-active>.nd-section-body[data-v-d898f7f1]{transition:opacity .25s cubic-bezier(.2,0,0,1) 80ms}.nd-section-enter-from[data-v-d898f7f1]{grid-template-rows:0fr}.nd-section-enter-from>.nd-section-body[data-v-d898f7f1]{opacity:0}.nd-section-leave-active[data-v-d898f7f1]{transition:grid-template-rows .28s cubic-bezier(.4,0,1,1)}.nd-section-leave-active>.nd-section-body[data-v-d898f7f1]{transition:opacity .15s cubic-bezier(.4,0,1,1)}.nd-section-leave-to[data-v-d898f7f1]{grid-template-rows:0fr}.nd-section-leave-to>.nd-section-body[data-v-d898f7f1]{opacity:0}.nd-inline[data-v-d898f7f1]{transition:width .3s cubic-bezier(.2,0,0,1)}.nd-inline .nd-collapse-h[data-v-d898f7f1]{max-height:80px;transition:max-height .3s cubic-bezier(.2,0,0,1)}.nd-inline.nd-collapsed .nd-collapse-h[data-v-d898f7f1]{max-height:0}.nd-inline .nd-section-body[data-v-d898f7f1]>*{transition:padding .3s cubic-bezier(.2,0,0,1)}@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-f8751672]{transition:opacity .28s}.ss-enter-from .ss-scrim[data-v-f8751672],.ss-leave-to .ss-scrim[data-v-f8751672]{opacity:0}.ss-panel[data-v-f8751672]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.ss-enter-from .ss-panel[data-v-f8751672]{opacity:0;transform:translate(40%)}.ss-leave-to .ss-panel[data-v-f8751672]{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-e83a5c10]{grid-template-rows:1fr;padding-bottom:8px;display:grid}.toast-row>.toast-inner[data-v-e83a5c10]{min-height:0}.m3-toast-bot-enter-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-bot-enter-from[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-enter-from>.toast-inner[data-v-e83a5c10]{opacity:0;transform:translateY(20px)scale(.94)}.m3-toast-bot-leave-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .18s}.m3-toast-bot-leave-to[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-leave-to>.toast-inner[data-v-e83a5c10]{opacity:0;transform:scale(.92)}.m3-toast-top-enter-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-top-enter-from[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-enter-from>.toast-inner[data-v-e83a5c10]{opacity:0;transform:translateY(-20px)scale(.94)}.m3-toast-top-leave-active[data-v-e83a5c10]{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-e83a5c10]{transition:opacity .18s,transform .18s}.m3-toast-top-leave-to[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-leave-to>.toast-inner[data-v-e83a5c10]{opacity:0;transform:scale(.92)}@keyframes m3-toast-progress-e83a5c10{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-6e4dc2b6] 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-6e4dc2b6] 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-6e4dc2b6] 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-6e4dc2b6] 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-6e4dc2b6] p{margin:.5em 0}.m3-markdown[data-v-6e4dc2b6] a{color:var(--color-primary);text-underline-offset:2px;text-decoration:underline}.m3-markdown[data-v-6e4dc2b6] a:hover{opacity:.8}.m3-markdown[data-v-6e4dc2b6] strong{color:var(--color-on-surface);font-weight:600}.m3-markdown[data-v-6e4dc2b6] em{font-style:italic}.m3-markdown[data-v-6e4dc2b6] ul,.m3-markdown[data-v-6e4dc2b6] ol{margin:.5em 0;padding-left:1.5em}.m3-markdown[data-v-6e4dc2b6] li{margin:.25em 0}.m3-markdown[data-v-6e4dc2b6] li::marker{color:var(--color-on-surface-variant)}.m3-markdown[data-v-6e4dc2b6] 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-6e4dc2b6] code{background:var(--color-surface-container-highest);color:var(--color-primary);border-radius:4px;padding:.15em .4em;font-family:JetBrains Mono,Fira Code,monospace;font-size:.875em}.m3-markdown[data-v-6e4dc2b6] 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-6e4dc2b6] pre code{color:var(--color-on-surface);background:0 0;padding:0}.m3-markdown[data-v-6e4dc2b6] hr{border:none;border-top:1px solid var(--color-outline-variant);margin:1.5em 0}.m3-markdown[data-v-6e4dc2b6] table{border-collapse:collapse;width:100%;margin:.75em 0}.m3-markdown[data-v-6e4dc2b6] 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-6e4dc2b6] td{border-bottom:1px solid var(--color-outline-variant);padding:.5em .75em}.m3-markdown[data-v-6e4dc2b6] img{border-radius:12px;max-width:100%;height:auto;margin:.5em 0}.rte-content[data-v-89c08c83] .tiptap{min-height:inherit;outline:none}.rte-content[data-v-89c08c83] .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-89c08c83] 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-89c08c83] 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-89c08c83] 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-89c08c83] p{margin:.5em 0}.rte-content[data-v-89c08c83] ul,.rte-content[data-v-89c08c83] ol{margin:.5em 0;padding-left:1.5em}.rte-content[data-v-89c08c83] blockquote{border-left:3px solid var(--color-primary);color:var(--color-on-surface-variant);margin:.5em 0;padding-left:1em}.rte-content[data-v-89c08c83] code{background:var(--color-surface-container-highest);border-radius:4px;padding:.15em .4em;font-size:.875em}.rte-content[data-v-89c08c83] pre{background:var(--color-surface-container-highest);border-radius:8px;margin:.5em 0;padding:1em;overflow-x:auto}.rte-content[data-v-89c08c83] pre code{background:0 0;padding:0}.rte-content[data-v-89c08c83] a{color:var(--color-primary);text-decoration:underline}.rte-content[data-v-89c08c83] mark{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px;padding:.1em .2em}.rte-content[data-v-89c08c83] 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.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Material 3 component library for Vue 3 + Tailwind CSS v4",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,6 +19,7 @@ const props = withDefaults(
19
19
  disabled?: boolean
20
20
  loading?: boolean
21
21
  icon?: string
22
+ to?: string | Record<string, any>
22
23
  }>(),
23
24
  {
24
25
  variant: 'filled',
@@ -29,6 +30,8 @@ const props = withDefaults(
29
30
  },
30
31
  )
31
32
 
33
+ const tag = computed(() => props.to ? 'RouterLink' : 'button')
34
+
32
35
  const isCustomColor = computed(
33
36
  () => !!props.color && !(NAMED_COLORS as readonly string[]).includes(props.color),
34
37
  )
@@ -97,8 +100,10 @@ function createRipple(event: PointerEvent) {
97
100
  </script>
98
101
 
99
102
  <template>
100
- <button
101
- :type="type"
103
+ <component
104
+ :is="tag"
105
+ :to="to || undefined"
106
+ :type="to ? undefined : type"
102
107
  :disabled="disabled || loading"
103
108
  :class="[base, variantClasses]"
104
109
  :style="customStyle"
@@ -107,5 +112,5 @@ function createRipple(event: PointerEvent) {
107
112
  <MSpinner v-if="loading" :size="18" />
108
113
  <MIcon v-else-if="icon" :name="icon" :size="20" />
109
114
  <slot />
110
- </button>
115
+ </component>
111
116
  </template>
@@ -10,6 +10,8 @@ const props = withDefaults(
10
10
  image?: string
11
11
  imageAlt?: string
12
12
  imageHeight?: string
13
+ title?: string
14
+ subtitle?: string
13
15
  }>(),
14
16
  { variant: 'elevated', clickable: false, elevated: false },
15
17
  )
@@ -51,6 +53,18 @@ const fieldBgByVariant: Record<string, string> = {
51
53
  <slot v-else name="media" />
52
54
  </div>
53
55
 
56
+ <div v-if="$slots.header" class="px-4 pt-4 pb-2">
57
+ <slot name="header" />
58
+ </div>
59
+ <div v-else-if="title" class="px-4 pt-4 pb-2">
60
+ <h3 class="text-title-large font-medium text-on-surface">{{ title }}</h3>
61
+ <p v-if="subtitle" class="mt-1 text-body-medium text-on-surface-variant">{{ subtitle }}</p>
62
+ </div>
63
+
54
64
  <slot />
65
+
66
+ <div v-if="$slots.actions" class="flex justify-end gap-2 px-4 pt-2 pb-4">
67
+ <slot name="actions" />
68
+ </div>
55
69
  </div>
56
70
  </template>
@@ -9,6 +9,7 @@ export interface ContextMenuItem {
9
9
  disabled?: boolean
10
10
  danger?: boolean
11
11
  divider?: boolean
12
+ to?: string | Record<string, any>
12
13
  children?: ContextMenuItem[]
13
14
  onClick?: () => void
14
15
  }
@@ -5,6 +5,7 @@ import MIcon from './MIcon.vue'
5
5
  export interface SpeedDialItem {
6
6
  icon: string
7
7
  label?: string
8
+ to?: string | Record<string, any>
8
9
  onClick?: () => void
9
10
  }
10
11
 
@@ -17,6 +18,7 @@ const props = withDefaults(
17
18
  disabled?: boolean
18
19
  items?: SpeedDialItem[]
19
20
  direction?: 'up' | 'down' | 'left' | 'right' | 'radial'
21
+ to?: string | Record<string, any>
20
22
  }>(),
21
23
  {
22
24
  color: 'primary',
@@ -26,6 +28,8 @@ const props = withDefaults(
26
28
  },
27
29
  )
28
30
 
31
+ const fabTag = computed(() => props.to ? 'RouterLink' : 'button')
32
+
29
33
  const emit = defineEmits<{ click: [MouseEvent] }>()
30
34
 
31
35
  const open = ref(false)
@@ -186,12 +190,14 @@ onUnmounted(() => {
186
190
 
187
191
  <template>
188
192
  <div ref="fabEl" class="relative inline-flex items-center justify-center">
189
- <button
190
- type="button"
193
+ <component
194
+ :is="fabTag"
195
+ :to="to || undefined"
196
+ :type="to ? undefined : 'button'"
191
197
  class="relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]"
192
198
  :class="[colorMap[color], fabSizeClasses]"
193
199
  :disabled="disabled"
194
- @pointerdown="(e) => { createRipple(e); handleFabClick(e) }"
200
+ @pointerdown="(e: PointerEvent) => { createRipple(e); handleFabClick(e) }"
195
201
  >
196
202
  <MIcon
197
203
  :name="icon"
@@ -200,7 +206,7 @@ onUnmounted(() => {
200
206
  :class="hasItems && open ? 'rotate-45' : ''"
201
207
  />
202
208
  <span v-if="label" class="text-label-large font-medium">{{ label }}</span>
203
- </button>
209
+ </component>
204
210
  </div>
205
211
 
206
212
  <Teleport to="body">
@@ -221,15 +227,17 @@ onUnmounted(() => {
221
227
  {{ item.label }}
222
228
  </span>
223
229
 
224
- <button
225
- type="button"
230
+ <component
231
+ :is="item.to ? 'RouterLink' : 'button'"
232
+ :to="item.to || undefined"
233
+ :type="item.to ? undefined : 'button'"
226
234
  class="relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]"
227
235
  :class="colorMap[color]"
228
236
  :style="{ width: `${ITEM_PX}px`, height: `${ITEM_PX}px` }"
229
- @pointerdown="(e) => handleItemClick(e, item, e.currentTarget as HTMLElement)"
237
+ @pointerdown="(e: PointerEvent) => handleItemClick(e, item, e.currentTarget as HTMLElement)"
230
238
  >
231
239
  <MIcon :name="item.icon" :size="20" />
232
- </button>
240
+ </component>
233
241
  </div>
234
242
  </template>
235
243
  </Teleport>
@@ -9,6 +9,7 @@ const props = withDefaults(
9
9
  variant?: 'standard' | 'filled' | 'tonal' | 'outlined'
10
10
  disabled?: boolean
11
11
  size?: number
12
+ to?: string | Record<string, any>
12
13
  }>(),
13
14
  {
14
15
  variant: 'standard',
@@ -17,6 +18,8 @@ const props = withDefaults(
17
18
  },
18
19
  )
19
20
 
21
+ const tag = computed(() => props.to ? 'RouterLink' : 'button')
22
+
20
23
  const base =
21
24
  'inline-flex shrink-0 items-center justify-center rounded-full transition-colors duration-150 cursor-pointer ' +
22
25
  'disabled:cursor-not-allowed disabled:opacity-[0.38]'
@@ -36,8 +39,10 @@ const variantClasses = computed(() => {
36
39
  </script>
37
40
 
38
41
  <template>
39
- <button
40
- type="button"
42
+ <component
43
+ :is="tag"
44
+ :to="to || undefined"
45
+ :type="to ? undefined : 'button'"
41
46
  :aria-label="label"
42
47
  :title="label"
43
48
  :disabled="disabled"
@@ -45,5 +50,5 @@ const variantClasses = computed(() => {
45
50
  :style="{ width: `${size}px`, height: `${size}px` }"
46
51
  >
47
52
  <MIcon :name="icon" :size="Math.round(size * 0.55)" />
48
- </button>
53
+ </component>
49
54
  </template>
@@ -1,15 +1,20 @@
1
1
  <script setup lang="ts">
2
+ import { computed } from 'vue'
2
3
  import MIcon from './MIcon.vue'
3
4
 
4
- withDefaults(defineProps<{ icon?: string }>(), {})
5
+ const props = withDefaults(defineProps<{ icon?: string; to?: string | Record<string, any> }>(), {})
6
+
7
+ const tag = computed(() => props.to ? 'RouterLink' : 'button')
5
8
  </script>
6
9
 
7
10
  <template>
8
- <button
9
- type="button"
11
+ <component
12
+ :is="tag"
13
+ :to="to || undefined"
14
+ :type="to ? undefined : 'button'"
10
15
  class="flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left text-body-large text-on-surface hover:bg-on-surface/8"
11
16
  >
12
17
  <MIcon v-if="icon" :name="icon" :size="20" class="text-on-surface-variant" />
13
18
  <slot />
14
- </button>
19
+ </component>
15
20
  </template>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { watch } from 'vue'
2
+ import { ref, watch } from 'vue'
3
3
  import MIcon from './MIcon.vue'
4
4
 
5
5
  export interface DrawerItem {
@@ -8,11 +8,14 @@ export interface DrawerItem {
8
8
  icon?: string
9
9
  badge?: string | number
10
10
  disabled?: boolean
11
+ to?: string | Record<string, any>
11
12
  }
12
13
 
13
14
  export interface DrawerSection {
14
15
  title?: string
16
+ icon?: string
15
17
  items: DrawerItem[]
18
+ collapsible?: boolean
16
19
  }
17
20
 
18
21
  const props = withDefaults(defineProps<{
@@ -21,6 +24,7 @@ const props = withDefaults(defineProps<{
21
24
  sections: DrawerSection[]
22
25
  title?: string
23
26
  modal?: boolean
27
+ collapsed?: boolean
24
28
  }>(), { modal: true })
25
29
 
26
30
  const emit = defineEmits<{
@@ -28,6 +32,18 @@ const emit = defineEmits<{
28
32
  select: [string | number]
29
33
  }>()
30
34
 
35
+ const openSections = ref<Record<string, boolean>>({})
36
+
37
+ function isSectionOpen(section: DrawerSection, index: number) {
38
+ const key = section.title ?? `__${index}`
39
+ return openSections.value[key] !== false
40
+ }
41
+
42
+ function toggleSection(section: DrawerSection, index: number) {
43
+ const key = section.title ?? `__${index}`
44
+ openSections.value[key] = !isSectionOpen(section, index)
45
+ }
46
+
31
47
  function close() { emit('update:modelValue', false) }
32
48
  function select(item: DrawerItem) {
33
49
  if (item.disabled) return
@@ -35,60 +51,87 @@ function select(item: DrawerItem) {
35
51
  if (props.modal) close()
36
52
  }
37
53
 
54
+ function itemTag(item: DrawerItem) {
55
+ return item.to && !item.disabled ? 'RouterLink' : 'button'
56
+ }
57
+
58
+
38
59
  watch(() => props.modelValue, (open) => {
39
- if (open) document.body.style.overflow = 'hidden'
40
- else document.body.style.overflow = ''
60
+ if (props.modal) {
61
+ document.body.style.overflow = open ? 'hidden' : ''
62
+ }
41
63
  })
42
64
  </script>
43
65
 
44
66
  <template>
45
- <!-- Modal variant -->
67
+ <!-- ── Modal variant ──────────────────────────────────────── -->
46
68
  <Teleport v-if="modal" to="body">
47
69
  <Transition name="nd" :duration="{ enter: 300, leave: 280 }">
48
70
  <div v-if="modelValue" class="fixed inset-0 z-[100] flex">
49
- <!-- Scrim -->
50
71
  <div class="nd-scrim absolute inset-0 bg-black/40" @click="close" />
51
72
 
52
- <!-- Panel -->
53
73
  <nav class="nd-panel relative flex h-full w-72 max-w-[85vw] flex-col bg-surface-container shadow-elevation-3">
54
- <!-- Header -->
55
74
  <div v-if="title || $slots.header" class="shrink-0 px-5 pt-6 pb-2">
56
75
  <slot name="header">
57
76
  <h2 class="text-title-small font-medium text-on-surface-variant">{{ title }}</h2>
58
77
  </slot>
59
78
  </div>
60
79
 
61
- <!-- Sections -->
62
80
  <div class="flex-1 overflow-y-auto px-3 py-2">
63
81
  <template v-for="(section, si) in sections" :key="si">
64
- <div v-if="si > 0" class="my-2 border-t border-outline-variant" />
65
- <p v-if="section.title" class="px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant">
66
- {{ section.title }}
67
- </p>
82
+ <div v-if="si > 0" class="my-1 border-t border-outline-variant" />
83
+
68
84
  <button
69
- v-for="item in section.items"
70
- :key="item.value"
85
+ v-if="section.title && section.collapsible"
71
86
  type="button"
72
- class="flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none"
73
- :class="[
74
- item.disabled
75
- ? 'cursor-not-allowed opacity-[0.38]'
76
- : item.value === selected
77
- ? 'bg-secondary-container text-on-secondary-container'
78
- : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',
79
- ]"
80
- :disabled="item.disabled"
81
- @click="select(item)"
87
+ class="mt-1 flex w-full cursor-pointer items-center gap-3 rounded-xl px-3 py-2.5 text-on-surface-variant transition-colors hover:bg-on-surface/8"
88
+ @click="toggleSection(section, si)"
82
89
  >
83
- <MIcon v-if="item.icon" :name="item.icon" :size="24" />
84
- <span class="flex-1 text-label-large font-medium">{{ item.label }}</span>
85
- <span
86
- v-if="item.badge != null"
87
- class="text-label-medium text-on-surface-variant"
88
- >
89
- {{ item.badge }}
90
- </span>
90
+ <MIcon v-if="section.icon" :name="section.icon" :size="24" class="shrink-0" />
91
+ <span class="flex-1 text-left text-title-small font-medium">{{ section.title }}</span>
92
+ <MIcon
93
+ :name="isSectionOpen(section, si) ? 'expand_less' : 'expand_more'"
94
+ :size="18"
95
+ class="shrink-0"
96
+ />
91
97
  </button>
98
+ <p v-else-if="section.title" class="px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant">
99
+ {{ section.title }}
100
+ </p>
101
+
102
+ <Transition name="nd-section">
103
+ <div v-if="!section.collapsible || isSectionOpen(section, si)" class="nd-section-grid">
104
+ <div class="nd-section-body">
105
+ <component
106
+ :is="itemTag(item)"
107
+ v-for="item in section.items"
108
+ :key="item.value"
109
+ :to="item.to && !item.disabled ? item.to : undefined"
110
+ :type="item.to ? undefined : 'button'"
111
+ class="flex w-full items-center gap-3 rounded-full py-2.5 text-left transition-colors focus-visible:outline-none"
112
+ :class="[
113
+ section.collapsible ? 'pl-8 pr-3' : 'px-4',
114
+ item.disabled
115
+ ? 'cursor-not-allowed opacity-[0.38]'
116
+ : item.value === selected
117
+ ? 'bg-secondary-container text-on-secondary-container'
118
+ : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',
119
+ ]"
120
+ :disabled="item.disabled && !item.to"
121
+ @click="select(item)"
122
+ >
123
+ <MIcon v-if="item.icon" :name="item.icon" :size="24" />
124
+ <span class="flex-1 text-label-large font-medium">{{ item.label }}</span>
125
+ <span
126
+ v-if="item.badge != null"
127
+ class="text-label-medium text-on-surface-variant"
128
+ >
129
+ {{ item.badge }}
130
+ </span>
131
+ </component>
132
+ </div>
133
+ </div>
134
+ </Transition>
92
135
  </template>
93
136
  </div>
94
137
  </nav>
@@ -96,49 +139,89 @@ watch(() => props.modelValue, (open) => {
96
139
  </Transition>
97
140
  </Teleport>
98
141
 
99
- <!-- Standard (inline) variant -->
100
- <nav
101
- v-else
102
- class="flex h-full w-72 flex-col border-r border-outline-variant bg-surface"
103
- >
104
- <div v-if="title || $slots.header" class="shrink-0 px-5 pt-6 pb-2">
105
- <slot name="header">
106
- <h2 class="text-title-small font-medium text-on-surface-variant">{{ title }}</h2>
107
- </slot>
108
- </div>
109
- <div class="flex-1 overflow-y-auto px-3 py-2">
110
- <template v-for="(section, si) in sections" :key="si">
111
- <div v-if="si > 0" class="my-2 border-t border-outline-variant" />
112
- <p v-if="section.title" class="px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant">
113
- {{ section.title }}
114
- </p>
115
- <button
116
- v-for="item in section.items"
117
- :key="item.value"
118
- type="button"
119
- class="flex w-full items-center gap-3 rounded-full px-4 py-3 text-left transition-colors focus-visible:outline-none"
120
- :class="[
121
- item.disabled
122
- ? 'cursor-not-allowed opacity-[0.38]'
123
- : item.value === selected
124
- ? 'bg-secondary-container text-on-secondary-container'
125
- : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',
126
- ]"
127
- :disabled="item.disabled"
128
- @click="select(item)"
129
- >
130
- <MIcon v-if="item.icon" :name="item.icon" :size="24" />
131
- <span class="flex-1 text-label-large font-medium">{{ item.label }}</span>
132
- <span v-if="item.badge != null" class="text-label-medium text-on-surface-variant">
133
- {{ item.badge }}
134
- </span>
135
- </button>
136
- </template>
137
- </div>
138
- </nav>
142
+ <!-- ── Standard (inline) variant ──────────────────────────── -->
143
+ <template v-else>
144
+ <nav
145
+ class="nd-inline flex h-full shrink-0 flex-col border-r border-outline-variant bg-surface"
146
+ :class="collapsed ? 'nd-collapsed w-[72px]' : 'w-72'"
147
+ >
148
+ <div v-if="$slots.header" class="shrink-0">
149
+ <slot name="header" />
150
+ </div>
151
+ <div v-else-if="title" class="nd-collapse-h shrink-0 overflow-hidden">
152
+ <div class="px-5 pt-6 pb-2">
153
+ <h2 class="whitespace-nowrap text-title-small font-medium text-on-surface-variant">{{ title }}</h2>
154
+ </div>
155
+ </div>
156
+
157
+ <div class="flex flex-col gap-1 overflow-y-auto overflow-x-hidden px-3 py-2">
158
+ <template v-for="(section, si) in sections" :key="si">
159
+ <div v-if="si > 0" class="my-1 border-t border-outline-variant" />
160
+
161
+ <!-- Collapsible section header -->
162
+ <button
163
+ v-if="section.title && section.collapsible"
164
+ type="button"
165
+ class="mt-1 flex w-full cursor-pointer items-center gap-3 overflow-hidden whitespace-nowrap rounded-xl px-3 py-2.5 text-on-surface-variant hover:bg-on-surface/8"
166
+ :title="collapsed ? section.title : undefined"
167
+ @click="toggleSection(section, si)"
168
+ >
169
+ <MIcon v-if="section.icon" :name="section.icon" :size="24" class="shrink-0" />
170
+ <span class="min-w-0 flex-1 text-left text-title-small font-medium">{{ section.title }}</span>
171
+ <MIcon
172
+ :name="isSectionOpen(section, si) ? 'expand_less' : 'expand_more'"
173
+ :size="18"
174
+ class="shrink-0"
175
+ />
176
+ </button>
177
+
178
+ <!-- Static section title -->
179
+ <div v-else-if="section.title" class="nd-collapse-h overflow-hidden">
180
+ <p class="whitespace-nowrap px-4 pt-4 pb-2 text-title-small font-medium text-on-surface-variant">
181
+ {{ section.title }}
182
+ </p>
183
+ </div>
184
+
185
+ <Transition name="nd-section">
186
+ <div v-if="!section.collapsible || isSectionOpen(section, si)" class="nd-section-grid">
187
+ <div class="nd-section-body">
188
+ <component
189
+ :is="itemTag(item)"
190
+ v-for="item in section.items"
191
+ :key="item.value"
192
+ :to="item.to && !item.disabled ? item.to : undefined"
193
+ :type="item.to ? undefined : 'button'"
194
+ :title="collapsed ? item.label : undefined"
195
+ class="flex w-full items-center gap-3 overflow-hidden whitespace-nowrap rounded-full py-2.5 text-left focus-visible:outline-none"
196
+ :class="[
197
+ section.collapsible && !collapsed ? 'pl-8 pr-3' : 'px-3',
198
+ item.disabled
199
+ ? 'cursor-not-allowed opacity-[0.38]'
200
+ : item.value === selected
201
+ ? 'bg-secondary-container text-on-secondary-container'
202
+ : 'cursor-pointer text-on-surface-variant hover:bg-on-surface/8',
203
+ ]"
204
+ :disabled="item.disabled && !item.to"
205
+ @click="select(item)"
206
+ >
207
+ <MIcon v-if="item.icon" :name="item.icon" :size="24" class="shrink-0" />
208
+ <span class="min-w-0 flex-1 text-label-large font-medium">{{ item.label }}</span>
209
+ <span v-if="item.badge != null" class="text-label-medium text-on-surface-variant">
210
+ {{ item.badge }}
211
+ </span>
212
+ </component>
213
+ </div>
214
+ </div>
215
+ </Transition>
216
+ </template>
217
+ </div>
218
+ </nav>
219
+
220
+ </template>
139
221
  </template>
140
222
 
141
223
  <style scoped>
224
+ /* ── Modal transitions ─────────────────────────────────── */
142
225
  .nd-scrim {
143
226
  transition: opacity 280ms ease;
144
227
  }
@@ -154,4 +237,58 @@ watch(() => props.modelValue, (open) => {
154
237
  .nd-leave-to .nd-panel {
155
238
  transform: translateX(-100%);
156
239
  }
240
+
241
+ /* ── Section collapse animation ────────────────────────── */
242
+ .nd-section-grid {
243
+ display: grid;
244
+ grid-template-rows: 1fr;
245
+ }
246
+ .nd-section-body {
247
+ min-height: 0;
248
+ overflow: hidden;
249
+ }
250
+
251
+ .nd-section-enter-active {
252
+ transition: grid-template-rows 350ms cubic-bezier(0.2, 0, 0, 1);
253
+ }
254
+ .nd-section-enter-active > .nd-section-body {
255
+ transition: opacity 250ms 80ms cubic-bezier(0.2, 0, 0, 1);
256
+ }
257
+ .nd-section-enter-from {
258
+ grid-template-rows: 0fr;
259
+ }
260
+ .nd-section-enter-from > .nd-section-body {
261
+ opacity: 0;
262
+ }
263
+
264
+ .nd-section-leave-active {
265
+ transition: grid-template-rows 280ms cubic-bezier(0.4, 0, 1, 1);
266
+ }
267
+ .nd-section-leave-active > .nd-section-body {
268
+ transition: opacity 150ms cubic-bezier(0.4, 0, 1, 1);
269
+ }
270
+ .nd-section-leave-to {
271
+ grid-template-rows: 0fr;
272
+ }
273
+ .nd-section-leave-to > .nd-section-body {
274
+ opacity: 0;
275
+ }
276
+
277
+ /* ── Inline sidebar width transition ───────────────────── */
278
+ .nd-inline {
279
+ transition: width 300ms cubic-bezier(0.2, 0, 0, 1);
280
+ }
281
+
282
+ /* Elements that should collapse to 0 height when sidebar is collapsed */
283
+ .nd-inline .nd-collapse-h {
284
+ max-height: 80px;
285
+ transition: max-height 300ms cubic-bezier(0.2, 0, 0, 1);
286
+ }
287
+ .nd-inline.nd-collapsed .nd-collapse-h {
288
+ max-height: 0;
289
+ }
290
+
291
+ .nd-inline .nd-section-body > * {
292
+ transition: padding 300ms cubic-bezier(0.2, 0, 0, 1);
293
+ }
157
294
  </style>
@@ -80,9 +80,11 @@ onBeforeUnmount(() => {
80
80
  : 'h-2 cursor-row-resize flex-row',
81
81
  dragging ? 'bg-primary/20' : 'bg-outline-variant/40 hover:bg-primary/12',
82
82
  ]"
83
+ style="touch-action: none"
83
84
  @pointerdown="onPointerDown"
84
85
  @pointermove="onPointerMove"
85
86
  @pointerup="onPointerUp"
87
+ @pointercancel="onPointerUp"
86
88
  >
87
89
  <div
88
90
  class="rounded-full bg-outline"