@ashley-shrok/viewmodel-shell 1.6.0 → 1.7.0
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/dist/browser.d.ts +4 -1
- package/dist/browser.js +41 -14
- package/dist/index.d.ts +6 -5
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -52,7 +52,10 @@ export declare class BrowserAdapter implements Adapter {
|
|
|
52
52
|
* every keystroke writes, Enter dispatches filterAction; pagination
|
|
53
53
|
* prev/next write the target page to paginationBind then dispatch
|
|
54
54
|
* prevAction/nextAction. Per-row controls (row.actions[]) are a mix of
|
|
55
|
-
* ButtonNode and CheckboxNode
|
|
55
|
+
* ButtonNode and CheckboxNode; the renderer partitions them by entry.type —
|
|
56
|
+
* CheckboxNodes render in a dedicated LEADING column (left, the data-grid
|
|
57
|
+
* selection convention), ButtonNodes in the TRAILING actions cell (right).
|
|
58
|
+
* When row.action
|
|
56
59
|
* is set, the entire <tr> becomes clickable + keyboard-activatable
|
|
57
60
|
* (Enter / Space — Space preventDefaults page scroll) and exposes
|
|
58
61
|
* role="button", tabindex=0, and an aria-label derived from cell text;
|
package/dist/browser.js
CHANGED
|
@@ -781,7 +781,10 @@ export class BrowserAdapter {
|
|
|
781
781
|
* every keystroke writes, Enter dispatches filterAction; pagination
|
|
782
782
|
* prev/next write the target page to paginationBind then dispatch
|
|
783
783
|
* prevAction/nextAction. Per-row controls (row.actions[]) are a mix of
|
|
784
|
-
* ButtonNode and CheckboxNode
|
|
784
|
+
* ButtonNode and CheckboxNode; the renderer partitions them by entry.type —
|
|
785
|
+
* CheckboxNodes render in a dedicated LEADING column (left, the data-grid
|
|
786
|
+
* selection convention), ButtonNodes in the TRAILING actions cell (right).
|
|
787
|
+
* When row.action
|
|
785
788
|
* is set, the entire <tr> becomes clickable + keyboard-activatable
|
|
786
789
|
* (Enter / Space — Space preventDefaults page scroll) and exposes
|
|
787
790
|
* role="button", tabindex=0, and an aria-label derived from cell text;
|
|
@@ -795,6 +798,17 @@ export class BrowserAdapter {
|
|
|
795
798
|
table.className = "vms-table";
|
|
796
799
|
const thead = document.createElement("thead");
|
|
797
800
|
const headerRow = document.createElement("tr");
|
|
801
|
+
// Per-row checkboxes render in a dedicated LEADING column. If ANY row
|
|
802
|
+
// carries a checkbox, every row gets a leading select cell (empty when the
|
|
803
|
+
// row has none) and the header/filter rows get a matching leading <th>, so
|
|
804
|
+
// body cells stay column-aligned with their headers. (The trailing actions
|
|
805
|
+
// cell needs no header because it's the LAST column — a leading column does.)
|
|
806
|
+
const tableHasCheckboxes = n.rows.some(r => r.actions?.some(e => e.type === "checkbox") ?? false);
|
|
807
|
+
if (tableHasCheckboxes) {
|
|
808
|
+
const selTh = document.createElement("th");
|
|
809
|
+
selTh.className = "vms-table__th vms-table__th--select";
|
|
810
|
+
headerRow.appendChild(selTh);
|
|
811
|
+
}
|
|
798
812
|
const sortIntent = (n.sortBind != null ? this.sa.read(n.sortBind) : null);
|
|
799
813
|
const sortedCol = sortIntent?.column;
|
|
800
814
|
const sortedDir = sortIntent?.direction;
|
|
@@ -832,6 +846,9 @@ export class BrowserAdapter {
|
|
|
832
846
|
const filterAction = n.filterAction;
|
|
833
847
|
const filterRow = document.createElement("tr");
|
|
834
848
|
filterRow.className = "vms-table__filter-row";
|
|
849
|
+
if (tableHasCheckboxes) {
|
|
850
|
+
filterRow.appendChild(document.createElement("th"));
|
|
851
|
+
}
|
|
835
852
|
n.columns.forEach(col => {
|
|
836
853
|
const th = document.createElement("th");
|
|
837
854
|
if (col.filterable) {
|
|
@@ -898,6 +915,22 @@ export class BrowserAdapter {
|
|
|
898
915
|
}
|
|
899
916
|
});
|
|
900
917
|
}
|
|
918
|
+
// Leading select cell — holds this row's checkbox controls (empty when
|
|
919
|
+
// the row has none). Rendered for every row whenever the table has any
|
|
920
|
+
// checkboxes, so columns line up with the leading <th> added above. When
|
|
921
|
+
// row.action is set, swallow clicks so toggling doesn't fire the row action.
|
|
922
|
+
if (tableHasCheckboxes) {
|
|
923
|
+
const selTd = document.createElement("td");
|
|
924
|
+
selTd.className = "vms-table__td vms-table__td--select";
|
|
925
|
+
for (const entry of row.actions ?? []) {
|
|
926
|
+
if (entry.type === "checkbox")
|
|
927
|
+
this.checkbox(entry, selTd, on);
|
|
928
|
+
}
|
|
929
|
+
if (row.action) {
|
|
930
|
+
selTd.addEventListener("click", (e) => { e.stopPropagation(); });
|
|
931
|
+
}
|
|
932
|
+
tr.appendChild(selTd);
|
|
933
|
+
}
|
|
901
934
|
n.columns.forEach(col => {
|
|
902
935
|
const td = document.createElement("td");
|
|
903
936
|
td.className = "vms-table__td";
|
|
@@ -921,21 +954,15 @@ export class BrowserAdapter {
|
|
|
921
954
|
}
|
|
922
955
|
tr.appendChild(td);
|
|
923
956
|
});
|
|
924
|
-
//
|
|
925
|
-
//
|
|
926
|
-
//
|
|
927
|
-
|
|
928
|
-
if (
|
|
957
|
+
// Trailing actions cell — per-row ButtonNodes only (checkboxes render in
|
|
958
|
+
// the leading select cell above). When row.action is set, swallow clicks
|
|
959
|
+
// on the actions td so pressing a button doesn't ALSO fire the row action.
|
|
960
|
+
const buttonEntries = (row.actions ?? []).filter((e) => e.type === "button");
|
|
961
|
+
if (buttonEntries.length > 0) {
|
|
929
962
|
const td = document.createElement("td");
|
|
930
963
|
td.className = "vms-table__td vms-table__td--actions";
|
|
931
|
-
for (const entry of
|
|
932
|
-
|
|
933
|
-
this.button(entry, td, on);
|
|
934
|
-
else if (entry.type === "checkbox")
|
|
935
|
-
this.checkbox(entry, td, on);
|
|
936
|
-
// forward-compatible: unknown entry types no-op (the closed TS union
|
|
937
|
-
// narrows; the runtime guard is for late-arriving wire shapes).
|
|
938
|
-
}
|
|
964
|
+
for (const entry of buttonEntries)
|
|
965
|
+
this.button(entry, td, on);
|
|
939
966
|
if (row.action) {
|
|
940
967
|
td.addEventListener("click", (e) => { e.stopPropagation(); });
|
|
941
968
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -320,11 +320,12 @@ export interface TableRow {
|
|
|
320
320
|
* per-row button, checkbox, or cell linkLabel anchor does NOT also fire
|
|
321
321
|
* `row.action` (the renderer stops propagation on those targets). */
|
|
322
322
|
action?: ActionEvent;
|
|
323
|
-
/** Per-row interactive controls
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
323
|
+
/** Per-row interactive controls. Each entry is either a ButtonNode (its own
|
|
324
|
+
* unique action name encodes per-row identity, e.g. `delete-row-42`) or a
|
|
325
|
+
* CheckboxNode (its own `bind` path per row). The renderer partitions by
|
|
326
|
+
* `entry.type`: CheckboxNodes render in a dedicated LEADING column (left —
|
|
327
|
+
* the data-grid selection convention), ButtonNodes in the TRAILING actions
|
|
328
|
+
* cell (right). A previous version typed this as `ButtonNode[]` and called
|
|
328
329
|
* the button renderer blindly, silently dropping non-button entries. */
|
|
329
330
|
actions?: (ButtonNode | CheckboxNode)[];
|
|
330
331
|
variant?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashley-shrok/viewmodel-shell",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A server-driven UI framework where the wire format is structured enough that agents can build full-stack apps without ever opening a browser and all UI tests are pure unit tests with no browser runtime. Server returns a JSON tree of typed nodes; a thin TypeScript adapter renders it to DOM. Backend-agnostic — a .NET reference backend ships with the repo, but any language can produce the JSON contract.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|