PivotTable Timeline Slicer

SpreadJS PivotTables supports timeline slicers, which can carry out date-slicing operations conveniently.

Try clicking on slicers in the below demo to see all of the available properties.

Description
app.vue
index.html
Copy to CodeMine

The Pivot Table Timeline Slicer is managed by SlicerCollection(WorkSheet.slicers), same as the Table Slicers.

Timeline Slicers can only be added to date field. The changes made on Timeline Slicers are the same as using the condition filter, which means "condition" in the label filter.

Add Timeline

If we want to add a Pivot Table Timeline Slicer, create a PivotTable named "pt" (The specific implementation of initPivotTable can be found at the end of the article.):

    var spread = GC.Spread.Sheets.findControl(document.getElementById('ss'));
    var sheet = spread.getActiveSheet();
    initPivotTable();

then add an Timeline slicer to the "date" field.

    var timeline_date = sheet.slicers.add("timeline_date", pt.name(), "date", GC.Spread.Sheets.Slicers.TimelineStyles.light1().name(), GC.Spread.Sheets.Slicers.SlicerType.pivotTimeline);

Using Timeline

Then we can control the timeline_date: For example, if you don't need to show the horizontal scrollbar:

    timeline_date.showHorizontalScrollbar(false);

If you want to scroll the timeline to some specific day:

    timeline_date.scrollPosition(new Date('2021-6-23'));

If you want to filter by year:

    timeline_date.level(GC.Spread.Sheets.Slicers.TimelineLevel.years);

A Sample to Create a Pivot Table

function initPivotTable () {
    var dataSource = [
        [ "name", "product", "date", "amount", "price", "sales" ],
        [ "chris", "desk", new Date("2020-10-08T16:00:00.000Z"), 5, 199, 995 ],
        [ "radow", "pen", new Date("2020-09-15T16:00:00.000Z"), 2, 5, 10 ],
        [ "peyton", "pencil", new Date("2021-06-22T16:00:00.000Z"), 6, 1.5, 9 ],
        [ "johnson", "chair", new Date("2021-07-19T16:00:00.000Z"), 7, 68, 476 ],
        [ "vic", "notebook", new Date("2021-01-13T16:00:00.000Z"), 7, 3.2, 22.4 ],
        [ "lan", "desk", new Date("2021-03-12T16:00:00.000Z"), 9, 199, 1791 ],
        [ "chris", "pen", new Date("2021-03-06T16:00:00.000Z"), 4, 5, 20 ],
        [ "chris", "pencil", new Date("2020-09-02T16:00:00.000Z"), 10, 1.5, 15 ],
        [ "radow", "chair", new Date("2020-08-09T16:00:00.000Z"), 3, 68, 204 ],
        [ "peyton", "notebook", new Date("2021-02-08T16:00:00.000Z"), 9, 3.2, 28.8 ],
        [ "johnson", "desk", new Date("2021-07-03T16:00:00.000Z"), 7, 199, 1393],
        [ "vic", "pen", new Date("2021-06-27T16:00:00.000Z"), 8, 5, 40],
        [ "lan", "pencil", new Date("2020-10-10T16:00:00.000Z"), 2, 1.5, 3],
        [ "chris", "chair", new Date("2021-03-04T16:00:00.000Z"), 2, 68, 136],
        [ "chris", "notebook", new Date("2021-02-21T16:00:00.000Z"), 11, 3.2, 35.2],
        [ "radow", "desk", new Date("2021-06-03T16:00:00.000Z"), 6, 199, 1194]
    ];
    var sourceSheet = spread.sheets[0];
    sourceSheet.setArray(0, 0, dataSource);
    sourceSheet.tables.add("table1", 0, 0, 17, 6);
    spread.sheets[0].name("sourceSheet");

    var pivotSheet = new GC.Spread.Sheets.Worksheet('pivotTable1');
    pivotSheet.setRowCount(2000);
    pivotSheet.setColumnCount(30);
    spread.addSheet(1, pivotSheet);

    var pt = pivotSheet.pivotTables.add("pivotTable1", "table1", 1, 1, GC.Spread.Pivot.PivotTableLayoutType.compact, GC.Spread.Pivot.PivotTableThemes.medium1.name(), null);
    pt.suspendLayout();
    pt.add("name", "name", GC.Spread.Pivot.PivotTableFieldType.rowField);
    pt.add("product", "product", GC.Spread.Pivot.PivotTableFieldType.rowField);
    pt.group({
        originFieldName: 'date',
        dateGroups: [
            {
                by: GC.Pivot.DateGroupType.years
            },
            {
                by: GC.Pivot.DateGroupType.quarters
            },
            {
                by: GC.Pivot.DateGroupType.months
            },
        ]
    });
    pt.add("Quarters (date)", "Quarters (date)", GC.Spread.Pivot.PivotTableFieldType.columnField);
    pt.add("sales", "sales", GC.Spread.Pivot.PivotTableFieldType.valueField, GC.Pivot.SubtotalType.sum);
    pt.resumeLayout();
    pt.autoFitColumn();
    spread.setActiveSheet("pivotTable1");
}

Custom Timeline Slicer Themes

SpreadJS supports customize the theme of the pivot table timeline slicer. Using code such as the following:

    const customTheme = new GC.Spread.Sheets.Slicers.TimelineStyle();
    customTheme.name("custom0");
    const wholeSlicerStyle = new GC.Spread.Sheets.Slicers.SlicerStyleInfo('red', 'white', '16px Calibri');
    customTheme.wholeSlicerStyle(wholeSlicerStyle);
    spread.customTimelineThemes.add(customTheme);

    slicer.style("custom0");

    slicer.getStyleName();  // "custom0"

    // set default table theme
    spread.defaultTimelineTheme('custom0');
The Pivot Table Timeline Slicer is managed by SlicerCollection(WorkSheet.slicers), same as the Table Slicers. Timeline Slicers can only be added to date field. The changes made on Timeline Slicers are the same as using the condition filter, which means "condition" in the label filter. Add Timeline If we want to add a Pivot Table Timeline Slicer, create a PivotTable named "pt" (The specific implementation of initPivotTable can be found at the end of the article.): then add an Timeline slicer to the "date" field. Using Timeline Then we can control the timeline_date: For example, if you don't need to show the horizontal scrollbar: If you want to scroll the timeline to some specific day: If you want to filter by year: A Sample to Create a Pivot Table Custom Timeline Slicer Themes SpreadJS supports customize the theme of the pivot table timeline slicer. Using code such as the following:
<template> <div class="sample-tutorial"> <gc-spread-sheets class="sample-spreadsheets" @workbookInitialized="initSpread" > </gc-spread-sheets> <div class="options-container"> <div class="block slicer-infos"> <div>Current Selected Timeline Info:</div> <br /> <div class="slicer-info"> <input type="checkbox" id="showHeader" v-model="showHeaderRef" @change="changeProperty('showHeader')" /> <label for="showHeader">Show Header</label> </div> <div class="slicer-info"> <input type="checkbox" id="showHorizontalScrollbar" v-model="showHorizontalScrollbarRef" @change="changeProperty('showHorizontalScrollbar')" /> <label for="showHorizontalScrollbar">Show Horizontal Scrollbar</label> </div> <div class="slicer-info"> <input type="checkbox" id="showSelectionLabel" v-model="showSelectionLabelRef" @change="changeProperty('showSelectionLabel')" /> <label for="showSelectionLabel" >Show Selection Label</label > </div> <div class="slicer-info"> <input type="checkbox" id="showTimeLevel" v-model="showTimeLevelRef" @change="changeProperty('showTimeLevel')" /> <label for="showTimeLevel" >Show Time Level</label > </div> </div> <div class="block"> <div>Add Timeline</div> <br /> <button id="addSlicerBtn" @click="addSlicer">Add Timeline Slicer</button> </div> <div class="block"> <div>Change Current Timeline Style</div> <br /> <div> <select class="select-list" name="slicerStyle" id="slicerStyle" v-model="styleNameRef"> <option value="light1">light1</option> <option value="light2">light2</option> <option value="light3">light3</option> <option value="light4">light4</option> <option value="light5">light5</option> <option value="light6">light6</option> <option value="dark1">dark1</option> <option value="dark2">dark2</option> <option value="dark3">dark3</option> <option value="dark4">dark4</option> <option value="dark5">dark5</option> <option value="dark6">dark6</option> <option value="custom1">custom1</option> <option value="custom2">custom2</option> </select> <button class="select-button" id="changeStyle" @click="setStyle">Change</button> </div> </div> </div> </div> </template> <script setup> import GC from "@mescius/spread-sheets"; import "@mescius/spread-sheets-shapes"; import "@mescius/spread-sheets-slicers"; import "@mescius/spread-sheets-vue"; import { shallowRef } from "vue"; import "@mescius/spread-sheets-pivot-addon"; let slicerProp = {}; let showHeaderRef = shallowRef(true); let showHorizontalScrollbarRef = shallowRef(true); let showSelectionLabelRef = shallowRef(true); let showTimeLevelRef = shallowRef(true); let styleNameRef = shallowRef(""); let slicerCountRef = shallowRef(0); let activeSlicerRef = shallowRef(null); let spreadRef = shallowRef(null); let ptNameRef = shallowRef(null); function initSpread(spread) { spreadRef.value = spread; initCustomThemes(spread); spread.setSheetCount(2); initSheets(spread); let pivotLayoutSheet = spread.getSheet(0); initPivotTable(pivotLayoutSheet); bindEvents(pivotLayoutSheet); initSlicers(pivotLayoutSheet); slicerProp["showHeader"] = showHeaderRef; slicerProp["showHorizontalScrollbar"] = showHorizontalScrollbarRef; slicerProp["showSelectionLabel"] = showSelectionLabelRef; slicerProp["showTimeLevel"] = showTimeLevelRef; } function initCustomThemes (spread) { const theme1 = new GC.Spread.Sheets.Slicers.TimelineStyle(); theme1.fromJSON(GC.Spread.Sheets.Slicers.TimelineStyles.light1().toJSON()); theme1.name('custom1'); theme1.wholeSlicerStyle(new GC.Spread.Sheets.Slicers.SlicerStyleInfo('Accent 5 80')); const theme2 = new GC.Spread.Sheets.Slicers.TimelineStyle(); theme2.fromJSON(GC.Spread.Sheets.Slicers.TimelineStyles.light2().toJSON()); theme2.name('custom2'); theme2.wholeSlicerStyle(new GC.Spread.Sheets.Slicers.SlicerStyleInfo('Accent 2 80')); spread.customTimelineThemes.add(theme1); spread.customTimelineThemes.add(theme2); } function initSheets(spread) { spread.suspendPaint(); let sheet = spread.getSheet(1); sheet.name("DataSource"); sheet.setRowCount(650); sheet.setColumnWidth(5, 120); sheet.getCell(-1, 5).formatter("YYYY-mm-DD"); sheet.getRange(-1,4,0,1).formatter("$ #,##0"); sheet.setArray(0, 0, pivotSales); let table = sheet.tables.add('tableSales', 0, 0, 637, 6); table.style(GC.Spread.Sheets.Tables.TableThemes["none"]); let sheet0 = spread.getSheet(0); sheet0.name("PivotLayout"); spread.resumePaint(); } function initPivotTable(sheet) { sheet.setRowCount(1000); let option = { showRowHeader: true, showColumnHeader: true, bandRows: true, bandColumns: true }; let pivotTable = sheet.pivotTables.add("pivotTable", "tableSales", 1, 0, GC.Spread.Pivot.PivotTableLayoutType.outline, GC.Spread.Pivot.PivotTableThemes.medium8.name(), option); pivotTable.suspendLayout(); let groupInfo = { originFieldName: "date", dateGroups: [ { by: GC.Pivot.DateGroupType.years }, { by: GC.Pivot.DateGroupType.quarters }, { by: GC.Pivot.DateGroupType.months }, ] }; pivotTable.group(groupInfo); pivotTable.add("Years (date)", "Years (date)", GC.Spread.Pivot.PivotTableFieldType.rowField); pivotTable.add("Quarters (date)", "Quarters (date)", GC.Spread.Pivot.PivotTableFieldType.rowField); pivotTable.add("Months (date)", "Months (date)", GC.Spread.Pivot.PivotTableFieldType.rowField); pivotTable.add("amount", "amount", GC.Spread.Pivot.PivotTableFieldType.valueField, GC.Pivot.SubtotalType.sum); pivotTable.options.subtotalsPosition = GC.Spread.Pivot.SubtotalsPosition.none; pivotTable.resumeLayout(); pivotTable.autoFitColumn(); ptNameRef.value = pivotTable.name(); } function initSlicers(sheet) { let ptName = ptNameRef.value; let timeline_year = sheet.slicers.add("timeline_year", ptName, "date", GC.Spread.Sheets.Slicers.TimelineStyles.dark6().name(), GC.Spread.Sheets.Slicers.SlicerType.pivotTimeline); timeline_year.position(new GC.Spread.Sheets.Point(355, 20)); timeline_year.level(GC.Spread.Sheets.Slicers.TimelineLevel.years); timeline_year.showSelectionLabel(false); timeline_year.showTimeLevel(false); timeline_year.showHorizontalScrollbar(false); timeline_year.height(100); timeline_year.captionName("Years"); let timeline_quarter = sheet.slicers.add("timeline_quarter", ptName, "date", GC.Spread.Sheets.Slicers.TimelineStyles.dark1().name(), GC.Spread.Sheets.Slicers.SlicerType.pivotTimeline); timeline_quarter.position(new GC.Spread.Sheets.Point(355, 130)); timeline_quarter.level(GC.Spread.Sheets.Slicers.TimelineLevel.quarters); timeline_quarter.captionName("Quarters"); timeline_quarter.showSelectionLabel(false); let timeline_month = sheet.slicers.add("timeline_month", ptName, "date", GC.Spread.Sheets.Slicers.TimelineStyles.light4().name(), GC.Spread.Sheets.Slicers.SlicerType.pivotTimeline); timeline_month.position(new GC.Spread.Sheets.Point(355, 290)); timeline_month.captionName("Months"); timeline_month.showTimeLevel(false); sheet.resumePaint(); } function bindEvents(sheet) { sheet.bind(GC.Spread.Sheets.Events.SlicerChanged, function () { let slicers = sheet.slicers.all(); for (let i = 0; i < slicers.length; i++) { if (slicers[i].isSelected()) { activeSlicerRef.value = slicers[i]; updateSlicerInfo(); break; } } }); } function updateSlicerInfo() { if (!activeSlicerRef.value) { return; } let slicer = activeSlicerRef.value; showHeaderRef.value = slicer.showHeader(); showHorizontalScrollbarRef.value = slicer.showHorizontalScrollbar(); showSelectionLabelRef.value = slicer.showSelectionLabel(); showTimeLevelRef.value = slicer.showTimeLevel(); const slicerStyleName = slicer.style().name().toLowerCase(); styleNameRef.value = slicerStyleName.includes('custom') ? slicerStyleName : slicerStyleName.substr(15); } function changeProperty(prop, v) { if (!activeSlicerRef.value) { return; } v = v || slicerProp[prop].value; if (v !== null && v !== undefined) { activeSlicerRef.value[prop](v); } } function setStyle() { if (!activeSlicerRef.value) { return; } activeSlicerRef.value.style(styleNameRef.value); } function addSlicer() { let sheet = spreadRef.value.getActiveSheet(); sheet.slicers.add( "timeline_" + slicerCountRef.value, ptNameRef.value, "date", GC.Spread.Sheets.Slicers.TimelineStyles.light1().name(), GC.Spread.Sheets.Slicers.SlicerType.pivotTimeline ); slicerCountRef.value += 1; } </script> <style scoped> .sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 330px); height: 100%; overflow: hidden; float: left; } .options-container { float: right; width: 330px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; font-size: 14px; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .slicer-info { margin-top: 5px; margin-bottom: 5px; } .block { border: 1px solid gray; padding-left: 5px; padding-top: 10px; padding-bottom: 10px; margin-bottom: 1px; } .select-list { width: 120px; } .select-button { width: 80px; } #app { height: 100%; } </style>
<!DOCTYPE html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>SpreadJS VUE</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/vue3/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/spread/source/data/pivotSales.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/vue3/node_modules/systemjs/dist/system.src.js"></script> <script src="./systemjs.config.js"></script> <script src="./compiler.js" type="module"></script> <script> var System = SystemJS; System.import("./src/app.js"); System.import('$DEMOROOT$/en/lib/vue3/license.js'); </script> </head> <body> <div id="app"></div> </body> </html>
(function (global) { SystemJS.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, packageConfigPaths: [ '../node_modules/*/package.json', "../node_modules/@mescius/*/package.json", "../node_modules/@babel/*/package.json", "../node_modules/@vue/*/package.json" ], map: { 'vue': "npm:vue/dist/vue.esm-browser.js", 'tiny-emitter': 'npm:tiny-emitter/index.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', "systemjs-babel-build": "npm:systemjs-plugin-babel/systemjs-babel-browser.js", '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-resources-en': 'npm:@mescius/spread-sheets-resources-en/index.js', '@mescius/spread-sheets-vue': 'npm:@mescius/spread-sheets-vue/index.js', '@mescius/spread-sheets-shapes': 'npm:@mescius/spread-sheets-shapes/index.js', '@mescius/spread-sheets-slicers': 'npm:@mescius/spread-sheets-slicers/index.js', '@mescius/spread-sheets-pivot-addon': 'npm:@mescius/spread-sheets-pivot-addon/index.js', }, meta: { '*.css': { loader: 'systemjs-plugin-css' }, '*.vue': { loader: "../plugin-vue/index.js" } } }); })(this);