Sort
You can sort any field in ascending or descending order.
Keep Structure
In GanttSheet, we sort the task tree under its original structure, so the relationship between the child task and parent task will not change.
We also support flat sorting, but that will break the tree structure. These actions will be forbidden after the GanttSheet has been flat sorted:
collapse/expand outline
indent/outdent tasks
renumber
Renumber
After sorting, the taskNumber will changed. If you want to keep the sorted state in your database, you can re-number all of the tasks.
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
<template>
<div id="split-view" class="sample-tutorial">
<gc-spread-sheets
class="sample-spreadsheets split-content"
@workbookInitialized="initSpread"
>
</gc-spread-sheets>
<div class="options-container split-panel">
<div class="option-row option-title">
Config the sort options.
</div>
<div class="option-block">
<div class="option-row selection-box">
<label for="sort-field">Sort Field</label>
<select id="sort-field">
<option value="taskNumber">taskNumber</option>
<option value="name">name</option>
<option value="duration">duration</option>
<option value="predecessors">predecessors</option>
</select>
</div>
</div>
<div class="option-block">
<div class="option-row">
<label class="option-checkbox active" id="sort-ascending" @click="toggleAscending()" >Ascending</label>
<div class="option-info">* checkbox toggle whether ascending or descending.</div>
</div>
</div>
<div class="option-block">
<div class="option-row">
<label class="option-checkbox active" id="sort-structure" @click="toggleStructure()" >Keep Structure</label>
<div class="option-info">* checkbox toggle whether to keep structure or not.</div>
</div>
</div>
<div class="option-block">
<div class="option-row">
<label class="option-checkbox" id="sort-renumber" @click="toggleRenumber()" >Renumber</label>
<div class="option-info">* checkbox toggle whether to renumber or not.</div>
</div>
</div>
<div class="option-row">
<input type="button" id="sort-action" class="option-button" value="Sort" @click="doSort()" />
</div>
</div>
</div>
</template>
<script>
import Vue from "vue";
import "@mescius/spread-sheets-vue";
import GC from "@mescius/spread-sheets";
import "@mescius/spread-sheets-tablesheet";
import "@mescius/spread-sheets-ganttsheet";
import "./styles.css";
let sortAscending = true;
let sortStructure = true;
let sortRenumber = false;
let App = Vue.extend({
name: "app",
myTable: null,
ganttSheet: null,
data: function() {
return {
spread: null
}
},
methods: {
initSpread: function (spread) {
this.spread = spread;
spread.suspendPaint();
spread.clearSheets();
this.initDataSource(spread);
this.initGanttSheet(spread);
spread.resumePaint();
initSplitView(spread);
},
initDataSource(spread) {
var tableName = "Gantt_Id";
var baseApiUrl = getBaseApiUrl();
var apiUrl = baseApiUrl + "/" + tableName;
var dataManager = spread.dataManager();
var myTable = dataManager.addTable("myTable", {
batch: true,
remote: {
read: {
url: apiUrl
}
},
schema: {
hierarchy: {
type: "Parent",
column: "parentId"
},
columns: {
id: { isPrimaryKey: true },
taskNumber: { dataType: "rowOrder" }
}
}
});
this.myTable = myTable;
},
initGanttSheet(spread) {
var ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
var view = this.myTable.addView("ganttView", [
{ value: "taskNumber", caption: "NO.", width: 60 },
{ value: "name", caption: "Task Name", width: 200 },
{ value: "duration", caption: "Duration", width: 90 },
{ value: "predecessors", caption: "Predecessors", width: 120 }
]);
view.fetch().then(function() {
ganttSheet.bindGanttView(view);
});
this.ganttSheet = ganttSheet;
},
toggleAscending() {
var sortAscendingItem = document.getElementById("sort-ascending");
if (sortAscendingItem.classList.contains("active")) {
sortAscendingItem.classList.remove("active");
sortAscending = false;
} else {
sortAscendingItem.classList.add("active");
sortAscending = true;
}
},
toggleStructure() {
var sortStructureItem = document.getElementById("sort-structure");
var sortRenumberItem = document.getElementById("sort-renumber");
if (sortStructureItem.classList.contains("active")) {
sortStructureItem.classList.remove("active");
sortStructure = false;
if (sortRenumber) {
sortRenumberItem.classList.remove("active");
sortRenumber = false;
}
} else {
sortStructureItem.classList.add("active");
sortStructure = true;
}
},
toggleRenumber() {
var sortStructureItem = document.getElementById("sort-structure");
var sortRenumberItem = document.getElementById("sort-renumber");
if (sortRenumberItem.classList.contains("active")) {
sortRenumberItem.classList.remove("active");
sortRenumber = false;
} else {
sortRenumberItem.classList.add("active");
sortRenumber = true;
if (!sortStructure) {
sortStructureItem.classList.add("active");
sortStructure = true;
}
}
},
doSort() {
var sortFieldItem = document.getElementById("sort-field");
var ganttSheet = this.ganttSheet;
var project = ganttSheet.project;
var sortField = sortFieldItem.value;
project.sort([sortField], [sortAscending], sortStructure, sortRenumber);
}
}
});
function initSplitView(spread) {
var host = document.getElementById("split-view");
var content = host.getElementsByClassName("split-content")[0];
var panel = host.getElementsByClassName("split-panel")[0];
new SplitView({
host: host,
content: content,
panel: panel,
refreshContent: function() {
spread.refresh();
}
});
}
function getBaseApiUrl() {
return window.location.href.match(/http.+spreadjs\/demos\//)[0] + 'server/api';
}
new Vue({
render: (h) => h(App),
}).$mount("#app");
</script>
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/vue/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/spread/source/splitView/splitView.css">
<!-- SystemJS -->
<script src="$DEMOROOT$/en/vue/node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<!-- plugins -->
<script src="$DEMOROOT$/spread/source/splitView/SplitView.js"></script>
<script>
System.import('./src/app.vue');
System.import('$DEMOROOT$/en/lib/vue/license.js');
</script>
</head>
<body>
<div id="app"></div>
</body>
</html>
.options-container {
float: right;
width: 280px;
padding: 12px;
height: 100%;
box-sizing: border-box;
background: #fbfbfb;
overflow: auto;
box-shadow: inset 0px 0 4px 0 rgba(0,0,0,0.4);
}
.option-block {
background: #fff;
padding: 8px;
margin: 12px 0;
border-radius: 4px;
border: 1px dashed #82bc00;
box-shadow: 0px 0 6px 0 rgba(0,0,0,0.1);
}
.option-block.toggle {
border: 1px dotted #f7a711;
}
.option-row {
font-size: 14px;
box-sizing: border-box;
padding: 4px 0;
}
.option-title {
font-weight: bold;
color: #656565;
}
.option-info {
font-size: 12px;
color: #919191;
margin-top: 6px;
font-weight: normal;
}
.option-info.valid {
color: #82bc00;
}
.option-info.toggle {
color: #f7a711;
}
.option-button {
width: 100%;
padding: 0;
line-height: 20px;
background: #82bc00;
color: #fff;
transition: 0.3s;
cursor: pointer;
outline: none;
border-radius: 4px;
box-sizing: border-box;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
border: none;
}
.option-button:hover {
background: #82bc00;
color: #fff;
box-shadow: 0 3px 8px 0 rgba(0,0,0,0.4);
}
.option-checkbox {
background: #fff;
border: 1px dashed #f7a711;
color: #f7a711;
padding: 2px 4px;
transition: 0.3s;
box-sizing: border-box;
cursor: pointer;
}
.option-checkbox.active {
color: #fff;
background: #f7a711;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
border-radius: 4px;
}
.selection-box {
position: relative;
}
.selection-box > select {
text-align: left;
width: 100%;
height: 20px;
padding: 0;
line-height: 20px;
background: transparent;
border: none;
border-bottom: 2px solid #656565;
color: #656565;
transition: 0.3s;
cursor: pointer;
outline: none;
box-sizing: border-box;
}
.selection-box > select > option {
background: white;
}
.selection-box > select:focus {
border-bottom: 2px solid #82bc00;
color: #82bc00;
box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3);
}
.selection-box > label {
position: absolute;
cursor: pointer;
font-size: 12px;
color: #fff;
background: #656565;
padding: 0 4px;
right: 0;
top: 6px;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
}
.input-box {
position: relative;
}
.input-box > input[type=text] {
width: 100%;
background: transparent;
border: none;
color: #656565;
border-bottom: 2px solid #656565;
outline: none;
box-sizing: border-box;
transition: 0.3s;
}
.input-box > input[type=text]:focus {
color: #82bc00;
border-bottom: 2px solid #82bc00;
}
.input-box > label {
cursor: pointer;
position: absolute;
right: 0;
top: 5px;
font-size: 12px;
color: #fff;
background: #656565;
padding: 0 4px;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
}
(function (global) {
System.config({
transpiler: 'plugin-babel',
babelOptions: {
es2015: true
},
meta: {
'*.css': { loader: 'css' },
'*.vue': { loader: 'vue-loader' }
},
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
'@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js',
'@mescius/spread-sheets-tablesheet': 'npm:@mescius/spread-sheets-tablesheet/index.js',
'@mescius/spread-sheets-ganttsheet': 'npm:@mescius/spread-sheets-ganttsheet/index.js',
'@mescius/spread-sheets-vue': 'npm:@mescius/spread-sheets-vue/index.js',
'@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js',
'jszip': 'npm:jszip/dist/jszip.js',
'css': 'npm:systemjs-plugin-css/css.js',
'vue': 'npm:vue/dist/vue.min.js',
'vue-loader': 'npm:systemjs-vue-browser/index.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'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
src: {
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
"node_modules": {
defaultExtension: 'js'
}
}
});
})(this);