@@ -204,20 +204,31 @@ const currentSortOrder = ref(-1);
204204function restoreOrder () {
205205 isSorting .value = false ;
206206 currentSortingIndex .value = undefined ;
207+ currentAdditionalSort .value = undefined ;
207208 currentSortOrder .value = - 1 ;
208209 mutableDataset .value = datasetWithOrders .value ;
209210}
210211
211- function orderDatasetByIndex (index ) {
212- if (! hasSorting (index)) return ;
212+ function orderDatasetByIndex (th , index ) {
213213
214- if (currentSortOrder .value === 1 ) {
215- currentSortOrder .value = - 1 ;
216- currentSortingIndex .value = undefined ;
217- restoreOrder ();
214+ if (([' name' , ' sum' , ' average' , ' median' ].includes (th .type ))) {
215+ orderDatasetByAttribute (th .type );
218216 return ;
219217 }
220218
219+ if (! hasSorting (index)) return ;
220+
221+ selectedDataIndex .value = index;
222+ currentAdditionalSort .value = undefined ;
223+
224+ // Maybe for later:
225+ // if (currentSortOrder.value === 1) {
226+ // currentSortOrder.value = -1;
227+ // currentSortingIndex.value = undefined;
228+ // restoreOrder();
229+ // return;
230+ // }
231+
221232 isSorting .value = true ;
222233 currentSortingIndex .value = index;
223234
@@ -247,39 +258,78 @@ const maxSeriesLength = computed(() => {
247258})
248259
249260const colNames = computed (() => {
250- let cn = usableColNames .value ;
261+ let cn = usableColNames .value .map (c => {
262+ return {
263+ type: ' reg' ,
264+ value: c
265+ }
266+ });
251267
252268 if (! cn .length ) {
253269 for (let i = 0 ; i < maxSeriesLength .value ; i += 1 ) {
254- cn .push (` col ${ i+ 1 } ` )
270+ cn .push ({type : ' reg ' , value : ` col ${ i+ 1 } ` } )
255271 }
256272 }
257273
258274 if (FINAL_CONFIG .value .showTotal ) {
259- cn = [... cn, FINAL_CONFIG .value .translations .total ];
275+ cn = [... cn, { type : ' sum ' , value : FINAL_CONFIG .value .translations .total } ];
260276 }
261277
262278 let res;
263279 if (FINAL_CONFIG .value .showAverage && FINAL_CONFIG .value .showMedian ) {
264280 res = [
265281 ... cn,
266- FINAL_CONFIG .value .translations .average ,
267- FINAL_CONFIG .value .translations .median ,
282+ { type : ' average ' , value : FINAL_CONFIG .value .translations .average } ,
283+ { type : ' median ' , value : FINAL_CONFIG .value .translations .median }
268284 ];
269285 } else if (FINAL_CONFIG .value .showAverage && ! FINAL_CONFIG .value .showMedian ) {
270- res = [... cn, FINAL_CONFIG .value .translations .average ];
286+ res = [... cn, { type : ' average ' , value : FINAL_CONFIG .value .translations .average } ];
271287 } else if (! FINAL_CONFIG .value .showAverage && FINAL_CONFIG .value .showMedian ) {
272- res = [... cn, FINAL_CONFIG .value .translations .median ];
288+ res = [... cn, { type : ' median ' , value : FINAL_CONFIG .value .translations .median } ];
273289 } else {
274290 res = cn;
275291 }
276292 if (FINAL_CONFIG .value .showSparklines ) {
277- return [... res, FINAL_CONFIG .value .translations .chart ];
293+ return [... res, { type : ' chart ' , value : FINAL_CONFIG .value .translations .chart } ];
278294 } else {
279295 return res;
280296 }
281297});
282298
299+ const sortOrders = ref ({
300+ name: - 1 ,
301+ sum: - 1 ,
302+ average: - 1 ,
303+ median: - 1
304+ })
305+
306+ const currentAdditionalSort = ref (undefined );
307+
308+ function orderDatasetByAttribute (attribute ) {
309+ if (! mutableDataset .value || mutableDataset .value .length === 0 ) return ;
310+ if (! hasAdditionalSorting (attribute)) return ;
311+
312+ currentAdditionalSort .value = attribute;
313+ currentSortingIndex .value = undefined ;
314+ isSorting .value = true ;
315+
316+ sortOrders .value [attribute] = sortOrders .value [attribute] === - 1 ? 1 : - 1 ;
317+ const sortOrder = sortOrders .value [attribute];
318+
319+ const sortedDataset = [... mutableDataset .value ].sort ((a , b ) => {
320+ const aValue = a[attribute] ?? (typeof a[attribute] === ' number' ? 0 : ' ' );
321+ const bValue = b[attribute] ?? (typeof b[attribute] === ' number' ? 0 : ' ' );
322+
323+ if (typeof aValue === ' string' && typeof bValue === ' string' ) {
324+ return sortOrder === - 1 ? aValue .localeCompare (bValue) : bValue .localeCompare (aValue);
325+ } else if (typeof aValue === ' number' && typeof bValue === ' number' ) {
326+ return sortOrder === - 1 ? aValue - bValue : bValue - aValue;
327+ }
328+ return 0 ;
329+ });
330+ mutableDataset .value = sortedDataset;
331+ }
332+
283333const selectedDataIndex = ref (undefined );
284334const selectedSerieIndex = ref (undefined );
285335
@@ -319,7 +369,31 @@ function generateCsv() {
319369}
320370
321371function hasSorting (index ) {
322- return FINAL_CONFIG .value .sortedColIndices .includes (index);
372+ return FINAL_CONFIG .value .sortedDataColumnIndices .includes (index);
373+ }
374+
375+ function hasAdditionalSorting (th ) {
376+ if (th .type === ' name' || th === ' name' ) {
377+ return FINAL_CONFIG .value .sortedSeriesName ;
378+ }
379+ if (th .type === ' sum' || th === ' sum' ) {
380+ return FINAL_CONFIG .value .sortedSum ;
381+ }
382+ if (th .type === ' average' || th === ' average' ) {
383+ return FINAL_CONFIG .value .sortedAverage ;
384+ }
385+ if (th .type === ' median' || th === ' median' ) {
386+ return FINAL_CONFIG .value .sortedMedian ;
387+ }
388+ return false ;
389+ }
390+
391+ function getArrowOpacity (index , th ) {
392+ if ([' sum' , ' average' , ' median' ].includes (th .type )) {
393+ return currentAdditionalSort .value === th .type ? 1 : 0.3 ;
394+ } else {
395+ return index === currentSortingIndex .value ? 1 : 0.3 ;
396+ }
323397}
324398
325399defineExpose ({
@@ -371,18 +445,38 @@ defineExpose({
371445 border: FINAL_CONFIG.thead.outline,
372446 textAlign: FINAL_CONFIG.thead.textAlign,
373447 fontWeight: FINAL_CONFIG.thead.bold ? 'bold' : 'normal',
374- }" class = " sticky-col-first" >
375- {{ FINAL_CONFIG .translations .serie }}
448+ cursor: FINAL_CONFIG.sortedSeriesName ? 'pointer' : 'default'
449+ }" class = " sticky-col-first" @click= " orderDatasetByIndex({type: 'name'}, null)" >
450+ < div
451+ : style= " {
452+ display: 'flex',
453+ flexDirection: 'row',
454+ alignItems: 'center',
455+ gap: '6px',
456+ justifyContent: FINAL_CONFIG.thead.textAlign
457+ }" >
458+ < span> {{ FINAL_CONFIG .translations .serie }}< / span>
459+
460+ < BaseIcon
461+ : size= " 18"
462+ v- if = " FINAL_CONFIG.sortedSeriesName"
463+ : name= " 'sort'"
464+ : stroke= " FINAL_CONFIG.thead.color"
465+ : style= " {
466+ opacity: currentAdditionalSort === 'name' ? 1 : 0.3
467+ }"
468+ / >
469+ < / div>
376470 < / th>
377471 < th role= " cell" v- for = " (th, i) in colNames" : style= " {
378472 background: FINAL_CONFIG.thead.backgroundColor,
379473 border: FINAL_CONFIG.thead.outline,
380474 textAlign: FINAL_CONFIG.thead.textAlign,
381475 fontWeight: FINAL_CONFIG.thead.bold ? 'bold' : 'normal',
382476 minWidth: i === colNames.length - 1 ? `${FINAL_CONFIG.sparkline.dimensions.width}px` : '48px',
383- cursor: hasSorting(i) ? 'pointer' : 'default',
477+ cursor: hasSorting(i) || hasAdditionalSorting(th) ? 'pointer' : 'default',
384478 paddingRight: i === colNames.length - 1 && FINAL_CONFIG.userOptions.show ? '36px' : '',
385- }" @click= " () => orderDatasetByIndex(i)" : class = " {'sticky-col': i === colNames.length - 1 && FINAL_CONFIG.showSparklines}"
479+ }" @click= " () => orderDatasetByIndex(th, i)" : class = " {'sticky-col': i === colNames.length - 1 && FINAL_CONFIG.showSparklines}"
386480 >
387481 < div
388482 : style= " {
@@ -392,16 +486,15 @@ defineExpose({
392486 gap: '6px',
393487 justifyContent: FINAL_CONFIG.thead.textAlign
394488 }" >
395- < span> {{ th }}< / span>
489+ < span> {{ th . value }}< / span>
396490
397- < slot name= " arrow" v- if = " $slots.arrow && hasSorting(i)" v- bind= " { isSorted: i === currentSortingIndex }" / >
398491 < BaseIcon
399492 : size= " 18"
400- v- else - if = " hasSorting(i)"
493+ v- if = " hasSorting(i) || hasAdditionalSorting(th )"
401494 : name= " 'sort'"
402495 : stroke= " FINAL_CONFIG.thead.color"
403496 : style= " {
404- opacity: i === currentSortingIndex ? 1 : 0.3
497+ opacity: getArrowOpacity(i, th)
405498 }"
406499 / >
407500 < / div>
0 commit comments