Skip to content

Conversation

@devhus
Copy link
Contributor

@devhus devhus commented Sep 9, 2022

BEGIN_COMMIT_OVERRIDE
BREAKING CHANGES: The content of BTable items prop will not be updated anymore! The table now uses an internal object for handling item manipulation when it is required in cases such as Table Sorting, Table Filtering, and Pagination.

feat(BTab): added a new lazyOnce prop, The prop works similarly to the lazy prop but it mounts the child component only once.
feat(BTable): implemented per page prop functionality.
feat(BTable): added functionality to current-page prop, The prop shows a specific page of the given items when the per-page prop is passed.
feat(BTable): added filter prop to filter the table content.
feat(BTable): added filterable prop to specify which fields should be filtered, this prop is known in bs-vue2 as filter-included-fields.
feat(Btable): sortable tables now have the sort icon added using CSS which makes them now globally customizable through CSS.

fix(BTable): Fixed sorting issues where sorting had no effect when trying to sort an object-formed column that includes multiple properties (ex: name: { first: 'Dickerson', last: 'Macdonald' }).
fix(BTable): fixed incorrect responsive-class prop name which was disabling the responsive functionality.
fix(BTable): the header display changed to be inline to enable more customizations such as centering the header text using CSS.
END_COMMIT_OVERRIDE

In this PR the BTable component will be updated with all of the advanced features such as Filtering, Pagination, Fixes for item manipulation, and the core logic of the component.
This PR might have many BREAKING CHANGES and a high impact on the component's logic.

I am going to try to get the full example of BTable of BootstrapVue2 to be fully working with this library, Which includes the same props, outputs, and behavior.

BootstrapVue2 BTable full example playground:
https://codesandbox.io/s/restless-cookies-8367fy?file=/src/App.vue

The working example:
image

<template>
  <b-container class="py-5">
    <!-- User Interface controls -->
    <b-row>
      <b-col lg="6" class="my-1">
        <b-form-group label="Sort" label-for="sort-by-select" label-cols-sm="3" label-align-sm="right" label-size="sm"
          class="mb-0" v-slot="{ ariaDescribedby }">
          <b-input-group size="sm">
            <b-form-select id="sort-by-select" v-model="sortBy" :options="sortOptions"
              :aria-describedby="ariaDescribedby" class="w-75">
              <template #first>
                <option value="">-- none --</option>
              </template>
            </b-form-select>

            <b-form-select v-model="sortDesc" :disabled="!sortBy" :aria-describedby="ariaDescribedby" size="sm"
              class="w-25">
              <option :value="false">Asc</option>
              <option :value="true">Desc</option>
            </b-form-select>
          </b-input-group>
        </b-form-group>
      </b-col>


      <b-col lg="6" class="my-1">
        <b-form-group label="Filter" label-for="filter-input" label-cols-sm="3" label-align-sm="right" label-size="sm"
          class="mb-0">
          <b-input-group size="sm">
            <b-form-input id="filter-input" v-model="filter" type="search" placeholder="Type to Search"></b-form-input>

            <b-input-group-append>
              <b-button :disabled="!filter" @click="filter = ''">Clear</b-button>
            </b-input-group-append>
          </b-input-group>
        </b-form-group>
      </b-col>

      <b-col lg="6" class="my-1">
        <b-form-group v-model="sortDirection" label="Filter On" description="Leave all unchecked to filter on all data"
          label-cols-sm="3" label-align-sm="right" label-size="sm" class="mb-0" v-slot="{ ariaDescribedby }">
          <div class="d-flex gap-2">
            <b-form-checkbox v-model="filterOn" value="name">Name</b-form-checkbox>
            <b-form-checkbox v-model="filterOn" value="age">Age</b-form-checkbox>
            <b-form-checkbox v-model="filterOn" value="isActive">Active</b-form-checkbox>
          </div>
        </b-form-group>
      </b-col>

      <b-col sm="5" md="6" class="my-1">
        <b-form-group label="Per page" label-for="per-page-select" label-cols-sm="6" label-cols-md="4" label-cols-lg="3"
          label-align-sm="right" label-size="sm" class="mb-0">
          <b-form-select id="per-page-select" v-model="perPage" :options="pageOptions" size="sm"></b-form-select>
        </b-form-group>
      </b-col>

      <b-col sm="7" md="6" class="my-1">
        <b-pagination v-model="currentPage" :total-rows="totalRows" :per-page="perPage" align="fill" size="sm"
          class="my-0"></b-pagination>
      </b-col>
    </b-row>

    <!-- Main table element -->
    <b-table :items="items" :fields="fields" :current-page="currentPage" :per-page="perPage" :filter="filter" responsive
      :filterable="filterOn" v-model:sort-by="sortBy" v-model:sort-desc="sortDesc" small @filtered="onFiltered"
      :sort-internal="true">
      <template #cell(name)="row">
        {{ row.value.first }} {{ row.value.last }}
      </template>

      <template #cell(actions)="row">
        <b-button size="sm" @click="info(row.item, row.index, $event.target)" class="mr-1">
          Info modal
        </b-button>
        <b-button size="sm" @click="row.toggleDetails">
          {{ row.detailsShowing ? 'Hide' : 'Show' }} Details
        </b-button>
      </template>

      <template #row-details="row">
        <b-card>
          <ul>
            <li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value }}</li>
            <b-button size="sm" @click="row.toggleDetails">
              Toggle Details
            </b-button>
          </ul>
        </b-card>
      </template>
    </b-table>

    <!-- Info modal -->
    <b-modal v-model="infoModal.open" :id="infoModal.id" :title="infoModal.title" ok-only @hide="resetInfoModal">
      <pre>{{ infoModal.content }}</pre>
    </b-modal>

  </b-container>
</template>

<script  lang="ts">
import { defineComponent } from "vue";
import { ColorVariant } from "./types";

export default defineComponent({
  data() {
    return {
      items: [
        { isActive: true, age: 40, name: { first: 'Dickerson', last: 'Macdonald' } },
        { isActive: false, age: 21, name: { first: 'Larsen', last: 'Shaw' } },
        {
          isActive: false,
          age: 9,
          name: { first: 'Mini', last: 'Navarro' },
          _rowVariant: 'success' as ColorVariant
        },
        { isActive: false, age: 89, name: { first: 'Geneva', last: 'Wilson' } },
        { isActive: true, age: 38, name: { first: 'Jami', last: 'Carney' } },
        { isActive: false, age: 27, name: { first: 'Essie', last: 'Dunlap' } },
        { isActive: true, age: 40, name: { first: 'Thor', last: 'Macdonald' } },
        {
          isActive: true,
          age: 87,
          name: { first: 'Larsen', last: 'Shaw' },
          _cellVariants: { age: 'danger', isActive: 'warning' } as Record<any, any>
        },
        { isActive: false, age: 26, name: { first: 'Mitzi', last: 'Navarro' } },
        { isActive: false, age: 22, name: { first: 'Genevieve', last: 'Wilson' } },
        { isActive: true, age: 38, name: { first: 'John', last: 'Carney' } },
        { isActive: false, age: 29, name: { first: 'Dick', last: 'Dunlap' } }
      ],
      fields: [
        { key: 'name', label: 'Person full name', sortable: true, sortDirection: 'desc' },
        { key: 'age', label: 'Person age', sortable: true, class: 'text-center' },
        {
          key: 'isActive',
          label: 'Is Active',
          formatter: (value: any, key: any, item: any) => {
            return value ? 'Yes' : 'No'
          },
          sortable: true,
          sortByFormatted: true,
          filterByFormatted: true
        },
        { key: 'actions', label: 'Actions' }
      ],
      totalRows: 1,
      currentPage: 1,
      perPage: 5,
      pageOptions: [5, 10, 15, { value: 100, text: "Show a lot" }],
      sortBy: '',
      sortDesc: false,
      sortDirection: 'asc',
      filter: '',
      filterOn: [],
      infoModal: {
        open: false,
        id: 'info-modal',
        title: '',
        content: ''
      }
    }
  },
  computed: {
    sortOptions() {
      // Create an options list from our fields
      return this.fields
        .filter(f => f.sortable)
        .map(f => {
          return { text: f.label, value: f.key }
        })
    }
  },
  mounted() {
    // Set the initial number of items
    this.totalRows = this.items.length
  },
  methods: {
    info(item: any, index: any, button: any) {
      this.infoModal.title = `Row index: ${index}`
      this.infoModal.content = JSON.stringify(item, null, 2)
      this.infoModal.open = true;
    },
    resetInfoModal() {
      this.infoModal.title = ''
      this.infoModal.content = ''
    },
    onFiltered(filteredItems: any) {
      // Trigger pagination to update the number of buttons/pages due to filtering
      this.totalRows = filteredItems.length
      this.currentPage = 1
    }
  }
});
</script>

Btab example

<b-tabs content-class="mt-3">
        <b-tab title="First" active>
          <p>I'm the first tab</p>
        </b-tab>
        <b-tab title="Second" lazy>
          <RenderedEveryTime></RenderedEveryTime>
        </b-tab>
        <b-tab title="Disabled" :lazyOnce="true">
          <RenderedJustOnce></RenderedJustOnce>
        </b-tab>
      </b-tabs>

After clicking the tabs in order multiple times
output:
image

…table now uses an internal object for the items when items manipulation is required in cases such as Table Sorting, Table Filtering, Pagination
@VividLemon
Copy link
Member

In addition, could you review the issue in #642 related to the BTable component? I mentioned it may be because of a function wrapper, but it seems it's losing reactivity.

…zy prop but it mounts the child component only once.
@devhus
Copy link
Contributor Author

devhus commented Sep 11, 2022

@VividLemon I think that will be it for this PR, I believe that the table now behaves more like BS-Vue2 with a bit more flavor.
Feel free to review the commits.

@VividLemon
Copy link
Member

Once #642 has a solution I will review this further. It's an active issue and merging one or the other PR will be a merge conflict. Not that it's a big deal, just best to be on the same page and all finding a solution.

@devhus
Copy link
Contributor Author

devhus commented Sep 11, 2022

Once #642 has a solution I will review this further. It's an active issue and merging one or the other PR will be a merge conflict. Not that it's a big deal, just best to be on the same page and all finding a solution.

I am not really sure what is the issue in #642 since the row.details toggle is working fine with me since the last release, However, if there is an issue it might get fixed with the recent changes because it is working as you can see in the video below:
https://www.loom.com/share/879533b7625b4b65a7f36ca7e6478a53

We can still wait for a bit from the issue creator to give me an example code that has the issue or at least explain how to replicate it.

@aceofwings
Copy link
Contributor

Once #642 has a solution I will review this further. It's an active issue and merging one or the other PR will be a merge conflict. Not that it's a big deal, just best to be on the same page and all finding a solution.

I am not really sure what is the issue in #642 since the row.details toggle is working fine with me since the last release, However, if there is an issue it might get fixed with the recent changes because it is working as you can see in the video below: https://www.loom.com/share/879533b7625b4b65a7f36ca7e6478a53

We can still wait for a bit from the issue creator to give me an example code that has the issue or at least explain how to replicate it.

Interesting, can you send a your min example. I attempted to add toggle functionality as followed in this file

Maybe its vuepress?

@VividLemon
Copy link
Member

VividLemon commented Sep 12, 2022

Perhaps it is best to merge this large change, then solve the issue. As we'd all be working with the same file

Obviously by "we", I mean you two. Just me totally taking credit

@nneto
Copy link

nneto commented Nov 7, 2022

Based on this example, when filtering the table, the hidden rows that are used for expanding/collapsing more details are gone. Is there a way to prevent that? Allowing expanding the rows to show more info even while filtering...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants