Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple enhancements, adjustments, and bug fixes to frontend code #809

Merged
merged 8 commits into from
Dec 6, 2024
Merged
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
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) {
rubenthoms marked this conversation as resolved.
Show resolved Hide resolved
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();
}
rubenthoms marked this conversation as resolved.
Show resolved Hide resolved
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
Loading