트렐리스

이 샘플은 모든 DataChartType에서 트렐리스 rowcolumn 인코딩을 사용하는 방법을 보여 줍니다. 각 워크시트에는 차트가 하나씩 있으며 워크시트 이름은 Column, Bubble, OHLC와 같은 차트 유형 이름과 일치합니다.

데이터 차트는 트렐리스 레이아웃에 row 및 column 인코딩을 지원하므로 하나의 차트 정의를 여러 비교 패널로 나눌 수 있습니다. 지역과 판매 채널처럼 두 범주 차원에서 동일한 측정값을 비교할 때 유용합니다. 이 샘플에서는 각 DataChartType에 전용 워크시트가 있으며 워크시트 이름은 차트 유형 이름과 같습니다. 지원되는 데이터 차트 유형은 Column, RangeColumn, StackedColumn, PercentStackedColumn, Bar, RangeBar, StackedBar, PercentStackedBar, Area, RangeArea, StackedArea, PercentStackedArea, Line, Pie, Donut, Rose, RadialStackedBar, Sunburst, PolarCoordinatesBar, PolarCoordinatesStackedBar, Radar, FilledRadar, Scatter, Bubble, Treemap, Funnel, Waterfall, Candlestick, OHLC입니다. 각 차트를 자연스러운 맥락에서 확인할 수 있도록 다음 예제 데이터 집합을 사용합니다. RetailPerformance: 열, 범위 열, 막대, 범위 막대, 영역, 범위 영역, 선, 원형 계열, 극좌표 막대, 선버스트 및 트리맵 차트 RetailMix: 누적/100% 누적 열, 막대, 영역, 방사형, 극좌표, 레이더 및 채워진 레이더 차트 CustomerPortfolio: 분산형 및 거품형 차트 SalesPipeline: 깔때기 차트 ProfitBridge: 폭포 차트 StockPerformance: 캔들스틱 및 OHLC 차트 동일한 트렐리스 패턴이 지원되는 모든 차트 유형에 적용되며 워크시트마다 차트 하나가 배치됩니다.
window.onload = async () => { const { spread, designer } = createSpreadAndDesigner(); await initDataManager(spread); spread.suspendPaint(); const DataChartType = GC.Spread.Sheets.DataCharts.DataChartType; const chartConfigs = createChartConfigs(DataChartType); spread.setSheetCount(chartConfigs.length); chartConfigs.forEach((cfg, index) => { const sheet = spread.getSheet(index); sheet.name(cfg.name); addChartToSheet(sheet, cfg); }); spread.setActiveSheetIndex(0); const firstChart = spread.getActiveSheet().dataCharts.all()[0]; if (firstChart) { firstChart.isSelected(true); } if (window !== top) { new GC.Spread.Sheets.DataCharts.DataChartConfigPanel('panel-host', spread); } spread.resumePaint(); if (designer) { designer.refresh(); } }; function createChartConfigs(DataChartType) { return [ { name: DataChartType.column, type: DataChartType.column, tableName: 'RetailPerformance', variant: 'retailSingle', valueField: 'Revenue', aggregate: 'sum', categoryField: 'Month', detailBinding: false, detailAndColorBinding: true, detailAndColorField: 'ProductLine', title: 'Monthly revenue by region and channel' }, { name: DataChartType.rangeColumn, type: DataChartType.rangeColumn, tableName: 'RetailPerformance', variant: 'retailRange', categoryField: 'Month', detailBinding: false, detailAndColorBinding: true, detailAndColorField: 'ProductLine', title: 'Revenue forecast band by region and channel' }, { name: DataChartType.stackedColumn, type: DataChartType.stackedColumn, tableName: 'RetailMix', variant: 'mixStacked', categoryField: 'Month', title: 'Offer revenue mix by month' }, { name: DataChartType.percentStackedColumn, type: DataChartType.percentStackedColumn, tableName: 'RetailMix', variant: 'percentMix', categoryField: 'Month', title: 'Monthly revenue share by offer type' }, { name: DataChartType.bar, type: DataChartType.bar, tableName: 'RetailPerformance', variant: 'retailSingle', valueField: 'UnitsSold', aggregate: 'sum', categoryField: 'Month', detailBinding: false, detailAndColorBinding: true, detailAndColorField: 'ProductLine', title: 'Units sold by product line' }, { name: DataChartType.rangeBar, type: DataChartType.rangeBar, tableName: 'RetailPerformance', variant: 'retailRange', categoryField: 'Month', detailBinding: false, detailAndColorBinding: true, detailAndColorField: 'ProductLine', title: 'Revenue forecast band by product line' }, { name: DataChartType.stackedBar, type: DataChartType.stackedBar, tableName: 'RetailMix', variant: 'mixStacked', categoryField: 'Quarter', title: 'Quarterly offer revenue mix' }, { name: DataChartType.percentStackedBar, type: DataChartType.percentStackedBar, tableName: 'RetailMix', variant: 'percentMix', categoryField: 'Quarter', title: 'Quarterly revenue share by offer type' }, { name: DataChartType.area, type: DataChartType.area, tableName: 'RetailPerformance', variant: 'retailSingle', valueField: 'Revenue', aggregate: 'sum', categoryField: 'Month', detailBinding: false, title: 'Revenue trend by month' }, { name: DataChartType.rangeArea, type: DataChartType.rangeArea, tableName: 'RetailPerformance', variant: 'retailRange', categoryField: 'Month', detailBinding: false, title: 'Revenue planning range by month' }, { name: DataChartType.stackedArea, type: DataChartType.stackedArea, tableName: 'RetailMix', variant: 'mixStacked', categoryField: 'Month', title: 'Offer revenue mix trend' }, { name: DataChartType.percentStackedArea, type: DataChartType.percentStackedArea, tableName: 'RetailMix', variant: 'percentMix', categoryField: 'Month', title: 'Revenue share trend by offer type' }, { name: DataChartType.line, type: DataChartType.line, tableName: 'RetailPerformance', variant: 'retailSingle', valueField: 'MarginRate', aggregate: 'avg', categoryField: 'Month', detailBinding: false, title: 'Average margin rate by month' }, { name: DataChartType.pie, type: DataChartType.pie, tableName: 'RetailPerformance', variant: 'pieLike', valueField: 'Revenue', aggregate: 'sum', categoryField: 'ProductLine', title: 'Product line revenue share' }, { name: DataChartType.donut, type: DataChartType.donut, tableName: 'RetailPerformance', variant: 'pieLike', valueField: 'Revenue', aggregate: 'sum', categoryField: 'ProductLine', title: 'Product line revenue contribution' }, { name: DataChartType.rose, type: DataChartType.rose, tableName: 'RetailPerformance', variant: 'pieLike', valueField: 'UnitsSold', aggregate: 'sum', categoryField: 'Month', detailAndColorField: 'ProductLine', title: 'Units sold by product line' }, { name: DataChartType.radialStackedBar, type: DataChartType.radialStackedBar, tableName: 'RetailMix', variant: 'mixStacked', categoryField: 'Quarter', title: 'Quarterly offer mix in radial layout' }, { name: DataChartType.sunburst, type: DataChartType.sunburst, tableName: 'RetailPerformance', variant: 'hierarchy', title: 'Quarter to product line revenue hierarchy' }, { name: DataChartType.polarCoordinatesBar, type: DataChartType.polarCoordinatesBar, tableName: 'RetailPerformance', variant: 'retailSingle', valueField: 'UnitsSold', aggregate: 'sum', categoryField: 'ProductLine', title: 'Units sold in polar coordinates' }, { name: DataChartType.polarCoordinatesStackedBar, type: DataChartType.polarCoordinatesStackedBar, tableName: 'RetailMix', variant: 'mixStacked', categoryField: 'Quarter', title: 'Quarterly offer mix in polar coordinates' }, { name: DataChartType.radar, type: DataChartType.radar, tableName: 'RetailMix', variant: 'mixSingle', categoryField: 'Month', title: 'Monthly offer revenue profile' }, { name: DataChartType.filledRadar, type: DataChartType.filledRadar, tableName: 'RetailMix', variant: 'mixSingle', categoryField: 'Month', title: 'Filled monthly offer revenue profile' }, { name: DataChartType.scatter, type: DataChartType.scatter, tableName: 'CustomerPortfolio', variant: 'scatter', title: 'Acquisition cost versus lifetime value by segment' }, { name: DataChartType.bubble, type: DataChartType.bubble, tableName: 'CustomerPortfolio', variant: 'bubble', title: 'Acquisition cost, lifetime value, and deal size by segment' }, { name: DataChartType.treemap, type: DataChartType.treemap, tableName: 'RetailPerformance', variant: 'hierarchy', title: 'Quarter to product line revenue footprint' }, { name: DataChartType.funnel, type: DataChartType.funnel, tableName: 'SalesPipeline', variant: 'funnel', title: 'Pipeline conversion by stage' }, { name: DataChartType.waterfall, type: DataChartType.waterfall, tableName: 'ProfitBridge', variant: 'waterfall', title: 'Profit bridge by operational driver' }, { name: DataChartType.candlestick, type: DataChartType.candlestick, tableName: 'StockPerformance', variant: 'stock', title: 'Daily price movement by market and desk' }, { name: DataChartType.ohlc, type: DataChartType.ohlc, tableName: 'StockPerformance', variant: 'stock', title: 'Daily open, high, low, and close by market and desk' }, ]; } function addChartToSheet(sheet, cfg) { const dataChart = sheet.dataCharts.add(cfg.name, 20, 20, 960, 620, cfg.type); dataChart.setChartConfig({ tableName: cfg.tableName, plots: [{ type: cfg.type, encodings: buildEncodings(cfg), config: buildPlotConfig(cfg), }], config: { header: { title: cfg.title, padding: { left: 10, right: 10, top: 10, bottom: 10 }, textStyle: { fontSize: 16, alignment: GC.Spread.Sheets.DataCharts.HAlign.left, }, }, }, }); } function buildEncodings(cfg) { const Aggregate = GC.Spread.Sheets.DataCharts.Aggregate; const retailFacet = { row: { field: 'Region' }, column: { field: 'Channel' } }; const financeFacet = { row: { field: 'Market' }, column: { field: 'Desk' } }; const aggregate = cfg.aggregate === 'avg' ? Aggregate.avg : Aggregate.sum; switch (cfg.variant) { case 'retailSingle': return { values: [{ field: cfg.valueField, aggregate, }], category: { field: cfg.categoryField }, ...(cfg.detailAndColorBinding ? getDetailAndColorFields(cfg.detailAndColorField || cfg.categoryField) : (cfg.detailBinding === false ? {} : { details: getRetailDetailFields(cfg.categoryField) })), ...retailFacet, }; case 'retailRange': return { values: [{ vectors: { lower: { field: 'ForecastLow' }, upper: { field: 'ForecastHigh' }, }, aggregate: Aggregate.sum, }], category: { field: cfg.categoryField }, ...(cfg.detailAndColorBinding ? getDetailAndColorFields(cfg.detailAndColorField || cfg.categoryField) : (cfg.detailBinding === false ? {} : { details: getRetailDetailFields(cfg.categoryField) })), ...retailFacet, }; case 'percentMix': return { values: [{ field: 'MixValue', aggregate: Aggregate.sum, }], category: { field: cfg.categoryField }, details: [{ field: 'OfferType' }], color: { field: 'OfferType' }, ...retailFacet, }; case 'mixSingle': return { values: [{ field: 'MixValue', aggregate: Aggregate.sum, }], category: { field: cfg.categoryField }, details: [{ field: 'OfferType' }], color: { field: 'OfferType' }, ...retailFacet, }; case 'mixStacked': return { values: [{ field: 'MixValue', aggregate: Aggregate.sum, }], category: { field: cfg.categoryField }, details: [{ field: 'OfferType' }], color: { field: 'OfferType' }, ...retailFacet, }; case 'pieLike': return { values: [{ field: cfg.valueField, aggregate, }], category: { field: cfg.categoryField }, ...getDetailAndColorFields(cfg.detailAndColorField || cfg.categoryField), ...retailFacet, }; case 'radar': return { values: [{ field: 'MarginRate', aggregate: Aggregate.avg, }], category: { field: 'Month' }, details: [{ field: 'Quarter' }], color: { field: 'ProductLine' }, ...retailFacet, }; case 'hierarchy': return { values: [{ field: 'Revenue', aggregate: Aggregate.sum, }], details: [{ field: 'Quarter' }, { field: 'ProductLine' }], tooltip: [{ field: 'Quarter' }, { field: 'ProductLine' }, { field: 'Revenue' }], ...retailFacet, }; case 'scatter': return { values: [ { field: 'AcquisitionCost', aggregate: Aggregate.avg }, { field: 'LifetimeValue', aggregate: Aggregate.avg }, ], category: { field: 'Segment' }, details: [{ field: 'Lifecycle' }], color: { field: 'Segment' }, ...retailFacet, }; case 'bubble': return { values: [ { field: 'AcquisitionCost', aggregate: Aggregate.avg }, { field: 'LifetimeValue', aggregate: Aggregate.avg }, ], category: { field: 'Segment' }, details: [{ field: 'Lifecycle' }], size: { field: 'DealSize' }, color: { field: 'Segment' }, ...retailFacet, }; case 'funnel': return { values: [{ field: 'Count', aggregate: Aggregate.sum, }], category: { field: 'Stage' }, details: [{ field: 'Quarter' }], ...retailFacet, }; case 'waterfall': return { values: [{ field: 'Impact', aggregate: Aggregate.sum, }], category: { field: 'Driver' }, ...retailFacet, }; case 'stock': return { values: [{ vectors: { open: { field: 'open' }, high: { field: 'high' }, low: { field: 'low' }, close: { field: 'close' }, }, }], category: { field: 'Day' }, ...financeFacet, }; default: return {}; } } function getRetailDetailFields(categoryField) { if (categoryField === 'Quarter') { return [{ field: 'Month' }]; } return [{ field: 'Quarter' }]; } function getDetailAndColorFields(field) { return { details: [{ field }], color: { field }, }; } function buildPlotConfig(cfg) { if (cfg.type === GC.Spread.Sheets.DataCharts.DataChartType.treemap) { return { treemap: { header: { template: '{detailFields.value}', }, }, }; } return undefined; } function createSpreadAndDesigner() { const demoHost = document.getElementById('demo-host'); if (window !== top) { const spread = new GC.Spread.Sheets.Workbook('spread-host', { sheetCount: 1 }); return { spread }; } const designer = new GC.Spread.Sheets.Designer.Designer(demoHost, undefined, undefined, { sheetCount: 1 }); return { designer, spread: designer.getWorkbook(), }; } async function initDataManager(spread) { const dataManager = spread.dataManager(); await dataManager.addTable('RetailPerformance', { data: createRetailPerformanceData(), }).fetch(); await dataManager.addTable('RetailMix', { data: createRetailMixData(), }).fetch(); await dataManager.addTable('CustomerPortfolio', { data: createCustomerPortfolioData(), }).fetch(); await dataManager.addTable('SalesPipeline', { data: createSalesPipelineData(), }).fetch(); await dataManager.addTable('ProfitBridge', { data: createProfitBridgeData(), }).fetch(); await dataManager.addTable('StockPerformance', { data: createStockPerformanceData(), }).fetch(); } function createRetailPerformanceData() { const regions = [ { name: 'North America', factor: 1.18, marginLift: 0.01 }, { name: 'Europe', factor: 0.97, marginLift: -0.01 }, ]; const channels = [ { name: 'Retail', factor: 1.08, marginLift: 0.0 }, { name: 'Online', factor: 0.94, marginLift: 0.01 }, ]; const months = [ { month: 'Jan', quarter: 'Q1', base: 180, marginLift: 0.0 }, { month: 'Feb', quarter: 'Q1', base: 198, marginLift: 0.01 }, { month: 'Apr', quarter: 'Q2', base: 225, marginLift: 0.015 }, { month: 'May', quarter: 'Q2', base: 238, marginLift: 0.02 }, ]; const productLines = [ { name: 'Laptops', revenueFactor: 1.55, unitsFactor: 0.42, marginBase: 0.32 }, { name: 'Accessories', revenueFactor: 0.62, unitsFactor: 1.18, marginBase: 0.39 }, { name: 'Software', revenueFactor: 0.88, unitsFactor: 0.58, marginBase: 0.45 }, { name: 'Services', revenueFactor: 0.74, unitsFactor: 0.36, marginBase: 0.49 }, ]; const records = []; regions.forEach((region) => { channels.forEach((channel) => { months.forEach((period, monthIndex) => { productLines.forEach((product) => { const seasonalLift = 1 + monthIndex * 0.08; const revenue = Math.round(period.base * region.factor * channel.factor * product.revenueFactor * seasonalLift); records.push({ Region: region.name, Channel: channel.name, Quarter: period.quarter, Month: period.month, ProductLine: product.name, Revenue: revenue, UnitsSold: Math.round(revenue * product.unitsFactor), MarginRate: roundNumber(product.marginBase + period.marginLift + region.marginLift + channel.marginLift, 2), ForecastLow: Math.round(revenue * 0.88), ForecastHigh: Math.round(revenue * 1.12), }); }); }); }); }); return records; } function createRetailMixData() { const regions = [ { name: 'North America', factor: 1.16, servicesLift: 1.1 }, { name: 'Europe', factor: 0.95, servicesLift: 0.92 }, ]; const channels = [ { name: 'Retail', deviceLift: 1.18, accessoryLift: 1.08, softwareLift: 0.82, servicesLift: 0.9 }, { name: 'Online', deviceLift: 0.86, accessoryLift: 0.95, softwareLift: 1.24, servicesLift: 1.18 }, ]; const months = [ { month: 'Jan', quarter: 'Q1', device: 360, accessory: 118, software: 154, services: 86 }, { month: 'Feb', quarter: 'Q1', device: 335, accessory: 126, software: 172, services: 98 }, { month: 'Apr', quarter: 'Q2', device: 308, accessory: 138, software: 205, services: 126 }, { month: 'May', quarter: 'Q2', device: 286, accessory: 146, software: 232, services: 148 }, ]; const offerTypes = [ { name: 'Devices', key: 'device', channelLift: 'deviceLift' }, { name: 'Accessories', key: 'accessory', channelLift: 'accessoryLift' }, { name: 'Software', key: 'software', channelLift: 'softwareLift' }, { name: 'Services', key: 'services', channelLift: 'servicesLift', regionLift: 'servicesLift' }, ]; const records = []; regions.forEach((region) => { channels.forEach((channel) => { months.forEach((period) => { offerTypes.forEach((offer) => { const regionLift = offer.regionLift ? region[offer.regionLift] : 1; records.push({ Region: region.name, Channel: channel.name, Quarter: period.quarter, Month: period.month, OfferType: offer.name, MixValue: Math.round(period[offer.key] * region.factor * regionLift * channel[offer.channelLift]), }); }); }); }); }); return records; } function createCustomerPortfolioData() { const regions = [ { name: 'North America', costFactor: 1.12, valueFactor: 1.15 }, { name: 'Europe', costFactor: 0.94, valueFactor: 0.98 }, ]; const channels = [ { name: 'Retail', costFactor: 1.05, valueFactor: 0.92 }, { name: 'Online', costFactor: 0.9, valueFactor: 1.08 }, ]; const segments = [ { name: 'SMB', acquisitionCost: 68, lifetimeValue: 180, dealSize: 24 }, { name: 'Mid-Market', acquisitionCost: 92, lifetimeValue: 248, dealSize: 38 }, { name: 'Enterprise', acquisitionCost: 138, lifetimeValue: 332, dealSize: 56 }, { name: 'Strategic', acquisitionCost: 172, lifetimeValue: 405, dealSize: 74 }, ]; const lifecycleStages = [ { name: 'New', costFactor: 1.18, valueFactor: 0.72, dealFactor: 0.68 }, { name: 'Growing', costFactor: 0.98, valueFactor: 1.0, dealFactor: 1.0 }, { name: 'Renewal', costFactor: 0.74, valueFactor: 1.24, dealFactor: 1.18 }, ]; const records = []; regions.forEach((region) => { channels.forEach((channel) => { segments.forEach((segment, index) => { const segmentLift = 1 + index * 0.06; lifecycleStages.forEach((stage, stageIndex) => { records.push({ Region: region.name, Channel: channel.name, Segment: segment.name, Lifecycle: stage.name, AcquisitionCost: Math.round(segment.acquisitionCost * region.costFactor * channel.costFactor * segmentLift * stage.costFactor), LifetimeValue: Math.round(segment.lifetimeValue * region.valueFactor * channel.valueFactor * segmentLift * stage.valueFactor), DealSize: Math.round(segment.dealSize * region.valueFactor * segmentLift * stage.dealFactor + stageIndex * 2), }); }); }); }); }); return records; } function createSalesPipelineData() { const regions = [ { name: 'North America', factor: 1.16 }, { name: 'Europe', factor: 0.93 }, ]; const channels = [ { name: 'Retail', factor: 1.04 }, { name: 'Online', factor: 0.96 }, ]; const stages = [ { name: 'Prospects', factor: 1.0 }, { name: 'Qualified', factor: 0.73 }, { name: 'Proposal', factor: 0.54 }, { name: 'Negotiation', factor: 0.36 }, { name: 'Closed Won', factor: 0.24 }, ]; const quarters = [ { name: 'Q1', factor: 1.0 }, { name: 'Q2', factor: 1.12 }, ]; const records = []; regions.forEach((region) => { channels.forEach((channel) => { const base = 420 * region.factor * channel.factor; quarters.forEach((quarter, quarterIndex) => { stages.forEach((stage, index) => { records.push({ Region: region.name, Channel: channel.name, Quarter: quarter.name, Stage: stage.name, Count: Math.round(base * quarter.factor * stage.factor - index * 6 + quarterIndex * 10), }); }); }); }); }); return records; } function createProfitBridgeData() { const regions = [ { name: 'North America', factor: 1.12 }, { name: 'Europe', factor: 0.91 }, ]; const channels = [ { name: 'Retail', factor: 1.08 }, { name: 'Online', factor: 0.95 }, ]; const drivers = [ { name: 'Subscription Renewals', impact: 180 }, { name: 'Cross-sell Campaigns', impact: 95 }, { name: 'Logistics Inflation', impact: -72 }, { name: 'Support Hiring', impact: -48 }, { name: 'Price Optimization', impact: 126 }, { name: 'FX Impact', impact: -34 }, { name: 'Services Expansion', impact: 88 }, ]; const records = []; regions.forEach((region) => { channels.forEach((channel) => { drivers.forEach((driver, index) => { const channelBias = channel.name === 'Retail' ? 8 : -6; records.push({ Region: region.name, Channel: channel.name, Driver: driver.name, Impact: Math.round(driver.impact * region.factor * channel.factor + channelBias - index * 2), }); }); }); }); return records; } function createStockPerformanceData() { const markets = [ { name: 'NASDAQ', basePrice: 184, drift: 1.55 }, { name: 'Euronext', basePrice: 132, drift: 1.12 }, ]; const desks = [ { name: 'Institutional', offset: 1.8, volatility: 1.35 }, { name: 'Wealth', offset: -0.9, volatility: 0.95 }, ]; const days = ['2026-04-01', '2026-04-02', '2026-04-03', '2026-04-06', '2026-04-07', '2026-04-08', '2026-04-09', '2026-04-10']; const records = []; markets.forEach((market) => { desks.forEach((desk) => { days.forEach((day, index) => { const base = market.basePrice + desk.offset + index * market.drift; const swing = ((index % 3) - 1) * desk.volatility; const open = roundNumber(base + swing, 2); const close = roundNumber(open + (index % 2 === 0 ? 1.14 : -0.78) + desk.volatility * 0.18, 2); const high = roundNumber(Math.max(open, close) + 1.25 + desk.volatility * 0.45, 2); const low = roundNumber(Math.min(open, close) - 1.08 - desk.volatility * 0.35, 2); records.push({ Market: market.name, Desk: desk.name, Day: day, open, high, low, close, }); }); }); }); return records; } function roundNumber(value, digits) { return Number(value.toFixed(digits)); }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="spreadjs culture" content="ko-kr" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-designer/styles/gc.spread.sheets.designer.light.min.css"> <link rel="stylesheet" type="text/css" href="styles.css"> <script src="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-print/dist/gc.spread.sheets.print.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-datacharts-addon/dist/gc.spread.sheets.datacharts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-resources-ko/dist/gc.spread.sheets.resources.ko.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script> const designerDependencyScripts = [ '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-barcode/dist/gc.spread.sheets.barcode.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-pivot-addon/dist/gc.spread.pivot.pivottables.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-slicers/dist/gc.spread.sheets.slicers.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-reportsheet-addon/dist/gc.spread.report.reportsheet.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-formula-panel/dist/gc.spread.sheets.formulapanel.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-io/dist/gc.spread.sheets.io.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-designer-resources-ko/dist/gc.spread.sheets.designer.resource.ko.min.js', '$DEMOROOT$/ko/purejs/node_modules/@mescius/spread-sheets-designer/dist/gc.spread.sheets.designer.all.min.js', '$DEMOROOT$/spread/source/js/designer/license.js', ] function appendScriptNode (src) { const script = document.createElement('script'); script.src = src; script.async = false; script.type = 'text/javascript'; document.head.appendChild(script); } if (top === window) { // not in iframe designerDependencyScripts.forEach(appendScriptNode); } </script> <script src="app.js" type="text/javascript"></script> </head> <body> <div class="sample-tutorial"> <div id="demo-host"> <div id="spread-host"></div> <div id="panel-host"></div> </div> </div> </body> </html>
body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } #demo-host { width: 100%; height: 100%; overflow: hidden; float: left; } #spread-host { position: absolute; top: 0; left: 0; width: calc(100% - 380px); height: 100%; } #panel-host { position: absolute; top: 0; right: 0; width: 380px; height: 100%; }