グリッド

FlexGridは、高速で柔軟なJavaScriptデータグリッドコントロールです。コアグリッドモジュールには、最も一般的な機能がすべて含まれています。また、さらにグリッドをカスタマイズするための拡張機能と柔軟なAPIも含まれています。

このサンプルでは、FlexGridソートグループ化検索フィルターデータマップセルテンプレートスパークラインカスタムエディタExcelエクスポートPDFエクスポート検証詳細行などの多くの機能を紹介しています。

データ項目の数を変更してみて、非常に大規模なデータセットであってもグリッドが高速のままであることを確認してください。FlexGridは、行と列を自動的に仮想化することにより、このレベルのパフォーマンスを実現します。

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import '@grapecity/wijmo.touch'; import * as wjcCore from '@grapecity/wijmo'; import * as wjcGrid from '@grapecity/wijmo.grid'; import * as wjcGridFilter from '@grapecity/wijmo.grid.filter'; import * as wjcGridSearch from '@grapecity/wijmo.grid.search'; import * as wjcGridGroupPanel from '@grapecity/wijmo.grid.grouppanel'; import * as wjcInput from '@grapecity/wijmo.input'; import { CellMaker, SparklineMarkers } from '@grapecity/wijmo.grid.cellmaker'; import { KeyValue, Country, DataService } from './data'; import { ExportService } from './export'; // class App { constructor(dataSvc, exportSvc) { this._itemsCount = 500; this._lastId = this._itemsCount; this._dataSvc = dataSvc; this._exportSvc = exportSvc; // initializes data maps this._productMap = this._buildDataMap(this._dataSvc.getProducts()); this._countryMap = new wjcGrid.DataMap(this._dataSvc.getCountries(), 'id', 'name'); this._colorMap = this._buildDataMap(this._dataSvc.getColors()); // initializes cell templates this._historyCellTemplate = CellMaker.makeSparkline({ markers: SparklineMarkers.High | SparklineMarkers.Low, maxPoints: 25, label: 'price history', }); this._ratingCellTemplate = CellMaker.makeRating({ range: [1, 5], label: 'rating' }); // initializes data size document.getElementById('itemsCount').addEventListener('change', (e) => { const value = e.target.value; this._itemsCount = wjcCore.changeType(value, wjcCore.DataType.Number); this._handleItemsCountChange(); }); // initializes export const btnExportToExcel = document.getElementById('btnExportToExcel'); this._excelExportContext = new ExcelExportContext(btnExportToExcel); btnExportToExcel.addEventListener('click', () => { this._exportToExcel(); }); document.getElementById('btnExportToPdf').addEventListener('click', () => { this._exportToPdf(); }); // initializes the grid this._initializeGrid(); // initializes items source this._itemsSource = this._createItemsSource(); this._theGrid.itemsSource = this._itemsSource; } close() { const ctx = this._excelExportContext; this._exportSvc.cancelExcelExport(ctx); } _initializeGrid() { // creates the grid this._theGrid = new wjcGrid.FlexGrid('#theGrid', { autoGenerateColumns: false, allowAddNew: true, allowDelete: true, allowPinning: wjcGrid.AllowPinning.SingleColumn, newRowAtTop: true, showMarquee: true, selectionMode: wjcGrid.SelectionMode.MultiRange, validateEdits: false, columns: [ { binding: 'id', header: 'ID', width: 70, isReadOnly: true }, { binding: 'date', header: '日付', format: 'd', isRequired: false, width: 130, editor: new wjcInput.InputDate(document.createElement('div'), { format: 'd', isRequired: false }) }, { binding: 'countryId', header: '国', dataMap: this._countryMap, width: 145, cellTemplate: (ctx) => { const dataItem = ctx.row.dataItem; if (wjcCore.isUndefined(dataItem) || dataItem === null) { return ''; } const country = this._getCountry(ctx.item); return `<span class="flag-icon flag-icon-${country.flag}"></span> ${country.name}`; } }, { binding: 'price', header: '金額', format: 'c', isRequired: false, width: 100 }, { binding: 'history', header: '履歴', width: 180, align: 'center', allowSorting: false, cellTemplate: this._historyCellTemplate }, { binding: 'change', header: '変化量', align: 'right', width: 115, cellTemplate: (ctx) => { const dataItem = ctx.row.dataItem; if (wjcCore.isUndefined(dataItem) || dataItem === null) { return ''; } const cls = this._getChangeCls(ctx.value); const value = this._formatChange(ctx.value); return `<span class="${cls}">${value}</span>`; } }, { binding: 'rating', header: 'レーティング', width: 180, align: 'center', cssClass: 'cell-rating', cellTemplate: this._ratingCellTemplate }, { binding: 'time', header: '時刻', format: 'HH:mm', isRequired: false, width: 95, editor: new wjcInput.InputTime(document.createElement('div'), { format: 'HH:mm', isRequired: false }) }, { binding: 'colorId', header: '色', dataMap: this._colorMap, width: 145, cellTemplate: (ctx) => { const dataItem = ctx.row.dataItem; if (wjcCore.isUndefined(dataItem) || dataItem === null) { return ''; } const color = this._getColor(ctx.item); return `<span class="color-tile" style="background: ${['Black', 'White', 'Red', 'Green', 'Blue'][ctx.item.colorId]}"></span> ${color.value}`; } }, { binding: 'productId', header: '商品', dataMap: this._productMap, width: 145 }, { binding: 'discount', header: '値引', format: 'p0', width: 130 }, { binding: 'active', header: '有効', width: 100 } ] }); // create the grid search box new wjcGridSearch.FlexGridSearch('#theSearch', { placeholder: 'フィルター', grid: this._theGrid, cssMatch: '' }); // adds Excel-like filter new wjcGridFilter.FlexGridFilter(this._theGrid, { filterColumns: [ 'id', 'date', 'time', 'countryId', 'productId', 'colorId', 'price', 'change', 'discount', 'rating', 'active' ] }); // adds group panel new wjcGridGroupPanel.GroupPanel('#theGroupPanel', { placeholder: 'ここに列をドラッグするとグループを作成します', grid: this._theGrid }); } _getCountry(item) { const country = this._countryMap.getDataItem(item.countryId); return country ? country : Country.NotFound; } _getColor(item) { const color = this._colorMap.getDataItem(item.colorId); return color ? color : KeyValue.NotFound; } _getChangeCls(value) { if (wjcCore.isNumber(value)) { if (value > 0) { return 'change-up'; } if (value < 0) { return 'change-down'; } } return ''; } _formatChange(value) { if (wjcCore.isNumber(value)) { return wjcCore.Globalize.formatNumber(value, 'c'); } if (!wjcCore.isUndefined(value) && value !== null) { return wjcCore.changeType(value, wjcCore.DataType.String); } return ''; } _exportToExcel() { const ctx = this._excelExportContext; if (!ctx.exporting) { this._exportSvc.startExcelExport(this._theGrid, ctx); } else { this._exportSvc.cancelExcelExport(ctx); } } _exportToPdf() { this._exportSvc.exportToPdf(this._theGrid, { countryMap: this._countryMap, colorMap: this._colorMap, historyCellTemplate: this._historyCellTemplate }); } _createItemsSource() { const data = this._dataSvc.getData(this._itemsCount); const view = new wjcCore.CollectionView(data, { getError: (item, prop) => { const displayName = this._theGrid.columns.getColumn(prop).header; return this._dataSvc.validate(item, prop, displayName); } }); view.collectionChanged.addHandler((s, e) => { // initializes new added item with a history data if (e.action === wjcCore.NotifyCollectionChangedAction.Add) { e.item.history = this._dataSvc.getHistoryData(); e.item.id = this._lastId; this._lastId++; } }); return view; } _disposeItemsSource(itemsSource) { if (itemsSource) { itemsSource.collectionChanged.removeAllHandlers(); } } // build a data map from a string array using the indices as keys _buildDataMap(items) { const map = []; for (let i = 0; i < items.length; i++) { map.push({ key: i, value: items[i] }); } return new wjcGrid.DataMap(map, 'key', 'value'); } _handleItemsCountChange() { this._disposeItemsSource(this._itemsSource); this._lastId = this._itemsCount; this._itemsSource = this._createItemsSource(); this._theGrid.itemsSource = this._itemsSource; } } // class ExcelExportContext { constructor(btn) { this._exporting = false; this._progress = 0; this._preparing = false; this._btn = btn; } get exporting() { return this._exporting; } set exporting(value) { if (value !== this._exporting) { this._exporting = value; this._onPropertyChanged(); } } get progress() { return this._progress; } set progress(value) { if (value !== this._progress) { this._progress = value; this._onPropertyChanged(); } } get preparing() { return this._preparing; } set preparing(value) { if (value !== this._preparing) { this._preparing = value; this._onPropertyChanged(); } } _onPropertyChanged() { wjcCore.enable(this._btn, !this._preparing); if (this._exporting) { const percent = wjcCore.Globalize.formatNumber(this._progress, 'p0'); this._btn.textContent = `キャンセル (${percent} 完了)`; } else { this._btn.textContent = 'Excelにエクスポート'; } } } // document.readyState === 'complete' ? init() : window.onload = init; // function init() { const dataSvc = new DataService(); const exportSvc = new ExportService(); const app = new App(dataSvc, exportSvc); window.addEventListener('unload', () => { app.close(); }); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <!-- filter/navigation --> <div class="row"> <!-- search box --> <div class="toolbar-item col-sm-3 col-md-5"> <div id="theSearch"></div> </div> <!-- data size --> <div class="toolbar-item col-sm-3 col-md-3"> <div class="input-group"> <span class="input-group-addon">データ数:</span> <select id="itemsCount" class="form-control"> <option value="5">5</option> <option value="50">50</option> <option value="500" selected>500</option> <option value="5000">5,000</option> <option value="50000">50,000</option> <option value="100000">100,000</option> </select> </div> </div> <!-- export to Excel --> <div class="toolbar-item col-sm-3 col-md-2"> <button id="btnExportToExcel" class="btn btn-default btn-block">Excelにエクスポート</button> </div> <!-- export to PDF --> <div class="toolbar-item col-sm-3 col-md-2"> <button id="btnExportToPdf" class="btn btn-default btn-block">PDFにエクスポート</button> </div> </div> <!-- the grid --> <div id="theGroupPanel"></div> <div id="theGrid"></div> </div> </body> </html>
import * as wjcCore from '@grapecity/wijmo'; import { RequiredValidator, MinNumberValidator, MinDateValidator, MaxNumberValidator, MaxDateValidator } from './validation'; // export class KeyValue { } KeyValue.NotFound = { key: -1, value: '' }; // export class Country { } Country.NotFound = { id: -1, name: '', flag: '' }; // export class DataService { constructor() { this._products = ['ウィジェット', 'ガジェット', 'ツール']; this._colors = ['黒', '白', '赤', '緑', '青']; this._countries = [ { id: 0, name: 'アメリカ', flag: 'us' }, { id: 1, name: 'ドイツ', flag: 'de' }, { id: 2, name: 'イギリス', flag: 'gb' }, { id: 3, name: '日本', flag: 'jp' }, { id: 4, name: 'イタリア', flag: 'it' }, { id: 5, name: 'ギリシャ', flag: 'gr' } ]; this._validationConfig = { 'date': [ new RequiredValidator(), new MinDateValidator(new Date('2000-01-01T00:00:00')), new MaxDateValidator(new Date('2100-01-01T00:00:00')) ], 'time': [ new RequiredValidator(), new MinDateValidator(new Date('2000-01-01T00:00:00')), new MaxDateValidator(new Date('2100-01-01T00:00:00')) ], 'productId': [ new RequiredValidator(), new MinNumberValidator(0, `{0}は{1}(${this._products[0]})より小さくすることはできません`), new MaxNumberValidator(this._products.length - 1, `{0}は{1}(${this._products[this._products.length - 1]})より大きくすることはできません`) ], 'countryId': [ new RequiredValidator(), new MinNumberValidator(0, `{0}は{1}(${this._countries[0].name})より小さくすることはできません`), new MaxNumberValidator(this._countries.length - 1, `{0}は{1}(${this._countries[this._countries.length - 1].name})より大きくすることはできません`) ], 'colorId': [ new RequiredValidator(), new MinNumberValidator(0, `{0}は{1}(${this._colors[0]})より小さくすることはできません`), new MaxNumberValidator(this._colors.length - 1, `{0}は{1}(${this._colors[this._colors.length - 1]})より大きくすることはできません`) ], 'price': [ new RequiredValidator(), new MinNumberValidator(0, `金額を負の値にすることはできません`) ] }; } getCountries() { return this._countries; } getProducts() { return this._products; } getColors() { return this._colors; } getHistoryData() { return this._getRandomArray(25, 100); } getData(count) { const data = []; const dt = new Date(); const year = dt.getFullYear(); const itemsCount = Math.max(count, 5); // add items for (let i = 0; i < itemsCount; i++) { const item = this._getItem(i, year); data.push(item); } // set invalid data to demonstrate errors visualization data[1].price = -2000; data[2].date = new Date('1970-01-01T00:00:00'); data[4].time = undefined; data[4].price = -1000; return data; } validate(item, prop, displayName) { const validators = this._validationConfig[prop]; if (wjcCore.isUndefined(validators)) { return ''; } const value = item[prop]; for (let i = 0; i < validators.length; i++) { const validationError = validators[i].validate(displayName, value); if (!wjcCore.isNullOrWhiteSpace(validationError)) { return validationError; } } } _getItem(i, year) { const date = new Date(year, i % 12, 25, i % 24, i % 60, i % 60); const countryIndex = this._getRandomIndex(this._countries); const productIndex = this._getRandomIndex(this._products); const colorIndex = this._getRandomIndex(this._colors); const item = { id: i, date: date, time: new Date(date.getTime() + Math.random() * 30 * (24 * 60 * 60 * 1000)), countryId: this._countries[countryIndex].id, productId: productIndex, colorId: colorIndex, price: wjcCore.toFixed(Math.random() * 10000 + 5000, 2, true), change: wjcCore.toFixed(Math.random() * 1000 - 500, 2, true), history: this.getHistoryData(), discount: wjcCore.toFixed(Math.random() / 4, 2, true), rating: this._getRating(), active: i % 4 == 0, size: Math.floor(100 + Math.random() * 900), weight: Math.floor(100 + Math.random() * 900), quantity: Math.floor(Math.random() * 10), description: "すべてのソフトウェア製品とサービスにおいて、お客様の目標達成を支援することに重点を置いています。お客様のビジネス目標を完全に理解し、品質に重点を置き、最高の倫理基準を遵守するという私たちの主要な原則は、私たちが行うすべての基礎となります。" }; return item; } _getRating() { return Math.ceil(Math.random() * 5); } _getRandomIndex(arr) { return Math.floor(Math.random() * arr.length); } _getRandomArray(len, maxValue) { const arr = []; for (let i = 0; i < len; i++) { arr.push(Math.floor(Math.random() * maxValue)); } return arr; } }
@import 'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/css/flag-icon.css'; body { font-size: 1.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Light", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; } .toolbar-item { margin-bottom: 6px; } .wj-flexgridsearch { width: 100%; } .wj-flexgrid { height: 330px; } .wj-flexgrid .wj-cell { padding: 7px; border: none; } .wj-cell.wj-state-invalid:not(.wj-header)::after { top: -14px; border: 14px solid transparent; border-right-color: red; } .flag-icon { box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4); } .color-tile { display: inline-block; position: relative; width: 1em; height: 1em; border-radius: 50%; box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4); vertical-align: middle; } .change-up { color: darkgreen; } .change-up:after { content: '\25b2'; margin-left: 0.5em; } .change-down { color: darkred; } .change-down:after { content: '\25bc'; margin-left: 0.5em; } .cell-rating { font-size: 12px; } .wj-flexgrid .wj-detail { padding: 4px 16px; } .wj-detail h3 { margin: 10px 0; }
import * as wjcCore from '@grapecity/wijmo'; import * as wjcGrid from '@grapecity/wijmo.grid'; import * as wjcInput from '@grapecity/wijmo.input'; // // *** CustomGridEditor class (transpiled from TypeScript) *** // export class CustomGridEditor { /** * Initializes a new instance of a CustomGridEditor. */ constructor(grid, binding, edtClass, options) { // save references this._grid = grid; this._col = grid.columns.getColumn(binding); // create error tooltip this._errorTip = new wjcCore.Tooltip({ isContentHtml: false, showDelay: 0, cssClass: 'wj-error-tip' }); // create editor this._ctl = new edtClass(document.createElement('div'), options); wjcCore.addClass(this._ctl.hostElement, 'custom-editor'); wjcCore.setCss(this._ctl.hostElement, { position: 'relative', height: '100%', borderRadius: '0px', border: 'none' }); // create hosting element this._host = document.createElement('div'); this._host.appendChild(this._ctl.hostElement); // connect grid events grid.beginningEdit.addHandler(this._beginningEdit, this); grid.sortingColumn.addHandler(() => { this._commitRowEdits(); }); grid.scrollPositionChanged.addHandler(() => { if (this._ctl.containsFocus()) { grid.focus(); } }); grid.selectionChanging.addHandler((s, e) => { if (e.row != s.selection.row) { this._commitRowEdits(); } }); // connect editor events this._ctl.addEventListener(this._ctl.hostElement, 'keydown', (e) => { switch (e.keyCode) { case wjcCore.Key.Tab: case wjcCore.Key.Enter: e.preventDefault(); // TFS 255685 this._closeEditor(true); this._grid.focus(); // forward event to the grid so it will move the selection var evt = document.createEvent('HTMLEvents'); evt.initEvent('keydown', true, true); 'altKey,metaKey,ctrlKey,shiftKey,keyCode'.split(',').forEach((prop) => { evt[prop] = e[prop]; }); this._grid.hostElement.dispatchEvent(evt); break; case wjcCore.Key.Escape: this._closeEditor(false); this._grid.focus(); break; } }); // close the editor when it loses focus this._ctl.lostFocus.addHandler(() => { setTimeout(() => { if (!this._ctl.containsFocus()) { this._closeEditor(true); // apply edits and close editor this._grid.onLostFocus(); // commit item edits if the grid lost focus } }); }); // commit edits when grid loses focus this._grid.lostFocus.addHandler(() => { setTimeout(() => { if (!this._grid.containsFocus() && !CustomGridEditor._isEditing) { this._commitRowEdits(); } }); }); // open drop-down on f4/alt-down this._grid.addEventListener(this._grid.hostElement, 'keydown', (e) => { // open drop-down on f4/alt-down this._openDropDown = false; if (e.keyCode == wjcCore.Key.F4 || (e.altKey && (e.keyCode == wjcCore.Key.Down || e.keyCode == wjcCore.Key.Up))) { var colIndex = this._grid.selection.col; if (colIndex > -1 && this._grid.columns[colIndex] == this._col) { this._openDropDown = true; this._grid.startEditing(true); e.preventDefault(); } } // commit edits on Enter (in case we're at the last row, TFS 268944) if (e.keyCode == wjcCore.Key.Enter) { this._commitRowEdits(); } }, true); // close editor when user resizes the window // REVIEW: hides editor when soft keyboard pops up (TFS 326875) window.addEventListener('resize', () => { if (this._ctl.containsFocus()) { this._closeEditor(true); this._grid.focus(); } }); } // gets an instance of the control being hosted by this grid editor get control() { return this._ctl; } // handle the grid's beginningEdit event by canceling the built-in editor, // initializing the custom editor and giving it the focus. _beginningEdit(grid, args) { // check that this is our column if (grid.columns[args.col] != this._col) { return; } // check that this is not the Delete key // (which is used to clear cells and should not be messed with) var evt = args.data; if (evt && evt.keyCode == wjcCore.Key.Delete) { return; } // cancel built-in editor args.cancel = true; // save cell being edited this._rng = args.range; CustomGridEditor._isEditing = true; // update cell before editing grid.refreshRange(this._rng); // show error if (grid._getShowErrors()) { var error = grid._getError(args.panel, args.row, args.col); this._showError(error); } else { this._clearError(); } // initialize editor host var rcCell = grid.getCellBoundingRect(args.row, args.col), rcBody = document.body.getBoundingClientRect(), ptOffset = new wjcCore.Point(-rcBody.left, -rcBody.top), zIndex = (args.row < grid.frozenRows || args.col < grid.frozenColumns) ? '3' : ''; wjcCore.setCss(this._host, { position: 'absolute', overflow: 'hidden', left: rcCell.left + ptOffset.x - 1, top: rcCell.top + ptOffset.y - 1, width: rcCell.width + 2, height: grid.rows[args.row].renderHeight + 2, backgroundColor: 'transparent', padding: '2px', zIndex: zIndex, }); // initialize editor content this._ctl['value'] = grid.getCellData(this._rng.row, this._rng.col, false); // start editing item var ecv = grid.editableCollectionView, item = grid.rows[args.row].dataItem; if (ecv && item && item != ecv.currentEditItem) { setTimeout(function () { grid.onRowEditStarting(args); ecv.editItem(item); grid.onRowEditStarted(args); }, 50); // wait for the grid to commit edits after losing focus } // activate editor document.body.appendChild(this._host); this._ctl.focus(); setTimeout(() => { // get the key that triggered the editor var key = (evt && evt.charCode > 32) ? String.fromCharCode(evt.charCode) : null; // get input element in the control var input = this._ctl.hostElement.querySelector('input'); // send key to editor if (input) { if (key) { input.value = key; wjcCore.setSelectionRange(input, key.length, key.length); var evtInput = document.createEvent('HTMLEvents'); evtInput.initEvent('input', true, false); input.dispatchEvent(evtInput); } else { input.select(); } } // give the control focus if (!input && !this._openDropDown) { this._ctl.focus(); } // open drop-down on F4/alt-down if (this._openDropDown && this._ctl instanceof wjcInput.DropDown) { this._ctl.isDroppedDown = true; this._ctl.dropDown.focus(); } }, 50); } // close the custom editor, optionally saving the edits back to the grid _closeEditor(saveEdits) { if (this._rng) { var flexGrid = this._grid, ctl = this._ctl; // raise grid's cellEditEnding event var e = new wjcGrid.CellEditEndingEventArgs(flexGrid.cells, this._rng); flexGrid.onCellEditEnding(e); // save editor value into grid if (saveEdits) { if (!wjcCore.isUndefined(ctl['value'])) { this._grid.setCellData(this._rng.row, this._rng.col, ctl['value']); } else if (!wjcCore.isUndefined(ctl['text'])) { this._grid.setCellData(this._rng.row, this._rng.col, ctl['text']); } else { throw 'Can\'t get editor value/text...'; } this._grid.invalidate(); } // close editor and remove it from the DOM if (ctl instanceof wjcInput.DropDown) { ctl.isDroppedDown = false; } this._host.parentElement.removeChild(this._host); this._rng = null; CustomGridEditor._isEditing = false; // raise grid's cellEditEnded event flexGrid.onCellEditEnded(e); } } // commit row edits, fire row edit end events (TFS 339615) _commitRowEdits() { var flexGrid = this._grid, ecv = flexGrid.editableCollectionView; this._closeEditor(true); if (ecv && ecv.currentEditItem) { var e = new wjcGrid.CellEditEndingEventArgs(flexGrid.cells, flexGrid.selection); ecv.commitEdit(); setTimeout(() => { flexGrid.onRowEditEnding(e); flexGrid.onRowEditEnded(e); flexGrid.invalidate(); }); } } _clearError() { this._showError(null); } _showError(error) { var hasError = !!error; wjcCore.toggleClass(this._ctl.hostElement, 'custom-editor-invalid', hasError); this._errorTip.setTooltip(this._ctl.hostElement, null); setTimeout(() => { this._errorTip.setTooltip(this._ctl.hostElement, error); }); } }
import * as wjcCore from '@grapecity/wijmo'; import * as wjcGrid from '@grapecity/wijmo.grid'; import * as wjcGridPdf from '@grapecity/wijmo.grid.pdf'; import * as wjcGridXlsx from '@grapecity/wijmo.grid.xlsx'; import * as wjcPdf from '@grapecity/wijmo.pdf'; import * as wjcXlsx from '@grapecity/wijmo.xlsx'; import { KeyValue, Country } from './data'; // const ExcelExportDocName = 'FlexGrid.xlsx'; const PdfExportDocName = 'FlexGrid.pdf'; const FakeColumn = new wjcGrid.Column(); const FakeRow = new wjcGrid.Row(); // class Fonts { } Fonts.ZapfDingbatsSm = new wjcPdf.PdfFont('zapfdingbats', 8, 'normal', 'normal'); Fonts.ZapfDingbatsLg = new wjcPdf.PdfFont('zapfdingbats', 16, 'normal', 'normal'); Fonts.Ipaexg = new wjcPdf.PdfFont('ipaexg'); // export class IExcelExportContext { } // export class ExportService { startExcelExport(flex, ctx) { if (ctx.preparing || ctx.exporting) { return; } ctx.exporting = false; ctx.progress = 0; ctx.preparing = true; wjcGridXlsx.FlexGridXlsxConverter.saveAsync(flex, { includeColumnHeaders: true, includeCellStyles: false, formatItem: this._formatExcelItem.bind(this) }, ExcelExportDocName, () => { console.log('Excelエクスポートが完了しました'); this._resetExcelContext(ctx); }, err => { console.error(`Excelエクスポートが失敗しました: ${err}`); this._resetExcelContext(ctx); }, prg => { if (ctx.preparing) { ctx.exporting = true; ctx.preparing = false; } ctx.progress = prg / 100.; }); console.log('Excelエクスポートが開始されました'); } cancelExcelExport(ctx) { wjcGridXlsx.FlexGridXlsxConverter.cancelAsync(() => { console.log('Excelエクスポートがキャンセルされました'); this._resetExcelContext(ctx); }); } exportToPdf(flex, options) { wjcGridPdf.FlexGridPdfConverter.export(flex, PdfExportDocName, { embeddedFonts: [{ source: 'https://demo.grapecity.com/wijmo/sample/fonts/ipaexg.ttf', name: 'ipaexg' }], maxPages: 100, exportMode: wjcGridPdf.ExportMode.All, scaleMode: wjcGridPdf.ScaleMode.ActualSize, documentOptions: { pageSettings: { layout: wjcPdf.PdfPageOrientation.Landscape }, header: { declarative: { text: '\t&[Page] / &[Pages]' } }, footer: { declarative: { text: '\t&[Page] / &[Pages]' } } }, styles: { cellStyle: { font: { family: 'ipaexg' }, backgroundColor: '#ffffff', borderColor: '#c6c6c6' }, altCellStyle: { backgroundColor: '#f9f9f9' }, groupCellStyle: { backgroundColor: '#dddddd' }, headerCellStyle: { backgroundColor: '#eaeaea' }, // Highlight Invalid Cells errorCellStyle: { backgroundColor: 'rgba(255, 0, 0, 0.3)' } }, customCellContent: false, formatItem: (e) => this._formatPdfItem(e, options) }); } _formatExcelItem(e) { const panel = e.panel; if (panel.cellType !== wjcGrid.CellType.Cell) { return; } // highlight invalid cells if (panel.grid._getError(panel, e.row, e.col)) { const fill = new wjcXlsx.WorkbookFill(); fill.color = '#ff0000'; e.xlsxCell.style.fill = fill; } } _resetExcelContext(ctx) { ctx.exporting = false; ctx.progress = 0; ctx.preparing = false; } _formatPdfItem(e, options) { const panel = e.panel; if (panel.cellType !== wjcGrid.CellType.Cell) { return; } switch (panel.columns[e.col].binding) { case 'countryId': this._formatPdfCountryCell(e, options.countryMap); break; case 'colorId': this._formatPdfColorCell(e, options.colorMap); break; case 'change': this._formatPdfChangeCell(e); break; case 'history': /*** Version #1: get grid cell produced before by a cell template ***/ // const cell = e.getFormattedCell(); // this._formatPdfHistoryCell(e, cell); /*** Version #2: create fake cell from a cell template ***/ const history = e.panel.getCellData(e.row, e.col, false); const cell = this._createCellFromCellTemplate(options.historyCellTemplate, history); this._formatPdfHistoryCell(e, cell); break; case 'rating': this._formatPdfRatingCell(e); break; } } _formatPdfCountryCell(e, countryMap) { e.drawBackground(e.style.backgroundColor); // check whether country exists const countryName = e.data; if (this._isCountryExist(countryName, countryMap)) { // bound rectangle of cell's content area const contentRect = e.contentRect; // draw flag image const image = e.canvas.openImage(`resources/${['us', 'germany', 'uk', 'japan', 'italy', 'greece'][countryMap.getKeyValue(countryName)]}.png`); const imageTop = contentRect.top + (contentRect.height - image.height) / 2; e.canvas.drawImage(image, contentRect.left, imageTop); // draw country name e.canvas.drawText(countryName, contentRect.left + image.width + 3, e.textTop, { font: Fonts.Ipaexg }); } // cancel standard cell content drawing e.cancel = true; } _formatPdfColorCell(e, colorMap) { e.drawBackground(e.style.backgroundColor); // check whether color exists const colorName = e.data; if (this._isColorExist(colorName, colorMap)) { // bound rectangle of cell's content area const contentRect = e.contentRect; // draw color indicator const imageHeight = Math.min(10, contentRect.height); const imageWidth = 1.33 * imageHeight; const imageTop = contentRect.top + (contentRect.height - imageHeight) / 2; e.canvas.paths .rect(contentRect.left, imageTop, imageWidth, imageHeight) .fillAndStroke(wjcCore.Color.fromString(['Black', 'White', 'Red', 'Green', 'Blue'][colorMap.getKeyValue(colorName)]), wjcCore.Color.fromString('gray')); // draw color name e.canvas.drawText(colorName, contentRect.left + imageWidth + 3, e.textTop, { font: Fonts.Ipaexg }); } // cancel standard cell content drawing e.cancel = true; } _formatPdfChangeCell(e) { e.drawBackground(e.style.backgroundColor); // get change value and text const cellData = e.panel.getCellData(e.row, e.col, false); let change = 0; let changeText = ''; if (wjcCore.isNumber(cellData)) { change = cellData; changeText = wjcCore.Globalize.formatNumber(change, 'c'); } else if (!wjcCore.isUndefined(cellData) && cellData !== null) { changeText = wjcCore.changeType(cellData, wjcCore.DataType.String); } // determine whether change is positive or negative let changeIndicator = ''; let changeColor = e.style.color; if (change > 0) { changeIndicator = '\x73'; // ▲ changeColor = 'darkgreen'; } else if (change < 0) { changeIndicator = '\x74'; // ▼ changeColor = 'darkred'; } // draw change indicator let indent = 10; e.canvas.drawText(changeIndicator, e.contentRect.right - indent, e.contentRect.top + indent, { brush: changeColor, font: Fonts.ZapfDingbatsSm }); // draw change text indent += 3; e.canvas.drawText(changeText, e.contentRect.left, e.textTop, { brush: changeColor, align: wjcPdf.PdfTextHorizontalAlign.Right, width: e.contentRect.width - indent }); // cancel standard cell content drawing e.cancel = true; } _formatPdfHistoryCell(e, cell) { e.drawBackground(e.style.backgroundColor); // draw history svg const svgUrl = this._getHistorySvgDataUrlFromCell(cell, e.clientRect.width, e.clientRect.height); if (svgUrl) { let cr = e.contentRect; e.canvas.drawSvg(svgUrl, cr.left + 2, cr.top + 2, { width: cr.width - 4, height: cr.height - 4 }); } // cancel standard cell content drawing e.cancel = true; } _getHistorySvgDataUrlFromCell(cell, width, height) { let dataUrl = null; // extract SVG from provided cell const svg = cell.getElementsByTagName('svg')[0]; if (svg) { const clone = svg.cloneNode(true); clone.setAttribute('version', '1.1'); clone.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns', 'http://www.w3.org/2000/svg'); clone.style.overflow = 'visible'; clone.style.stroke = '#376092'; clone.style.fill = '#376092'; const s = document.createElement('style'); s.setAttribute('type', 'text/css'); s.innerHTML = `<![CDATA[ line { stroke-width: 2; } circle { stroke-width: 0; stroke-opacity: 0; } .wj-marker { fill: #d00000; opacity: 1; } ]]>`; const defs = document.createElement('defs'); defs.appendChild(s); clone.insertBefore(defs, clone.firstChild); const outer = document.createElement('div'); outer.appendChild(clone); dataUrl = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(outer.innerHTML))); } return dataUrl; } _formatPdfRatingCell(e) { e.drawBackground(e.style.backgroundColor); // check whether rating is defined let rating = wjcCore.changeType(e.data, wjcCore.DataType.Number); if (wjcCore.isInt(rating)) { const ratingIndicator = '\x48'; // ★ const ratingNormalColor = wjcCore.Color.fromRgba(255, 165, 0, 1); // orange const ratingLightColor = wjcCore.Color.fromRgba(255, 165, 0, 0.2); // draw rating indicators const indent = 16; const count = 5; const width = count * indent; const y = e.clientRect.top + indent; let x = e.contentRect.left + (e.contentRect.width - width) / 2; rating = wjcCore.clamp(rating, 1, count); for (let i = 0; i < count; i++) { e.canvas.drawText(ratingIndicator, x, y, { brush: (i < rating) ? ratingNormalColor : ratingLightColor, font: Fonts.ZapfDingbatsLg, height: e.clientRect.height }); x += indent; } } // cancel standard cell content drawing e.cancel = true; } _isCountryExist(countryName, countryMap) { const countryId = countryMap.getKeyValue(countryName); if (wjcCore.isUndefined(countryId) || countryId === null) { return false; } if (countryId === Country.NotFound.id) { return false; } return true; } _isColorExist(colorName, colorMap) { const colorId = colorMap.getKeyValue(colorName); if (wjcCore.isUndefined(colorId) || colorId === null) { return false; } if (colorId === KeyValue.NotFound.key) { return false; } return true; } _createCellFromCellTemplate(cellTemplate, data) { const cell = document.createElement('div'); cellTemplate({ col: FakeColumn, row: FakeRow, value: data, item: null, text: null }, cell); return cell; } }
import * as wjcCore from '@grapecity/wijmo'; export class RequiredValidator { validate(name, value) { const message = name + 'は入力必須です'; if (wjcCore.isUndefined(value)) { return message; } const str = wjcCore.changeType(value, wjcCore.DataType.String); if (wjcCore.isNullOrWhiteSpace(str)) { return message; } return ''; } } export class MinValueValidator { constructor(minValue, message = '{0}は{1}より小さくすることはできません', format = null) { this.minValue = minValue; this.message = message; this.format = format; } validate(name, value) { if (value < this.minValue) { return wjcCore.format(this.message, { 0: name, 1: this._formatValue(this.minValue) }); } return ''; } } export class MaxValueValidator { constructor(maxValue, message = '{0}は{1}より大きくすることはできません', format = null) { this.maxValue = maxValue; this.message = message; this.format = format; } validate(name, value) { if (value > this.maxValue) { return wjcCore.format(this.message, { 0: name, 1: this._formatValue(this.maxValue) }); } return ''; } } export class MinNumberValidator extends MinValueValidator { constructor(minValue, message = '{0}は{1}より小さくすることはできません', format = 'n') { super(minValue, message, format); } _formatValue(value) { return wjcCore.Globalize.formatNumber(value, this.format); } } export class MaxNumberValidator extends MaxValueValidator { constructor(maxValue, message = '{0}は{1}より大きくすることはできません', format = 'n') { super(maxValue, message, format); } _formatValue(value) { return wjcCore.Globalize.formatNumber(value, this.format); } } export class MinDateValidator extends MinValueValidator { constructor(minValue, message = '{0}は{1}より小さくすることはできません', format = 'd') { super(minValue, message, format); } _formatValue(value) { return wjcCore.Globalize.formatDate(value, this.format); } } export class MaxDateValidator extends MaxValueValidator { constructor(maxValue, message = '{0}は{1}より大きくすることはできません', format = 'd') { super(maxValue, message, format); } _formatValue(value) { return wjcCore.Globalize.formatDate(value, this.format); } }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.transposedmultirow': 'npm:@grapecity/wijmo.grid.transposedmultirow/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.pdf.security': 'npm:@grapecity/wijmo.pdf.security/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', '@grapecity/wijmo.barcode': 'npm:@grapecity/wijmo.barcode/index.js', '@grapecity/wijmo.barcode.common': 'npm:@grapecity/wijmo.barcode.common/index.js', '@grapecity/wijmo.barcode.composite': 'npm:@grapecity/wijmo.barcode.composite/index.js', '@grapecity/wijmo.barcode.specialized': 'npm:@grapecity/wijmo.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', '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: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);