Skip to content

Commit 636cccd

Browse files
authored
Merge pull request #225 from ToposInstitute/theoryselection
Selection widget for theories
2 parents c5af141 + d38e117 commit 636cccd

File tree

6 files changed

+189
-29
lines changed

6 files changed

+189
-29
lines changed

packages/frontend/src/document/model_document_editor.tsx

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
import type { ChangeFn, DocHandle } from "@automerge/automerge-repo";
22
import { MultiProvider } from "@solid-primitives/context";
33
import { useNavigate, useParams } from "@solidjs/router";
4-
import {
5-
type Accessor,
6-
For,
7-
Match,
8-
Switch,
9-
createMemo,
10-
createResource,
11-
useContext,
12-
} from "solid-js";
4+
import { type Accessor, Match, Switch, createMemo, createResource, useContext } from "solid-js";
135
import invariant from "tiny-invariant";
146

157
import type { JsonValue, Permissions } from "catcolab-api";
@@ -43,6 +35,7 @@ import { type TheoryLibrary, TheoryLibraryContext } from "../stdlib";
4335
import type { Theory } from "../theory";
4436
import { PermissionsButton } from "../user";
4537
import { type IndexedMap, indexMap } from "../util/indexing";
38+
import { TheorySelectorDialog } from "./theory_selector";
4639
import { type ModelDocument, newAnalysisDocument } from "./types";
4740

4841
import "./model_document_editor.css";
@@ -239,26 +232,16 @@ export function ModelPane(props: {
239232
placeholder="Untitled"
240233
/>
241234
</div>
242-
<div class="model-theory">
243-
<select
244-
required
245-
disabled={doc().notebook.cells.some((cell) => cell.tag === "formal")}
246-
value={doc().theory ?? ""}
247-
onInput={(evt) => {
248-
const id = evt.target.value;
249-
changeDoc((model) => {
250-
model.theory = id ? id : undefined;
251-
});
252-
}}
253-
>
254-
<option value="" disabled selected hidden>
255-
Choose a logic
256-
</option>
257-
<For each={Array.from(theories.metadata())}>
258-
{(meta) => <option value={meta.id}>{meta.name}</option>}
259-
</For>
260-
</select>
261-
</div>
235+
<TheorySelectorDialog
236+
theory={props.liveDoc.theory()}
237+
setTheory={(id) => {
238+
changeDoc((model) => {
239+
model.theory = id;
240+
});
241+
}}
242+
theories={theories}
243+
disabled={doc().notebook.cells.some((cell) => cell.tag === "formal")}
244+
/>
262245
</div>
263246
<MultiProvider
264247
values={[
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
.theory-selector-button {
2+
border: none;
3+
font-size: 1.25em;
4+
padding: 16px;
5+
background-color: transparent;
6+
font-family: var(--main-font);
7+
cursor: pointer;
8+
font-weight: 500;
9+
10+
& .placeholder {
11+
color: darkgray;
12+
}
13+
14+
&:hover,
15+
& .placeholder:hover {
16+
color: var(--topos-color);
17+
}
18+
}
19+
20+
.theory-selector {
21+
display: flex;
22+
flex-direction: column;
23+
gap: 10px;
24+
25+
background-color: transparent;
26+
text-align: left;
27+
border-top: transparent;
28+
border-left: transparent;
29+
min-height: 30vh;
30+
31+
& input[type="radio"] {
32+
position: absolute;
33+
opacity: 0;
34+
}
35+
36+
& label:hover {
37+
color: var(--topos-color);
38+
cursor: pointer;
39+
}
40+
41+
& .division {
42+
display: flex;
43+
flex-direction: column;
44+
gap: 5px;
45+
}
46+
47+
& .division-name {
48+
font-weight: 600;
49+
padding-bottom: 1px;
50+
opacity: 35%;
51+
}
52+
53+
& .theory {
54+
margin-left: 2ex;
55+
}
56+
57+
& .description {
58+
color: rgb(106, 106, 106);
59+
font-size: 11px;
60+
text-align: left;
61+
}
62+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import Dialog from "@corvu/dialog";
2+
import { For, Show, createMemo, createSignal } from "solid-js";
3+
4+
import type { TheoryLibrary, TheoryMeta } from "../stdlib";
5+
import type { TheoryId } from "../theory";
6+
7+
import "./theory_selector.css";
8+
9+
type TheorySelectorProps = {
10+
theory: TheoryMeta | undefined;
11+
setTheory: (theory: TheoryId | undefined) => void;
12+
theories: TheoryLibrary;
13+
};
14+
15+
export function TheorySelectorDialog(
16+
props: {
17+
disabled?: boolean;
18+
} & TheorySelectorProps,
19+
) {
20+
const [theorySelectorOpen, setTheorySelectorOpen] = createSignal(false);
21+
22+
return (
23+
<Dialog open={theorySelectorOpen()} onOpenChange={setTheorySelectorOpen}>
24+
<Dialog.Trigger class="theory-selector-button" disabled={props.disabled}>
25+
<Show
26+
when={props.theory}
27+
fallback={<span class="placeholder">Choose a logic</span>}
28+
>
29+
{props.theory?.name}
30+
</Show>
31+
</Dialog.Trigger>
32+
<Dialog.Portal>
33+
<Dialog.Overlay class="overlay" />
34+
<Dialog.Content class="popup">
35+
<TheorySelector
36+
theory={props.theory}
37+
setTheory={(id) => {
38+
props.setTheory(id);
39+
setTheorySelectorOpen(false);
40+
}}
41+
theories={props.theories}
42+
/>
43+
</Dialog.Content>
44+
</Dialog.Portal>
45+
</Dialog>
46+
);
47+
}
48+
49+
export function TheorySelector(props: TheorySelectorProps) {
50+
const groupedTheories = createMemo(() => {
51+
const grouped = new Map<string, TheoryMeta[]>();
52+
53+
for (const theory of props.theories.metadata()) {
54+
const category = theory.divisionCategory ?? "Other";
55+
const group = grouped.get(category) || [];
56+
group.push(theory);
57+
grouped.set(category, group);
58+
}
59+
60+
return Array.from(grouped.entries()).sort((a, b) => a[0].localeCompare(b[0]));
61+
});
62+
63+
return (
64+
<div class="theory-selector">
65+
<For each={groupedTheories()}>
66+
{([category, theories]) => (
67+
<div class="division">
68+
<h4 class="division-name">{category}</h4>
69+
<For each={theories}>
70+
{(meta) => (
71+
<div class="theory">
72+
<input
73+
type="radio"
74+
name="theory"
75+
id={meta.id}
76+
value={meta.id}
77+
onchange={(evt) => {
78+
const id = evt.target.value as TheoryId;
79+
props.setTheory(id ? id : undefined);
80+
}}
81+
/>
82+
<label for={meta.id}>
83+
<div class="name">{meta.name}</div>
84+
<div class="description">{meta.description}</div>
85+
</label>
86+
</div>
87+
)}
88+
</For>
89+
</div>
90+
)}
91+
</For>
92+
</div>
93+
);
94+
}

packages/frontend/src/index.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
--mono-font: Roboto Mono, monospace;
44

55
--main-color: black;
6+
--topos-color: #53b6b2;
67

78
--h1-font-size: 3em;
89
--h2-font-size: 2em;
@@ -62,6 +63,15 @@ h3 {
6263
rgba(15, 15, 15, 0.2) 0px 9px 24px;
6364
}
6465

66+
.overlay {
67+
position: absolute;
68+
top: 0;
69+
left: 0;
70+
right: 0;
71+
bottom: 0;
72+
background-color: rgba(0, 0, 0, 0.5);
73+
}
74+
6575
.growable-container {
6676
flex-grow: 1;
6777
}

packages/frontend/src/stdlib/theories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ stdTheories.add(
2020
id: "simple-olog",
2121
name: "Olog",
2222
description: "Ontology log, a simple conceptual model",
23+
divisionCategory: "Knowledge and Data",
2324
},
2425
(meta) => {
2526
const thCategory = new catlog.ThCategory();
@@ -63,6 +64,7 @@ stdTheories.add(
6364
id: "schema",
6465
name: "Schema",
6566
description: "Schema for a categorical database",
67+
divisionCategory: "Knowledge and Data",
6668
},
6769
(meta) => {
6870
const thSchema = new catlog.ThSchema();
@@ -132,6 +134,8 @@ stdTheories.add(
132134
{
133135
id: "reg-net",
134136
name: "Regulatory network",
137+
description: "Signed graphs to model networks",
138+
divisionCategory: "Life Sciences",
135139
},
136140
(meta) => {
137141
const thSignedCategory = new catlog.ThSignedCategory();
@@ -198,6 +202,8 @@ stdTheories.add(
198202
{
199203
id: "causal-loop",
200204
name: "Causal loop diagram",
205+
description: "Model cause-and-effect relationships",
206+
divisionCategory: "System Dynamics",
201207
},
202208
(meta) => {
203209
const thSignedCategory = new catlog.ThSignedCategory();
@@ -327,6 +333,8 @@ stdTheories.add(
327333
{
328334
id: "stock-flow",
329335
name: "Stock and flow",
336+
description: "Model accumulation (stocks) and change (flows)",
337+
divisionCategory: "System Dynamics",
330338
},
331339
(meta) => {
332340
const thCategoryLinks = new catlog.ThCategoryLinks();

packages/frontend/src/stdlib/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export type TheoryMeta = {
1414

1515
/** Short description of models of theory. */
1616
description?: string;
17+
18+
/** division Category */
19+
divisionCategory?: string;
1720
};
1821

1922
/** Library of double theories configured for the frontend.

0 commit comments

Comments
 (0)