Skip to content

Commit c4442f4

Browse files
authored
feat(b-table): new sorting icons using SVG, plus option to place icon on left of header cell (closes #3687, #3696, #3918, #3966) (#3968)
1 parent ee13491 commit c4442f4

File tree

4 files changed

+178
-42
lines changed

4 files changed

+178
-42
lines changed

src/_variables.scss

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,17 @@ $b-custom-file-height-inner-sm: calc(
6565
$b-table-busy-opacity: 0.55 !default;
6666

6767
// Table sorting
68-
$b-table-sort-icon-null: "\2195" !default; // Up-Down arrow
69-
$b-table-sort-icon-ascending: "\2193" !default; // Down arrow
70-
$b-table-sort-icon-descending: "\2191" !default; // Up arrow
71-
$b-table-sort-icon-margin-left: 0.5em !default;
72-
$b-table-sort-icon-width: 0.5em !default;
68+
$b-table-sort-icon-bg-width: 0.65em !default;
69+
$b-table-sort-icon-bg-height: 1em !default;
70+
// Sort icons are square, but "squished" horizontally by the above variables
71+
$b-table-sort-icon-bg-not-sorted: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='101' height='101' view-box='0 0 101 101' preserveAspectRatio='none'><path fill='black' opacity='.3' d='M51 1l25 23 24 22H1l25-22zM51 101l25-23 24-22H1l25 22z'/></svg>") !default;
72+
$b-table-sort-icon-bg-ascending: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='101' height='101' view-box='0 0 101 101' preserveAspectRatio='none'><path fill='black' d='M51 1l25 23 24 22H1l25-22z'/><path fill='black' opacity='.3' d='M51 101l25-23 24-22H1l25 22z'/></svg>") !default;
73+
$b-table-sort-icon-bg-descending: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='101' height='101' view-box='0 0 101 101' preserveAspectRatio='none'><path fill='black' opacity='.3' d='M51 1l25 23 24 22H1l25-22z'/><path fill='black' d='M51 101l25-23 24-22H1l25 22z'/></svg>") !default;
74+
// Icons to use on dark table or dark header/footer (lighter color icons)
75+
// We simply just replace the fill color 'black' white 'white'
76+
$b-table-sort-icon-bg-dark-not-sorted: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='101' height='101' view-box='0 0 101 101' preserveAspectRatio='none'><path fill='white' opacity='.3' d='M51 1l25 23 24 22H1l25-22zM51 101l25-23 24-22H1l25 22z'/></svg>") !default;
77+
$b-table-sort-icon-bg-dark-ascending: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='101' height='101' view-box='0 0 101 101' preserveAspectRatio='none'><path fill='white' d='M51 1l25 23 24 22H1l25-22z'/><path fill='white' opacity='.3' d='M51 101l25-23 24-22H1l25 22z'/></svg>") !default;
78+
$b-table-sort-icon-bg-dark-descending: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='101' height='101' view-box='0 0 101 101' preserveAspectRatio='none'><path fill='white' opacity='.3' d='M51 1l25 23 24 22H1l25-22z'/><path fill='white' d='M51 101l25-23 24-22H1l25 22z'/></svg>") !default;
7379

7480
// Flag to enable sticky table header and column CSS generation
7581
$bv-enable-table-sticky: true !default;

src/components/table/README.md

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,21 +1658,20 @@ rows.
16581658
## Sorting
16591659

16601660
As mentioned in the [Fields](#fields-column-definitions) section above, you can make columns
1661-
sortable. Clicking on a sortable column header will sort the column in ascending direction (smallest
1662-
first), while clicking on it again will switch the direction of sorting. Clicking on a non-sortable
1663-
column will clear the sorting. The prop `no-sort-reset` can be used to disable this feature.
1661+
sortable in `<b-table>`. Clicking on a sortable column header will sort the column in ascending
1662+
direction (smallest first), while clicking on it again will switch the direction of sorting to
1663+
descending (largest first). Clicking on a non-sortable column will clear the sorting (the prop
1664+
`no-sort-reset` can be used to disable this feature).
16641665

16651666
You can control which column is pre-sorted and the order of sorting (ascending or descending). To
16661667
pre-specify the column to be sorted, set the `sort-by` prop to the field's key. Set the sort
16671668
direction by setting `sort-desc` to either `true` (for descending) or `false` (for ascending, the
16681669
default).
16691670

16701671
- **Ascending**: Items are sorted lowest to highest (i.e. `A` to `Z`) and will be displayed with the
1671-
lowest value in the first row with progressively higher values in the following rows. The header
1672-
indicator arrow will point in the direction of lowest to highest. (i.e. down for ascending).
1672+
lowest value in the first row with progressively higher values in the following rows.
16731673
- **Descending**: Items are sorted highest to lowest (i.e. `Z` to `A`) and will be displayed with
1674-
the highest value in the first row with progressively lower values in the following rows. The
1675-
header indicator arrow will point in the direction of lowest to highest (i.e. up for descending).
1674+
the highest value in the first row with progressively lower values in the following rows.
16761675

16771676
The props `sort-by` and `sort-desc` can be turned into _two-way_ (syncable) props by adding the
16781677
`.sync` modifier. Your bound variables will then be updated accordingly based on the current sort
@@ -1730,6 +1729,63 @@ clicks in the footer, set the `no-footer-sorting` prop to true.
17301729
<!-- b-table-sorting.vue -->
17311730
```
17321731

1732+
### Sort icon alignment
1733+
1734+
By default the sorting icons appear right aligned in the header cell. You can change the icons to be
1735+
left aligned by setting the prop `sort-icon-left` on `<b-table>`.
1736+
1737+
```html
1738+
<template>
1739+
<div>
1740+
<b-table
1741+
:items="items"
1742+
:fields="fields"
1743+
:sort-by.sync="sortBy"
1744+
:sort-desc.sync="sortDesc"
1745+
sort-icon-left
1746+
responsive="sm"
1747+
></b-table>
1748+
1749+
<div>
1750+
Sorting By: <b>{{ sortBy }}</b>, Sort Direction:
1751+
<b>{{ sortDesc ? 'Descending' : 'Ascending' }}</b>
1752+
</div>
1753+
</div>
1754+
</template>
1755+
1756+
<script>
1757+
export default {
1758+
data() {
1759+
return {
1760+
sortBy: 'age',
1761+
sortDesc: false,
1762+
fields: [
1763+
{ key: 'last_name', sortable: true },
1764+
{ key: 'first_name', sortable: true },
1765+
{ key: 'age', sortable: true },
1766+
{ key: 'isActive', sortable: false }
1767+
],
1768+
items: [
1769+
{ isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
1770+
{ isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
1771+
{ isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
1772+
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
1773+
]
1774+
}
1775+
}
1776+
}
1777+
</script>
1778+
1779+
<!-- b-table-sorting-left.vue -->
1780+
```
1781+
1782+
### Customizing the sort icons
1783+
1784+
The sorting icons are generated via the use of SVG background images. The icons can be altered by
1785+
updating SASS/SCSS variables and recompiling the SCSS source code. Refer to the
1786+
[theming](/docs/reference/theming) section for details on customizing Bootstrap and BootstrapVue's
1787+
CSS.
1788+
17331789
### Sort-compare routine
17341790

17351791
The internal built-in default `sort-compare` function sorts the specified field `key` based on the

src/components/table/_table.scss

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -185,40 +185,108 @@
185185

186186
// --- Header sort styling ---
187187

188+
// Bootstrap v4.4 will include this variable as `$escaped-characters`
189+
// But if we want to preserve backwards compatibility with v4.3, we leave this in
190+
$bv-escaped-characters: (("<", "%3c"), (">", "%3e"), ("#", "%23"));
191+
192+
// Bootstrap v4.4 will include this method as `escape-svg`
193+
// But if we want to preserve backwards compatibility with v4.3, we leave this in
194+
// See https://codepen.io/kevinweber/pen/dXWoRw
195+
@function bv-escape-svg($string) {
196+
@if str-index($string, "data:image/svg+xml") {
197+
@each $char, $encoded in $bv-escaped-characters {
198+
$string: str-replace($string, $char, $encoded);
199+
}
200+
}
201+
202+
@return $string;
203+
}
204+
188205
.table.b-table {
189206
> thead,
190207
> tfoot {
191208
> tr {
192-
> th {
193-
&[aria-sort] {
194-
// `&.sorting`
195-
cursor: pointer;
196-
197-
// Up/down `sort=null` indicator
198-
&::before {
199-
float: right;
200-
margin-left: $b-table-sort-icon-margin-left;
201-
width: $b-table-sort-icon-width;
202-
font-size: inherit;
203-
line-height: inherit;
204-
opacity: 0.4;
205-
content: $b-table-sort-icon-null; // Up/down arrow
206-
speak: none;
207-
}
209+
> [aria-sort] {
210+
cursor: pointer;
211+
background-image: none;
212+
background-repeat: no-repeat;
213+
background-size: $b-table-sort-icon-bg-width $b-table-sort-icon-bg-height;
214+
215+
&:not(.b-table-sort-icon-left) {
216+
// Default is icon on the right
217+
background-position: right calc(#{$table-cell-padding} / 2) center;
218+
padding-right: calc(#{$table-cell-padding} + #{$b-table-sort-icon-bg-width});
219+
}
208220

209-
// Ascending indicator
210-
&[aria-sort="ascending"]::before {
211-
// `&.sorting_asc::after.sorting_asc`
212-
opacity: 1;
213-
content: $b-table-sort-icon-ascending; // Down arrow
214-
}
221+
&.b-table-sort-icon-left {
222+
// Left aligned sort icon
223+
background-position: left calc(#{$table-cell-padding} / 2) center;
224+
padding-left: calc(#{$table-cell-padding} + #{$b-table-sort-icon-bg-width});
225+
}
226+
}
215227

216-
// Descending indicator
217-
&[aria-sort="descending"]::before {
218-
// `&.sorting_desc::after`
219-
opacity: 1;
220-
content: $b-table-sort-icon-descending; // Up arrow
221-
}
228+
> [aria-sort="none"] {
229+
background-image: bv-escape-svg($b-table-sort-icon-bg-not-sorted);
230+
}
231+
232+
> [aria-sort="ascending"] {
233+
background-image: bv-escape-svg($b-table-sort-icon-bg-ascending);
234+
}
235+
236+
> [aria-sort="descending"] {
237+
background-image: bv-escape-svg($b-table-sort-icon-bg-descending);
238+
}
239+
}
240+
}
241+
242+
// Sort icons for dark tables, headers, footers
243+
&.table-dark > thead > tr,
244+
&.table-dark > tfoot > tr,
245+
> .thead-dark > tr {
246+
> [aria-sort="none"] {
247+
background-image: bv-escape-svg($b-table-sort-icon-bg-dark-not-sorted);
248+
}
249+
250+
> [aria-sort="ascending"] {
251+
background-image: bv-escape-svg($b-table-sort-icon-bg-dark-ascending);
252+
}
253+
254+
> [aria-sort="descending"] {
255+
background-image: bv-escape-svg($b-table-sort-icon-bg-dark-descending);
256+
}
257+
}
258+
259+
// Sort icons when header cell has `table-dark` class
260+
> thead > tr > .table-dark,
261+
> tfoot > tr > .table-dark {
262+
&[aria-sort="none"] {
263+
background-image: bv-escape-svg($b-table-sort-icon-bg-dark-not-sorted);
264+
}
265+
266+
&[aria-sort="ascending"] {
267+
background-image: bv-escape-svg($b-table-sort-icon-bg-dark-ascending);
268+
}
269+
270+
&[aria-sort="descending"] {
271+
background-image: bv-escape-svg($b-table-sort-icon-bg-dark-descending);
272+
}
273+
}
274+
275+
// Padding and position adjustment for small tables
276+
&.table-sm {
277+
> thead,
278+
> tfoot {
279+
> tr > [aria-sort] {
280+
&:not(.b-table-sort-icon-left) {
281+
// Default is icon on the right
282+
background-position: right calc(#{$table-cell-padding-sm} / 2) center;
283+
padding-right: calc(#{$table-cell-padding-sm} + #{$b-table-sort-icon-bg-width});
284+
}
285+
286+
&.b-table-sort-icon-left {
287+
// Left aligned sort icon
288+
background-position: left calc(#{$table-cell-padding-sm} / 2) center;
289+
padding-left: calc(#{$table-cell-padding-sm} + #{$b-table-sort-icon-bg-width});
222290
}
223291
}
224292
}

src/components/table/helpers/mixin-sorting.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ export default {
7373
noFooterSorting: {
7474
type: Boolean,
7575
default: false
76+
},
77+
sortIconLeft: {
78+
// Place the sorting icon on the left of the header cells
79+
type: Boolean,
80+
default: false
7681
}
7782
},
7883
data() {
@@ -226,8 +231,9 @@ export default {
226231
// methods to compute classes and attrs for thead>th cells
227232
sortTheadThClasses(key, field, isFoot) {
228233
return {
229-
// No Classes for sorting currently...
230-
// All styles targeted using aria-* attrs
234+
// If sortable and sortIconLeft are true, then place sort icon on the left
235+
'b-table-sort-icon-left':
236+
field.sortable && this.sortIconLeft && !(isFoot && this.noFooterSorting)
231237
}
232238
},
233239
sortTheadThAttrs(key, field, isFoot) {

0 commit comments

Comments
 (0)