add command LemmaTab to specify default tab when loading level
This commit is contained in:
@@ -4,14 +4,14 @@ import './inventory.css'
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons'
|
import { faLock, faLockOpen, faBook, faHammer, faBan } from '@fortawesome/free-solid-svg-icons'
|
||||||
import Markdown from './Markdown';
|
import Markdown from './Markdown';
|
||||||
import { useLoadDocQuery, ComputedInventoryItem } from '../state/api';
|
import { useLoadDocQuery, ComputedInventoryItem, LevelInfo } from '../state/api';
|
||||||
import { GameIdContext } from '../App';
|
import { GameIdContext } from '../App';
|
||||||
|
|
||||||
export function Inventory({ tactics, lemmas, definitions, setInventoryDoc } :
|
export function Inventory({levelInfo, setInventoryDoc } :
|
||||||
{lemmas: ComputedInventoryItem[],
|
{
|
||||||
tactics: ComputedInventoryItem[],
|
levelInfo: LevelInfo,
|
||||||
definitions: ComputedInventoryItem[],
|
setInventoryDoc: (inventoryDoc: {name: string, type: string}) => void,
|
||||||
setInventoryDoc: (inventoryDoc: {name: string, type: string}) => void}) {
|
}) {
|
||||||
|
|
||||||
function openDoc(name, type) {
|
function openDoc(name, type) {
|
||||||
setInventoryDoc({name, type})
|
setInventoryDoc({name, type})
|
||||||
@@ -22,18 +22,28 @@ export function Inventory({ tactics, lemmas, definitions, setInventoryDoc } :
|
|||||||
{/* TODO: Click on Tactic: show info
|
{/* TODO: Click on Tactic: show info
|
||||||
TODO: click on paste icon -> paste into command line */}
|
TODO: click on paste icon -> paste into command line */}
|
||||||
<h2>Tactics</h2>
|
<h2>Tactics</h2>
|
||||||
<InventoryList items={tactics} docType="Tactic" openDoc={openDoc} />
|
<InventoryList items={levelInfo?.tactics} docType="Tactic" openDoc={openDoc} />
|
||||||
|
|
||||||
<h2>Definitions</h2>
|
<h2>Definitions</h2>
|
||||||
<InventoryList items={definitions} docType="Definition" openDoc={openDoc} />
|
<InventoryList items={levelInfo?.definitions} docType="Definition" openDoc={openDoc} />
|
||||||
|
|
||||||
<h2>Lemmas</h2>
|
<h2>Lemmas</h2>
|
||||||
<InventoryList items={lemmas} docType="Lemma" openDoc={openDoc} />
|
<InventoryList items={levelInfo?.lemmas} docType="Lemma" openDoc={openDoc}
|
||||||
|
defaultTab={levelInfo?.lemmaTab} level={levelInfo}/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function InventoryList({items, docType, openDoc} : {items: ComputedInventoryItem[], docType: string, openDoc(name: string, type: string): void}) {
|
function InventoryList({items, docType, openDoc, defaultTab=null, level=undefined} :
|
||||||
|
{
|
||||||
|
items: ComputedInventoryItem[],
|
||||||
|
docType: string,
|
||||||
|
openDoc(name: string, type: string): void,
|
||||||
|
defaultTab? : string,
|
||||||
|
level? : LevelInfo,
|
||||||
|
}) {
|
||||||
|
// TODO: `level` is only used in the `useEffect` below to check if a new level has
|
||||||
|
// been loaded. Is there a better way to observe this?
|
||||||
|
|
||||||
const categorySet = new Set<string>()
|
const categorySet = new Set<string>()
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
@@ -41,20 +51,27 @@ function InventoryList({items, docType, openDoc} : {items: ComputedInventoryItem
|
|||||||
}
|
}
|
||||||
const categories = Array.from(categorySet).sort()
|
const categories = Array.from(categorySet).sort()
|
||||||
|
|
||||||
const [tab, setTab] = useState(categories[0]);
|
const [tab, setTab] = useState(defaultTab);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// If the level specifies `LemmaTab "Nat"`, we switch to this tab on loading.
|
||||||
|
// `defaultTab` is `null` or `undefined` otherwise, in which case we don't want to switch.
|
||||||
|
if (defaultTab) {
|
||||||
|
setTab(defaultTab)
|
||||||
|
}}, [level])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{categories.length > 1 &&
|
{categories.length > 1 &&
|
||||||
<div className="tab-bar">
|
<div className="tab-bar">
|
||||||
{categories.map((cat) =>
|
{categories.map((cat) =>
|
||||||
<div className={`tab ${cat == tab ? "active": ""}`} onClick={() => { setTab(cat) }}>{cat}</div>)}
|
<div className={`tab ${cat == (tab ?? categories[0]) ? "active": ""}`} onClick={() => { setTab(cat) }}>{cat}</div>)}
|
||||||
</div>}
|
</div>}
|
||||||
<div className="inventory-list">
|
<div className="inventory-list">
|
||||||
{ [...items].sort(
|
{ [...items].sort(
|
||||||
// sort unavailable tactics/lemmas/def to the back.
|
// Sort entries `available > disabled > locked`.
|
||||||
(x, y) => +x.locked - +y.locked || +x.disabled - +y.disabled
|
(x, y) => +x.locked - +y.locked || +x.disabled - +y.disabled
|
||||||
).map(item => {
|
).map(item => {
|
||||||
if (tab == item.category) {
|
if ((tab ?? categories[0]) == item.category) {
|
||||||
return <InventoryItem key={item.name} showDoc={() => {openDoc(item.name, docType)}}
|
return <InventoryItem key={item.name} showDoc={() => {openDoc(item.name, docType)}}
|
||||||
name={item.name} displayName={item.displayName} locked={item.locked} disabled={item.disabled} />
|
name={item.name} displayName={item.displayName} locked={item.locked} disabled={item.disabled} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,8 +218,7 @@ function PlayableLevel({worldId, levelId}) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="inventory-panel">
|
<div className="inventory-panel">
|
||||||
{!level.isLoading &&
|
{!level.isLoading &&
|
||||||
<Inventory tactics={level?.data?.tactics} lemmas={level?.data?.lemmas}
|
<Inventory levelInfo={level?.data} setInventoryDoc={setInventoryDoc} />}
|
||||||
definitions={level?.data?.definitions} setInventoryDoc={setInventoryDoc} />}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="doc-panel">
|
<div className="doc-panel">
|
||||||
{inventoryDoc && <Documentation name={inventoryDoc.name} type={inventoryDoc.type} />}
|
{inventoryDoc && <Documentation name={inventoryDoc.name} type={inventoryDoc.type} />}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.inventory {
|
.inventory {
|
||||||
padding: 0 1em;
|
padding: 0 1em 1em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inventory h2 {
|
.inventory h2 {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export interface ComputedInventoryItem {
|
|||||||
locked: boolean
|
locked: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LevelInfo {
|
export interface LevelInfo {
|
||||||
title: null|string,
|
title: null|string,
|
||||||
introduction: null|string,
|
introduction: null|string,
|
||||||
conclusion: null|string,
|
conclusion: null|string,
|
||||||
@@ -28,6 +28,7 @@ interface LevelInfo {
|
|||||||
definitions: ComputedInventoryItem[],
|
definitions: ComputedInventoryItem[],
|
||||||
descrText: null|string,
|
descrText: null|string,
|
||||||
descrFormat: null|string,
|
descrFormat: null|string,
|
||||||
|
lemmaTab: null|string,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Doc {
|
interface Doc {
|
||||||
|
|||||||
@@ -400,6 +400,12 @@ elab "LemmaDoc" name:ident "as" displayName:str "in" category:str content:str :
|
|||||||
category := category.getString,
|
category := category.getString,
|
||||||
content := content.getString })
|
content := content.getString })
|
||||||
|
|
||||||
|
/-- Define which tab of Lemmas is opened by default. Usage: `LemmaTab "Nat"`.
|
||||||
|
|
||||||
|
If omitted, the first tab will be open by default. -/
|
||||||
|
elab "LemmaTab" category:str : command =>
|
||||||
|
modifyCurLevel fun level => pure {level with lemmaTab := category.getString}
|
||||||
|
|
||||||
/-- Declare lemmas that are introduced by this level. -/
|
/-- Declare lemmas that are introduced by this level. -/
|
||||||
elab "NewLemma" args:ident* : command => do
|
elab "NewLemma" args:ident* : command => do
|
||||||
let names := args.map (·.getId)
|
let names := args.map (·.getId)
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ structure GameLevel where
|
|||||||
tactics: InventoryInfo := default
|
tactics: InventoryInfo := default
|
||||||
definitions: InventoryInfo := default
|
definitions: InventoryInfo := default
|
||||||
lemmas: InventoryInfo := default
|
lemmas: InventoryInfo := default
|
||||||
|
lemmaTab: Option String := none
|
||||||
hints: Array GoalHintEntry := default
|
hints: Array GoalHintEntry := default
|
||||||
/-- The statement in Lean. -/
|
/-- The statement in Lean. -/
|
||||||
goal : TSyntax `Lean.Parser.Command.declSig := default
|
goal : TSyntax `Lean.Parser.Command.declSig := default
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ structure LevelInfo where
|
|||||||
conclusion : String
|
conclusion : String
|
||||||
descrText : String := ""
|
descrText : String := ""
|
||||||
descrFormat : String := ""
|
descrFormat : String := ""
|
||||||
|
lemmaTab : Option String
|
||||||
deriving ToJson, FromJson
|
deriving ToJson, FromJson
|
||||||
|
|
||||||
structure LoadLevelParams where
|
structure LoadLevelParams where
|
||||||
@@ -128,7 +129,8 @@ partial def handleServerEvent (ev : ServerEvent) : GameServerM Bool := do
|
|||||||
descrText := lvl.descrText,
|
descrText := lvl.descrText,
|
||||||
descrFormat := lvl.descrFormat --toExpr <| format (lvl.goal.raw) --toString <| Syntax.formatStx (lvl.goal.raw) --Syntax.formatStx (lvl.goal.raw) , -- TODO
|
descrFormat := lvl.descrFormat --toExpr <| format (lvl.goal.raw) --toString <| Syntax.formatStx (lvl.goal.raw) --Syntax.formatStx (lvl.goal.raw) , -- TODO
|
||||||
introduction := lvl.introduction
|
introduction := lvl.introduction
|
||||||
conclusion := lvl.conclusion }
|
conclusion := lvl.conclusion
|
||||||
|
lemmaTab := lvl.lemmaTab }
|
||||||
c.hOut.writeLspResponse ⟨id, ToJson.toJson levelInfo⟩
|
c.hOut.writeLspResponse ⟨id, ToJson.toJson levelInfo⟩
|
||||||
return true
|
return true
|
||||||
| Message.request id "loadDoc" params =>
|
| Message.request id "loadDoc" params =>
|
||||||
|
|||||||
Reference in New Issue
Block a user