WLJS LogoWLJS Notebook

ViewBox

Wolfram Kernel
ViewBox[expr_, displayView_, opts___]

is a low-level box used to alter expr visibly with custom HTML/JS elements in StandardForm, while keeping the input form of expr behind.

Most decorations generated by TemplateBox, graphics functions Graphics, Graphics3D, InputButton and etc are ViewBox-es.

  • the expression is not editable by a user and is fully replaced in the editor by the provided displayView
  • the inner expr is revealed on the next evaluation

RGBColor, DateObject and InterpretationBox (together with EditorView) and many more use this structure to display a human-readable form of the content behind.

displayView must be frontend symbol (i.e., executed by WLJS Interpreter) and must be defined in advance

Options

"Event"

Manually enables events generation. Using the provided event object identifier (_String), it fires various events based on its state. You can attach a normal EventHandler to the given id

uid = CreateUUID[];
EventHandler[uid, {pattern_String :> handler_, ..}]

ViewBox[.., "Event"->uid]

The following patterns are available to be attached to

  • "Mounted" fires once a widget is visible. A unique identifier is provided as a payload in a form of association
  • "Destroy" fires once it was removed

Once mounted, it provides a unique ID as a string from the key Instance in the "Mounted" event, which can be used in the constructor FrontInstanceReference. You can also apply ViewBox`InnerExpression and ViewBox`OuterExpression.

Examples

This method is much faster than InterpretationBox or Interpretation, since it does not spawn EditorView inside for displaying regular boxes.

Replacing expression with custom DOM element

You can define your own style for cell boxes. The visible DOM element in the editor is provided as element property of env object:

.js
core.MyView = async (args, env) => {
  env.element.style.background = "red";
  env.element.style.width = "2em";
  env.element.style.height = "1em";
}
myBox /: MakeBoxes[myBox[expr_], StandardForm] := ViewBox[myBox[expr], MyView]

and then try

myBox[1/2]

This is basically how RGBColor, DateObject are implemented

Mutability

In general it is possible to update the expression underneath indirectly. For this reason, there are multiple way of accessing this feature

From Wolfram Kernel

Please see methods below

ViewBox`InnerExpression

Used to replace a covered expression in a ViewBox with a given string.

It must be evaluated in the context of an instance of ViewBox, use FrontInstanceReference and FrontSubmit in order to apply this to a specific box

Returns the current content if no string is specified as an argument. Use it with FrontInstanceReference and FrontFetch.

For example, let's make a dummy view-box that covers a simple string with a disk

dummy /: MakeBoxes[_dummy, StandardForm] := With[{uid = CreateUUID[]},
	EventHandler[uid, {
		"Mounted" -> Function[res, ref = res["Instance"]]
	}];
	
	ViewBox["Covered", Graphics[Disk[]], "Event"->uid]
]

dummy[]

Now our element has reported itself to the ref global symbol. Let's fetch this covered expression

FrontFetch[ViewBox`InnerExpression[], FrontInstanceReference[ref]]
"\"Covered\""

Now let's change it

FrontSubmit[ViewBox`InnerExpression["1+1"], FrontInstanceReference[ref]]

Now if you evaluate our disk, you will see

2

ViewBox`OuterExpression

Used to replace or update the content string within the box (including the box itself)

This will destroy an instance of the ViewBox once applied.

Example

ref = FrontInstanceReference[];
Plot[x, {x,0,1}, Epilog->{ref}]

and then we can destroy it and replace with our text

FrontSubmit[ViewBox`OuterExpression["Hello World"], ref] 

From JavaScript function

The displayView expression must be a frontend symbol

core.displayView = async (args, env) => {
	env.global.EditorWidget
}

this object contains a property to update the content

EditorWidget.applyChanges('"new content"')
Example

Let's create a special object that will act like a checkbox

CheckObject /: MakeBoxes[CheckObject[state_:(True | False)], StandardForm] := With[{},
  ViewBox[CheckObject[state], CheckBoxDecorator[]]
]

now JS part

.js

core.CheckBoxDecorator = async (args, env) => {
  let state = false;

  //check the raw data from the viewbox to determine the state
  if (env.global.EditorWidget.getDoc() == 'CheckObject[True]') state = true;

  //make a rectangle
  env.element.style.width = "1em";
  env.element.style.height = "1em";

  const update = (s) => env.element.style.background = s ? 'red' : 'blue';

  //color it depending on state
  update(state);

  //logic for updates when a user click on it
  env.element.addEventListener("click", () => {
    state = !state;
    update(state);
    
    const stringState = state ? 'True' : 'False';
    env.global.EditorWidget.applyChanges('CheckObject['+stringState+']');
  });
}

Let's test it!

CheckObject[True]

then try to click on it, copy and paste it

whatever you do, it will keep its state synced. No communication with WL Kernel happens, everything is running within the code-editor in the browser.

Or even cooler

Table[CheckObject[True], {i, 3}, {j, 3}] // MatrixForm 

Supported output forms

On this page