Search code examples
javascriptcssvue.jsvuetify.jsright-to-left

Override Vuetify RTL for specific component/component tree


We have started using RTL in Vuetify, it works great but there is a slight problem in the slider component.
The problem is that we use this component as the slider for a video time, but even when viewing RTL languages, the video slider starts from the left (well, at least that's how it's managed in Israel which is our top RTL country).

enter image description here

I would love an option to bypass the rtl config for specific places in the application, I can't find a way in Vuetify docs. Anyone has a clue?

Thanks in advance


Solution

  • The slider uses this.$vuetify.rtl to determine the direction. However, this.$vuetify is a global object, where changing the rtl value will change it for the whole application.

    But you can replace the object in the component instance. To do this, you need to access the component instance, which you can do through a template ref or using a directive:

    Vue.directive(
      'vuetify-ltr', 
      (el, binding, vnode) => {
        const instance = vnode.componentInstance
        if (!instance.$vuetify) return
        instance.$vuetify = new Proxy(instance.$vuetify, {
          get(target, prop, receiver) {
            return prop === 'rtl' ? false : target[prop]
          },
        })
      }
    )
    

    This uses a Proxy to keep the other data from $vuetify bound to the original object. But since VSlider does not use any other values from $vuetify, you could also just override the whole thing.

    With the directive, you can now switch the direction for individual Vuetify components:

    <v-slider
      v-vuetify-ltr
      ...
    />
    

    Here it is in a snippet:

    Vue.directive(
      'vuetify-ltr', 
      (el, binding, vnode) => {
        const instance = vnode.componentInstance
        if(!instance.$vuetify) return
        instance.$vuetify = new Proxy(instance.$vuetify, {
          get(target, prop, receiver) {
            return prop === 'rtl' ? false : target[prop]
          },
        })
      }
    )
    
    new Vue({
      el: '#app',
      vuetify: new Vuetify({
        rtl: true,
      }),
    })
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
    
    <div id="app">
      <v-app>
        <v-main>
          <v-slider label="rlt"></v-slider>
          <v-slider label="ltr" v-vuetify-ltr></v-slider>
          <v-slider label="still rlt"></v-slider>
        </v-main>
      </v-app>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>