@toclocoinc/lattice-grid 1.6.1 → 1.7.1
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 +1 -1
- package/docs/API.html +114 -20
- package/docs/api-detail.html +288 -14
- package/lattice-grid.d.ts +1 -1
- package/lattice-grid.esm.min.js +583 -250
- package/lattice-grid.min.cjs +575 -250
- package/lattice-grid.min.css +1 -1
- package/lattice-grid.min.js +575 -250
- package/modules/devtools.esm.min.js +2 -2
- package/modules/dhtmlx-compat.esm.min.js +56843 -0
- package/modules/htmx.esm.min.js +56552 -0
- package/modules/htmx.min.cjs +56533 -0
- package/modules/htmx.min.js +56533 -0
- package/modules/react.esm.min.js +2 -2
- package/modules/svelte.esm.min.js +2 -2
- package/modules/vue.esm.min.js +2 -2
- package/modules/webcomponent.esm.min.js +575 -250
- package/package.json +3 -1
- package/docs/AI-SKILL.md +0 -461
package/docs/api-detail.html
CHANGED
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
<div class="shell">
|
|
438
438
|
<aside class="rail">
|
|
439
439
|
<p class="rail__brand">Lattice Grid</p>
|
|
440
|
-
<p class="rail__sub">Developer guide · v1.
|
|
440
|
+
<p class="rail__sub">Developer guide · v1.7.1</p>
|
|
441
441
|
<nav>
|
|
442
442
|
<div class="rail__group">
|
|
443
443
|
<span class="rail__label">Start here</span>
|
|
@@ -445,6 +445,8 @@
|
|
|
445
445
|
<a href="#install">Install</a>
|
|
446
446
|
<a href="#first-grid">Your first grid</a>
|
|
447
447
|
<a href="#adapters-guide">React, Vue, Svelte</a>
|
|
448
|
+
<a href="#dhtmlx-guide">Coming from dhtmlx Grid</a>
|
|
449
|
+
<a href="#htmx-guide">Using with htmx</a>
|
|
448
450
|
<a href="#concepts">How it works</a>
|
|
449
451
|
</div>
|
|
450
452
|
<div class="rail__group">
|
|
@@ -535,7 +537,7 @@
|
|
|
535
537
|
<a href="API.html">reference tables</a> are the shorter version for when you already know.
|
|
536
538
|
</p>
|
|
537
539
|
<p class="chips">
|
|
538
|
-
<span class="chip">Version 1.
|
|
540
|
+
<span class="chip">Version 1.7.1</span>
|
|
539
541
|
<span class="chip">Zero dependencies</span>
|
|
540
542
|
<span class="chip">No build step</span>
|
|
541
543
|
</p>
|
|
@@ -638,10 +640,28 @@ createGrid(element, { direction: 'rtl' }); <span class="cmt">// or say so out
|
|
|
638
640
|
</div>
|
|
639
641
|
|
|
640
642
|
<h2 id="install">Install</h2>
|
|
641
|
-
<p class="lead-in">Two files. Nothing is fetched at runtime — no CDN, no font, no sprite sheet
|
|
643
|
+
<p class="lead-in">Two files. Nothing is fetched at runtime — no CDN, no font, no sprite sheet — however the two files themselves got onto the page. Four equally valid ways to get them there:</p>
|
|
642
644
|
|
|
643
645
|
<div class="example">
|
|
644
|
-
<p class="example__label">
|
|
646
|
+
<p class="example__label">npm</p>
|
|
647
|
+
<pre><code>npm install @toclocoinc/lattice-grid</code></pre>
|
|
648
|
+
<pre><code><span class="kw">import</span> { createGrid } <span class="kw">from</span> '@toclocoinc/lattice-grid';
|
|
649
|
+
<span class="kw">import</span> '@toclocoinc/lattice-grid/css';
|
|
650
|
+
<span class="kw">const</span> grid = createGrid(document.getElementById('grid'), config);</code></pre>
|
|
651
|
+
</div>
|
|
652
|
+
|
|
653
|
+
<div class="example">
|
|
654
|
+
<p class="example__label">jsDelivr — no npm install, no bundler</p>
|
|
655
|
+
<pre><code><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.7.1/lattice-grid.min.css">
|
|
656
|
+
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.7.1/lattice-grid.min.js"></script>
|
|
657
|
+
|
|
658
|
+
<script>
|
|
659
|
+
<span class="kw">const</span> grid = LatticeGrid.createGrid(document.getElementById('grid'), config);
|
|
660
|
+
</script></code></pre>
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
<div class="example">
|
|
664
|
+
<p class="example__label">Script tag, your own build</p>
|
|
645
665
|
<pre><code><link rel="stylesheet" href="lattice-grid.min.css">
|
|
646
666
|
<script src="lattice-grid.min.js"></script>
|
|
647
667
|
|
|
@@ -651,16 +671,20 @@ createGrid(element, { direction: 'rtl' }); <span class="cmt">// or say so out
|
|
|
651
671
|
</div>
|
|
652
672
|
|
|
653
673
|
<div class="example">
|
|
654
|
-
<p class="example__label">ES modules</p>
|
|
674
|
+
<p class="example__label">ES modules, your own build</p>
|
|
655
675
|
<pre><code><span class="kw">import</span> { createGrid } <span class="kw">from</span> './lattice-grid.esm.min.js';
|
|
656
676
|
<span class="kw">const</span> grid = createGrid(document.getElementById('grid'), config);</code></pre>
|
|
657
677
|
</div>
|
|
658
678
|
|
|
659
679
|
<div class="note">
|
|
660
|
-
<p><strong>
|
|
661
|
-
<code>
|
|
662
|
-
|
|
663
|
-
|
|
680
|
+
<p><strong>jsDelivr mirrors every version published to npm</strong> at
|
|
681
|
+
<code>cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@<version>/<file></code> — pin an
|
|
682
|
+
exact version, e.g. <code>@1.7.1</code> rather than <code>@latest</code>, so a later release
|
|
683
|
+
does not change what a page already in production loads. The same convention reaches a
|
|
684
|
+
module: <code>.../modules/htmx.esm.min.js</code>, <code>.../modules/dhtmlx-compat.esm.min.js</code>,
|
|
685
|
+
and so on. Type declarations resolve automatically through npm's own <code>types</code> field;
|
|
686
|
+
for editor tooling against the CDN or a plain script tag, point your <code>tsconfig</code> at
|
|
687
|
+
<code>lattice-grid.d.ts</code> directly.</p>
|
|
664
688
|
</div>
|
|
665
689
|
|
|
666
690
|
<p class="lead-in">Everything else in the distribution is an alternative packaging or a
|
|
@@ -838,6 +862,226 @@ createGrid(element, { direction: 'rtl' }); <span class="cmt">// or say so out
|
|
|
838
862
|
freely.</p>
|
|
839
863
|
</div>
|
|
840
864
|
|
|
865
|
+
<h2 id="dhtmlx-guide">Coming from dhtmlx Grid</h2>
|
|
866
|
+
<p class="lead-in">
|
|
867
|
+
<code>lattice-grid/modules/dhtmlx-compat</code> exposes a <code>Grid</code> class shaped
|
|
868
|
+
like dhtmlx's own <code>dhx.Grid</code> — the same constructor call, the same
|
|
869
|
+
<code>.data</code>, <code>.selection</code>, <code>.history</code>, <code>.export</code> and
|
|
870
|
+
<code>.events</code> namespaces — sitting on top of a real Lattice grid underneath.
|
|
871
|
+
Swap the import and, for the surface below, the calling code does not change.
|
|
872
|
+
</p>
|
|
873
|
+
|
|
874
|
+
<div class="example">
|
|
875
|
+
<p class="example__label">A drop-in constructor</p>
|
|
876
|
+
<pre><code><span class="kw">import</span> { Grid } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/dhtmlx-compat';
|
|
877
|
+
|
|
878
|
+
<span class="kw">const</span> grid = <span class="kw">new</span> Grid(container, {
|
|
879
|
+
columns: [
|
|
880
|
+
{ id: 'name', header: [{ text: 'Name' }], width: 200, sortable: <span class="kw">true</span> },
|
|
881
|
+
{ id: 'qty', header: [{ text: 'Qty' }], type: 'number', editable: <span class="kw">true</span>, editorType: 'input' },
|
|
882
|
+
],
|
|
883
|
+
data: rows,
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
grid.events.on('cellClick', (row, column, event) => {
|
|
887
|
+
<span class="cmt">// row and column carry .id, row's own fields sit alongside it —</span>
|
|
888
|
+
<span class="cmt">// the same shape dhtmlx's own IRow/ICol declare.</span>
|
|
889
|
+
});</code></pre>
|
|
890
|
+
</div>
|
|
891
|
+
|
|
892
|
+
<div class="why">
|
|
893
|
+
<p><strong>What is covered:</strong> column definitions (<code>header</code>, <code>width</code>,
|
|
894
|
+
<code>sortable</code>, <code>resizable</code>, <code>hidden</code>, <code>editorType</code>,
|
|
895
|
+
<code>editorConfig</code>, <code>options</code>, <code>template</code>, <code>summary</code>);
|
|
896
|
+
<code>.data</code>'s <code>add</code>/<code>update</code>/<code>remove</code>/
|
|
897
|
+
<code>removeAll</code>/<code>parse</code>/<code>load</code>/<code>find</code>/
|
|
898
|
+
<code>findAll</code>/<code>exists</code>/<code>getItem</code>/<code>getId</code>/
|
|
899
|
+
<code>getIndex</code>/<code>getLength</code>/<code>forEach</code>/<code>serialize</code>/
|
|
900
|
+
<code>sort</code>/<code>filter</code>/<code>resetFilter</code>; <code>.selection</code>'s
|
|
901
|
+
<code>setCell</code>/<code>getCell</code>/<code>getCells</code>/<code>isSelectedCell</code>/
|
|
902
|
+
<code>removeCell</code>; <code>.history</code>'s <code>undo</code>/<code>redo</code>/
|
|
903
|
+
<code>canUndo</code>/<code>canRedo</code>/<code>clear</code>/<code>getHistory</code>;
|
|
904
|
+
<code>.export.csv</code>/<code>.xlsx</code>; and the events <code>cellClick</code>,
|
|
905
|
+
<code>cellDblClick</code>, <code>cellRightClick</code>, <code>afterEditStart</code>,
|
|
906
|
+
<code>afterEditEnd</code>, <code>afterSort</code>, <code>filterChange</code>,
|
|
907
|
+
<code>afterColumnDrop</code>, <code>resize</code>, <code>afterResizeEnd</code>,
|
|
908
|
+
<code>afterColumnHide</code>, <code>afterColumnShow</code>, <code>afterExpand</code>,
|
|
909
|
+
<code>afterCollapse</code>, <code>afterSelect</code>, <code>afterUnSelect</code>,
|
|
910
|
+
<code>afterCopy</code>, <code>afterRowDrop</code> and <code>scroll</code>. Grid-level
|
|
911
|
+
<code>dragItem: 'row'</code> becomes <code>rowReorder: true</code> — same-grid
|
|
912
|
+
drag-to-reorder.</p>
|
|
913
|
+
<p><code>cellClick</code>, <code>cellDblClick</code>, <code>cellRightClick</code>,
|
|
914
|
+
<code>afterEditStart</code>, <code>afterEditEnd</code> and <code>afterSort</code> call your
|
|
915
|
+
handler with dhtmlx's own positional arguments — <code>(row, column, event)</code>,
|
|
916
|
+
not Lattice's own event object — because that is dhtmlx's own documented signature for
|
|
917
|
+
them. <code>afterRowDrop</code> calls your handler with <code>(data, event)</code>, firing
|
|
918
|
+
from either a same-grid reorder settling or a row landing here from another grid —
|
|
919
|
+
dhtmlx has one event name for what Lattice models as two. Every other mapped event calls your
|
|
920
|
+
handler with Lattice's own event object, under Lattice's own field names, since a wrong guess
|
|
921
|
+
at a fabricated positional shape is worse than an honest one.</p>
|
|
922
|
+
<p><strong>Index means current display order</strong> — after sort, filter and
|
|
923
|
+
grouping — everywhere <code>.data</code> takes or returns one. dhtmlx is not fully
|
|
924
|
+
consistent about this across its own methods; this is the one meaning, held everywhere.</p>
|
|
925
|
+
<p><strong>Cross-grid dragging needs an explicit <code>rowTransfer</code>.</strong> dhtmlx
|
|
926
|
+
lets any two grids with <code>dragItem: 'row'</code> on the same page exchange rows by
|
|
927
|
+
default; Lattice's <code>rowTransfer</code> is deliberately opt-in per pair, with no
|
|
928
|
+
dhtmlx property to derive it from, so a caller wanting that behaviour passes
|
|
929
|
+
<code>rowTransfer</code> straight through as a bonus config key.</p>
|
|
930
|
+
</div>
|
|
931
|
+
|
|
932
|
+
<div class="why">
|
|
933
|
+
<p><strong>What is not.</strong> Every <code>before*</code>/<code>can*</code>/<code>cancel*</code>
|
|
934
|
+
event is unmapped: dhtmlx's convention lets a handler return <code>false</code> to cancel the
|
|
935
|
+
action, and Lattice has no cancelable-event model to honour that with — approximating one
|
|
936
|
+
would silently ignore a <code>return false</code> a caller depends on. Row and column
|
|
937
|
+
drag-<em>negotiation</em> events — <code>beforeRowDrag</code>, <code>dragRowOut</code>,
|
|
938
|
+
<code>canRowDrop</code>, <code>cancelRowDrop</code>, <code>beforeRowDrop</code> and their
|
|
939
|
+
column equivalents — are unmapped for the same reason: each can refuse or steer a drag
|
|
940
|
+
mid-gesture, which Lattice has no live protocol to offer. <code>export.pdf()</code> and
|
|
941
|
+
<code>export.png()</code> throw — there is no raster export to translate to.
|
|
942
|
+
<code>.rangeSelection</code> is offered on a best-effort basis and its range shape is this
|
|
943
|
+
wrapper's own design, not dhtmlx's — a genuine <code>RangeSelection</code> module is a
|
|
944
|
+
separate part of dhtmlx's own product, and this wrapper does not know its exact shape.
|
|
945
|
+
Classic <code>dhtmlXGridObject</code> (pre-Suite 5, string-configured,
|
|
946
|
+
index-addressed) is a different product in every respect that matters here and is not covered
|
|
947
|
+
at all.</p>
|
|
948
|
+
<p>Calling an unmapped event name does not fail silently: the first subscription logs which
|
|
949
|
+
name has no Lattice equivalent, so a caller relying on it finds out in development rather
|
|
950
|
+
than in production.</p>
|
|
951
|
+
</div>
|
|
952
|
+
|
|
953
|
+
<h2 id="htmx-guide">Using with htmx</h2>
|
|
954
|
+
<p class="lead-in">
|
|
955
|
+
<code>lattice-grid/modules/htmx</code> lets a grid survive htmx's own DOM swaps,
|
|
956
|
+
hydrate from a server-rendered <code><table></code>, and drive sort, filter and
|
|
957
|
+
infinite scroll over plain htmx requests — the server owns pagination and the
|
|
958
|
+
request lifecycle; this module only wires the grid's own state to it. Importing it
|
|
959
|
+
is enough for the lifecycle half: it registers itself against
|
|
960
|
+
<code>document</code> on load.
|
|
961
|
+
</p>
|
|
962
|
+
|
|
963
|
+
<div class="example">
|
|
964
|
+
<p class="example__label">Declarative init</p>
|
|
965
|
+
<pre><code><script src="https://unpkg.com/htmx.org@2"></script>
|
|
966
|
+
<script type="module" src="dist/modules/htmx.esm.min.js"></script>
|
|
967
|
+
|
|
968
|
+
<div id="grid" data-lattice-grid></div>
|
|
969
|
+
<script type="application/json" data-lattice-config>
|
|
970
|
+
{ "columns": [{ "field": "name" }, { "field": "qty", "type": "number" }] }
|
|
971
|
+
</script>
|
|
972
|
+
|
|
973
|
+
<span class="cmt">// Anywhere on the page, once:</span>
|
|
974
|
+
<span class="kw">import</span> { autoInit } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/htmx';
|
|
975
|
+
autoInit(document);</code></pre>
|
|
976
|
+
</div>
|
|
977
|
+
|
|
978
|
+
<div class="why">
|
|
979
|
+
<p><strong>One file, not two.</strong> <code>autoInit</code> comes from
|
|
980
|
+
<code>modules/htmx</code> itself here, not from the base package — this module
|
|
981
|
+
already carries a complete, independently-bundled copy of <code>createGrid</code> and
|
|
982
|
+
everything it depends on, the same way every module built this way does (a bundle
|
|
983
|
+
inlines what it imports; it has no way to reach across to a copy some other
|
|
984
|
+
<code><script></code> tag happens to have loaded). Left unexported, a page using
|
|
985
|
+
htmx integration would load that engine twice: once for <code>modules/htmx</code>,
|
|
986
|
+
again for the base package's own <code>createGrid</code>. This module re-exports
|
|
987
|
+
<code>createGrid</code>, <code>autoInit</code>, <code>hydrateTable</code>,
|
|
988
|
+
<code>readTable</code>, <code>serialiseState</code> and <code>restoreState</code>
|
|
989
|
+
precisely so a page never has to choose between the two — it is the complete package
|
|
990
|
+
for anything touching htmx, not an add-on alongside the base one.</p>
|
|
991
|
+
<p><strong>Surviving a swap.</strong> <code>autoInit(root)</code> builds a grid on
|
|
992
|
+
every <code>[data-lattice-grid]</code> element under <code>root</code> it has not
|
|
993
|
+
already built one for — idempotent, so calling it again after a swap only picks
|
|
994
|
+
up what is new. A sibling <code><script type="application/json"
|
|
995
|
+
data-lattice-config></code> supplies columns and options; without one, a
|
|
996
|
+
<code><table></code> element is hydrated instead, reading its header row for
|
|
997
|
+
columns and its body rows for data, then replacing itself with the grid. Once
|
|
998
|
+
imported, this module listens for htmx's own <code>htmx:beforeCleanupElement</code>
|
|
999
|
+
and <code>htmx:load</code> and calls <code>grid.destroy()</code> / <code>autoInit</code>
|
|
1000
|
+
at the right moments automatically — a grid inside a swapped-out subtree is torn
|
|
1001
|
+
down before htmx detaches it; a grid inside newly-loaded content is built without
|
|
1002
|
+
re-scanning the whole page. A page with JavaScript disabled sees the plain
|
|
1003
|
+
<code><table></code>, still readable, since it is only ever replaced once the
|
|
1004
|
+
grid has actually mounted.</p>
|
|
1005
|
+
<p><strong>Finding a live grid from its element</strong> works everywhere in this
|
|
1006
|
+
library, not just through <code>autoInit</code>: <code>element.__lattice</code> holds
|
|
1007
|
+
the instance for any element <code>createGrid</code> was called on, and is cleared
|
|
1008
|
+
when the grid is destroyed.</p>
|
|
1009
|
+
</div>
|
|
1010
|
+
|
|
1011
|
+
<div class="example">
|
|
1012
|
+
<p class="example__label">Server-driven sort, filter and infinite scroll</p>
|
|
1013
|
+
<pre><code><span class="kw">import</span> { createGrid, driveServerMode, driveInfiniteScroll } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/htmx';
|
|
1014
|
+
|
|
1015
|
+
<span class="kw">const</span> grid = createGrid(host, { columns, rows: [], rowKey: 'id' });
|
|
1016
|
+
<span class="kw">const</span> COLUMNS = columns.map(c => ({ field: c.field }));
|
|
1017
|
+
|
|
1018
|
+
<span class="cmt">// Replaces the view outright — fires whenever sort or filter changes.</span>
|
|
1019
|
+
driveServerMode(grid, document.getElementById('query-trigger'), { columns: COLUMNS });
|
|
1020
|
+
|
|
1021
|
+
<span class="cmt">// Appends the next chunk — fires as the grid's own visible rows near the end.</span>
|
|
1022
|
+
driveInfiniteScroll(grid, document.getElementById('sentinel'), { columns: COLUMNS });</code></pre>
|
|
1023
|
+
<pre><code><div id="query-trigger" hx-get="/rows" hx-trigger="lattice:query-changed" hx-swap="none" hidden></div>
|
|
1024
|
+
<div id="sentinel" hx-get="/rows" hx-trigger="revealed, lattice:scroll-near-end" hx-swap="none" hidden></div></code></pre>
|
|
1025
|
+
</div>
|
|
1026
|
+
|
|
1027
|
+
<div class="why">
|
|
1028
|
+
<p><strong>Two triggers, two elements, deliberately.</strong> A sort or filter
|
|
1029
|
+
change and a scroll asking for more rows are different operations — one
|
|
1030
|
+
replaces every loaded row, the other appends to them — and there is no way to
|
|
1031
|
+
tell the two apart once a response has landed if they share a trigger. Each function
|
|
1032
|
+
configures the request (<code>offset</code>, <code>limit</code>, <code>sort</code>,
|
|
1033
|
+
<code>filters</code> — a small, stable convention any server-side language can
|
|
1034
|
+
read with a JSON parser and a slice) and reads the response back into the grid
|
|
1035
|
+
itself, so <code>hx-swap="none"</code> is required on both: htmx sends the request
|
|
1036
|
+
and nothing else, since a grid is not an HTML swap target.</p>
|
|
1037
|
+
<p><strong>The sentinel's own trigger names two events for a reason.</strong>
|
|
1038
|
+
<code>revealed</code> is htmx's own once-per-element mechanism and gets the very
|
|
1039
|
+
first chunk, firing the moment htmx has processed the element — reliable
|
|
1040
|
+
because it needs nothing from this module's own timing. Every chunk after that fires
|
|
1041
|
+
through <code>lattice:scroll-near-end</code>, which <code>driveInfiniteScroll</code>
|
|
1042
|
+
dispatches once the grid's own visible row window comes within
|
|
1043
|
+
<code>opts.threshold</code> rows (default 20) of what is loaded. That split matters
|
|
1044
|
+
for a fixed-height, virtualised grid specifically: nothing about it ever leaves the
|
|
1045
|
+
page's own viewport once first revealed — new rows land inside the grid's own
|
|
1046
|
+
scroll area, not the page's — so a page-scroll-only trigger would fire exactly
|
|
1047
|
+
once and then go silent. Reading the grid's own render position instead is what
|
|
1048
|
+
makes every later chunk fire on genuine scroll, not on the repaint a successful load
|
|
1049
|
+
causes by itself.</p>
|
|
1050
|
+
<p><strong>Out-of-band updates.</strong> <code>driveOobUpdates(grid, opts)</code>
|
|
1051
|
+
watches for htmx's own out-of-band swaps landing on an element carrying
|
|
1052
|
+
<code>data-lattice-row="<key>"</code>, reads the swapped fragment as that row's
|
|
1053
|
+
cells, and applies it to the grid in place — scroll position, selection and
|
|
1054
|
+
filter state are untouched, since nothing about the view is reloaded.</p>
|
|
1055
|
+
<p><strong>Browser history.</strong> On <code>htmx:beforeHistorySave</code>, every
|
|
1056
|
+
live grid's state (<code>serialiseState</code> — sort, filters, column order and
|
|
1057
|
+
widths, scroll position and selection, base64url-encoded and diffed against defaults
|
|
1058
|
+
so an untouched grid costs almost nothing) is written onto its element as
|
|
1059
|
+
<code>data-lattice-state</code>, which rides along in htmx's own history snapshot. On
|
|
1060
|
+
<code>htmx:historyRestore</code>, it is read back and applied — browser back
|
|
1061
|
+
returns a visitor to the sort, filter and scroll position they had, not a blank
|
|
1062
|
+
slate. A cache miss (the page was re-fetched from the server) restores nothing, since
|
|
1063
|
+
a fresh response is already the truth.</p>
|
|
1064
|
+
<p><strong>A failed request leaves the grid alone.</strong> <code>driveServerMode</code>
|
|
1065
|
+
and <code>driveInfiniteScroll</code> both listen for
|
|
1066
|
+
<code>htmx:responseError</code>/<code>htmx:sendError</code>; the grid's rows are
|
|
1067
|
+
never touched by a failed request, and a recoverable message is shown through
|
|
1068
|
+
<code>grid.overlay</code> rather than the grid going blank.</p>
|
|
1069
|
+
</div>
|
|
1070
|
+
|
|
1071
|
+
<div class="why">
|
|
1072
|
+
<p><strong>What this does not do.</strong> It does not call <code>fetch</code> or
|
|
1073
|
+
<code>htmx.ajax()</code> anywhere — htmx owns every request end to end; this
|
|
1074
|
+
only supplies the moment and the parameters, and reads the response back in. It does
|
|
1075
|
+
not import htmx: every htmx-specific call goes through <code>globalThis.htmx</code>,
|
|
1076
|
+
read at call time, so loading this module never requires htmx to already be on the
|
|
1077
|
+
page, only to be present by the time a driven request actually fires. It ships as
|
|
1078
|
+
ESM and as a plain <code><script src></code> build with no bundler required,
|
|
1079
|
+
with zero runtime dependencies beyond the grid itself and, at call time, htmx —
|
|
1080
|
+
but because it references the grid's own internals directly rather than the copy
|
|
1081
|
+
already on the page, the bundle carries a full copy of the grid core alongside its
|
|
1082
|
+
own code, the same trade-off the web component and dhtmlx wrappers already make.</p>
|
|
1083
|
+
</div>
|
|
1084
|
+
|
|
841
1085
|
<h2 id="concepts">How it works</h2>
|
|
842
1086
|
<p class="lead-in">
|
|
843
1087
|
Four ideas explain most of the API. If you read nothing else, read this section — the rest of
|
|
@@ -924,7 +1168,7 @@ off(); <span class="cmt">// every subscrip
|
|
|
924
1168
|
</p>
|
|
925
1169
|
<div class="example">
|
|
926
1170
|
<p class="example__label">Which version am I running?</p>
|
|
927
|
-
<pre><code>grid.getVersion(); <span class="cmt">// '1.
|
|
1171
|
+
<pre><code>grid.getVersion(); <span class="cmt">// '1.7.1'</span>
|
|
928
1172
|
LatticeGrid.getVersion(); <span class="cmt">// the same, when you have no grid to hand</span></code></pre>
|
|
929
1173
|
</div>
|
|
930
1174
|
<p class="lead-in">
|
|
@@ -1981,6 +2225,14 @@ createGrid(right, { columns, rows,
|
|
|
1981
2225
|
is announced rather than left silent.
|
|
1982
2226
|
</p>
|
|
1983
2227
|
|
|
2228
|
+
<p class="lead-in">
|
|
2229
|
+
<strong>Picking up a row shows it, wherever the pointer goes.</strong> The row being dragged
|
|
2230
|
+
dims in its own grid, and a small label naming it follows the pointer for as long as the drag
|
|
2231
|
+
is held — over the gap between two grids, over one that is about to refuse the drop, anywhere
|
|
2232
|
+
the row's own dimming cannot reach. Both clear on release, and a handle press never also starts
|
|
2233
|
+
a range selection underneath it.
|
|
2234
|
+
</p>
|
|
2235
|
+
|
|
1984
2236
|
<h2 id="column-tags">Column tags</h2>
|
|
1985
2237
|
<p class="lead-in">
|
|
1986
2238
|
Tag columns, then let a user show only the ones carrying a chosen tag. Sixty columns of
|
|
@@ -4466,11 +4718,33 @@ grid.on('view:default', e => api.patch(`/views/${e.view.id}`, { isDefault: tr
|
|
|
4466
4718
|
</div>
|
|
4467
4719
|
<p class="lead-in">
|
|
4468
4720
|
Each event carries the one view that moved, so you send a single record rather than diffing
|
|
4469
|
-
two lists.
|
|
4470
|
-
<code>views.
|
|
4471
|
-
catch the failure and call <code>grid.views.reload()</code>.
|
|
4721
|
+
two lists. Since the grid does not track whether your write landed, catch the failure and
|
|
4722
|
+
call <code>grid.views.reload()</code>.
|
|
4472
4723
|
</p>
|
|
4473
4724
|
|
|
4725
|
+
<div class="example">
|
|
4726
|
+
<p class="example__label">With no backend at all</p>
|
|
4727
|
+
<pre><code>createGrid(el, {
|
|
4728
|
+
views: { local: <span class="kw">true</span>, allowSave: <span class="kw">true</span> },
|
|
4729
|
+
});</code></pre>
|
|
4730
|
+
</div>
|
|
4731
|
+
|
|
4732
|
+
<div class="why">
|
|
4733
|
+
<p><strong>The other half of the same seam.</strong> <code>views.storage</code> above is where
|
|
4734
|
+
a developer plugs in their own backend — a real server, reached over the network. Not every
|
|
4735
|
+
grid has one to plug in, and a picker offering "Save" that quietly does nothing until a backend
|
|
4736
|
+
exists is worse than not offering it. <code>views.local: true</code> is the no-backend answer:
|
|
4737
|
+
saved views live in this browser's own <code>localStorage</code>, under a default key shared by
|
|
4738
|
+
every grid on the origin unless you pass one of your own —
|
|
4739
|
+
<code>views: { local: { key: 'orders-grid-views' } }</code> — to keep two grids' views apart.
|
|
4740
|
+
Given alongside an explicit <code>storage</code>, the explicit adapter always wins and
|
|
4741
|
+
<code>local</code> is silently — well, not silently: it warns once — ignored, so a page cannot
|
|
4742
|
+
end up writing to both without meaning to. The adapter itself is exported as
|
|
4743
|
+
<code>createLocalViewStorage(opts)</code>, for anyone who wants it directly — a custom key
|
|
4744
|
+
without the shorthand, or a different <code>Storage</code>-shaped backing such as
|
|
4745
|
+
<code>sessionStorage</code> for views scoped to one tab rather than persisted across visits.</p>
|
|
4746
|
+
</div>
|
|
4747
|
+
|
|
4474
4748
|
<h2 id="history-guide">Undo</h2>
|
|
4475
4749
|
<p class="lead-in">
|
|
4476
4750
|
Undo covers the whole grid, not only edits. Sorts, filters, column moves, grouping, an applied
|
|
@@ -4850,7 +5124,7 @@ grid.licence.state(); <span class="cmt">// 'licensed' | 'localhost' | 'trial'<
|
|
|
4850
5124
|
|
|
4851
5125
|
<footer>
|
|
4852
5126
|
<p>
|
|
4853
|
-
Lattice Grid 1.
|
|
5127
|
+
Lattice Grid 1.7.1 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
|
|
4854
5128
|
Written against the shipped source. Where this guide and the code disagree, the code wins —
|
|
4855
5129
|
please <a href="https://www.latticegrid.dev">tell us</a>.
|
|
4856
5130
|
</p>
|
package/lattice-grid.d.ts
CHANGED