@yuneta/gobj-ui 7.23.36 → 7.23.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/gobj-ui.cjs.js +32 -3
- package/dist/gobj-ui.es.js +32 -3
- package/package.json +1 -1
- package/src/c_yui_fsm_graph.js +50 -2
- package/src/c_yui_gclass.css +16 -0
- package/src/c_yui_gclass.js +25 -1
- package/src/gclass_view_model.test.js +14 -0
package/README.md
CHANGED
|
@@ -1467,7 +1467,10 @@ for the other side:
|
|
|
1467
1467
|
|
|
1468
1468
|
The last one is not a rename: `states2json()` cannot name an action, so a cell
|
|
1469
1469
|
from a **backend** gclass says THAT there is one and never which. The viewer
|
|
1470
|
-
draws a mark rather than printing a name nobody wrote
|
|
1470
|
+
draws a mark rather than printing a name nobody wrote — and an em dash for the
|
|
1471
|
+
third case, an event declared with NO action (`{EV_TX_READY, 0, 0}`, which
|
|
1472
|
+
C_WEBSOCKET has in all four states): legal in that state, and deliberately
|
|
1473
|
+
doing nothing.
|
|
1471
1474
|
|
|
1472
1475
|
**The machine is a matrix**, rows = events and columns = states — the shape the
|
|
1473
1476
|
FSM is declared in, and the one that survives 12 events against 3 states as
|
|
@@ -1507,7 +1510,11 @@ What it refuses to draw is the interesting half:
|
|
|
1507
1510
|
- **One edge per PAIR of states**, not per transition, labelled with the events
|
|
1508
1511
|
that make the jump (three, then a count). G6 draws overlapping edges on top
|
|
1509
1512
|
of each other, so a second edge between the same two states is invisible and
|
|
1510
|
-
nothing tells the reader it is there.
|
|
1513
|
+
nothing tells the reader it is there. A RECIPROCAL pair (`A -> B` and
|
|
1514
|
+
`B -> A`) is bowed apart into two arcs — with the SAME `curveOffset` on both,
|
|
1515
|
+
because G6 measures it from each edge's own direction, and with the type
|
|
1516
|
+
varied through a FUNCTION on `edge.type`, because a `type` written on the
|
|
1517
|
+
datum is overridden by the graph-level default.
|
|
1511
1518
|
- **An event a state handles without leaving is not an edge.** Ten self-loops
|
|
1512
1519
|
on one card say nothing a number cannot say better, so the card carries
|
|
1513
1520
|
`n events · m leave` instead.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -14010,7 +14010,7 @@ function build_graph$3(gobj) {
|
|
|
14010
14010
|
zoomRange: [.2, 3],
|
|
14011
14011
|
padding: 24,
|
|
14012
14012
|
edge: {
|
|
14013
|
-
type: "polyline",
|
|
14013
|
+
type: (d) => d && d.data && d.data.reciprocal ? "quadratic" : "polyline",
|
|
14014
14014
|
style: {
|
|
14015
14015
|
stroke: edge_color(),
|
|
14016
14016
|
lineWidth: 1.2,
|
|
@@ -14151,11 +14151,18 @@ function build_data(gobj) {
|
|
|
14151
14151
|
let shown = pair.events.slice(0, MAX_EDGE_LABELS);
|
|
14152
14152
|
let label = shown.join("\n");
|
|
14153
14153
|
if (pair.events.length > shown.length) label += "\n+" + (pair.events.length - shown.length);
|
|
14154
|
+
let style = { labelText: label };
|
|
14155
|
+
let reciprocal = by_pair.has(pair.target + " " + pair.source);
|
|
14156
|
+
if (reciprocal) {
|
|
14157
|
+
style.curveOffset = 34;
|
|
14158
|
+
style.labelPlacement = .5;
|
|
14159
|
+
}
|
|
14154
14160
|
edges.push({
|
|
14155
14161
|
id: "e_" + edges.length,
|
|
14156
14162
|
source: pair.source,
|
|
14157
14163
|
target: pair.target,
|
|
14158
|
-
|
|
14164
|
+
data: { reciprocal },
|
|
14165
|
+
style
|
|
14159
14166
|
});
|
|
14160
14167
|
}
|
|
14161
14168
|
return {
|
|
@@ -15542,7 +15549,7 @@ function build_machine(gobj, fsm) {
|
|
|
15542
15549
|
{ class: "GCLASS_MX_ACTION" },
|
|
15543
15550
|
cell.action
|
|
15544
15551
|
]);
|
|
15545
|
-
else items.push([
|
|
15552
|
+
else if (cell.has_action) items.push([
|
|
15546
15553
|
"span",
|
|
15547
15554
|
{
|
|
15548
15555
|
class: "GCLASS_MX_ACTION is-unnamed",
|
|
@@ -15551,6 +15558,15 @@ function build_machine(gobj, fsm) {
|
|
|
15551
15558
|
},
|
|
15552
15559
|
"●"
|
|
15553
15560
|
]);
|
|
15561
|
+
else items.push([
|
|
15562
|
+
"span",
|
|
15563
|
+
{
|
|
15564
|
+
class: "GCLASS_MX_ACTION is-none-action",
|
|
15565
|
+
title: (0, i18next.t)("accepted with no action"),
|
|
15566
|
+
"data-i18n-title": "accepted with no action"
|
|
15567
|
+
},
|
|
15568
|
+
"—"
|
|
15569
|
+
]);
|
|
15554
15570
|
if (cell.next_state) {
|
|
15555
15571
|
items.push([
|
|
15556
15572
|
"span",
|
|
@@ -15679,6 +15695,19 @@ function build_legend(fsm, current) {
|
|
|
15679
15695
|
(0, i18next.t)("not declared in this state")
|
|
15680
15696
|
]]
|
|
15681
15697
|
]);
|
|
15698
|
+
if (fsm.rows.some((row) => row.cells.some((cell) => cell && !cell.has_action))) items.push([
|
|
15699
|
+
"span",
|
|
15700
|
+
{ class: "GCLASS_LEGEND_ITEM" },
|
|
15701
|
+
[[
|
|
15702
|
+
"i",
|
|
15703
|
+
{ class: "GCLASS_LEGEND_SWATCH is-none-action" },
|
|
15704
|
+
"—"
|
|
15705
|
+
], [
|
|
15706
|
+
"span",
|
|
15707
|
+
{ i18n: "accepted with no action" },
|
|
15708
|
+
(0, i18next.t)("accepted with no action")
|
|
15709
|
+
]]
|
|
15710
|
+
]);
|
|
15682
15711
|
if (current && fsm.states.some((s) => s.name === current)) items.push([
|
|
15683
15712
|
"span",
|
|
15684
15713
|
{ class: "GCLASS_LEGEND_ITEM" },
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -14005,7 +14005,7 @@ function build_graph$3(gobj) {
|
|
|
14005
14005
|
zoomRange: [.2, 3],
|
|
14006
14006
|
padding: 24,
|
|
14007
14007
|
edge: {
|
|
14008
|
-
type: "polyline",
|
|
14008
|
+
type: (d) => d && d.data && d.data.reciprocal ? "quadratic" : "polyline",
|
|
14009
14009
|
style: {
|
|
14010
14010
|
stroke: edge_color(),
|
|
14011
14011
|
lineWidth: 1.2,
|
|
@@ -14146,11 +14146,18 @@ function build_data(gobj) {
|
|
|
14146
14146
|
let shown = pair.events.slice(0, MAX_EDGE_LABELS);
|
|
14147
14147
|
let label = shown.join("\n");
|
|
14148
14148
|
if (pair.events.length > shown.length) label += "\n+" + (pair.events.length - shown.length);
|
|
14149
|
+
let style = { labelText: label };
|
|
14150
|
+
let reciprocal = by_pair.has(pair.target + " " + pair.source);
|
|
14151
|
+
if (reciprocal) {
|
|
14152
|
+
style.curveOffset = 34;
|
|
14153
|
+
style.labelPlacement = .5;
|
|
14154
|
+
}
|
|
14149
14155
|
edges.push({
|
|
14150
14156
|
id: "e_" + edges.length,
|
|
14151
14157
|
source: pair.source,
|
|
14152
14158
|
target: pair.target,
|
|
14153
|
-
|
|
14159
|
+
data: { reciprocal },
|
|
14160
|
+
style
|
|
14154
14161
|
});
|
|
14155
14162
|
}
|
|
14156
14163
|
return {
|
|
@@ -15537,7 +15544,7 @@ function build_machine(gobj, fsm) {
|
|
|
15537
15544
|
{ class: "GCLASS_MX_ACTION" },
|
|
15538
15545
|
cell.action
|
|
15539
15546
|
]);
|
|
15540
|
-
else items.push([
|
|
15547
|
+
else if (cell.has_action) items.push([
|
|
15541
15548
|
"span",
|
|
15542
15549
|
{
|
|
15543
15550
|
class: "GCLASS_MX_ACTION is-unnamed",
|
|
@@ -15546,6 +15553,15 @@ function build_machine(gobj, fsm) {
|
|
|
15546
15553
|
},
|
|
15547
15554
|
"●"
|
|
15548
15555
|
]);
|
|
15556
|
+
else items.push([
|
|
15557
|
+
"span",
|
|
15558
|
+
{
|
|
15559
|
+
class: "GCLASS_MX_ACTION is-none-action",
|
|
15560
|
+
title: t("accepted with no action"),
|
|
15561
|
+
"data-i18n-title": "accepted with no action"
|
|
15562
|
+
},
|
|
15563
|
+
"—"
|
|
15564
|
+
]);
|
|
15549
15565
|
if (cell.next_state) {
|
|
15550
15566
|
items.push([
|
|
15551
15567
|
"span",
|
|
@@ -15674,6 +15690,19 @@ function build_legend(fsm, current) {
|
|
|
15674
15690
|
t("not declared in this state")
|
|
15675
15691
|
]]
|
|
15676
15692
|
]);
|
|
15693
|
+
if (fsm.rows.some((row) => row.cells.some((cell) => cell && !cell.has_action))) items.push([
|
|
15694
|
+
"span",
|
|
15695
|
+
{ class: "GCLASS_LEGEND_ITEM" },
|
|
15696
|
+
[[
|
|
15697
|
+
"i",
|
|
15698
|
+
{ class: "GCLASS_LEGEND_SWATCH is-none-action" },
|
|
15699
|
+
"—"
|
|
15700
|
+
], [
|
|
15701
|
+
"span",
|
|
15702
|
+
{ i18n: "accepted with no action" },
|
|
15703
|
+
t("accepted with no action")
|
|
15704
|
+
]]
|
|
15705
|
+
]);
|
|
15677
15706
|
if (current && fsm.states.some((s) => s.name === current)) items.push([
|
|
15678
15707
|
"span",
|
|
15679
15708
|
{ class: "GCLASS_LEGEND_ITEM" },
|
package/package.json
CHANGED
package/src/c_yui_fsm_graph.js
CHANGED
|
@@ -395,7 +395,15 @@ function build_graph(gobj)
|
|
|
395
395
|
* the edge of the box. */
|
|
396
396
|
padding: 24,
|
|
397
397
|
edge: {
|
|
398
|
-
|
|
398
|
+
/*
|
|
399
|
+
* A FUNCTION, not a name: a `type` written on the edge
|
|
400
|
+
* datum is overridden by the graph-level default, so a
|
|
401
|
+
* reciprocal pair went on drawing as two straight
|
|
402
|
+
* segments on top of each other. Asking the datum here is
|
|
403
|
+
* what G6 documents for varying the type per element.
|
|
404
|
+
*/
|
|
405
|
+
type: (d) => (d && d.data && d.data.reciprocal)?
|
|
406
|
+
'quadratic': 'polyline',
|
|
399
407
|
style: {
|
|
400
408
|
stroke: edge_color(),
|
|
401
409
|
lineWidth: 1.2,
|
|
@@ -609,11 +617,51 @@ function build_data(gobj)
|
|
|
609
617
|
if(pair.events.length > shown.length) {
|
|
610
618
|
label += "\n+" + (pair.events.length - shown.length);
|
|
611
619
|
}
|
|
620
|
+
|
|
621
|
+
let style = {labelText: label};
|
|
622
|
+
|
|
623
|
+
/*
|
|
624
|
+
* A pair that goes BOTH ways is drawn as two arcs, one bowing
|
|
625
|
+
* each way.
|
|
626
|
+
*
|
|
627
|
+
* Grouping by pair fixed the transitions that share an ordered
|
|
628
|
+
* pair; a RECIPROCAL pair is a different key and lands on the
|
|
629
|
+
* same straight segment -- so the two arrows overlap and the
|
|
630
|
+
* second label is painted under the first. Real case:
|
|
631
|
+
* C_WEBSOCKET goes to ST_WAIT_HANDSHAKE on EV_CONNECTED and
|
|
632
|
+
* comes back on EV_DISCONNECTED, and only the return was
|
|
633
|
+
* readable.
|
|
634
|
+
*/
|
|
635
|
+
let reciprocal = by_pair.has(pair.target + " " + pair.source);
|
|
636
|
+
if(reciprocal) {
|
|
637
|
+
/* Bowed opposite ways so the two arrows separate -- and
|
|
638
|
+
* the two LABELS moved off the midpoint as well, one to
|
|
639
|
+
* each third of its own curve. Bowing alone was not
|
|
640
|
+
* enough: both labels are placed at the middle by default,
|
|
641
|
+
* they carry an opaque background, and the one drawn last
|
|
642
|
+
* simply covered the other. */
|
|
643
|
+
/* The SAME offset on both, not opposite ones. G6 measures
|
|
644
|
+
* `curveOffset` from the edge's OWN direction, and a
|
|
645
|
+
* reciprocal pair runs in opposite directions -- so equal
|
|
646
|
+
* signs bow them to opposite sides of the screen, and
|
|
647
|
+
* opposite signs put them on the same side, one on top of
|
|
648
|
+
* the other.
|
|
649
|
+
*
|
|
650
|
+
* The label stays at the MIDDLE of its arc, where the two
|
|
651
|
+
* are furthest apart. Moving it along the curve instead
|
|
652
|
+
* (0.3 / 0.7) pushed it under a state card: G6's html
|
|
653
|
+
* nodes are DOM elements ABOVE the canvas, so whatever the
|
|
654
|
+
* curve draws under one is invisible. */
|
|
655
|
+
style.curveOffset = 34;
|
|
656
|
+
style.labelPlacement = 0.5;
|
|
657
|
+
}
|
|
658
|
+
|
|
612
659
|
edges.push({
|
|
613
660
|
id: "e_" + edges.length,
|
|
614
661
|
source: pair.source,
|
|
615
662
|
target: pair.target,
|
|
616
|
-
|
|
663
|
+
data: {reciprocal: reciprocal},
|
|
664
|
+
style: style,
|
|
617
665
|
});
|
|
618
666
|
}
|
|
619
667
|
|
package/src/c_yui_gclass.css
CHANGED
|
@@ -506,3 +506,19 @@
|
|
|
506
506
|
min-height: 16rem;
|
|
507
507
|
max-height: 70vh;
|
|
508
508
|
}
|
|
509
|
+
|
|
510
|
+
/*
|
|
511
|
+
* An event declared with no action at all: legal here, and it does
|
|
512
|
+
* nothing. Quieter than an action and louder than an empty cell,
|
|
513
|
+
* because it is neither.
|
|
514
|
+
*/
|
|
515
|
+
.C_YUI_GCLASS .GCLASS_MX_ACTION.is-none-action {
|
|
516
|
+
color: var(--gclass-muted);
|
|
517
|
+
cursor: help;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.C_YUI_GCLASS .GCLASS_LEGEND_SWATCH.is-none-action {
|
|
521
|
+
border-color: transparent;
|
|
522
|
+
color: var(--gclass-muted);
|
|
523
|
+
width: auto;
|
|
524
|
+
}
|
package/src/c_yui_gclass.js
CHANGED
|
@@ -910,13 +910,24 @@ function build_machine(gobj, fsm)
|
|
|
910
910
|
let items = [];
|
|
911
911
|
if(cell.action) {
|
|
912
912
|
items.push(['span', {class: 'GCLASS_MX_ACTION'}, cell.action]);
|
|
913
|
-
} else {
|
|
913
|
+
} else if(cell.has_action) {
|
|
914
914
|
/* The C side cannot name an action: `states2json()`
|
|
915
915
|
* writes the literal "action" for all of them. A mark
|
|
916
916
|
* is the honest drawing of "there is one". */
|
|
917
917
|
items.push(['span', {class: 'GCLASS_MX_ACTION is-unnamed',
|
|
918
918
|
title: t("action"),
|
|
919
919
|
'data-i18n-title': 'action'}, "●"]);
|
|
920
|
+
} else {
|
|
921
|
+
/* Declared with NO action and no next state --
|
|
922
|
+
* `{EV_TX_READY, 0, 0}`, four of them in C_WEBSOCKET.
|
|
923
|
+
* It is not the same as an empty cell (which throws)
|
|
924
|
+
* and it is not an action either: the event is legal
|
|
925
|
+
* here and deliberately does nothing. Drawing it as a
|
|
926
|
+
* mark claimed an action the gclass never declared. */
|
|
927
|
+
items.push(['span', {class: 'GCLASS_MX_ACTION is-none-action',
|
|
928
|
+
title: t("accepted with no action"),
|
|
929
|
+
'data-i18n-title': 'accepted with no action'},
|
|
930
|
+
"—"]);
|
|
920
931
|
}
|
|
921
932
|
if(cell.next_state) {
|
|
922
933
|
items.push(['span', {class: 'GCLASS_MX_ARROW'}, "▸"]);
|
|
@@ -1004,6 +1015,19 @@ function build_legend(fsm, current)
|
|
|
1004
1015
|
['span', {i18n: 'not declared in this state'}, t("not declared in this state")]
|
|
1005
1016
|
]]);
|
|
1006
1017
|
|
|
1018
|
+
/* Only if there is one on screen: a legend for a mark nobody drew
|
|
1019
|
+
* is noise, and most gclasses declare no actionless event. */
|
|
1020
|
+
let has_actionless = fsm.rows.some(
|
|
1021
|
+
(row) => row.cells.some((cell) => cell && !cell.has_action)
|
|
1022
|
+
);
|
|
1023
|
+
if(has_actionless) {
|
|
1024
|
+
items.push(['span', {class: 'GCLASS_LEGEND_ITEM'}, [
|
|
1025
|
+
['i', {class: 'GCLASS_LEGEND_SWATCH is-none-action'}, "—"],
|
|
1026
|
+
['span', {i18n: 'accepted with no action'},
|
|
1027
|
+
t("accepted with no action")]
|
|
1028
|
+
]]);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1007
1031
|
if(current && fsm.states.some((s) => s.name === current)) {
|
|
1008
1032
|
items.push(['span', {class: 'GCLASS_LEGEND_ITEM'}, [
|
|
1009
1033
|
['i', {class: 'GCLASS_LEGEND_SWATCH is-current'}],
|
|
@@ -215,6 +215,20 @@ describe("build_fsm", () => {
|
|
|
215
215
|
expect(fsm.published[0].flags).toEqual(["EVF_OUTPUT_EVENT"]);
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
+
it("tells a declared-but-actionless cell from an action", () => {
|
|
219
|
+
/* `{EV_TX_READY, 0, 0}` -- four of them in C_WEBSOCKET: the
|
|
220
|
+
* event is legal in that state and deliberately does nothing. */
|
|
221
|
+
const none = build_fsm({
|
|
222
|
+
events: [{event_name: "EV_TX_READY", event_flag: ""}],
|
|
223
|
+
states: {"ST_IDLE": [
|
|
224
|
+
{event_name: "EV_TX_READY", action: "", next_state: ""},
|
|
225
|
+
]},
|
|
226
|
+
});
|
|
227
|
+
expect(none.rows[0].cells[0]).toEqual({
|
|
228
|
+
action: "", has_action: false, next_state: "",
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
218
232
|
it("says an action exists without inventing its name", () => {
|
|
219
233
|
expect(fsm.rows[0].cells[1]).toEqual({
|
|
220
234
|
action: "", has_action: true, next_state: "",
|