Skip to content

Commit afb459e

Browse files
committed
ENH: Cell type changes on frontend
1 parent 8efb3c3 commit afb459e

File tree

4 files changed

+100
-7
lines changed

4 files changed

+100
-7
lines changed

packages/frontend/src/model/model_editor.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export function ModelNotebookEditor(props: { liveModel: LiveModelDoc }) {
2727
const liveDoc = () => props.liveModel.liveDoc;
2828

2929
const cellConstructors = () => {
30+
// const cells = liveDoc().doc.notebook.cellOrder;
31+
// console.log(cells);
32+
3033
const theory = props.liveModel.theory();
3134
return theory ? modelCellConstructors(theory) : [];
3235
};
@@ -54,6 +57,7 @@ export function ModelCellEditor(props: FormalCellEditorProps<ModelJudgment>) {
5457
const liveModel = useContext(LiveModelContext);
5558
invariant(liveModel, "Live model should be provided as context");
5659

60+
console.log(props);
5761
return (
5862
<Switch>
5963
<Match when={props.content.tag === "object" && liveModel().theory()}>
@@ -106,6 +110,7 @@ function modelCellConstructors(theory: Theory): CellConstructor<ModelJudgment>[]
106110
},
107111
});
108112
}
113+
109114
for (const meta of theory.modelTypes ?? []) {
110115
constructors.push(modelCellConstructor(meta));
111116
}

packages/frontend/src/notebook/notebook_cell.tsx

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ export function NotebookCell(props: {
108108
tag?: string;
109109
currentDropTarget: string | null;
110110
setCurrentDropTarget: (cellId: string | null) => void;
111+
isActive: boolean;
112+
replaceCommands: Completion[];
111113
}) {
112114
let rootRef!: HTMLDivElement;
113115
let handleRef!: HTMLButtonElement;
@@ -248,13 +250,43 @@ export function NotebookCell(props: {
248250
<div class="drop-indicator-with-dots" />
249251
</Show>
250252
</div>
251-
<Show when={props.tag}>
252-
<div class="cell-tag">{props.tag}</div>
253-
</Show>
253+
<CellSwitcher tag={props.tag} completions={props.replaceCommands}></CellSwitcher>
254254
</div>
255255
);
256256
}
257257

258+
export function CellSwitcher(props: { tag: string | undefined; completions: Completion[] }) {
259+
const [isSwitchMenuOpen, setSwitchMenuOpen] = createSignal(false);
260+
const openSwitchMenu = () => setSwitchMenuOpen(true);
261+
const closeSwitchMenu = () => setSwitchMenuOpen(false);
262+
263+
return (
264+
<Show when={props.tag}>
265+
<Popover
266+
open={isSwitchMenuOpen()}
267+
onOpenChange={setSwitchMenuOpen}
268+
floatingOptions={{
269+
autoPlacement: {
270+
allowedPlacements: ["left-start", "bottom-start", "top-start"],
271+
},
272+
}}
273+
trapFocus={false}
274+
>
275+
<Popover.Anchor as="span">
276+
<button type="button" class="plain" onClick={openSwitchMenu}>
277+
{props.tag}
278+
</button>
279+
</Popover.Anchor>
280+
<Popover.Portal>
281+
<Popover.Content class="popup">
282+
<Completions completions={props.completions} onComplete={closeSwitchMenu} />
283+
</Popover.Content>
284+
</Popover.Portal>
285+
</Popover>
286+
</Show>
287+
);
288+
}
289+
258290
/** Editor for rich text cells, a simple wrapper around `RichTextEditor`.
259291
*/
260292
export function RichTextCellEditor(

packages/frontend/src/notebook/notebook_editor.tsx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
keyEventHasModifier,
2323
type ModifierKey,
2424
} from "catcolab-ui-components";
25-
import type { Cell, Notebook } from "catlog-wasm";
25+
import type { Cell, Notebook, MorType, ObType } from "catlog-wasm";
2626
import {
2727
type CellActions,
2828
type FormalCellEditorProps,
@@ -139,6 +139,33 @@ export function NotebookEditor<T>(props: {
139139
});
140140
};
141141

142+
const isFormalCell = (cell: Cell<T>): cell is { tag: "formal"; id: string; content: T } => {
143+
return cell.tag === "formal";
144+
};
145+
146+
const isObType = (content: any): content is { tag: "object"; obType: ObType } => {
147+
return content && content.tag === "object";
148+
};
149+
150+
const isMorType = (content: any): content is { tag: "morphism"; morType: MorType } => {
151+
return content && content.tag === "morphism";
152+
};
153+
154+
const retypeCellAs = (i: number, newCell: Cell<T>) => {
155+
if (!newCell || !isFormalCell(newCell)) return;
156+
const mutator = (cellContent: T) => {
157+
if (isObType(cellContent) && isObType(newCell.content)) {
158+
cellContent.obType = newCell.content.obType;
159+
} else if (isMorType(cellContent) && isMorType(newCell.content)) {
160+
cellContent.morType = newCell.content.morType;
161+
}
162+
};
163+
props.changeNotebook((nb) => {
164+
NotebookUtils.retypeCell(nb, i, mutator);
165+
});
166+
setActiveCell(i);
167+
};
168+
142169
const cellConstructors = (): CellConstructor<T>[] => [
143170
{
144171
name: "Text",
@@ -160,6 +187,17 @@ export function NotebookEditor<T>(props: {
160187
};
161188
});
162189

190+
const retypeCommands = (i: number): Completion[] =>
191+
cellConstructors().map((cc) => {
192+
const { name, description, shortcut } = cc;
193+
return {
194+
name,
195+
description,
196+
shortcut,
197+
onComplete: () => retypeCellAs(i, cc.construct()),
198+
};
199+
});
200+
163201
makeEventListener(window, "keydown", (evt) => {
164202
if (props.noShortcuts) {
165203
return;
@@ -289,6 +327,19 @@ export function NotebookEditor<T>(props: {
289327
const cell = props.notebook.cellContents[cellId];
290328
invariant(cell, `Failed to find contents for cell '${cellId}'`);
291329

330+
// const tag = (cell.tag === "formal" ? props.cellLabel?.(cell.content) : undefined) as string;
331+
const cellName = (cell: Cell<T>) =>
332+
(cell.tag === "formal"
333+
? props.cellLabel?.(cell.content)
334+
: undefined) as string;
335+
// const cellType = isFormalCell(cell)
336+
// ? isObType(cell.content)
337+
// ? "ObType"
338+
// : isMorType(cell.content)
339+
// ? "MorType"
340+
// : ""
341+
// : "";
342+
292343
if (cell.tag !== "rich-text") {
293344
cellActions.duplicate = () => {
294345
const index = i();
@@ -310,12 +361,12 @@ export function NotebookEditor<T>(props: {
310361
index={i()}
311362
actions={cellActions}
312363
tag={
313-
cell.tag === "formal"
314-
? props.cellLabel?.(cell.content)
315-
: undefined
364+
cellName(cell)
316365
}
317366
currentDropTarget={currentDropTarget()}
318367
setCurrentDropTarget={setCurrentDropTarget}
368+
isActive={isActive()}
369+
replaceCommands={retypeCommands(i())}
319370
>
320371
<Switch>
321372
<Match when={cell.tag === "rich-text"}>

packages/frontend/src/notebook/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ export namespace NotebookUtils {
131131
return notebook.cellOrder.some((cellId) => notebook.cellContents[cellId]?.tag === "formal");
132132
}
133133

134+
export function retypeCell<T>(notebook: Notebook<T>, index: number, mutator: (cellContent: T) => void) {
135+
const cellId = getCellIdByIndex(notebook, index);
136+
cellId && mutateCellContentById(notebook, cellId, mutator);
137+
}
138+
134139
export function numCells<T>(notebook: Notebook<T>): number {
135140
return notebook.cellOrder.length;
136141
}

0 commit comments

Comments
 (0)