Skip to content

Commit

Permalink
Multiple enhancements, adjustments, and bug fixes to frontend code (#809
Browse files Browse the repository at this point in the history
)
  • Loading branch information
rubenthoms authored Dec 6, 2024
1 parent 44ba1d9 commit 0eef0de
Show file tree
Hide file tree
Showing 40 changed files with 519 additions and 233 deletions.
8 changes: 7 additions & 1 deletion frontend/src/framework/ModuleDataTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export enum ModuleDataTagId {
OBSERVATIONS = "observations",
SEISMIC = "seismic",
WELL_COMPLETIONS = "well-completions",
VFP = "vfp"
VFP = "vfp",
POLYGONS = "polygons",
}

export type ModuleDataTag = {
Expand All @@ -30,6 +31,11 @@ export const ModuleDataTags: ModuleDataTag[] = [
name: "3D grid model",
description: "3D grid model",
},
{
id: ModuleDataTagId.POLYGONS,
name: "Polygons",
description: "Polygons",
},
{
id: ModuleDataTagId.GROUP_TREE,
name: "Group tree",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/framework/WorkbenchSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class WorkbenchSession {
}
}

function createEnsembleRealizationFilterFuncForWorkbenchSession(workbenchSession: WorkbenchSession) {
export function createEnsembleRealizationFilterFuncForWorkbenchSession(workbenchSession: WorkbenchSession) {
return function ensembleRealizationFilterFunc(ensembleIdent: EnsembleIdent): readonly number[] {
const realizationFilterSet = workbenchSession.getRealizationFilterSet();
const realizationFilter = realizationFilterSet.getRealizationFilterForEnsembleIdent(ensembleIdent);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import React from "react";

import { Ensemble } from "@framework/Ensemble";
import { EnsembleIdent } from "@framework/EnsembleIdent";
import { EnsembleSet } from "@framework/EnsembleSet";
import { ColorTile } from "@lib/components/ColorTile";
import { Dropdown, DropdownOption, DropdownProps } from "@lib/components/Dropdown";

type EnsembleDropdownProps = {
ensembleSet: EnsembleSet;
ensembles: readonly Ensemble[];
value: EnsembleIdent | null;
onChange: (ensembleIdent: EnsembleIdent | null) => void;
} & Omit<DropdownProps<string>, "options" | "value" | "onChange">;

export function EnsembleDropdown(props: EnsembleDropdownProps): JSX.Element {
const { ensembleSet, value, onChange, ...rest } = props;
const { onChange, value, ...rest } = props;

function handleSelectionChanged(selectedEnsembleIdentStr: string) {
const foundEnsemble = ensembleSet.findEnsembleByIdentString(selectedEnsembleIdentStr);
onChange(foundEnsemble ? foundEnsemble.getIdent() : null);
}
const handleSelectionChange = React.useCallback(
function handleSelectionChange(selectedEnsembleIdentStr: string) {
const foundEnsemble = props.ensembles.find(
(ensemble) => ensemble.getIdent().toString() === selectedEnsembleIdentStr
);
onChange(foundEnsemble ? foundEnsemble.getIdent() : null);
},
[props.ensembles, onChange]
);

const optionsArr: DropdownOption[] = [];
for (const ens of ensembleSet.getEnsembleArr()) {
for (const ens of props.ensembles) {
optionsArr.push({
value: ens.getIdent().toString(),
label: ens.getDisplayName(),
Expand All @@ -30,5 +37,5 @@ export function EnsembleDropdown(props: EnsembleDropdownProps): JSX.Element {
});
}

return <Dropdown options={optionsArr} value={value?.toString()} onChange={handleSelectionChanged} {...rest} />;
return <Dropdown options={optionsArr} value={value?.toString()} onChange={handleSelectionChange} {...rest} />;
}
37 changes: 21 additions & 16 deletions frontend/src/framework/components/EnsembleSelect/ensembleSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import React from "react";

import { Ensemble } from "@framework/Ensemble";
import { EnsembleIdent } from "@framework/EnsembleIdent";
import { EnsembleSet } from "@framework/EnsembleSet";
import { ColorTile } from "@lib/components/ColorTile";
import { Select, SelectOption, SelectProps } from "@lib/components/Select";

type EnsembleSelectProps = {
ensembleSet: EnsembleSet;
ensembles: readonly Ensemble[];
value: EnsembleIdent[];
onChange: (ensembleIdentArr: EnsembleIdent[]) => void;
} & Omit<SelectProps<string>, "options" | "value" | "onChange">;

export function EnsembleSelect(props: EnsembleSelectProps): JSX.Element {
const { ensembleSet, value, onChange, multiple, ...rest } = props;

function handleSelectionChanged(selectedEnsembleIdentStrArr: string[]) {
const identArr: EnsembleIdent[] = [];
for (const identStr of selectedEnsembleIdentStrArr) {
const foundEnsemble = ensembleSet.findEnsembleByIdentString(identStr);
if (foundEnsemble) {
identArr.push(foundEnsemble.getIdent());
export function EnsembleSelect(props: EnsembleSelectProps): React.ReactNode {
const { ensembles, value, onChange, multiple, ...rest } = props;

const handleSelectionChange = React.useCallback(
function handleSelectionChanged(selectedEnsembleIdentStrArr: string[]) {
const identArr: EnsembleIdent[] = [];
for (const identStr of selectedEnsembleIdentStrArr) {
const foundEnsemble = ensembles.find((ens) => ens.getIdent().toString() === identStr);
if (foundEnsemble) {
identArr.push(foundEnsemble.getIdent());
}
}
}

onChange(identArr);
}
onChange(identArr);
},
[ensembles, onChange]
);

const optionsArr: SelectOption[] = [];
for (const ens of ensembleSet.getEnsembleArr()) {
for (const ens of ensembles) {
optionsArr.push({
value: ens.getIdent().toString(),
label: ens.getDisplayName(),
Expand All @@ -48,7 +53,7 @@ export function EnsembleSelect(props: EnsembleSelectProps): JSX.Element {
<Select
options={optionsArr}
value={selectedArr}
onChange={handleSelectionChanged}
onChange={handleSelectionChange}
multiple={isMultiple}
{...rest}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ function DetailsPopup(props: DetailsPopupProps): React.ReactNode {
</div>
{makeDevState(props.module.getDevState())}
<div className="text-xs mt-2">{props.module.getDescription()}</div>
<div className="text-xs mt-2 flex gap-2 text-bold">{makeDataTags()}</div>
<div className="text-xs mt-2 flex gap-2 text-bold flex-wrap">{makeDataTags()}</div>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/framework/utils/colorPalettes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const defaultContinuousSequentialColorPalettes = [
export const defaultContinuousDivergingColorPalettes = [
new ColorPalette({
name: "Red to Blue",
colors: ["#b2182b", "#ef8a62", "#fddbc7", "#f8f6e9", "#d1e5f0", "#67a9cf", "#2166ac"],
colors: ["#b2182b", "#ef8a62", "#fddbc7", "#fff", "#d1e5f0", "#67a9cf", "#2166ac"],
id: "red-to-blue",
}),
new ColorPalette({
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/lib/components/Input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,22 @@ export const Input = React.forwardRef((props: InputProps, ref: React.ForwardedRe
setValue(event.target.value);

if (props.onChange) {
if (props.type === "number") {
let newValue = 0;

if (!isNaN(parseFloat(event.target.value as string))) {
newValue = parseFloat((event.target.value as string) || "0");
if (props.min !== undefined) {
newValue = Math.max(props.min, newValue);
}

if (props.max !== undefined) {
newValue = Math.min(props.max, newValue);
}
}

event.target.value = newValue.toString();
}
props.onChange(event);
}
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/components/MenuButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { MenuButton } from "./menuButton";
export type { MenuButtonProps } from "./menuButton";
19 changes: 19 additions & 0 deletions frontend/src/lib/components/MenuButton/menuButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

import { MenuButton as MuiMenuButton } from "@mui/base/MenuButton";

export type MenuButtonProps = {
ref?: React.Ref<HTMLButtonElement>;
label?: string;
disabled?: boolean;
children?: React.ReactNode;
};

export function MenuButton(props: MenuButtonProps): React.ReactNode {
return (
<MuiMenuButton
{...props}
className="hover:bg-blue-200 focus:outline-blue-600 p-1 text-sm rounded flex gap-1 items-center focus:outline focus:outline-1 hover:text-gray-900 text-gray-600"
/>
);
}
14 changes: 13 additions & 1 deletion frontend/src/lib/components/MenuHeading/menuHeading.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { resolveClassNames } from "@lib/utils/resolveClassNames";

export type MenuHeadingProps = {
classNames?: string;
style?: React.CSSProperties;
children: React.ReactNode;
};

export function MenuHeading(props: MenuHeadingProps): React.ReactNode {
return (
<div className="text-xs text-gray-500 uppercase font-semibold tracking-wider px-3 py-1">{props.children}</div>
<div
className={resolveClassNames(
"text-xs text-gray-500 uppercase font-semibold tracking-wider px-3 py-1",
props.classNames ?? ""
)}
style={props.style}
>
{props.children}
</div>
);
}
4 changes: 2 additions & 2 deletions frontend/src/lib/components/SortableList/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { SortableList } from "./sortableList";
export { SortableList, ItemType } from "./sortableList";
export { SortableListItem } from "./sortableListItem";
export { SortableListGroup } from "./sortableListGroup";

export type { SortableListProps } from "./sortableList";
export type { SortableListProps, IsMoveAllowedArgs } from "./sortableList";
export type { SortableListItemProps } from "./sortableListItem";
export type { SortableListGroupProps } from "./sortableListGroup";
31 changes: 20 additions & 11 deletions frontend/src/lib/components/SortableList/sortableListGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import { createPortal } from "@lib/utils/createPortal";
import { resolveClassNames } from "@lib/utils/resolveClassNames";
import { DragIndicator, ExpandLess, ExpandMore } from "@mui/icons-material";

import { isEqual } from "lodash";

import { HoveredArea, SortableListContext } from "./sortableList";
import { SortableListDropIndicator } from "./sortableListDropIndicator";
import { SortableListItemProps } from "./sortableListItem";

import { DenseIconButton } from "../DenseIconButton";

export type SortableListGroupProps = {
id: string;
title: React.ReactNode;
initiallyExpanded?: boolean;
expanded?: boolean;
startAdornment?: React.ReactNode;
endAdornment?: React.ReactNode;
headerStyle?: React.CSSProperties;
Expand All @@ -26,18 +30,24 @@ export type SortableListGroupProps = {
* @param {SortableListGroupProps} props Object of properties for the SortableListGroup component (see below for details).
* @param {string} props.id ID that is unique among all components inside the sortable list.
* @param {React.ReactNode} props.title Title of the list item.
* @param {boolean} props.initiallyExpanded Whether the group should be expanded by default.
* @param {boolean} props.expanded Whether the group should be expanded.
* @param {React.ReactNode} props.startAdornment Start adornment to display to the left of the title.
* @param {React.ReactNode} props.endAdornment End adornment to display to the right of the title.
* @param {React.CSSProperties} props.headerStyle Style object to apply to the header of the group.
* @param {React.CSSProperties} props.contentStyle Style object to apply to the content of the group.
* @param {React.ReactNode} props.contentWhenEmpty Content to display when the group is empty.
* @param {React.ReactNode} props.children Child components to display as the content of the list item.
*
* @returns {React.ReactNode} A sortable list group component.
*/
export function SortableListGroup(props: SortableListGroupProps): React.ReactNode {
const [isExpanded, setIsExpanded] = React.useState<boolean>(props.initiallyExpanded ?? true);
const [isExpanded, setIsExpanded] = React.useState<boolean>(props.expanded ?? true);
const [prevExpanded, setPrevExpanded] = React.useState<boolean | undefined>(props.expanded);

if (!isEqual(props.expanded, prevExpanded)) {
if (props.expanded !== undefined) {
setIsExpanded(props.expanded);
}
setPrevExpanded(props.expanded);
}

const divRef = React.useRef<HTMLDivElement>(null);
const boundingClientRect = useElementBoundingRect(divRef);
Expand Down Expand Up @@ -71,11 +81,11 @@ export function SortableListGroup(props: SortableListGroupProps): React.ReactNod
})}
></div>
<Header
{...props}
onToggleExpanded={handleToggleExpanded}
expanded={isExpanded}
expandable={hasContent}
hovered={isHeaderHovered}
{...props}
/>
{isDragging &&
dragPosition &&
Expand Down Expand Up @@ -143,17 +153,16 @@ function Header(props: HeaderProps): React.ReactNode {
<DragIndicator fontSize="inherit" className="pointer-events-none" />
</div>
{props.expandable && (
<div
className="hover:cursor-pointer hover:text-blue-800 p-0.5 rounded"
<DenseIconButton
onClick={props.onToggleExpanded}
title={props.expanded ? "Hide children" : "Show children"}
>
{props.expanded ? <ExpandLess fontSize="inherit" /> : <ExpandMore fontSize="inherit" />}
</div>
</DenseIconButton>
)}
<div className="flex items-center gap-2 flex-grow">
<div className="flex items-center gap-2 flex-grow min-w-0">
{props.startAdornment}
<div className="flex-grow font-bold">{props.title}</div>
<div className="flex-grow font-bold min-w-0">{props.title}</div>
{props.endAdornment}
</div>
</div>
Expand Down
9 changes: 3 additions & 6 deletions frontend/src/lib/components/SortableList/sortableListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export type SortableListItemProps = {
* @param {SortableListItemProps} props Object of properties for the SortableListItem component (see below for details).
* @param {string} props.id ID that is unique among all components inside the sortable list.
* @param {React.ReactNode} props.title Title component of the list item.
* @param {string} props.headerClassNames Class names to apply to the header of the list item.
* @param {React.ReactNode} props.startAdornment Start adornment to display to the left of the title.
* @param {React.ReactNode} props.endAdornment End adornment to display to the right of the title.
* @param {React.ReactNode} props.children Child components to display as the content of the list item.
Expand Down Expand Up @@ -69,9 +68,7 @@ export function SortableListItem(props: SortableListItemProps): React.ReactNode
<Header {...props} />
</div>
)}
{props.children !== undefined && (
<div className={resolveClassNames("bg-white border-b shadow")}>{props.children}</div>
)}
<div className={resolveClassNames("bg-white border-b shadow")}>{props.children}</div>
</div>
{isHovered && sortableListContext.hoveredArea === HoveredArea.BOTTOM && <SortableListDropIndicator />}
</>
Expand All @@ -96,9 +93,9 @@ function Header(props: HeaderProps): React.ReactNode {
<div className={resolveClassNames("sortable-list-element-indicator hover:cursor-grab")}>
<DragIndicator fontSize="inherit" className="pointer-events-none" />
</div>
<div className="flex items-center gap-2 flex-grow">
<div className="flex items-center gap-2 flex-grow min-w-0">
{props.startAdornment}
<div className="flex-grow">{props.title}</div>
<div className="flex-grow min-w-0">{props.title}</div>
{props.endAdornment}
</div>
</div>
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/lib/utils/ColorPalette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export type ColorPaletteOptions = {
id: string;
};

export type ColorPaletteSerialization = {
name: string;
colors: string[];
id: string;
};

export class ColorPalette {
private _id: string;
private _name: string;
Expand Down Expand Up @@ -123,4 +129,20 @@ export class ColorPalette {

return gradient;
}

serialize(): ColorPaletteSerialization {
return {
name: this._name,
colors: this.getColors(),
id: this._id,
};
}

static fromSerialized(serialized: ColorPaletteSerialization): ColorPalette {
return new ColorPalette({
name: serialized.name,
colors: serialized.colors,
id: serialized.id,
});
}
}
Loading

0 comments on commit 0eef0de

Please sign in to comment.