グリッド (Angular)

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

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

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

このサンプルはAngularを使用しています。

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; // import '@angular/compiler'; import { Component, Inject, enableProdMode, ViewChild, OnDestroy, ɵresolveComponentResources } from '@angular/core'; import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import '@mescius/wijmo.touch'; import * as wjcCore from '@mescius/wijmo'; import * as wjcGrid from '@mescius/wijmo.grid'; import { CellMaker, SparklineMarkers } from '@mescius/wijmo.grid.cellmaker'; import { WjGridModule } from '@mescius/wijmo.angular2.grid'; import { WjGridGrouppanelModule } from '@mescius/wijmo.angular2.grid.grouppanel'; import { WjGridFilterModule } from '@mescius/wijmo.angular2.grid.filter'; import { WjGridSearchModule } from '@mescius/wijmo.angular2.grid.search'; import { WjInputModule } from '@mescius/wijmo.angular2.input'; import { AppPipesModule } from './app.pipe'; import { KeyValue, Country, DataService } from './app.data'; import { IExcelExportContext, ExportService } from './app.export'; // @Component({ standalone: true, providers: [DataService, ExportService], imports: [WjInputModule, WjGridModule, WjGridGrouppanelModule, WjGridFilterModule, WjGridSearchModule, AppPipesModule, BrowserModule, FormsModule], selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent implements OnDestroy { private _itemsCount: number = 500; private _lastId: number = this._itemsCount; private _dataSvc: DataService; private _exportSvc: ExportService; private _itemsSource: wjcCore.CollectionView; private _productMap: wjcGrid.DataMap<number, KeyValue>; private _countryMap: wjcGrid.DataMap<number, Country>; private _colorMap: wjcGrid.DataMap<number, KeyValue>; private _historyCellTemplate: wjcGrid.ICellTemplateFunction; private _ratingCellTemplate: wjcGrid.ICellTemplateFunction; private _excelExportContext: IExcelExportContext; // references FlexGrid named 'flex' in the view @ViewChild('flex', { static: true }) flex: wjcGrid.FlexGrid; get productMap(): wjcGrid.DataMap<number, KeyValue> { return this._productMap; } get countryMap(): wjcGrid.DataMap<number, Country> { return this._countryMap; } get colorMap(): wjcGrid.DataMap<number, KeyValue> { return this._colorMap; } get itemsSource(): wjcCore.CollectionView { return this._itemsSource; } get itemsCount(): number { return this._itemsCount; } set itemsCount(value: number) { if (this._itemsCount != value) { this._itemsCount = value; this._handleItemsCountChange(); } } get historyCellTemplate(): wjcGrid.ICellTemplateFunction { return this._historyCellTemplate; } get ratingCellTemplate(): wjcGrid.ICellTemplateFunction { return this._ratingCellTemplate; } get excelExportContext(): IExcelExportContext { return this._excelExportContext; } constructor(@Inject(DataService) dataSvc: DataService, @Inject(ExportService) exportSvc: ExportService) { this._dataSvc = dataSvc; this._exportSvc = exportSvc; // initializes items source this._itemsSource = this._createItemsSource(); // initializes data maps this._productMap = this._buildDataMap(this._dataSvc.getProducts()); this._countryMap = new wjcGrid.DataMap<number, Country>(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 export this._excelExportContext = { exporting: false, progress: 0, preparing: false }; } ngOnDestroy() { const ctx = this._excelExportContext; this._exportSvc.cancelExcelExport(ctx); } getCountry(item: any) { const country = this._countryMap.getDataItem(item.countryId); return country ? country : Country.NotFound; } getColor(item: any) { const color = this._colorMap.getDataItem(item.colorId); return color ? color : KeyValue.NotFound; } getChangeCls(value: any) { if (wjcCore.isNumber(value)) { if (value > 0) { return 'change-up'; } if (value < 0) { return 'change-down'; } } return ''; } exportToExcel() { const ctx = this._excelExportContext; if (!ctx.exporting) { this._exportSvc.startExcelExport(this.flex, ctx); } else { this._exportSvc.cancelExcelExport(ctx); } } exportToPdf() { this._exportSvc.exportToPdf(this.flex, { countryMap: this._countryMap, colorMap: this._colorMap, historyCellTemplate: this._historyCellTemplate }); } private _createItemsSource(): wjcCore.CollectionView { const data = this._dataSvc.getData(this._itemsCount); const view = new wjcCore.CollectionView(data, { getError: (item: any, prop: any) => { const displayName = this.flex.columns.getColumn(prop).header; return this._dataSvc.validate(item, prop, displayName); } }); view.collectionChanged.addHandler((s: wjcCore.CollectionView, e: wjcCore.NotifyCollectionChangedEventArgs) => { // 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; } private _disposeItemsSource(itemsSource: wjcCore.CollectionView): void { if (itemsSource) { itemsSource.collectionChanged.removeAllHandlers(); } } // build a data map from a string array using the indices as keys private _buildDataMap(items: string[]): wjcGrid.DataMap<number, KeyValue> { const map: KeyValue[] = []; for (let i = 0; i < items.length; i++) { map.push({ key: i, value: items[i] }); } return new wjcGrid.DataMap<number, KeyValue>(map, 'key', 'value'); } private _handleItemsCountChange() { this._disposeItemsSource(this._itemsSource); this._lastId = this._itemsCount; this._itemsSource = this._createItemsSource(); } } // // enableProdMode(); // Resolve resources (templateUrl, styleUrls etc), After resolution all URLs have been converted into `template` strings. ɵresolveComponentResources(fetch).then(() => { // Bootstrap application bootstrapApplication(AppComponent).catch(err => console.error(err)); });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo FlexGrid Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div class="container-fluid"> <div class="row"> <!-- search box --> <div class="toolbar-item col-sm-3 col-md-5"> <wj-flex-grid-search [placeholder]="'フィルター'" [grid]="flex" cssMatch=""> </wj-flex-grid-search> </div> <!-- data size --> <div class="toolbar-item col-sm-3 col-md-3"> <div class="input-group"> <span class="input-group-addon">データ数:</span> <select class="form-control" [(ngModel)]="itemsCount"> <option value="5">5</option> <option value="50">50</option> <option value="500">500</option> <option value="5000">5,000</option> <option value="50000">50,000</option> <option value="100000">100,000</option> </select> </div> </div> <!-- Excelにエクスポート --> <div class="toolbar-item col-sm-3 col-md-2"> <button [disabled]="excelExportContext.preparing" (click)="exportToExcel()" class="btn btn-default btn-block"> {{excelExportContext.exporting ? ('キャンセル (' + (excelExportContext.progress | percent) + ' 完了)') : 'Excelにエクスポート'}} </button> </div> <!-- PDFにエクスポート --> <div class="toolbar-item col-sm-3 col-md-2"> <button (click)="exportToPdf()" class="btn btn-default btn-block">PDFにエクスポート</button> </div> </div> <!-- group panel --> <wj-group-panel [grid]="flex" [placeholder]="'ここに列をドラッグするとグループを作成します'"> </wj-group-panel> <!-- the grid --> <wj-flex-grid #flex [autoGenerateColumns]="false" [allowAddNew]="true" [allowDelete]="true" [allowPinning]="'SingleColumn'" [itemsSource]="itemsSource" [newRowAtTop]="true" [showMarquee]="true" [selectionMode]="'MultiRange'" [validateEdits]="false"> <wj-flex-grid-filter [filterColumns]="['id', 'date', 'time', 'countryId', 'productId', 'colorId', 'price', 'change', 'discount', 'rating', 'active']"> </wj-flex-grid-filter> <wj-flex-grid-column binding="id" header="ID" [width]="70" [isReadOnly]="true"></wj-flex-grid-column> <wj-flex-grid-column binding="date" header="日付" format="d" [isRequired]="false" [width]="130" [editor]="theInputDate"> </wj-flex-grid-column> <wj-flex-grid-column binding="countryId" header="国" [dataMap]="countryMap" [width]="145"> <ng-template wjFlexGridCellTemplate cellType="Cell" let-cell="cell"> <span class="flag-icon flag-icon-{{getCountry(cell.item).flag}}"></span> {{getCountry(cell.item).name}} </ng-template> </wj-flex-grid-column> <wj-flex-grid-column binding="price" header="金額" format="c" [isRequired]="false" [width]="100"> </wj-flex-grid-column> <wj-flex-grid-column binding="history" header="履歴" [width]="180" [align]="'center'" [allowSorting]="false" [cellTemplate]="historyCellTemplate"> </wj-flex-grid-column> <wj-flex-grid-column binding="change" header="変化量" [align]="'right'" [width]="115"> <ng-template wjFlexGridCellTemplate [cellType]="'Cell'" let-cell="cell"> <span [ngClass]="getChangeCls(cell.item.change)"> {{cell.item.change | safeCurrency}} </span> </ng-template> </wj-flex-grid-column> <wj-flex-grid-column binding="rating" header="レーティング" [width]="180" [align]="'center'" cssClass="cell-rating" [cellTemplate]="ratingCellTemplate"> </wj-flex-grid-column> <wj-flex-grid-column binding="time" header="時刻" format="HH:mm" [isRequired]="false" [width]="95" [editor]="theInputTime"> </wj-flex-grid-column> <wj-flex-grid-column binding="colorId" header="色" [dataMap]="colorMap" [width]="145"> <ng-template wjFlexGridCellTemplate [cellType]="'Cell'" let-cell="cell"> <span class="color-tile" [ngStyle]="{'background': ['Black', 'White', 'Red', 'Green', 'Blue'][cell.item.colorId]}"></span> {{getColor(cell.item).value}} </ng-template> </wj-flex-grid-column> <wj-flex-grid-column binding="productId" header="商品" [dataMap]="productMap" [width]="145"> </wj-flex-grid-column> <wj-flex-grid-column binding="discount" header="値引" format="p0" [width]="130"></wj-flex-grid-column> <wj-flex-grid-column binding="active" header="有効" [width]="100"></wj-flex-grid-column> </wj-flex-grid> <!-- custom editors --> <wj-input-date #theInputDate format="d" [isRequired]="false"> </wj-input-date> <wj-input-time #theInputTime format="HH:mm" [isRequired]="false"> </wj-input-time> </div>
import { Injectable } from '@angular/core'; import * as wjcCore from '@mescius/wijmo'; import { IValidator, RequiredValidator, MinNumberValidator, MinDateValidator, MaxNumberValidator, MaxDateValidator } from './app.validation'; // export class KeyValue { key: number; value: string; static NotFound: KeyValue = { key: -1, value: '' }; } // export class Country { name: string; id: number; flag: string; static NotFound: Country = { id: -1, name: '', flag: '' }; } // @Injectable() export class DataService { private _products: string[] = ['ウィジェット', 'ガジェット', 'ツール']; private _colors: string[] = ['黒', '白', '赤', '緑', '青']; private _countries: Country[] = [ { 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' } ]; private _validationConfig: { [prop: string]: IValidator[] } = { '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(): Country[] { return this._countries; } getProducts(): string[] { return this._products; } getColors(): string[] { return this._colors; } getHistoryData(): number[] { return this._getRandomArray(25, 100); } getData(count: number): any[] { 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: any, prop: string, displayName: string): string { const validators: IValidator[] = 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; } } } private _getItem(i: number, year: number): any { 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; } private _getRating(): number { return Math.ceil(Math.random() * 5); } private _getRandomIndex(arr: any[]): number { return Math.floor(Math.random() * arr.length); } private _getRandomArray(len: number, maxValue: number): number[] { const arr: number[] = []; 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'; } .change-down { color: darkred; } .change-down:after { content: '\25bc'; } .cell-rating { font-size: 12px; } .wj-flexgrid .wj-detail { padding: 4px 16px; } .wj-detail h3 { margin: 10px 0; }
import { Injectable } from '@angular/core'; import * as wjcCore from '@mescius/wijmo'; import * as wjcGrid from '@mescius/wijmo.grid'; import * as wjcGridPdf from '@mescius/wijmo.grid.pdf'; import * as wjcGridXlsx from '@mescius/wijmo.grid.xlsx'; import * as wjcPdf from '@mescius/wijmo.pdf'; import * as wjcXlsx from '@mescius/wijmo.xlsx'; import { KeyValue, Country } from './app.data'; // const ExcelExportDocName = 'FlexGrid.xlsx'; const PdfExportDocName = 'FlexGrid.pdf'; const FakeColumn: wjcGrid.Column = new wjcGrid.Column(); const FakeRow: wjcGrid.Row = new wjcGrid.Row(); // class Fonts { static ZapfDingbatsSm = new wjcPdf.PdfFont('zapfdingbats', 8, 'normal', 'normal'); static ZapfDingbatsLg = new wjcPdf.PdfFont('zapfdingbats', 16, 'normal', 'normal'); static Ipaexg = new wjcPdf.PdfFont('ipaexg'); } // export class IExcelExportContext { exporting: boolean; progress: number; preparing: boolean; } // @Injectable() export class ExportService { startExcelExport(flex: wjcGrid.FlexGrid, ctx: IExcelExportContext) { if (ctx.preparing || ctx.exporting) { return; } ctx.exporting = false; ctx.progress = 0; ctx.preparing = true; wjcGridXlsx.FlexGridXlsxConverter.saveAsync(flex, { includeColumnHeaders: true, includeStyles: 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.; }, true ); console.log('Excelエクスポートが開始されました'); } cancelExcelExport(ctx: IExcelExportContext) { wjcGridXlsx.FlexGridXlsxConverter.cancelAsync(() => { console.log('Export to Excel canceled'); this._resetExcelContext(ctx); }); } exportToPdf(flex: wjcGrid.FlexGrid, options: any) { wjcGridPdf.FlexGridPdfConverter.export(flex, PdfExportDocName, { embeddedFonts: [{ source: 'https://demo.mescius.jp/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: wjcGridPdf.PdfFormatItemEventArgs) => this._formatPdfItem(e, options) }); } private _formatExcelItem(e: wjcGridXlsx.XlsxFormatItemEventArgs) { 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; } } private _resetExcelContext(ctx: IExcelExportContext) { ctx.exporting = false; ctx.progress = 0; ctx.preparing = false; } private _formatPdfItem(e: wjcGridPdf.PdfFormatItemEventArgs, options: any) { 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; } } private _formatPdfCountryCell(e: wjcGridPdf.PdfFormatItemEventArgs, countryMap: wjcGrid.DataMap<number, Country>) { 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; } private _formatPdfColorCell(e: wjcGridPdf.PdfFormatItemEventArgs, colorMap: wjcGrid.DataMap<number, KeyValue>) { 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; } private _formatPdfChangeCell(e: wjcGridPdf.PdfFormatItemEventArgs) { 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; } private _formatPdfHistoryCell(e: wjcGridPdf.PdfFormatItemEventArgs, cell: HTMLElement) { 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; } private _getHistorySvgDataUrlFromCell(cell: HTMLElement, width: number, height: number): string { let dataUrl: string = null; // extract SVG from provided cell const svg = cell.getElementsByTagName('svg')[0]; if (svg) { const clone = <any>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; } private _formatPdfRatingCell(e: wjcGridPdf.PdfFormatItemEventArgs) { 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; } private _isCountryExist(countryName: any, countryMap: wjcGrid.DataMap<number, Country>) { const countryId = countryMap.getKeyValue(countryName); if (wjcCore.isUndefined(countryId) || countryId === null) { return false; } if (countryId === Country.NotFound.id) { return false; } return true; } private _isColorExist(colorName: any, colorMap: wjcGrid.DataMap<number, KeyValue>) { const colorId = colorMap.getKeyValue(colorName); if (wjcCore.isUndefined(colorId) || colorId === null) { return false; } if (colorId === KeyValue.NotFound.key) { return false; } return true; } private _createCellFromCellTemplate(cellTemplate: wjcGrid.ICellTemplateFunction, data: any) { const cell = document.createElement('div'); cellTemplate({ col: FakeColumn, row: FakeRow, value: data, item: null, text: null }, cell); return cell; } }
import * as wjcCore from '@mescius/wijmo'; export interface IValidator { validate(name: string, value: any): string; } export class RequiredValidator implements IValidator { validate(name: string, value: any): string { 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 abstract class MinValueValidator<TValue> implements IValidator { readonly minValue: TValue; readonly message: string; readonly format: string; constructor(minValue: TValue, message: string = '{0}は{1}より小さくすることはできません', format: string = null) { this.minValue = minValue; this.message = message; this.format = format; } validate(name: string, value: any): string { if (value < this.minValue) { return wjcCore.format(this.message, { 0: name, 1: this._formatValue(this.minValue) }); } return ''; } protected abstract _formatValue(value: TValue): string; } export abstract class MaxValueValidator<TValue> implements IValidator { readonly maxValue: TValue; readonly message: string; readonly format: string; constructor(maxValue: TValue, message: string = '{0}は{1}より大きくすることはできません', format: string = null) { this.maxValue = maxValue; this.message = message; this.format = format; } validate(name: string, value: any): string { if (value > this.maxValue) { return wjcCore.format(this.message, { 0: name, 1: this._formatValue(this.maxValue) }); } return ''; } protected abstract _formatValue(value: TValue): string; } export class MinNumberValidator extends MinValueValidator<number> { constructor(minValue: number, message: string = '{0}は{1}より小さくすることはできません', format: string = 'n') { super(minValue, message, format); } protected _formatValue(value: number): string { return wjcCore.Globalize.formatNumber(value, this.format); } } export class MaxNumberValidator extends MaxValueValidator<number> { constructor(maxValue: number, message: string = '{0}は{1}より大きくすることはできません', format: string = 'n') { super(maxValue, message, format); } protected _formatValue(value: number): string { return wjcCore.Globalize.formatNumber(value, this.format); } } export class MinDateValidator extends MinValueValidator<Date> { constructor(minValue: Date, message: string = '{0}は{1}より小さくすることはできません', format: string = 'd') { super(minValue, message, format); } protected _formatValue(value: Date): string { return wjcCore.Globalize.formatDate(value, this.format); } } export class MaxDateValidator extends MaxValueValidator<Date> { constructor(maxValue: Date, message: string = '{0}は{1}より大きくすることはできません', format: string = 'd') { super(maxValue, message, format); } protected _formatValue(value: Date): string { return wjcCore.Globalize.formatDate(value, this.format); } }
(function (global) { SystemJS.config({ transpiler: './plugin-typescript.js', typescriptOptions: { "target": "ES2022", "module": "system", "emitDecoratorMetadata": true, "experimentalDecorators": true, }, baseURL: 'node_modules/', meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'systemjs-plugin-css' } }, paths: { // paths serve as alias 'npm:': '' }, packageConfigPaths: [ '/node_modules/*/package.json', "/node_modules/@angular/*/package.json", "/node_modules/@mescius/*/package.json" ], map: { 'core-js': 'https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js', 'typescript': 'https://cdnjs.cloudflare.com/ajax/libs/typescript/5.2.2/typescript.min.js', "rxjs": "https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js", 'systemjs-plugin-css': 'https://cdn.jsdelivr.net/npm/systemjs-plugin-css@0.1.37/css.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', "@mescius/wijmo.angular2.chart.analytics": "npm:@mescius/wijmo.angular2.chart.analytics/index.js", "@mescius/wijmo.angular2.chart.animation": "npm:@mescius/wijmo.angular2.chart.animation/index.js", "@mescius/wijmo.angular2.chart.annotation": "npm:@mescius/wijmo.angular2.chart.annotation/index.js", "@mescius/wijmo.angular2.chart.finance.analytics": "npm:@mescius/wijmo.angular2.chart.finance.analytics/index.js", "@mescius/wijmo.angular2.chart.finance": "npm:@mescius/wijmo.angular2.chart.finance/index.js", "@mescius/wijmo.angular2.chart.hierarchical": "npm:@mescius/wijmo.angular2.chart.hierarchical/index.js", "@mescius/wijmo.angular2.chart.interaction": "npm:@mescius/wijmo.angular2.chart.interaction/index.js", "@mescius/wijmo.angular2.chart.radar": "npm:@mescius/wijmo.angular2.chart.radar/index.js", '@mescius/wijmo.angular2.chart.map': 'npm:@mescius/wijmo.angular2.chart.map/index.js', "@mescius/wijmo.angular2.chart": "npm:@mescius/wijmo.angular2.chart/index.js", "@mescius/wijmo.angular2.core": "npm:@mescius/wijmo.angular2.core/index.js", "@mescius/wijmo.angular2.gauge": "npm:@mescius/wijmo.angular2.gauge/index.js", "@mescius/wijmo.angular2.grid.detail": "npm:@mescius/wijmo.angular2.grid.detail/index.js", "@mescius/wijmo.angular2.grid.filter": "npm:@mescius/wijmo.angular2.grid.filter/index.js", "@mescius/wijmo.angular2.grid.grouppanel": "npm:@mescius/wijmo.angular2.grid.grouppanel/index.js", "@mescius/wijmo.angular2.grid.search": "npm:@mescius/wijmo.angular2.grid.search/index.js", "@mescius/wijmo.angular2.grid.multirow": "npm:@mescius/wijmo.angular2.grid.multirow/index.js", "@mescius/wijmo.angular2.grid.sheet": "npm:@mescius/wijmo.angular2.grid.sheet/index.js", '@mescius/wijmo.angular2.grid.transposed': 'npm:@mescius/wijmo.angular2.grid.transposed/index.js', '@mescius/wijmo.angular2.grid.transposedmultirow': 'npm:@mescius/wijmo.angular2.grid.transposedmultirow/index.js', "@mescius/wijmo.angular2.grid": "npm:@mescius/wijmo.angular2.grid/index.js", "@mescius/wijmo.angular2.input": "npm:@mescius/wijmo.angular2.input/index.js", "@mescius/wijmo.angular2.olap": "npm:@mescius/wijmo.angular2.olap/index.js", "@mescius/wijmo.angular2.viewer": "npm:@mescius/wijmo.angular2.viewer/index.js", "@mescius/wijmo.angular2.nav": "npm:@mescius/wijmo.angular2.nav/index.js", "@mescius/wijmo.angular2.directivebase": "npm:@mescius/wijmo.angular2.directivebase/index.js", '@mescius/wijmo.angular2.barcode.common': 'npm:@mescius/wijmo.angular2.barcode.common/index.js', '@mescius/wijmo.angular2.barcode.composite': 'npm:@mescius/wijmo.angular2.barcode.composite/index.js', '@mescius/wijmo.angular2.barcode.specialized': 'npm:@mescius/wijmo.angular2.barcode.specialized/index.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'jszip': 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js', "@angular/common/http": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/http.mjs", "@angular/core": "https://cdn.jsdelivr.net/npm/@angular/core@16.2.6/fesm2022/core.mjs", "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@angular/platform-browser@16.2.6/fesm2022/platform-browser.mjs", "@angular/common": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/common.mjs", "@angular/compiler": "https://cdn.jsdelivr.net/npm/@angular/compiler@16.2.6/fesm2022/compiler.mjs", "@angular/forms": "https://cdn.jsdelivr.net/npm/@angular/forms@16.2.6/fesm2022/forms.mjs", "@angular/localize": "https://cdn.jsdelivr.net/npm/@angular/localize@16.2.6/fesm2022/localize.mjs", "@angular/platform-browser-dynamic": "https://cdn.jsdelivr.net/npm/@angular/platform-browser-dynamic@16.2.6/fesm2022/platform-browser-dynamic.mjs", }, // packages tells the System loader how to load when no filename and/or no extension packages: { "./src": { defaultExtension: 'ts' }, "node_modules": { defaultExtension: 'js' }, wijmo: { defaultExtension: 'js', } } }); })(this);