From 36e1f4cef7887eb603bc060a99076ec56a6bdb0e Mon Sep 17 00:00:00 2001 From: Daven Quinn Date: Wed, 30 Oct 2024 05:40:15 -0500 Subject: [PATCH 1/3] Fixed entity styles --- pages/integrations/xdd/extractions/lib/index.ts | 3 +-- .../xdd/extractions/lib/main.module.sass | 2 +- .../xdd/feedback/@sourceTextID/+Page.client.ts | 14 ++++++++++++-- .../xdd/feedback/@sourceTextID/lib/edit-state.ts | 1 - 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pages/integrations/xdd/extractions/lib/index.ts b/pages/integrations/xdd/extractions/lib/index.ts index 7e79263e..81019701 100644 --- a/pages/integrations/xdd/extractions/lib/index.ts +++ b/pages/integrations/xdd/extractions/lib/index.ts @@ -130,8 +130,7 @@ export function EntityTag({ data, highlighted = true, active = false }) { return h(Tag, { style, className }, [ h("span.entity-name", name), " ", - h("code.entity-type.bp5-code", type.name), - h(Match, { data: match }), + h("code.entity-type.bp5-code", [type.name, h(Match, { data: match })]), ]); } diff --git a/pages/integrations/xdd/extractions/lib/main.module.sass b/pages/integrations/xdd/extractions/lib/main.module.sass index f308b4df..d8630eef 100644 --- a/pages/integrations/xdd/extractions/lib/main.module.sass +++ b/pages/integrations/xdd/extractions/lib/main.module.sass @@ -7,4 +7,4 @@ .entity margin: 0.2em 0 0.5em - + padding-right: 3px diff --git a/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts b/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts index 38bd4375..90293c4f 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts @@ -10,8 +10,14 @@ import { } from "../../extractions/lib/data-service"; import { FeedbackComponent } from "./lib"; import { Intent, NonIdealState, OverlaysProvider } from "@blueprintjs/core"; -import { ErrorBoundary, Pagination } from "@macrostrat/ui-components"; +import { + ErrorBoundary, + FlexRow, + Pagination, + Spacer, +} from "@macrostrat/ui-components"; import { useState } from "react"; +import { AuthStatus } from "@macrostrat/auth-components"; /** * Get a single text window for feedback purposes @@ -22,7 +28,11 @@ export function Page() { OverlaysProvider, h(ContentPage, [ h(PageBreadcrumbs), - h("h1", "Feedback"), + h(FlexRow, { alignItems: "center" }, [ + h("h1", "Feedback"), + h(Spacer), + h(AuthStatus), + ]), h(ExtractionIndex), ]) ); diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts index ff68d39d..c61b1c5a 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts @@ -93,7 +93,6 @@ async function treeActionHandler( }, body: JSON.stringify(data), }); - console.log(response); if (!response.ok) { throw new Error("Failed to save model information"); } From 4c026e2ac7d2e411587c1b138e096b5844c22037 Mon Sep 17 00:00:00 2001 From: Daven Quinn Date: Wed, 30 Oct 2024 06:14:27 -0500 Subject: [PATCH 2/3] Better opening of selector --- .../integrations/xdd/extractions/lib/index.ts | 20 +++++++++-- pages/integrations/xdd/feedback/+title.ts | 1 + .../feedback/@sourceTextID/lib/edit-state.ts | 34 +++++++++++++++++-- .../xdd/feedback/@sourceTextID/lib/index.ts | 25 ++++++++++---- .../xdd/feedback/@sourceTextID/lib/node.ts | 12 ++++++- 5 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 pages/integrations/xdd/feedback/+title.ts diff --git a/pages/integrations/xdd/extractions/lib/index.ts b/pages/integrations/xdd/extractions/lib/index.ts index 81019701..07b445d0 100644 --- a/pages/integrations/xdd/extractions/lib/index.ts +++ b/pages/integrations/xdd/extractions/lib/index.ts @@ -115,7 +115,12 @@ export function ModelInfo({ data }) { return h("p.model-name", ["Model: ", h("code.bp5-code", data.name)]); } -export function EntityTag({ data, highlighted = true, active = false }) { +export function EntityTag({ + data, + highlighted = true, + active = false, + onClickType, +}) { const { name, type, match } = data; const className = classNames( { @@ -130,7 +135,18 @@ export function EntityTag({ data, highlighted = true, active = false }) { return h(Tag, { style, className }, [ h("span.entity-name", name), " ", - h("code.entity-type.bp5-code", [type.name, h(Match, { data: match })]), + h( + "code.entity-type.bp5-code", + { + onClick(evt) { + if (active && onClickType != null) { + onClickType(type); + evt.stopPropagation(); + } + }, + }, + [type.name, h(Match, { data: match })] + ), ]); } diff --git a/pages/integrations/xdd/feedback/+title.ts b/pages/integrations/xdd/feedback/+title.ts new file mode 100644 index 00000000..87150fc2 --- /dev/null +++ b/pages/integrations/xdd/feedback/+title.ts @@ -0,0 +1 @@ +export const title = "Feedback"; diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts index c61b1c5a..7d58538d 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts @@ -1,5 +1,11 @@ import { TreeData } from "./types"; -import { Dispatch, useCallback, useReducer } from "react"; +import { + createContext, + Dispatch, + useCallback, + useContext, + useReducer, +} from "react"; import update, { Spec } from "immutability-helper"; import { EntityType } from "#/integrations/xdd/extractions/lib/data-service"; import { knowledgeGraphAPIURL } from "@macrostrat-web/settings"; @@ -12,6 +18,7 @@ interface TreeState { entityTypesMap: Map; selectedEntityType: EntityType; lastInternalId: number; + isSelectingEntityType: boolean; } type TextRange = { @@ -37,6 +44,7 @@ type TreeAction = | { type: "toggle-node-selected"; payload: { ids: number[] } } | { type: "create-node"; payload: TextRange } | { type: "select-entity-type"; payload: EntityType } + | { type: "toggle-entity-type-selector"; payload?: boolean | null } | { type: "reset" }; export type TreeDispatch = Dispatch; @@ -55,6 +63,7 @@ export function useUpdatableTree( entityTypesMap: entityTypes, selectedEntityType: type, lastInternalId: 0, + isSelectingEntityType: false, }); const handler = useCallback( @@ -72,6 +81,16 @@ export function useUpdatableTree( const AppToaster = Toaster.create(); +export const TreeDispatchContext = createContext(null); + +export function useTreeDispatch() { + const dispatch = useContext(TreeDispatchContext); + if (dispatch == null) { + throw new Error("No dispatch context available"); + } + return dispatch; +} + async function treeActionHandler( action: TreeAsyncAction | TreeAction ): Promise { @@ -116,6 +135,7 @@ async function treeActionHandler( } function treeReducer(state: TreeState, action: TreeAction) { + console.log(action); switch (action.type) { case "move-node": // For each node in the tree, if the node is in the dragIds, remove it from the tree and collect it @@ -159,8 +179,9 @@ function treeReducer(state: TreeState, action: TreeAction) { ), }; case "select-node": - return { ...state, selectedNodes: action.payload.ids }; - + const { ids } = action.payload; + return { ...state, selectedNodes: ids }; + // otherwise fall through to toggle-node-selected for a single ID case "toggle-node-selected": const nodesToAdd = action.payload.ids.filter( (id) => !state.selectedNodes.includes(id) @@ -187,6 +208,13 @@ function treeReducer(state: TreeState, action: TreeAction) { selectedNodes: [newId], lastInternalId: newId, }; + + /** Entity type selection */ + case "toggle-entity-type-selector": + return { + ...state, + isSelectingEntityType: action.payload ?? !state.isSelectingEntityType, + }; case "select-entity-type": { // For each selected node, update the type let newTree2 = state.tree; diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts index 9400b11a..b62fcfcd 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts @@ -4,7 +4,7 @@ import Node from "./node"; import { FeedbackText } from "./text-visualizer"; import { Entity, InternalEntity, TreeData } from "./types"; import { ModelInfo } from "#/integrations/xdd/extractions/lib"; -import { useUpdatableTree } from "./edit-state"; +import { TreeDispatchContext, useUpdatableTree } from "./edit-state"; import { useEffect, useRef, useState } from "react"; import { DataField } from "~/components/unit-details"; import { ButtonGroup, Card } from "@blueprintjs/core"; @@ -34,9 +34,10 @@ export function FeedbackComponent({ entityTypes ); - const { selectedNodes, tree, selectedEntityType } = state; + const { selectedNodes, tree, selectedEntityType, isSelectingEntityType } = + state; - return h("div", [ + return h(TreeDispatchContext.Provider, { value: dispatch }, [ h(FeedbackText, { text, dispatch, @@ -89,6 +90,9 @@ export function FeedbackComponent({ onChange(payload) { dispatch({ type: "select-entity-type", payload }); }, + isOpen: isSelectingEntityType, + setOpen: (isOpen: boolean) => + dispatch({ type: "toggle-entity-type-selector", payload: isOpen }), }), ]), h(ManagedSelectionTree, { @@ -109,8 +113,13 @@ function processEntity(entity: Entity): InternalEntity { }; } -function EntityTypeSelector({ entityTypes, selected, onChange }) { - const [isOpen, setOpen] = useState(false); +function EntityTypeSelector({ + entityTypes, + selected, + isOpen, + setOpen, + onChange, +}) { // Show all entity types when selected is null const _selected = selected != null ? selected : undefined; return h(DataField, { label: "Entity type", inline: true }, [ @@ -180,7 +189,11 @@ function ManagedSelectionTree(props) { }); }, onSelect(nodes) { - const ids = nodes.map((d) => parseInt(d.id)); + let ids = nodes.map((d) => parseInt(d.id)); + if (ids.length == 1 && ids[0] == selectedNodes[0]) { + // Deselect + ids = []; + } dispatch({ type: "select-node", payload: { ids } }); }, children: Node, diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/node.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/node.ts index c8f6680a..637a6b70 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/node.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/node.ts @@ -2,6 +2,7 @@ import { NodeApi, TreeApi } from "react-arborist"; import { TreeData } from "./types"; import h from "./feedback.module.sass"; import { EntityTag } from "../../../extractions/lib"; +import { useTreeDispatch } from "./edit-state"; function isSelected(searchNode: TreeData, treeNode: TreeData) { return searchNode.id == treeNode.id; @@ -41,10 +42,19 @@ function Node({ node, style, dragHandle, tree }: any) { let highlighted: boolean = isNodeHighlighted(node, tree); let active: boolean = isNodeActive(node, tree); + const dispatch = useTreeDispatch(); + return h( "div.node", { style, ref: dragHandle }, - h(EntityTag, { data: node.data, active, highlighted }) + h(EntityTag, { + data: node.data, + active, + highlighted, + onClickType() { + dispatch({ type: "toggle-entity-type-selector" }); + }, + }) ); } From 8d8cf7d613a1b9d2e04042c1cd2ccc5baed648c9 Mon Sep 17 00:00:00 2001 From: Daven Quinn Date: Wed, 30 Oct 2024 10:46:57 -0500 Subject: [PATCH 3/3] Updated knowledge graph state management --- .../feedback/@sourceTextID/+Page.client.ts | 24 ++-- .../feedback/@sourceTextID/lib/edit-state.ts | 3 + .../@sourceTextID/lib/feedback.module.sass | 29 +++- .../xdd/feedback/@sourceTextID/lib/index.ts | 124 ++++++++++-------- 4 files changed, 115 insertions(+), 65 deletions(-) diff --git a/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts b/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts index 90293c4f..9f4d91dc 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts @@ -1,5 +1,5 @@ -import h from "@macrostrat/hyper"; -import { ContentPage } from "~/layouts"; +import h from "./lib/feedback.module.sass"; +import { ContentPage, FullscreenPage } from "~/layouts"; import { PageBreadcrumbs } from "~/components"; import { usePageContext } from "vike-react/usePageContext"; import { enhanceData } from "../../extractions/lib"; @@ -11,7 +11,9 @@ import { import { FeedbackComponent } from "./lib"; import { Intent, NonIdealState, OverlaysProvider } from "@blueprintjs/core"; import { + Box, ErrorBoundary, + FlexCol, FlexRow, Pagination, Spacer, @@ -26,14 +28,16 @@ import { AuthStatus } from "@macrostrat/auth-components"; export function Page() { return h( OverlaysProvider, - h(ContentPage, [ - h(PageBreadcrumbs), - h(FlexRow, { alignItems: "center" }, [ - h("h1", "Feedback"), - h(Spacer), - h(AuthStatus), + h(FullscreenPage, [ + h("div.feedback-main", [ + h(PageBreadcrumbs), + h(FlexRow, { alignItems: "center" }, [ + h("h1", "Feedback"), + h(Spacer), + h(AuthStatus), + ]), + h(ExtractionIndex), ]), - h(ExtractionIndex), ]) ); } @@ -67,7 +71,7 @@ function MultiFeedbackInterface({ data, models, entityTypes }) { const currentData = data[ix]; const count = data.length; - return h("div.feedback", [ + return h("div.feedback-interface", [ h.if(data.length > 1)([ h(NonIdealState, { icon: "warning-sign", diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts index 7d58538d..1364cfba 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/edit-state.ts @@ -45,6 +45,7 @@ type TreeAction = | { type: "create-node"; payload: TextRange } | { type: "select-entity-type"; payload: EntityType } | { type: "toggle-entity-type-selector"; payload?: boolean | null } + | { type: "deselect" } | { type: "reset" }; export type TreeDispatch = Dispatch; @@ -232,6 +233,8 @@ function treeReducer(state: TreeState, action: TreeAction) { selectedEntityType: action.payload, }; } + case "deselect": + return { ...state, selectedNodes: [] }; case "reset": return { ...state, diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/feedback.module.sass b/pages/integrations/xdd/feedback/@sourceTextID/lib/feedback.module.sass index 95adcbed..61c6eac0 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/feedback.module.sass +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/feedback.module.sass @@ -14,7 +14,6 @@ .entity-panel position: relative - padding: 1em .control-panel max-width: 15em @@ -23,3 +22,31 @@ right: 1em padding: 0.2em 0.5em + +.feedback-main + display: flex + flex-direction: column + max-width: 70em + margin: 0 auto + position: relative + max-height: 100% + +.feedback-interface + display: flex + flex-direction: column + flex: 1 + min-height: 100px + + +.entity-panel + flex: 1 + min-height: 100px + padding: 1em + background: var(--panel-secondary-background-color) + border-radius: 4px + // Inset box shadow + box-shadow: 0 0 0 1px var(--panel-border-color) inset + +.selection-tree + margin: -1em 0 + padding: 1em 0 diff --git a/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts b/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts index b62fcfcd..cae61521 100644 --- a/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts +++ b/pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts @@ -10,6 +10,7 @@ import { DataField } from "~/components/unit-details"; import { ButtonGroup, Card } from "@blueprintjs/core"; import { OmniboxSelector } from "./type-selector"; import { CancelButton, SaveButton } from "@macrostrat/ui-components"; +import useElementDimensions from "use-element-dimensions"; function setsAreTheSame(a: Set, b: Set) { if (a.size !== b.size) return false; @@ -37,6 +38,8 @@ export function FeedbackComponent({ const { selectedNodes, tree, selectedEntityType, isSelectingEntityType } = state; + const [{ width, height }, ref] = useElementDimensions(); + return h(TreeDispatchContext.Provider, { value: dispatch }, [ h(FeedbackText, { text, @@ -45,62 +48,73 @@ export function FeedbackComponent({ selectedNodes, }), h(ModelInfo, { data: model }), - h("div.entity-panel", [ - h(Card, { className: "control-panel" }, [ - h( - ButtonGroup, - { - vertical: true, - fill: true, - minimal: true, - alignText: "left", - }, - [ - h( - CancelButton, - { - icon: "trash", - disabled: state.initialTree == state.tree, - onClick() { - dispatch({ type: "reset" }); + h( + "div.entity-panel", + { + ref, + }, + [ + h(Card, { className: "control-panel" }, [ + h( + ButtonGroup, + { + vertical: true, + fill: true, + minimal: true, + alignText: "left", + }, + [ + h( + CancelButton, + { + icon: "trash", + disabled: state.initialTree == state.tree, + onClick() { + dispatch({ type: "reset" }); + }, }, - }, - "Reset" - ), - h( - SaveButton, - { - onClick() { - dispatch({ - type: "save", - tree, - sourceTextID: sourceTextID, - supersedesRunIDs: [runID], - }); + "Reset" + ), + h( + SaveButton, + { + onClick() { + dispatch({ + type: "save", + tree, + sourceTextID: sourceTextID, + supersedesRunIDs: [runID], + }); + }, + disabled: state.initialTree == state.tree, }, - disabled: state.initialTree == state.tree, - }, - "Save" - ), - ] - ), - h(EntityTypeSelector, { - entityTypes, - selected: selectedEntityType, - onChange(payload) { - dispatch({ type: "select-entity-type", payload }); - }, - isOpen: isSelectingEntityType, - setOpen: (isOpen: boolean) => - dispatch({ type: "toggle-entity-type-selector", payload: isOpen }), + "Save" + ), + ] + ), + h(EntityTypeSelector, { + entityTypes, + selected: selectedEntityType, + onChange(payload) { + dispatch({ type: "select-entity-type", payload }); + }, + isOpen: isSelectingEntityType, + setOpen: (isOpen: boolean) => + dispatch({ + type: "toggle-entity-type-selector", + payload: isOpen, + }), + }), + ]), + h(ManagedSelectionTree, { + selectedNodes, + dispatch, + tree, + width, + height, }), - ]), - h(ManagedSelectionTree, { - selectedNodes, - dispatch, - tree, - }), - ]), + ] + ), ]); } @@ -148,7 +162,7 @@ function EntityTypeSelector({ } function ManagedSelectionTree(props) { - const { selectedNodes, dispatch, tree, ...rest } = props; + const { selectedNodes, dispatch, tree, height, width, ...rest } = props; const ref = useRef>(); @@ -170,6 +184,8 @@ function ManagedSelectionTree(props) { return h(Tree, { className: "selection-tree", + height, + width, ref, data: tree, onMove({ dragIds, parentId, index }) {