Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/components/Configure/Configure.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ function Configure(props) {
props.changeSettings(true);
}

function toggleSheetIsImageHandler(sheetIdx, colIdx) {
console.log('[Configure.js] toggleSheetIsImageHandler', sheetIdx);
const meta = props.meta;
const sheet = meta[sheetIdx];
sheet.columns[colIdx].isImage = !sheet.columns[colIdx].isImage;
props.updateMeta(meta);
props.changeSettings(true);
}

function array_move(arr, old_index, new_index) {
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
Expand Down Expand Up @@ -204,7 +213,7 @@ function Configure(props) {
tabs={tabs}
><div style={configBody}>
{ tab === 0 ? <SelectSheets sheets={props.meta} selectSheet={selectSheetHandler} changeOrder={changeSheetOrderHandler} changeName={changeSheetNameHandler} /> : null }
{ tab === 1 ? <SelectColumns sheets={props.meta} colSelect={selectColumnHandler} changeName={changeColumnNameHandler} changeOrder={changeColumnOrderHandler}/> : null }
{ tab === 1 ? <SelectColumns sheets={props.meta} colSelect={selectColumnHandler} changeName={changeColumnNameHandler} changeOrder={changeColumnOrderHandler} toggleIsImage={toggleSheetIsImageHandler} /> : null }
{ tab === 2 ? <ConfigureTab label={props.label} filename={props.filename} style={props.style} updateLabel={updateLabelHandler} updateButtonStyle={updateButtonStyleHandler} updateFilename={updateFilenameHandler}/> : null }
</div>
</Tabs>
Expand Down
1 change: 1 addition & 0 deletions src/components/Configure/SelectColumns/SelectColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function SelectColumns(props) {
colSelect={props.colSelect}
changeName={props.changeName}
changeOrder={props.changeOrder}
toggleIsImage={props.toggleIsImage}
/>
</div>
);
Expand Down
12 changes: 11 additions & 1 deletion src/components/Configure/SelectColumns/Sheet/Column/Column.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const useStyles = makeStyles(theme => ({
alignItems: 'center',
},
column: {
flexBasis: '50%',
flexBasis: '33%',
},
label: {
display: 'block',
Expand Down Expand Up @@ -106,6 +106,16 @@ function Column(props) {
<Stepper min={1} max={props.cols.length} step={1} pageSteps={1} value={props.id + 1} onValueChange={value => props.changeOrder(value)} className={classes.stepper} />
</div>
</div>
<div className={classes.column}>
<div className={classes.group}>
<label className={classes.label}>Is Image</label>
<Checkbox
checked={props.isImage}
onChange={props.toggleIsImage}
>
</Checkbox>
</div>
</div>
</AccordionDetails>
</ExpansionPanel>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Configure/SelectColumns/Sheet/Sheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ function Sheets(props) {
key={col.index}
name={col.name}
rename={col.changeName}
isImage={col.isImage}
selected={col.selected}
select={() => props.colSelect(props.id, index)}
toggleIsImage={() => props.toggleIsImage(props.id, index)}
changeName={(name) => props.changeName(props.id, index, name)}
cols={props.cols}
changeOrder={(newPos) => props.changeOrder(props.id, index, newPos)}
Expand Down
137 changes: 124 additions & 13 deletions src/components/func/func.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import XLSX from 'xlsx';
import ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';

// Declare this so our linter knows that tableau is a global object
Expand Down Expand Up @@ -76,6 +77,7 @@ const getSheetColumns = (sheet, existingCols, modified) => new Promise((resolve,
col.dataType = columns[j].dataType;
col.changeName = null;
col.selected = false;
col.isImage = columns[j].isImage;
cols.push(col);
}
for (var i = 0; i < existingCols.length; i++) {
Expand All @@ -93,6 +95,7 @@ const getSheetColumns = (sheet, existingCols, modified) => new Promise((resolve,
ret.selected = existingCols[eIdx].selected;
ret.changeName = existingCols[eIdx].changeName;
ret.order = eIdx;
ret.isImage = existingCols[eIdx].isImage;
} else {
ret.order = maxPos;
maxPos += 1;
Expand All @@ -106,6 +109,7 @@ const getSheetColumns = (sheet, existingCols, modified) => new Promise((resolve,
newCol.name = columns[k].fieldName;
newCol.dataType = columns[k].dataType;
newCol.selected = true;
newCol.isImage = columns[k].isImage;
newCol.order = k + 1;
cols.push(newCol);
}
Expand Down Expand Up @@ -200,25 +204,54 @@ const revalidateMeta = (existing) => new Promise((resolve, reject) => {
});
});

const exportToExcel = (meta, env, filename) => new Promise((resolve, reject) => {
let xlsFile = "export.xlsx";
if (filename && filename.length > 0) {
xlsFile = filename + ".xlsx";
}
buildExcelBlob(meta).then(wb => {
// add ignoreEC:false to prevent excel crashes during text to column
var wopts = { bookType:'xlsx', bookSST:false, type:'array', ignoreEC:false };
var wbout = XLSX.write(wb,wopts);
saveAs(new Blob([wbout],{type:"application/octet-stream"}), xlsFile);
resolve();
const exportToExcel = async (meta, env, filename) =>
new Promise(async (resolve, reject) => {
let xlsFile = "export.xlsx";
if (filename && filename.length > 0) {
xlsFile = filename + ".xlsx";
}
if (hasEmbedImage(meta)) {
const workbook = new ExcelJS.Workbook();
await buildExcelBlobWithEmbedImage(workbook, meta);
const buffer = await workbook.xlsx.writeBuffer();
saveAs(
new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}),
xlsFile
);
resolve();
} else {
buildExcelBlob(meta).then(wb => {
// add ignoreEC:false to prevent excel crashes during text to column
var wopts = { bookType:'xlsx', bookSST:false, type:'array', ignoreEC:false };
var wbout = XLSX.write(wb,wopts);
saveAs(new Blob([wbout],{type:"application/octet-stream"}), xlsFile);
resolve();
});
};
});
});

const hasEmbedImage = (meta) => {
let hasImage = false;
console.log("[func.js] Checking for images in meta", meta);
meta.forEach((sheet) => {
if (sheet && sheet.columns) {
sheet.columns.forEach((col) => {
console.log("[func.js] Checking column", col);
if (col && col.isImage) {
hasImage = true;
}
});
}
});
return hasImage;
};


// krisd: move excel creation to caller (to support extra export to methodss)
// callback receives a blob to save or transfer
const buildExcelBlob = (meta) => new Promise((resolve, reject) => {
const buildExcelBlob = async (meta) => new Promise((resolve, reject) => {
console.log("[func.js] Got Meta", meta);
// func.saveSettings(meta, function(newSettings) {
// console.log("Saved settings", newSettings);
Expand Down Expand Up @@ -283,6 +316,84 @@ const buildExcelBlob = (meta) => new Promise((resolve, reject) => {
});
});

const buildExcelBlobWithEmbedImage = async (workbook, meta) => {
console.log("[func.js] buildExcelBlob: Got Meta", meta);
const worksheets = tableau.extensions.dashboardContent.dashboard.worksheets;

for (const sheetMeta of meta) {
// 選択されていないシートはスキップ
if (!sheetMeta.selected) continue;

let tabName = sheetMeta.changeName || sheetMeta.sheetName;
tabName = tabName.replace(/[*?/\\[\]]/gi, '');

const tableauSheet = worksheets.find(s => s.name === sheetMeta.sheetName);
if (!tableauSheet) continue;

const summaryData = await tableauSheet.getSummaryDataAsync({ ignoreSelection: true });
const columns = summaryData.columns.map(col => {
const cMeta = sheetMeta.columns.find(x => x.name === col.fieldName) || {};
return {
...col,
selected: cMeta.selected,
outputName: cMeta.changeName || cMeta.name,
isImage: cMeta.isImage || false // 画像列フラグ
};
});

// 実データ部分をdecode
const decodedRows = await decodeDataset(columns, summaryData.data);

const newSheet = workbook.addWorksheet(tabName);

// まずはヘッダー行を設定(画像列含む全選択カラムのheaderを準備)
const selectedCols = columns.filter(c => c.selected);
const headers = selectedCols.map(c => c.outputName);
newSheet.addRow(headers); // 1行目にヘッダー

for (let rIndex = 0; rIndex < decodedRows.length; rIndex++) {
const rowObj = decodedRows[rIndex];
const rowValues = [];
selectedCols.forEach((col) => {
if (!col.isImage) {
rowValues.push(rowObj[col.outputName]?.v ?? null);
} else {
rowValues.push(null);
}
});

const newRow = newSheet.addRow(rowValues);

await Promise.all(selectedCols.map(async (col, colIndex) => {
if (!col.isImage) return;

const cellData = rowObj[col.outputName];
const imageUrl = cellData?.v;
if (!imageUrl) return;

try {
const resp = await fetch(imageUrl);
const arrayBuf = await resp.arrayBuffer();
const imageId = workbook.addImage({
buffer: arrayBuf,
extension: 'jpeg'
});
newSheet.addImage(imageId, {
tl: { col: colIndex, row: (1 + (rIndex + 1)) },
ext: { width: 50, height: 50 },
editAs: 'oneCell'
});
} catch (err) {
console.warn('Image fetch error:', err);
newRow.getCell(colIndex + 1).value = 'Image Error';
}
}));
}

const headerRow = newSheet.getRow(1);
headerRow.font = { bold: true };
}
};

// krisd: Remove recursion to work with larger data sets
// and translate cell data types
Expand Down