Dashboard 2 - Multi-state switch

@BartButenaers Don't mess with strings just like that :slight_smile:
Also don't take away option to deal with indexes. Some of us do like math and numbers :stuck_out_tongue:

TEMPLATE

<template>
    <div class="mss-wrapper">
        <v-chip variant="text">
            {{label}}
        </v-chip>
        <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>
</template>

<script>
    export default {
        data() {
            return {
                //define me here
                label:"Multistate Prototype",                
                options:[
                    {label:"",value:"on",icon:"mdi-antenna",color:"green"},// label can be empty 
                    {value:"off", icon:"mdi-basketball",color:"red"},//can omit the label
                    {label:"Auto",value:"auto", color:"blue",icon:"mdi-car-wrench"},// can omit the icon
                    {label:"Manual", value:"manual", color:"orange",icon:"mdi-car-shift-pattern"}// can omit color - defaults to theme color
                ],
                // "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",              
                
                // 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.msg.payload != undefined){
                    if(typeof this.msg.payload === "number"){                   
                        if(this.isValidIndex(this.msg.payload)){
                            this.changeByInput = true
                            this.selection = this.msg.payload
                        }                        
                    }
                    else if(typeof this.msg.payload === "string"){
                        if(this.isValidValue(this.msg.payload)){
                            this.changeByInput = true
                            this.selection = this.findOptionIndexByValue(this.msg.payload) // may be null but quarded
                        }
                    }
                    
                }
            },
            selection:function(){                
                if(this.changeByInput == true){
                    this.changeByInput = false
                }
                else{
                    this.send(this.getOutputMessage())
                }
            }
        },

        methods: {
            isValidIndex: function (idx){
                if(idx < 0){
                    return false
                }
                if(idx >= this.options.length){
                    return false
                }
                return true                 
            },
            isValidValue:function (val){
                console.log('validate value',val)
                if(val == null){
                    return false
                }
                if(val == undefined){
                    return false
                }
                if(val == ""){
                    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
            },
            findOptionIndexByValue:function(val){
                let idx = this.options.findIndex(option => option.value === val)                
                if(idx != -1){
                    return idx
                }
                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>

CSS

.mss-wrapper {
    display:grid;
    grid-template-columns:1fr 1fr;
    align-items:center;
}
.mss-wrapper .v-chip__content{
    white-space:normal;
}
.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;
}

EDIT - made label also optional.

image

3 Likes