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.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import './styles.css';
import { AppFunc } from './app-func';
import { App } from './app-class';
// 1. Functional Component sample
ReactDOM.render(<AppFunc />, document.getElementById('app'));
// 2. Class Component sample
// ReactDOM.render(<App />, document.getElementById('app'));
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
import * as React from 'react';
import GC from '@mescius/spread-sheets';
import "@mescius/spread-sheets-tablesheet";
import "@mescius/spread-sheets-ganttsheet";
import { SpreadSheets } from '@mescius/spread-sheets-react';
import './styles.css';
let sortAscending = true;
let sortStructure = true;
let sortRenumber = false;
let ganttSheet = null;
let myTable = null;
export function AppFunc() {
const initSpread = (spread) => {
spread.suspendPaint();
spread.clearSheets();
initDataSource(spread);
initGanttSheet(spread);
spread.resumePaint();
initSplitView(spread);
}
const initDataSource = (spread) => {
var tableName = "Gantt_Id";
var baseApiUrl = getBaseApiUrl();
var apiUrl = baseApiUrl + "/" + tableName;
var dataManager = spread.dataManager();
myTable = dataManager.addTable("myTable", {
batch: true,
remote: {
read: {
url: apiUrl
}
},
schema: {
hierarchy: {
type: "Parent",
column: "parentId"
},
columns: {
id: { isPrimaryKey: true },
taskNumber: { dataType: "rowOrder" }
}
}
});
}
const initGanttSheet = (spread) => {
ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
var view = 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);
});
}
const 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;
}
}
const 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;
}
}
const 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;
}
}
}
const doSort = () => {
var sortFieldItem = document.getElementById("sort-field");
var project = ganttSheet.project;
var sortField = sortFieldItem.value;
project.sort([sortField], [sortAscending], sortStructure, sortRenumber);
}
return (
<div id="split-view" class="sample-tutorial">
<div class="sample-spreadsheets split-content">
<SpreadSheets workbookInitialized={spread => initSpread(spread)}></SpreadSheets>
</div>
<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" onClick={() => { 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" onClick={() => { 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" onClick={() => { 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" onClick={() => { doSort() }} />
</div>
</div>
</div>
);
}
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';
}
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
import * as React from 'react';
import GC from '@mescius/spread-sheets';
import "@mescius/spread-sheets-tablesheet";
import "@mescius/spread-sheets-ganttsheet";
import { SpreadSheets } from '@mescius/spread-sheets-react';
import './styles.css';
const Component = React.Component;
let sortAscending = true;
let sortStructure = true;
let sortRenumber = false;
export class App extends Component {
constructor(props) {
super(props);
this.ganttSheet = null;
}
render() {
return (
<div id="split-view" class="sample-tutorial">
<div class="sample-spreadsheets split-content">
<SpreadSheets workbookInitialized={spread => this.initSpread(spread)}></SpreadSheets>
</div>
<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" onClick={ () => { this.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" onClick={ () => { this.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" onClick={ () => { this.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" onClick={ () => { this.doSort() } } />
</div>
</div>
</div>
);
}
initSpread(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';
}
<!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/react/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/react/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('$DEMOROOT$/en/lib/react/license.js').then(function () {
System.import('./src/app');
});
</script>
</head>
<body>
<div id="app" style="height: 100%;"></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,
react: true
},
meta: {
'*.css': { loader: 'css' }
},
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-react': 'npm:@mescius/spread-sheets-react/index.js',
'@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js',
'react': 'npm:react/umd/react.production.min.js',
'react-dom': 'npm:react-dom/umd/react-dom.production.min.js',
'css': 'npm:systemjs-plugin-css/css.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: 'jsx'
},
"node_modules": {
defaultExtension: 'js'
},
}
});
})(this);