2. Static decorations
One of the feature advantage of Wolfram Mathematica and WLJS Notebook is a multimodal cells with a powerful syntax sugar. A visual representation of an instance of an object makes the programming experience more educative for sure.
Summary Item
The easiest way of providing a bit more information, but still keeping the actual expression intact is to use ArrangeSummaryBox
StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
With[{
summary = {BoxForm`SummaryItem[{"State: ", s["State"]}]}
},
BoxForm`ArrangeSummaryBox[
StateMachine,
s,
None,
summary,
Null
]
]
]
Here we redefined a standard output form to a decorated summary box, providing the visible state field
StateMachine["State" -> 3]
Despite the fact of looking different, you can still work with it normally: setting and getting properties, i.e.
is 100% valid
Custom decorations
One can do all decorations from scratch using Graphics for instance
StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
With[{
g = Graphics[{ Opacity[0.5],
Table[
Rotate[{
Hue[i/12.0, 1.0, 0.5],
Rectangle[{-1,-1}, {1,1}]
}, i / (s["State"]+1)]
, {i, 0, 6Pi, Pi}]
}, ImageSize->{100,100}, ImagePadding->None]
},
ViewBox[s, g]
]
]
The result will look like
machine = StateMachine[]
StateMachineChange[machine, 2]
Summary Item and Custom decoration
Why not to merge both leaving the graphics as an icon?
StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
With[{
summary = {BoxForm`SummaryItem[{"State: ", s["State"]}]},
icon = Graphics[{ Opacity[0.5],
Table[
Rotate[{
Hue[i/12.0, 1.0, 0.5],
Rectangle[{-1,-1}, {1,1}]
}, i / (s["State"]+1), {0.,0.}]
, {i, 0, 6Pi, Pi}]
}, ImageSize->{50,50}, AspectRatio->1, ImagePadding->None]
},
BoxForm`ArrangeSummaryBox[
StateMachine,
s,
icon,
summary,
Null
]
]
]
machine = StateMachine["State"->2]
Javascript decoration
There is also an option to use pure Javascript to render an object, let us make our layout much simpler starting with
StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
ViewBox[s, CustomDecorator[s["State"]]]
]
Here we mentioned CustomDecorator
which is going to be our WLJS Function
Then, create a new cell
.js
core.CustomDecorator = async (args, env) => {
const state = await interpretate(args[0], env);
const element = env.element;
element.classList.add('flex', 'rounded-md', 'p-2');
element.style.border = "1px solid #999";
element.style.boxShadow = "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)";
element.style.transitionDuration = '0.8s';
element.style.transitionProperty = 'transform';
setTimeout(() => {
element.style.transform = "rotate(360deg)";
}, 100);
element.innerText = state;
}
The result will be following
Animated decoration in Summary Item
Why not also animate it using Wolfram Language?
Let it be many balls bouncing the walls. Firstly let us make proof of concept
balls = RandomReal[{-1,1}, {4,2}];
velocities = RandomReal[{-1,1}, {4,2}];
EventHandler["animate", Function[Null,
{balls, velocities} = Map[With[{
v = {If[Abs[#[[1,1]]] >= 1, -1, 1], If[Abs[#[[1, 2]]] >= 1, -1, 1]} #[[2]],
p = #[[1]]
},
{p + 0.2 v, v}
]&, Transpose[{balls, velocities}]] // Transpose;
]]
Graphics[{PointSize[0.03], Point[balls // Offload], AnimationFrameListener[balls // Offload, "Event"->"animate"]}, PlotRange->{{-1,1}, {-1,1}}, TransitionType->None]
Now we need only to scope our variables and embed it to summary item
StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{
balls = RandomReal[{-1,1}, {s["State"],2}],
velocities = RandomReal[{-1,1}, {s["State"],2}],
animateEvent = CreateUUID[]
},
EventHandler[animateEvent, Function[Null,
{balls, velocities} = Map[With[{
v = {If[Abs[#[[1,1]]] >= 1, -1, 1], If[Abs[#[[1, 2]]] >= 1, -1, 1]} #[[2]],
p = #[[1]]
},
{p + 0.2 v, v}
]&, Transpose[{balls, velocities}]] // Transpose;
]];
With[{
summary = {BoxForm`SummaryItem[{"State: ", s["State"]}]},
icon = Graphics[{
PointSize[0.03], Point[balls // Offload],
AnimationFrameListener[balls // Offload, "Event"->animateEvent]
},
PlotRange->{{-1,1}, {-1,1}},
TransitionType->None,
ImageSize->{50,50},
AspectRatio->1,
ImagePadding->None
]
},
BoxForm`ArrangeSummaryBox[
StateMachine,
s,
icon,
summary,
Null
]
]
]
Then let us see the result
machine = StateMachine["State"->2]