Compare commits

...

6 Commits

@ -25,7 +25,7 @@ h2 {
line-height: 48px;
font-size: 28px;
margin: 0;
padding: 12px;
padding: 12px 24px;
}
.HeaderContent h2 {

@ -36,7 +36,7 @@ test("generateTableColumns", () => {
headerSort: false,
headerWordWrap: true,
hozAlign: "left",
minWidth: 75,
minWidth: 80,
resizable: false,
title: "1",
});

@ -5,7 +5,7 @@
*/
.SiteHeaderButton {
padding: 6px 36px;
padding: 6px 24px;
margin: 0 0 0 8px;
line-height: 24px;
border: var(--box-border);

@ -12,7 +12,11 @@ import {
} from "tabulator-tables";
import { ValidationError } from "../../../core/Data/validate";
import Papa from "papaparse";
import { parseBool, parseNumber } from "../../../core/Operations/commonOps";
import {
parseBool,
parseNullableNumber,
parseNumber,
} from "../../../core/Operations/commonOps";
import { UnitCommitmentScenario } from "../../../core/Data/types";
export interface ColumnSpec {
@ -243,6 +247,12 @@ export const parseCsv = (
data[name][spec.title] = val;
break;
}
case "number?": {
const [val, err] = parseNullableNumber(row[spec.title]);
if (err) return [null, { message: err.message + rowRef }];
data[name][spec.title] = val;
break;
}
case "busRef":
const busName = row[spec.title];
if (!(busName in scenario.Buses)) {
@ -359,6 +369,7 @@ const DataTable = (props: DataTableProps) => {
const tableContainerRef = useRef<HTMLDivElement | null>(null);
const tableRef = useRef<Tabulator | null>(null);
const [isTableBuilt, setTableBuilt] = useState<Boolean>(false);
const [activeCell, setActiveCell] = useState<CellComponent | null>(null);
useEffect(() => {
const onCellEdited = (cell: CellComponent) => {
@ -396,6 +407,7 @@ const DataTable = (props: DataTableProps) => {
const height = computeTableHeight(data);
if (tableRef.current === null) {
console.log("new Tabulator");
tableRef.current = new Tabulator(tableContainerRef.current, {
layout: "fitColumns",
data: data,
@ -411,13 +423,31 @@ const DataTable = (props: DataTableProps) => {
const newColumns = columns;
const newData = data;
const oldRows = tableRef.current.getRows();
const activeRowPosition = activeCell?.getRow().getPosition() as number;
const activeField = activeCell?.getField();
// Update data
tableRef.current.replaceData(newData).then(() => {});
// Restore active cell selection
if (activeCell) {
tableRef.current
?.getRowFromPosition(activeRowPosition!!)
?.getCell(activeField!!)
?.edit();
}
// Update columns
if (newColumns.length !== tableRef.current.getColumns().length) {
let newColCount = 0;
newColumns.forEach((col) => {
if (col.columns) newColCount += col.columns.length;
else newColCount += 1;
});
if (newColCount !== tableRef.current.getColumns().length) {
tableRef.current.setColumns(newColumns);
const rows = tableRef.current!.getRows()!;
const firstRow = rows[0];
if (firstRow) firstRow.scrollTo().then((r) => {});
}
// Update height
@ -435,15 +465,32 @@ const DataTable = (props: DataTableProps) => {
}, 10);
}
// Update callbacks
// Remove old callbacks
tableRef.current.off("cellEdited");
tableRef.current.off("cellEditing");
tableRef.current.off("cellEditCancelled");
// Set new callbacks
tableRef.current.on("cellEditing", (cell) => {
console.log("cellEditing", cell);
setActiveCell(cell);
});
tableRef.current.on("cellEditCancelled", (cell) => {
setActiveCell(null);
});
tableRef.current.on("cellEdited", (cell) => {
onCellEdited(cell);
});
}
}, [props, isTableBuilt]);
return <div className="tableContainer" ref={tableContainerRef} />;
return (
<div className="tableWrapper">
<div ref={tableContainerRef} />
</div>
);
};
export default DataTable;

@ -4,17 +4,21 @@
* Released under the modified BSD license. See COPYING.md for more details.
*/
.FormWrapper {
margin: 0 auto;
max-width: var(--site-max-width);
}
.Form {
background-color: var(--contrast-0);
border: var(--box-border);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
min-height: 48px;
margin: 0 auto;
margin: 0 12px;
min-width: var(--site-min-width);
max-width: var(--site-max-width);
max-height: 500px;
padding: 12px 0;
padding: 12px;
}
.FormRow {

@ -8,7 +8,11 @@ import { ReactNode } from "react";
import styles from "./Form.module.css";
function Form({ children }: { children: ReactNode }) {
return <div className={styles.Form}>{children}</div>;
return (
<div className={styles.FormWrapper}>
<div className={styles.Form}>{children}</div>
</div>
);
}
export default Form;

@ -4,16 +4,20 @@
* Released under the modified BSD license. See COPYING.md for more details.
*/
.tableWrapper {
margin: 0 auto;
max-width: var(--site-max-width);
}
.tabulator {
background-color: var(--contrast-0);
border: var(--box-border) !important;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
min-height: 48px;
margin: 0 auto;
min-width: var(--site-min-width);
max-width: var(--site-max-width);
padding: 0;
margin: 0 12px;
}
.tabulator .tabulator-header {

@ -6,7 +6,7 @@
import formStyles from "./Form.module.css";
import HelpButton from "../Buttons/HelpButton";
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { ValidationError } from "../../../core/Data/validate";
interface TextInputRowProps {
@ -21,6 +21,13 @@ function TextInputRow(props: TextInputRowProps) {
const [savedValue, setSavedValue] = useState(props.initialValue);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.value = props.initialValue;
}
setSavedValue(props.initialValue);
}, [props.initialValue]);
const onBlur = (event: React.FocusEvent<HTMLInputElement>) => {
const newValue = event.target.value;
if (newValue === savedValue) return;
@ -29,8 +36,8 @@ function TextInputRow(props: TextInputRowProps) {
inputRef.current!.value = savedValue;
return;
}
setSavedValue(newValue);
};
return (
<div className={formStyles.FormRow}>
<label>

@ -13,7 +13,7 @@
.SectionHeader h1 {
margin: 0;
padding: 0 12px;
padding: 0 24px;
font-size: 16px;
line-height: 64px;
}
@ -21,4 +21,5 @@
.SectionButtonsContainer {
float: right;
height: 64px;
margin-right: 12px;
}

@ -29,11 +29,23 @@ export const changeTimeHorizon = (
Object.values(newScenario.Buses).forEach((bus) => {
bus["Load (MW)"] = bus["Load (MW)"].slice(0, newT);
});
Object.values(newScenario.Generators).forEach((generator) => {
if (generator.Type === "Profiled") {
generator["Minimum power (MW)"] = generator["Minimum power (MW)"].slice(0, newT);
generator["Maximum power (MW)"] = generator["Maximum power (MW)"].slice(0, newT);
}
});
} else {
const padding = Array(newT - oldT).fill(0);
Object.values(newScenario.Buses).forEach((bus) => {
bus["Load (MW)"] = bus["Load (MW)"].concat(padding);
});
Object.values(newScenario.Generators).forEach((generator) => {
if (generator.Type === "Profiled") {
generator["Minimum power (MW)"] = generator["Minimum power (MW)"].concat(padding);
generator["Maximum power (MW)"] = generator["Maximum power (MW)"].concat(padding);
}
});
}
return [newScenario, null];
};
@ -110,6 +122,40 @@ export const changeTimeStep = (
};
}
const newGenerators: { [name: string]: any } = {};
for (const generatorName in scenario.Generators) {
const generator = scenario.Generators[generatorName]!;
if (generator.Type === "Profiled") {
// Build data_y for minimum power
const minPower = generator["Minimum power (MW)"];
const minData_y = Array(oldT + 1).fill(0);
for (let i = 0; i < oldT; i++) minData_y[i] = minPower[i];
minData_y[oldT] = minData_y[0];
// Build data_y for maximum power
const maxPower = generator["Maximum power (MW)"];
const maxData_y = Array(oldT + 1).fill(0);
for (let i = 0; i < oldT; i++) maxData_y[i] = maxPower[i];
maxData_y[oldT] = maxData_y[0];
// Run interpolation for both
const newMinPower = Array(newT).fill(0);
const newMaxPower = Array(newT).fill(0);
for (let i = 0; i < newT; i++) {
newMinPower[i] = evaluatePwlFunction(data_x, minData_y, newTimeStep * i);
newMaxPower[i] = evaluatePwlFunction(data_x, maxData_y, newTimeStep * i);
}
newGenerators[generatorName] = {
...generator,
"Minimum power (MW)": newMinPower,
"Maximum power (MW)": newMaxPower,
};
} else {
newGenerators[generatorName] = generator;
}
}
return [
{
...scenario,
@ -118,6 +164,7 @@ export const changeTimeStep = (
"Time step (min)": newTimeStep,
},
Buses: newBuses,
Generators: newGenerators,
},
null,
];

Loading…
Cancel
Save