DB2 multi state switch revisited

@BartButenaers and @hotNipi posted some multi state switch code and rather than flog that thread I opened a new one. Modified the code somewhat, consolidated it really but what I would like to do is make it multi line if possible.

<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>
        <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>
            <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:"",                
                options:[
                     
                    {label:"Timer 1",value:"off"},// can omit the icon
                    {label:"Reset", value:"reset", color:"blue",}// 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:"default",              
                
                // 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>

<style>

.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;
}

</style>

played with this quite a bit and found numerous ways to break it but never got the separate line or separate switches figured out. Would like to have different switches on lines 2 and 3. Is this even possible?
Thanks

well, I got to here

<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 }} <!-- Correct usage of option.label -->
            </v-btn>
        </v-btn-toggle>
    </div>

    <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 }}
                <!-- Correct usage of option.label -->
            </v-btn>
        </v-btn-toggle>
    </div>
    
</template>

<script>
export default {
    data() {
        return {
            label: "Select an Action",
            options: [
                { label: "Start", value: "start", icon: "mdi-timer",color:"red"},
                { label: "Pause", value: "pause", icon: "mdi-pause",color:"blue" },
                { label: "Reset", value: "reset", icon: "mdi-stop-circle",color:"grey" },
            ],
            options: [
                { label: "Start Timer", value: "start", icon: "mdi-timer",color:"green" },
                { label: "Pause Timer", value: "pause", icon: "mdi-pause",color:"brown" },
                { label: "Reset Timer", value: "reset", icon: "mdi-stop-circle",color:"yellow" },
            ],
            look: "default",
            selection: null,
            changeByInput: false,
        };
    },

    watch: {
        msg: function() {
            if (this.msg?.payload !== undefined) {
                this.changeByInput = true;

                if (typeof this.msg.payload === "number" && this.isValidIndex(this.msg.payload)) {
                    this.selection = this.msg.payload;
                } else if (typeof this.msg.payload === "string" && this.isValidValue(this.msg.payload)) {
                    this.selection = this.findOptionIndexByValue(this.msg.payload);
                }
            }
        },
        selection: function() {
            if (!this.changeByInput) {
                this.send(this.getOutputMessage());
            }
            this.changeByInput = false;
        }
    },

    methods: {
        isValidIndex(idx) {
            return idx >= 0 && idx < this.options.length;
        },
        isValidValue(val) {
            return val != null && this.options.some(option => option.value === val);
        },
        findOptionIndexByValue(val) {
            return this.options.findIndex(option => option.value === val) || null;
        },
        getOutputMessage() {
            return { payload: this.options[this.selection]?.value, topic: 'multistate' };
        }
    },

    computed: {
        selectedColor() {
            return this.selection !== null ? (this.options[this.selection].color || "rgb(var(--v-theme-primary))") : "";
        },
        variant() {
            return this.look === "default" ? null : this.look;
        }
    }
};
</script>

<style>
.mss-wrapper {
    display: grid;
    grid-template-columns: 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;
}
</style>

but still not right, but closer

Hi @gerry,
I will post the link here for completeness. After we had that discussion at the time being it was decided that the multistate switch should become a core node: the ui-button-group node was born.

Last weekend I migrated my own dashboard finally to D2. And now I use that button group node as a replacement for all my old ui-multistate-switch nodes.

Instead of changing hotnipi's sample code, I would appreciate if you could try to do your changes on the ui-button-group node instead. You can find the frontend code here. That is the only way to improve our core ui nodes. And I assume Joe doesn't mind to get some help from the community...

About your multiline question: since CSS isn't my best friend, I am afraid I need to pass that question on to others reading this.

Good luck!
Bart

2 Likes