Dashboard 2 - Multi-state switch

2 cups of tea if you please :slight_smile:

:teapot: If to create it as for dedicated use, it is possible to use container query and change the layout quite a lot.

:teapot: But for common usage the outcome may be not so pleasant. Query may be clever but the break point depends on states you have configured - character count, if icons .... So it is not predictable.

Something to play with:

<template>
    <div :style="`--container:${container};`" class="mss-widget">
        <div class="mss-wrapper">
            <header>
               {{label}}              
            </header>
            <v-btn-toggle mandatory divided rounded="xl" :variant="variant" :color="selectedColor" v-model="selection">
                <v-btn v-for="(option, index) in options" :key='index' :class="option.label ? '' : 'icon-only'">
                    <template v-if="option.icon" v-slot:prepend>
                        <v-icon size="x-large"> {{option.icon}} </v-icon>
                    </template>
                    {{option.label}}
                </v-btn>
            </v-btn-toggle>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                //define me here
                label:"Multistate Prototype",
                options:[
                    {label:"first",value:"first",icon:"mdi-basketball",color:"green"},
                    {label:"second", value:"second", icon:"mdi-basketball",color:"red"},
                   /* {label:"1",value:1, color:"blue",icon:"mdi-car-wrench"},
                    {label:"2", value:2, color:"orange",icon:"mdi-car-wrench"},*/
                    {label:"TRUE FALSE",value:true,color:"green"},
                    {label:"false",value:false,icon:"mdi-antenna",color:"green"}
                ],
                // "outlined" (if the site bg is white or very light)"
                // "default" (if the site bg is dark)"
                // ("text" or "plain" also available but requires some styling)
                look:"outlined",
                container:"four",//name for container query (four and six available, different break points)
                
                // no need to change those
                selection: null,
                changeByInput:false, // in case of input - render but don't send the msg out
               
            }
        },
      
        watch: {
            msg: function(){        
                if(this.isValidValue(this.msg.payload)){
                    this.changeByInput = true
                    this.selection = this.options.findIndex(option => option.value === this.msg.payload )
                }
            },
            selection:function(){                
                if(this.changeByInput == true){
                    this.changeByInput = false
                }
                else{
                    this.send(this.getOutputMessage())
                }
            }
        },

        methods: {
            isValidValue:function (val){
                if(val === null){
                    return false
                }
                if(val === undefined){
                    return false
                }
                if(val === ""){
                    return false
                }
                if(!['number', 'string', 'boolean'].includes(typeof value)){
                    return false
                }
                if(this.options.findIndex(option => option.value === val ) == -1){
                    return false
                }
                return true

            },
            findOptionByValue:function(val){
                let opt = this.options.find(option => option.value === val)
                if(opt){
                    return opt
                }
                return null
            },
            getOutputMessage:function(){                
                return {payload:this.options[this.selection].value,topic:'multistate'}                
            }
        },
        
        computed: {            
            selectedColor:function(){
                if(this.selection == null){
                    return ""
                }
                return this.options[this.selection].color ?? "rgb(var(--v-theme-primary))"
            },
            variant:function(){
                return this.look == "default"  ? null : this.look
            }
        }
    }
</script>

<style>
    .mss-widget{
    container-type:inline-size;
    container-name:var(--container);
    }
    .mss-wrapper {
    display:grid;
    grid-template-columns:1fr 1fr;
    align-items:center;
    }
    .mss-wrapper header{
    white-space:normal;
    padding-right:var(--layout-gap);
    }
    .mss-wrapper header i{
    display:none;
    }
    .mss-wrapper .v-chip.v-chip--size-default{
    padding:0;
    }
    .mss-wrapper .v-btn-group{
    width:max-content;
    border-color:rgba(var(--v-border-color),.3);
    }
    .mss-wrapper .v-btn--variant-elevated, .mss-wrapper .v-btn--variant-outlined{
    color:#cccccc;
    }
    .mss-wrapper .icon-only .v-btn__prepend{
    margin-inline:0;
    }
    @container four (max-width: 500px){
    .mss-wrapper {
    grid-template-columns:none;
    grid-template-rows:1fr 2fr;
    align-items:center;
    justify-content:center;
    text-align:center;
    }
    .mss-wrapper .v-btn__prepend{
    margin-inline:0;
    }
    .mss-wrapper .v-btn__content{
    white-space: normal;
    max-width: min-content;
    }
    .mss-wrapper .v-btn:has(.v-btn__prepend) .v-btn__content{
    display:none;
    }
    }
    @container six (max-width: 650px){
    .mss-wrapper {
    grid-template-columns:none;
    grid-template-rows:1fr 2fr;
    align-items:center;
    justify-content:center;
    text-align:center;
    }
    .mss-wrapper .v-btn__prepend{
    margin-inline:0;
    }
    .mss-wrapper .v-btn__content{
    white-space: normal;
    max-width: min-content;
    }
    .mss-wrapper .v-btn:has(.v-btn__prepend) .v-btn__content{
    display:none;
    }
    }
</style>

And if you like to have something stronger than tea:

:tumbler_glass: Hi, I have problem, I configured 12 states for my multistate switch with nice long names and icons. Can you figure out why it doesn't fit into my mobile screen

3 Likes