No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    Custom QCollapsible title content (excluding the toggle button)

    Help
    2
    4
    2155
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      dewdad last edited by

      My use case is having a side-nav with linkable (router-link) collapsible titles so essentially it would be just like a file explorer or menu pattern. Example here (OpenUI5).
      0_1511676484760_side-nav.png

      1 Reply Last reply Reply Quote 0
      • benoitranque
        benoitranque last edited by benoitranque

        My solution to this was to rebuild the q-collapsible. I do not have the time to go in detail at the moment.
        The behavior was unlimited nesting of the colapsible.

        Here is the code:

        // Usage in layout
        
        <sidebar-menu-item v-for="child in pages" :key="'/' + child.path.join('/')" :item="child" group="/" :itemlist="pages"></sidebar-menu-item>
        
        // SideBarMenuItem.vue
        
        <template lang="html">
          <div>
            <q-item class="q-item-link relative-position" v-ripple v-if="!item.children" @click="goToPage()">
              <q-item-main :label="$t(item.name)" />
            </q-item>
            <template v-else>
              <!-- Missing some sidelink classes, close panel after click -->
              <!-- <q-side-link item to="/">link</q-side-link> -->
              <q-item class="q-item-link relative-position" v-ripple @click="toggle() || goToPage()">
                <q-item-main :label="$t(item.name)"/>
                <q-item-tile
                @click.stop="toggle()"
                icon="keyboard_arrow_down"
                class="transition-generic"
                :class="{'rotate-180': item.expanded}"
                ></q-item-tile>
              </q-item>
              <q-slide-transition>
                <div v-show="item.expanded">
                  <div class="q-collapsible-sub-item relative-position indent">
                    <sidebar-menu-item v-for="child in item.children" :key="'/' + child.path.join('/')"  :item="child" :group="'/' + item.path.join('/')" :itemlist="itemlist"></sidebar-menu-item>
                  </div>
                </div>
              </q-slide-transition>
            </template>
          </div>
        </template>
        
        <script>
        import SidebarMenuItem from '@/SidebarMenuItem'
        import {
          Events,
          QList,
          QItem,
          QItemMain,
          QItemSide,
          QItemTile,
          QSlideTransition,
          Ripple
        } from 'quasar'
        
        const eventName = 'q:collapsible:close'
        
        export default {
          name: 'SidebarMenuItem',
          i18nOptions: { namespaces: ['comon'], keyPrefix: 'pages' },
          directives: {
            Ripple
          },
          components: {
            SidebarMenuItem,
            QList,
            QItem,
            QItemMain,
            QItemSide,
            QItemTile,
            QSlideTransition
          },
          props: {
            item: {
              required: true,
              type: Object
            },
            itemlist: {
              required: true,
              type: Array
            },
            group: {
              required: true,
              type: String
            }
          },
          inject: ['layout'],
          watch: {
            'item.expanded' (val) {
              if (val && this.group) {
                Events.$emit(eventName, this)
              }
              if (!val && this.item.children) {
                this.collapse(this.item.children)
              }
            }
          },
          methods: {
            goToPage () {
              this.collapse(this.itemlist)
              this.layout.hideRight(() => {
                this.$router.push(`/${this.item.path.join('/')}`)
              })
            },
            toggle () {
              this.item.expanded = !this.item.expanded
              return this.item.expanded
            },
            collapse (items) {
              items.forEach(item => {
                item.expanded = false
                if (item.children) {
                  this.collapse(item.children)
                }
              })
            },
            __eventHandler (comp) {
              if (this.group && this !== comp && comp.group === this.group) {
                this.item.expanded = false
              }
            }
          },
          created () {
            Events.$on(eventName, this.__eventHandler)
          },
          beforeDestroy () {
            Events.$off(eventName, this.__eventHandler)
          }
        }
        </script>
        
        <style lang="stylus" scoped>
        @import '~variables'
        .q-item
          color $primary
          user-select none
        .q-item.active,
        .q-item.router-link-active,
        .q-item:hover
          background-color rgba($secondary, 0.3)
          color $secondary
        </style>
        
        
        

        Play around with that, also take a look at the source for the collapsible.

        I’ll be glad to answer any questions later on

        1 Reply Last reply Reply Quote 1
        • D
          dewdad last edited by dewdad

          Thank you @benoitranque for the quick response. I ended up with this:

          <template>
            <div :class="[{'q-collapsible': this.$slots.content}, {'q-collapsible-closed': this.$slots.content && !expanded}, {'q-collapsible-opened': this.$slots.content && expanded}]">
              <q-item v-bind="this.$props">
                <slot/>
                <q-item-side right v-if="this.$slots.content" @click.stop="toggle" :icon="iconClass? ``: `keyboard_arrow_down`" />
              </q-item>
              <q-slide-transition v-if="this.$slots.content">
                <div v-show="expanded">
                  <div class="q-collapsible-sub-item relative-position">
                    <slot name="content"/>
                  </div>
                </div>
              </q-slide-transition>
            </div>
          </template>
          
          <script>
          import {
            QIcon,
            QList,
            QItem,
            QSideLink,
            QItemMain,
            QItemSide,
            QSlideTransition,
            QCollapsible
          } from 'quasar'
          
          // const eventName = 'q:collapsible:close'
          
          export default {
            mixins: [QSideLink, QCollapsible],
            name: 'QCompositeItem',
            components: {
              QIcon,
              QList,
              QItem,
              QSideLink,
              QItemMain,
              QItemSide,
              QSlideTransition
            },
            data () {
              return {
                expanded: this.opened || false
              }
            },
            props: ['iconClass'],
            methods: {
              toggle () {
                this.expanded = !this.expanded
              }
            },
            mounted () {
              this.iconToggle = true
            }
          }
          </script>
          

          And I use it like this:

                  <q-composite-item :to="{name: 'catalog'}">
                    <q-item-side class="fio-inspection" />
                    <q-item-main label="Service Catalog" />
                    <template slot="content">
                      <q-side-link item :to="{name: 'wish_list'}" key="wishlist">
                        <q-item-main label="Wish List" />
                      </q-side-link>
                    </template>
                  </q-composite-item>
                  <q-composite-item label="empty item">
                    <q-item-side left class="fio-inspection" />
                    <q-item-main label="Empty Label" />
                  </q-composite-item>
          

          This enables me to use item as a dynamic component that will know to link and/or collapse based on the “to” attribute and “content” slot. Also it reuses QCollapsible and QItem through mixins so there is not much code to upkeep.

          1 Reply Last reply Reply Quote 0
          • D
            dewdad last edited by

            One really annoying bug I have is that on narrow resolutions when the sidebar is hovered in click on the modal background (layout content portion) when I’m on the parent-nav-item route, the app unexpectedly routes to the the sub-nav-item route

            1 Reply Last reply Reply Quote 0
            • First post
              Last post