Interactive Elements

Many interactive elements share common functionality such as animating a DOM element or listening to a slide gesture. For consistency, browser-compatibility and accessibility, we should use the shared utility methods in the @mathigon/boost library. Refer to its documentation for more information about:

Linking Markdown and TypeScript

Every course is divided into multiple steps, separated by ---s. Every step has a unique ID which is provided in the > block at the beginning of a step, in the content.md file:

---
> id: my-step-1

{.my-class} Here is a paragraph

---

Note: Specifying an ID for every step is optional, but recommended since the IDs are used to identify student progress in our database. Missing step IDs could lead to discrepancies if we insert new steps into an existing course.

The step IDs correspond to the names of functions exported in the functions.ts file for the same course. The function is executed whenever the step is revealed for the first time, and takes a $step argument, which is a reference to the custom HTML <x-step> element that wraps around the step. Check types.d.ts for the available properties and methods.

export function myStep1($step: Step) {
  const $paragraph = $step.$('.my-class');
}

Note: Step IDs are in kebab-case while function names are in camelCase.

Goals and progress

TODO…

Models and templates

Every step contains an observable object, which can be used to create reactive

export function myStep1($step: Step) {
  $step.model.a = 10
  $step.model.b = 11
}

Any variables you assign to $step.model can then be accessed in Markdown. If the model changes, the template will update automatically.

Here is ${a} and ${b}.

Many built-in interactive elements automatically integrate with the model:

Here is a variable slider ${a}{a|5|0,10,1} and some variable values: a = ${a}, b = ${b}.
Here is [a button](action:increment(1)) that triggers a function whenever you click it.

    // A large horizontal slider that binds to model.b
    x-slider(steps=100 :bind="b")
export function myStep1($step: Step) {
  $step.model.click = (n: number) => $step.model.b += 1;

  console.log($step.model.a);  // Get the current value of model.a.

  $step.model.watch(() => {
    // This callback is triggered whenever model.a changes, but not when model.b changes.
    console.log('a =', $step.model.a);
  });
}

Note: The model.watch function is very efficient: when executed for the first time, it tracks which model properties are accessed within its body. Then it will keep executing the callback any time these properties change, but not when other properties of model change.


Copyright © 2024 Mathigon.org