@livenetworks/ashlar 1.3.2
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 +177 -0
- package/js/COMPONENTS.md +1102 -0
- package/js/index.js +41 -0
- package/js/ln-accordion/README.md +137 -0
- package/js/ln-accordion/ln-accordion.js +1 -0
- package/js/ln-accordion/src/ln-accordion.js +41 -0
- package/js/ln-ajax/README.md +91 -0
- package/js/ln-ajax/ln-ajax.js +1 -0
- package/js/ln-ajax/src/ln-ajax.js +277 -0
- package/js/ln-api-connector/README.md +150 -0
- package/js/ln-api-connector/ln-api-connector.js +1 -0
- package/js/ln-api-connector/src/ln-api-connector.js +265 -0
- package/js/ln-autoresize/README.md +80 -0
- package/js/ln-autoresize/ln-autoresize.js +1 -0
- package/js/ln-autoresize/src/ln-autoresize.js +47 -0
- package/js/ln-autosave/README.md +92 -0
- package/js/ln-autosave/ln-autosave.js +1 -0
- package/js/ln-autosave/src/ln-autosave.js +147 -0
- package/js/ln-circular-progress/README.md +161 -0
- package/js/ln-circular-progress/ln-circular-progress.js +1 -0
- package/js/ln-circular-progress/src/ln-circular-progress.js +133 -0
- package/js/ln-confirm/README.md +86 -0
- package/js/ln-confirm/_ln-confirm.scss +13 -0
- package/js/ln-confirm/ln-confirm.js +1 -0
- package/js/ln-confirm/src/ln-confirm.js +131 -0
- package/js/ln-core/crypto.js +83 -0
- package/js/ln-core/helpers.js +411 -0
- package/js/ln-core/index.js +5 -0
- package/js/ln-core/persist.js +71 -0
- package/js/ln-core/positioning.js +207 -0
- package/js/ln-core/reactive.js +74 -0
- package/js/ln-couchdb-connector/README.md +156 -0
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
- package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +348 -0
- package/js/ln-data-coordinator/README.md +165 -0
- package/js/ln-data-coordinator/ln-data-coordinator.js +1 -0
- package/js/ln-data-coordinator/src/ln-data-coordinator.js +249 -0
- package/js/ln-data-store/README.md +94 -0
- package/js/ln-data-store/ln-data-store.js +1 -0
- package/js/ln-data-store/src/ln-data-store.js +699 -0
- package/js/ln-data-table/README.md +110 -0
- package/js/ln-data-table/ln-data-table.js +1 -0
- package/js/ln-data-table/ln-data-table.scss +10 -0
- package/js/ln-data-table/src/ln-data-table.js +1103 -0
- package/js/ln-date/README.md +151 -0
- package/js/ln-date/ln-date.js +1 -0
- package/js/ln-date/src/ln-date.js +442 -0
- package/js/ln-dropdown/README.md +117 -0
- package/js/ln-dropdown/ln-dropdown.js +1 -0
- package/js/ln-dropdown/ln-dropdown.scss +15 -0
- package/js/ln-dropdown/src/ln-dropdown.js +174 -0
- package/js/ln-external-links/README.md +341 -0
- package/js/ln-external-links/ln-external-links.js +1 -0
- package/js/ln-external-links/src/ln-external-links.js +116 -0
- package/js/ln-filter/README.md +99 -0
- package/js/ln-filter/ln-filter.js +1 -0
- package/js/ln-filter/ln-filter.scss +7 -0
- package/js/ln-filter/src/ln-filter.js +404 -0
- package/js/ln-form/README.md +101 -0
- package/js/ln-form/ln-form.js +1 -0
- package/js/ln-form/src/ln-form.js +199 -0
- package/js/ln-http/README.md +89 -0
- package/js/ln-http/ln-http.js +1 -0
- package/js/ln-http/src/ln-http.js +219 -0
- package/js/ln-icons/README.md +88 -0
- package/js/ln-icons/ln-icons.js +1 -0
- package/js/ln-icons/src/ln-icons.js +169 -0
- package/js/ln-link/README.md +303 -0
- package/js/ln-link/ln-link.js +1 -0
- package/js/ln-link/src/ln-link.js +196 -0
- package/js/ln-modal/README.md +154 -0
- package/js/ln-modal/ln-modal.js +1 -0
- package/js/ln-modal/ln-modal.scss +11 -0
- package/js/ln-modal/src/ln-modal.js +201 -0
- package/js/ln-nav/README.md +70 -0
- package/js/ln-nav/ln-nav.js +1 -0
- package/js/ln-nav/src/ln-nav.js +177 -0
- package/js/ln-number/README.md +122 -0
- package/js/ln-number/ln-number.js +1 -0
- package/js/ln-number/src/ln-number.js +302 -0
- package/js/ln-popover/README.md +127 -0
- package/js/ln-popover/ln-popover.js +1 -0
- package/js/ln-popover/src/ln-popover.js +288 -0
- package/js/ln-progress/README.md +442 -0
- package/js/ln-progress/ln-progress.js +1 -0
- package/js/ln-progress/src/ln-progress.js +150 -0
- package/js/ln-search/README.md +83 -0
- package/js/ln-search/ln-search.js +1 -0
- package/js/ln-search/ln-search.scss +7 -0
- package/js/ln-search/src/ln-search.js +114 -0
- package/js/ln-sortable/README.md +95 -0
- package/js/ln-sortable/ln-sortable.js +1 -0
- package/js/ln-sortable/src/ln-sortable.js +203 -0
- package/js/ln-table/README.md +101 -0
- package/js/ln-table/ln-table-sort.js +1 -0
- package/js/ln-table/ln-table.js +1 -0
- package/js/ln-table/ln-table.scss +11 -0
- package/js/ln-table/src/ln-table-sort.js +168 -0
- package/js/ln-table/src/ln-table.js +473 -0
- package/js/ln-tabs/README.md +137 -0
- package/js/ln-tabs/ln-tabs.js +1 -0
- package/js/ln-tabs/src/ln-tabs.js +171 -0
- package/js/ln-time/README.md +81 -0
- package/js/ln-time/ln-time.js +1 -0
- package/js/ln-time/src/ln-time.js +192 -0
- package/js/ln-toast/README.md +122 -0
- package/js/ln-toast/ln-toast.js +15 -0
- package/js/ln-toast/src/ln-toast.js +210 -0
- package/js/ln-toast/template.html +14 -0
- package/js/ln-toggle/README.md +137 -0
- package/js/ln-toggle/ln-toggle.js +1 -0
- package/js/ln-toggle/src/ln-toggle.js +139 -0
- package/js/ln-tooltip/README.md +58 -0
- package/js/ln-tooltip/ln-tooltip.js +1 -0
- package/js/ln-tooltip/ln-tooltip.scss +9 -0
- package/js/ln-tooltip/src/ln-tooltip.js +169 -0
- package/js/ln-translations/README.md +96 -0
- package/js/ln-translations/ln-translations.js +1 -0
- package/js/ln-translations/src/ln-translations.js +275 -0
- package/js/ln-upload/README.md +180 -0
- package/js/ln-upload/ln-upload.js +1 -0
- package/js/ln-upload/ln-upload.scss +20 -0
- package/js/ln-upload/src/ln-upload.js +407 -0
- package/js/ln-validate/README.md +108 -0
- package/js/ln-validate/ln-validate.js +1 -0
- package/js/ln-validate/src/ln-validate.js +160 -0
- package/package.json +55 -0
- package/scss/base/_global.scss +83 -0
- package/scss/base/_reset.scss +17 -0
- package/scss/base/_typography.scss +125 -0
- package/scss/components/_accordion.scss +34 -0
- package/scss/components/_ajax.scss +15 -0
- package/scss/components/_alert.scss +5 -0
- package/scss/components/_app-shell.scss +15 -0
- package/scss/components/_avatar.scss +6 -0
- package/scss/components/_breadcrumbs.scss +33 -0
- package/scss/components/_button.scss +20 -0
- package/scss/components/_card.scss +10 -0
- package/scss/components/_chip.scss +5 -0
- package/scss/components/_circular-progress.scss +29 -0
- package/scss/components/_confirm.scss +5 -0
- package/scss/components/_data-table.scss +83 -0
- package/scss/components/_dropdown.scss +25 -0
- package/scss/components/_empty-state.scss +22 -0
- package/scss/components/_form.scss +100 -0
- package/scss/components/_layout.scss +8 -0
- package/scss/components/_link.scss +11 -0
- package/scss/components/_ln-table.scss +60 -0
- package/scss/components/_loader.scss +6 -0
- package/scss/components/_modal.scss +20 -0
- package/scss/components/_nav.scss +9 -0
- package/scss/components/_page-header.scss +10 -0
- package/scss/components/_popover.scss +10 -0
- package/scss/components/_progress.scss +17 -0
- package/scss/components/_prose.scss +5 -0
- package/scss/components/_scrollbar.scss +32 -0
- package/scss/components/_sections.scss +12 -0
- package/scss/components/_sidebar.scss +5 -0
- package/scss/components/_stat-card.scss +5 -0
- package/scss/components/_status-badge.scss +4 -0
- package/scss/components/_stepper.scss +5 -0
- package/scss/components/_table.scss +19 -0
- package/scss/components/_tabs.scss +21 -0
- package/scss/components/_timeline.scss +14 -0
- package/scss/components/_toast.scss +41 -0
- package/scss/components/_toggle.scss +81 -0
- package/scss/components/_tooltip.scss +18 -0
- package/scss/components/_translations.scss +111 -0
- package/scss/components/_upload.scss +51 -0
- package/scss/config/_breakpoints.scss +72 -0
- package/scss/config/_density.scss +117 -0
- package/scss/config/_icons.scss +37 -0
- package/scss/config/_mixins.scss +13 -0
- package/scss/config/_theme.scss +216 -0
- package/scss/config/_tokens.scss +419 -0
- package/scss/config/mixins/_accordion.scss +52 -0
- package/scss/config/mixins/_ajax.scss +39 -0
- package/scss/config/mixins/_alert.scss +82 -0
- package/scss/config/mixins/_app-shell.scss +312 -0
- package/scss/config/mixins/_avatar.scss +109 -0
- package/scss/config/mixins/_borders.scss +36 -0
- package/scss/config/mixins/_breadcrumbs.scss +72 -0
- package/scss/config/mixins/_breakpoints.scss +62 -0
- package/scss/config/mixins/_btn.scss +179 -0
- package/scss/config/mixins/_card.scss +338 -0
- package/scss/config/mixins/_chip.scss +66 -0
- package/scss/config/mixins/_circular-progress.scss +71 -0
- package/scss/config/mixins/_collapsible.scss +24 -0
- package/scss/config/mixins/_colors.scss +46 -0
- package/scss/config/mixins/_confirm.scss +31 -0
- package/scss/config/mixins/_data-table.scss +346 -0
- package/scss/config/mixins/_display.scss +32 -0
- package/scss/config/mixins/_dropdown.scss +143 -0
- package/scss/config/mixins/_empty-state.scss +30 -0
- package/scss/config/mixins/_focus.scss +55 -0
- package/scss/config/mixins/_footer.scss +42 -0
- package/scss/config/mixins/_form.scss +601 -0
- package/scss/config/mixins/_index.scss +58 -0
- package/scss/config/mixins/_interaction.scss +15 -0
- package/scss/config/mixins/_kbd.scss +22 -0
- package/scss/config/mixins/_layout.scss +117 -0
- package/scss/config/mixins/_link.scss +55 -0
- package/scss/config/mixins/_ln-table.scss +420 -0
- package/scss/config/mixins/_loader.scss +26 -0
- package/scss/config/mixins/_modal.scss +66 -0
- package/scss/config/mixins/_motion.scss +19 -0
- package/scss/config/mixins/_nav.scss +273 -0
- package/scss/config/mixins/_page-header.scss +69 -0
- package/scss/config/mixins/_popover.scss +25 -0
- package/scss/config/mixins/_position.scss +32 -0
- package/scss/config/mixins/_progress.scss +56 -0
- package/scss/config/mixins/_prose.scss +127 -0
- package/scss/config/mixins/_shadows.scss +8 -0
- package/scss/config/mixins/_sidebar.scss +95 -0
- package/scss/config/mixins/_sizing.scss +6 -0
- package/scss/config/mixins/_spacing.scss +19 -0
- package/scss/config/mixins/_stat-card.scss +68 -0
- package/scss/config/mixins/_status-badge.scss +83 -0
- package/scss/config/mixins/_stepper.scss +78 -0
- package/scss/config/mixins/_table.scss +215 -0
- package/scss/config/mixins/_tabs.scss +64 -0
- package/scss/config/mixins/_timeline.scss +69 -0
- package/scss/config/mixins/_toast.scss +148 -0
- package/scss/config/mixins/_tooltip.scss +111 -0
- package/scss/config/mixins/_transitions.scss +10 -0
- package/scss/config/mixins/_translations.scss +124 -0
- package/scss/config/mixins/_typography.scss +57 -0
- package/scss/config/mixins/_upload.scss +168 -0
- package/scss/ln-ashlar.scss +62 -0
- package/scss/tabler-icons.txt +5039 -0
- package/scss/utilities/_animations.scss +83 -0
- package/scss/utilities/_utilities.scss +49 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# ln-data-table
|
|
2
|
+
|
|
3
|
+
A zero-dependency, event-driven **Data Presentation Shell** that manages row template rendering, interactive sorting, column filtering, search states, multi-row selections, virtual scrolling (10,000+ items), and keyboard layouts.
|
|
4
|
+
|
|
5
|
+
It decouples data presentation from data fetching: announcing query intents via request events and awaiting rendered datasets asynchronously.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🧭 Philosophy & Architecture
|
|
10
|
+
|
|
11
|
+
1. **Presenter Separation:** The component possesses no fetching capabilities. It translates column and search interactions into unified query events and delegates execution to a coordinator (such as `ln-store` or direct server fetchers).
|
|
12
|
+
2. **Dynamic Template Scoping:** Row designs are written cleanly inside standard HTML `<template>` slots. Values are safely bound using double-curly brackets (`{{ field }}`) using browser-native `textContent` for robust XSS protection.
|
|
13
|
+
3. **Flicker-Free Virtual Rendering:** Massive tables auto-activate high-performance virtual scrolling. Only the visible portion of rows plus a buffer are mounted, keeping the DOM extremely small.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 📦 Minimal Blueprint
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<section data-ln-data-table="documents" data-ln-data-table-selectable id="documents-table">
|
|
21
|
+
<header class="ln-table__toolbar">
|
|
22
|
+
<h3>Documents</h3>
|
|
23
|
+
<input type="search" placeholder="Search..." data-ln-data-table-search>
|
|
24
|
+
</header>
|
|
25
|
+
|
|
26
|
+
<table>
|
|
27
|
+
<thead>
|
|
28
|
+
<tr>
|
|
29
|
+
<th data-ln-col-select></th>
|
|
30
|
+
<th data-ln-col="title">
|
|
31
|
+
Title
|
|
32
|
+
<button data-ln-col-sort aria-label="Sort">
|
|
33
|
+
<svg class="ln-icon" data-ln-sort-icon="none"><use href="#ln-arrows-sort"></use></svg>
|
|
34
|
+
<svg class="ln-icon" data-ln-sort-icon="asc"><use href="#ln-arrow-up"></use></svg>
|
|
35
|
+
<svg class="ln-icon" data-ln-sort-icon="desc"><use href="#ln-arrow-down"></use></svg>
|
|
36
|
+
</button>
|
|
37
|
+
</th>
|
|
38
|
+
</tr>
|
|
39
|
+
</thead>
|
|
40
|
+
<tbody data-ln-data-table-body></tbody>
|
|
41
|
+
</table>
|
|
42
|
+
|
|
43
|
+
<!-- Row Template -->
|
|
44
|
+
<template data-ln-template="documents-row">
|
|
45
|
+
<tr data-ln-row>
|
|
46
|
+
<td><input type="checkbox" data-ln-row-select></td>
|
|
47
|
+
<td>{{ title }}</td>
|
|
48
|
+
</tr>
|
|
49
|
+
</template>
|
|
50
|
+
</section>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> [!IMPORTANT]
|
|
54
|
+
> Directly nested `<table>` elements must not be wrapped in scroll-trapping `overflow-y: auto` containers. The sticky header and virtual scroll calculations automatically bind to the nearest scrollable page-level container.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 🛠️ Declarative API Contract
|
|
59
|
+
|
|
60
|
+
### HTML Attributes
|
|
61
|
+
|
|
62
|
+
| Attribute | Elements | Description |
|
|
63
|
+
| :--- | :--- | :--- |
|
|
64
|
+
| `data-ln-data-table` | `<section>` | Component root and namespace. |
|
|
65
|
+
| `data-ln-data-table-selectable` | `<section>` | Opt-in. Activates checkbox columns and selection tracking. |
|
|
66
|
+
| `data-ln-col="field"` | `<th>` | Binds column interactions (sort, filter) to a data property. |
|
|
67
|
+
| `data-ln-data-table-search` | `<input>` | Binds keystroke entries to instant search events. |
|
|
68
|
+
|
|
69
|
+
### JS API
|
|
70
|
+
|
|
71
|
+
Access the presentation state directly via the `lnDataTable` property on the section container:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const table = document.getElementById('documents-table');
|
|
75
|
+
|
|
76
|
+
// 1. Inspect state
|
|
77
|
+
const selection = table.lnDataTable.selectedIds; // Set of active string IDs
|
|
78
|
+
const sortedBy = table.lnDataTable.currentSort; // { field, direction }
|
|
79
|
+
|
|
80
|
+
// 2. Tear down listeners
|
|
81
|
+
table.lnDataTable.destroy();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## ⚡ DOM Events
|
|
87
|
+
|
|
88
|
+
### Emitted (Telemetry & Intent)
|
|
89
|
+
|
|
90
|
+
| Event | Payload | Description |
|
|
91
|
+
| :--- | :--- | :--- |
|
|
92
|
+
| `ln-data-table:request-data` | `{ table, sort, filters, search }` | Intent event. Fired whenever query states change. |
|
|
93
|
+
| `ln-data-table:row-click` | `{ table, id, record }` | Fired when clicking a row (ignored on inputs/links). |
|
|
94
|
+
| `ln-data-table:row-action` | `{ table, id, action, record }` | Fired on clicking `[data-ln-row-action="action"]` slots. |
|
|
95
|
+
| `ln-data-table:select` | `{ table, selectedIds, count }` | Fired on selecting row checkboxes. |
|
|
96
|
+
|
|
97
|
+
### Received (State Synchronization)
|
|
98
|
+
|
|
99
|
+
| Event | Payload | Description |
|
|
100
|
+
| :--- | :--- | :--- |
|
|
101
|
+
| `ln-data-table:set-data` | `{ data: Array, total, filtered }` | Populates table, rebuilds filters, formats counts. |
|
|
102
|
+
| `ln-data-table:set-loading` | `{ loading: Boolean }` | Toggles dim loading state (pointer-events disabled). |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## ⚠️ Common Pitfalls
|
|
107
|
+
|
|
108
|
+
- **Bypassing the Template Slot:** Putting static row markup directly inside `<tbody>` will result in it being immediately purged on the first data render event. Always build row cells within `<template>`.
|
|
109
|
+
- **Horizontal Overflow Traps:** Attempting to wrap tables in scroll layers breaks sticky header positioning. Let layout views manage full viewport boundaries instead.
|
|
110
|
+
- **Direct JS string injections:** Attempting to render raw HTML inside templates bypasses the safe `textContent` parser, opening XSS risks. Use custom action dispatchers for complex fields.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){"use strict";const x={};function F(a,r){x[a]||(x[a]=document.querySelector('[data-ln-template="'+a+'"]'));const f=x[a];return f?f.content.cloneNode(!0):(console.warn("["+r+'] Template "'+a+'" not found'),null)}function w(a,r,f){a.dispatchEvent(new CustomEvent(r,{bubbles:!0,detail:f||{}}))}function D(a,r){if(!a||!r)return a;const f=a.querySelectorAll("[data-ln-field]");for(let u=0;u<f.length;u++){const s=f[u],p=s.getAttribute("data-ln-field");r[p]!=null&&(s.textContent=r[p])}const y=a.querySelectorAll("[data-ln-attr]");for(let u=0;u<y.length;u++){const s=y[u],p=s.getAttribute("data-ln-attr").split(",");for(let e=0;e<p.length;e++){const t=p[e].trim().split(":");if(t.length!==2)continue;const n=t[0].trim(),l=t[1].trim();r[l]!=null&&s.setAttribute(n,r[l])}}const S=a.querySelectorAll("[data-ln-show]");for(let u=0;u<S.length;u++){const s=S[u],p=s.getAttribute("data-ln-show");p in r&&s.classList.toggle("hidden",!r[p])}const m=a.querySelectorAll("[data-ln-class]");for(let u=0;u<m.length;u++){const s=m[u],p=s.getAttribute("data-ln-class").split(",");for(let e=0;e<p.length;e++){const t=p[e].trim().split(":");if(t.length!==2)continue;const n=t[0].trim(),l=t[1].trim();l in r&&s.classList.toggle(n,!!r[l])}}return a}function T(a,r){if(!a||!r)return a;const f=document.createTreeWalker(a,NodeFilter.SHOW_TEXT);for(;f.nextNode();){const y=f.currentNode;y.textContent.indexOf("{{")!==-1&&(y.textContent=y.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(S,m){return r[m]!==void 0?r[m]:""}))}return a}function R(a,r){if(!document.body){document.addEventListener("DOMContentLoaded",function(){R(a,r)}),console.warn("["+r+'] Script loaded before <body> — add "defer" to your <script> tag');return}a()}function k(a,r,f){if(a){const y=a.querySelector('[data-ln-template="'+r+'"]');if(y)return y.content.cloneNode(!0)}return F(r,f)}function I(a,r,f,y){if(a.nodeType!==1)return;const m=r.indexOf("[")!==-1||r.indexOf(".")!==-1||r.indexOf("#")!==-1?r:"["+r+"]",u=Array.from(a.querySelectorAll(m));a.matches&&a.matches(m)&&u.push(a);for(const s of u)s[f]||(s[f]=new y(s))}function O(a,r,f,y,S={}){const m=S.extraAttributes||[],u=S.onAttributeChange||null,s=S.onInit||null;function p(e){const t=e||document.body;I(t,a,r,f),s&&s(t)}return R(function(){const e=new MutationObserver(function(n){for(let l=0;l<n.length;l++){const i=n[l];if(i.type==="childList")for(let o=0;o<i.addedNodes.length;o++){const c=i.addedNodes[o];c.nodeType===1&&(I(c,a,r,f),s&&s(c))}else i.type==="attributes"&&(u&&i.target[r]?u(i.target,i.attributeName):(I(i.target,a,r,f),s&&s(i.target)))}});let t=[];if(a.indexOf("[")!==-1){const n=/\[([\w-]+)/g;let l;for(;(l=n.exec(a))!==null;)t.push(l[1])}else t.push(a);e.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:t.concat(m)})},y),window[r]=p,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){p(document.body)}):p(document.body),p}const q={};function H(a,r){q[a]=r}function N(a){return q[a]||{ingress:r=>r,egress:r=>r}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=H,window.lnCore.getDataMapper=N),(function(){const a="data-ln-data-table",r="lnDataTable";if(window[r]!==void 0)return;const S=typeof Intl<"u"?new Intl.NumberFormat(document.documentElement.lang||void 0):null;function m(e){return S?S.format(e):String(e)}function u(e){let t=e.parentElement;for(;t&&t!==document.body&&t!==document.documentElement;){const l=getComputedStyle(t).overflowY;if(l==="auto"||l==="scroll")return t;t=t.parentElement}return null}function s(e){this.dom=e,this.name=e.getAttribute(a)||"",this.table=e.querySelector("table"),this.tbody=e.querySelector("[data-ln-data-table-body]")||e.querySelector("tbody"),this.thead=e.querySelector("thead"),this.ths=this.thead?Array.from(this.thead.querySelectorAll("th")):[],this.isLoaded=!1,this.totalCount=0,this.visibleCount=0,this.currentSort=null,this.currentFilters={},this.currentSearch="",this.selectedIds=new Set,this._data=[],this._lastTotal=0,this._lastFiltered=0,this._filterOptions={},this._filterableFields=this.ths.filter(function(n){return n.getAttribute("data-ln-col")&&n.querySelector("[data-ln-col-filter]")}).map(function(n){return n.getAttribute("data-ln-col")}),this._virtual=!1,this._rowHeight=0,this._vStart=-1,this._vEnd=-1,this._rafId=null,this._scrollHandler=null,this._scrollContainer=null,this._totalSpan=e.querySelector("[data-ln-data-table-total]"),this._filteredSpan=e.querySelector("[data-ln-data-table-filtered]"),this._filteredSpan&&(this._filteredWrap=this._filteredSpan.parentElement!==e?this._filteredSpan.closest("[data-ln-data-table-filtered-wrap]")||this._filteredSpan.parentNode:null),this._selectedSpan=e.querySelector("[data-ln-data-table-selected]"),this._selectedSpan&&(this._selectedWrap=this._selectedSpan.parentElement!==e?this._selectedSpan.closest("[data-ln-data-table-selected-wrap]")||this._selectedSpan.parentNode:null);const t=this;return this._onSetData=function(n){const l=n.detail||{};t._data=l.data||[],t._lastTotal=l.total!=null?l.total:t._data.length,t._lastFiltered=l.filtered!=null?l.filtered:t._data.length,t.totalCount=t._lastTotal,t.visibleCount=t._lastFiltered,t.isLoaded=!0,t._updateFilterOptions(l.filterOptions),t._vStart=-1,t._vEnd=-1,t._renderRows(),t._updateFooter(),w(e,"ln-data-table:rendered",{table:t.name,total:t.totalCount,visible:t.visibleCount})},e.addEventListener("ln-data-table:set-data",this._onSetData),this._onSetLoading=function(n){const l=n.detail&&n.detail.loading;e.classList.toggle("ln-data-table--loading",!!l),l&&(t.isLoaded=!1)},e.addEventListener("ln-data-table:set-loading",this._onSetLoading),this._sortButtons=Array.from(e.querySelectorAll("[data-ln-col-sort]")),this._onSortClick=function(n){const l=n.target.closest("[data-ln-col-sort]");if(!l)return;const i=l.closest("th");if(!i)return;const o=i.getAttribute("data-ln-col");o&&t._handleSort(o,i)},this.thead&&this.thead.addEventListener("click",this._onSortClick),this._activeDropdown=null,this._onFilterClick=function(n){const l=n.target.closest("[data-ln-col-filter]");if(!l)return;n.stopPropagation();const i=l.closest("th");if(!i)return;const o=i.getAttribute("data-ln-col");if(o){if(t._activeDropdown&&t._activeDropdown.field===o){t._closeFilterDropdown();return}t._openFilterDropdown(o,i,l)}},this.thead&&this.thead.addEventListener("click",this._onFilterClick),this._onDocClick=function(){t._activeDropdown&&t._closeFilterDropdown()},document.addEventListener("click",this._onDocClick),this._onClearAll=function(n){n.target.closest("[data-ln-data-table-clear-all]")&&(t.currentFilters={},t._updateFilterIndicators(),w(e,"ln-data-table:clear-filters",{table:t.name}),t._requestData())},e.addEventListener("click",this._onClearAll),this._selectable=e.hasAttribute("data-ln-data-table-selectable"),this._selectableActive=!1,this._selectable&&this._enableSelection(),this._onRowClick=function(n){if(n.target.closest("[data-ln-row-select]")||n.target.closest("[data-ln-row-action]")||n.target.closest("a")||n.target.closest("button")||n.ctrlKey||n.metaKey||n.button===1)return;const l=n.target.closest("[data-ln-row]");if(!l)return;const i=l.getAttribute("data-ln-row-id"),o=l._lnRecord||{};w(e,"ln-data-table:row-click",{table:t.name,id:i,record:o})},this.tbody&&this.tbody.addEventListener("click",this._onRowClick),this._onRowAction=function(n){const l=n.target.closest("[data-ln-row-action]");if(!l)return;n.stopPropagation();const i=l.closest("[data-ln-row]");if(!i)return;const o=l.getAttribute("data-ln-row-action"),c=i.getAttribute("data-ln-row-id"),d=i._lnRecord||{};w(e,"ln-data-table:row-action",{table:t.name,id:c,action:o,record:d})},this.tbody&&this.tbody.addEventListener("click",this._onRowAction),this._searchInput=e.querySelector("[data-ln-data-table-search]"),this._searchInput&&(this._onSearchInput=function(){t.currentSearch=t._searchInput.value,w(e,"ln-data-table:search",{table:t.name,query:t.currentSearch}),t._requestData()},this._searchInput.addEventListener("input",this._onSearchInput)),this._focusedRowIndex=-1,this._onKeydown=function(n){if(!e.contains(document.activeElement)&&document.activeElement!==document.body||document.activeElement&&(document.activeElement.tagName==="INPUT"||document.activeElement.tagName==="TEXTAREA"))return;if(n.key==="/"){t._searchInput&&(n.preventDefault(),t._searchInput.focus());return}const l=t.tbody?Array.from(t.tbody.querySelectorAll("[data-ln-row]")):[];if(l.length)switch(n.key){case"ArrowDown":n.preventDefault(),t._focusedRowIndex=Math.min(t._focusedRowIndex+1,l.length-1),t._focusRow(l);break;case"ArrowUp":n.preventDefault(),t._focusedRowIndex=Math.max(t._focusedRowIndex-1,0),t._focusRow(l);break;case"Home":n.preventDefault(),t._focusedRowIndex=0,t._focusRow(l);break;case"End":n.preventDefault(),t._focusedRowIndex=l.length-1,t._focusRow(l);break;case"Enter":if(t._focusedRowIndex>=0&&t._focusedRowIndex<l.length){n.preventDefault();const i=l[t._focusedRowIndex];w(e,"ln-data-table:row-click",{table:t.name,id:i.getAttribute("data-ln-row-id"),record:i._lnRecord||{}})}break;case" ":if(t._selectable&&t._focusedRowIndex>=0&&t._focusedRowIndex<l.length){n.preventDefault();const i=l[t._focusedRowIndex].querySelector("[data-ln-row-select]");i&&(i.checked=!i.checked,i.dispatchEvent(new Event("change",{bubbles:!0})))}break;case"Escape":t._activeDropdown&&t._closeFilterDropdown();break}},document.addEventListener("keydown",this._onKeydown),w(e,"ln-data-table:request-data",{table:this.name,sort:this.currentSort,filters:this.currentFilters,search:this.currentSearch}),this}s.prototype._handleSort=function(e,t){let n;!this.currentSort||this.currentSort.field!==e?n="asc":this.currentSort.direction==="asc"?n="desc":n=null;for(let l=0;l<this.ths.length;l++)this.ths[l].classList.remove("ln-sort-asc","ln-sort-desc");n?(this.currentSort={field:e,direction:n},t.classList.add(n==="asc"?"ln-sort-asc":"ln-sort-desc")):this.currentSort=null,w(this.dom,"ln-data-table:sort",{table:this.name,field:e,direction:n}),this._requestData()},s.prototype._requestData=function(){w(this.dom,"ln-data-table:request-data",{table:this.name,sort:this.currentSort,filters:this.currentFilters,search:this.currentSearch})},s.prototype._updateSelectAll=function(){if(!this._selectAllCheckbox||!this.tbody)return;const e=this.tbody.querySelectorAll("[data-ln-row]");let t=e.length>0;for(let n=0;n<e.length;n++){const l=e[n].getAttribute("data-ln-row-id");if(l!=null&&!this.selectedIds.has(l)){t=!1;break}}this._selectAllCheckbox.checked=t},Object.defineProperty(s.prototype,"selectedCount",{get:function(){return this.selectedIds.size},set:function(){}}),s.prototype._enableSelection=function(){if(this._selectableActive)return;this._selectableActive=!0;const e=this;if(this._onSelectionChange=function(t){const n=t.target.closest("[data-ln-row-select]");if(!n)return;const l=n.closest("[data-ln-row]");if(!l)return;const i=l.getAttribute("data-ln-row-id");i!=null&&(n.checked?(e.selectedIds.add(i),l.classList.add("ln-row-selected")):(e.selectedIds.delete(i),l.classList.remove("ln-row-selected")),e.selectedCount=e.selectedIds.size,e._updateSelectAll(),e._updateFooter(),w(e.dom,"ln-data-table:select",{table:e.name,selectedIds:e.selectedIds,count:e.selectedCount}))},this.tbody&&this.tbody.addEventListener("change",this._onSelectionChange),this._selectAllCheckbox=this.dom.querySelector('[data-ln-col-select] input[type="checkbox"]')||this.dom.querySelector("[data-ln-col-select]"),this._selectAllCheckbox&&this._selectAllCheckbox.tagName==="TH"){const t=document.createElement("input");t.type="checkbox",t.setAttribute("aria-label","Select all"),this._selectAllCheckbox.appendChild(t),this._selectAllCheckbox=t}if(this._selectAllCheckbox&&(this._onSelectAll=function(){const t=e._selectAllCheckbox.checked,n=e.tbody?e.tbody.querySelectorAll("[data-ln-row]"):[];for(let l=0;l<n.length;l++){const i=n[l].getAttribute("data-ln-row-id"),o=n[l].querySelector("[data-ln-row-select]");i!=null&&(t?(e.selectedIds.add(i),n[l].classList.add("ln-row-selected")):(e.selectedIds.delete(i),n[l].classList.remove("ln-row-selected")),o&&(o.checked=t))}e.selectedCount=e.selectedIds.size,w(e.dom,"ln-data-table:select-all",{table:e.name,selected:t}),w(e.dom,"ln-data-table:select",{table:e.name,selectedIds:e.selectedIds,count:e.selectedCount}),e._updateFooter()},this._selectAllCheckbox.addEventListener("change",this._onSelectAll)),this.tbody){const t=this.tbody.querySelectorAll("[data-ln-row]");for(let n=0;n<t.length;n++){const l=t[n].querySelector("[data-ln-row-select]"),i=t[n].getAttribute("data-ln-row-id");l&&l.checked&&i!=null&&(this.selectedIds.add(i),t[n].classList.add("ln-row-selected"))}this.selectedCount=this.selectedIds.size,this.selectedCount>0&&this._updateSelectAll()}},s.prototype._disableSelection=function(){if(!this._selectableActive)return;this._selectableActive=!1,this.tbody&&this._onSelectionChange&&this.tbody.removeEventListener("change",this._onSelectionChange),this._selectAllCheckbox&&this._onSelectAll&&this._selectAllCheckbox.removeEventListener("change",this._onSelectAll);const e=this.dom.querySelector("[data-ln-col-select]");if(e){const t=e.querySelector('input[type="checkbox"]');t&&t.remove()}if(this._selectAllCheckbox=null,this.selectedIds.clear(),this.selectedCount=0,this.tbody){const t=this.tbody.querySelectorAll("[data-ln-row]");for(let n=0;n<t.length;n++){t[n].classList.remove("ln-row-selected");const l=t[n].querySelector("[data-ln-row-select]");l&&(l.checked=!1)}}this._updateFooter()},s.prototype._focusRow=function(e){for(let t=0;t<e.length;t++)e[t].classList.remove("ln-row-focused"),e[t].removeAttribute("tabindex");if(this._focusedRowIndex>=0&&this._focusedRowIndex<e.length){const t=e[this._focusedRowIndex];t.classList.add("ln-row-focused"),t.setAttribute("tabindex","0"),t.focus(),t.scrollIntoView({block:"nearest"})}},s.prototype._openFilterDropdown=function(e,t,n){this._closeFilterDropdown();const l=k(this.dom,this.name+"-column-filter","ln-data-table")||k(this.dom,"column-filter","ln-data-table");if(!l)return;const i=l.firstElementChild;if(!i)return;const o=this._getUniqueValues(e),c=i.querySelector("[data-ln-filter-options]"),d=i.querySelector("[data-ln-filter-search]"),b=this.currentFilters[e]||[],g=this;if(d&&o.length<=8&&d.classList.add("hidden"),c){const C=c.querySelector("[data-ln-filter-reset]");C&&(C.checked=b.length===0);const v=k(i,this.name+"-column-filter-item","ln-data-table")||k(i,"column-filter-item","ln-data-table");if(v)for(let h=0;h<o.length;h++){const _=o[h],A=v.cloneNode(!0);D(A,{value:_});const L=A.querySelector('input[type="checkbox"]');L&&(L.value=_,L.checked=b.length>0&&b.indexOf(_)!==-1),c.appendChild(A)}c.addEventListener("change",function(h){h.target.type==="checkbox"&&(g._applyFilterMutualExclusion(h.target,c),g._onFilterChange(e,c))})}d&&d.addEventListener("input",function(){const C=d.value.toLowerCase(),v=c.querySelectorAll("li");for(let h=0;h<v.length;h++){const _=v[h].textContent.toLowerCase();v[h].classList.toggle("hidden",C&&_.indexOf(C)===-1)}});const E=i.querySelector("[data-ln-filter-clear]");E&&E.addEventListener("click",function(){delete g.currentFilters[e],g._closeFilterDropdown(),g._updateFilterIndicators(),w(g.dom,"ln-data-table:filter",{table:g.name,field:e,values:[]}),g._requestData()}),t.appendChild(i),this._activeDropdown={field:e,th:t,el:i},i.addEventListener("click",function(C){C.stopPropagation()})},s.prototype._closeFilterDropdown=function(){this._activeDropdown&&(this._activeDropdown.el&&this._activeDropdown.el.parentNode&&this._activeDropdown.el.parentNode.removeChild(this._activeDropdown.el),this._activeDropdown=null)},s.prototype._applyFilterMutualExclusion=function(e,t){const n=e.hasAttribute("data-ln-filter-reset"),l=t.querySelector("[data-ln-filter-reset]"),i=t.querySelectorAll('input[type="checkbox"]:not([data-ln-filter-reset])');if(n){e.checked=!0;for(let o=0;o<i.length;o++)i[o].checked=!1}else if(e.checked)l&&(l.checked=!1);else{let o=!1;for(let c=0;c<i.length;c++)if(i[c].checked){o=!0;break}!o&&l&&(l.checked=!0)}},s.prototype._onFilterChange=function(e,t){const n=t.querySelector("[data-ln-filter-reset]"),l=t.querySelectorAll('input[type="checkbox"]:not([data-ln-filter-reset])'),i=[];for(let c=0;c<l.length;c++)l[c].checked&&i.push(l[c].value);const o=n&&n.checked||i.length===0;o?delete this.currentFilters[e]:this.currentFilters[e]=i,this._updateFilterIndicators(),w(this.dom,"ln-data-table:filter",{table:this.name,field:e,values:o?[]:i}),this._requestData()},s.prototype._updateFilterOptions=function(e){if(e!==null&&typeof e=="object"&&!Array.isArray(e)){const t=Object.keys(e);for(let n=0;n<t.length;n++){const l=t[n],i=e[l];if(!Array.isArray(i))continue;const o={},c=[];for(let d=0;d<i.length;d++){const b=String(i[d]);o[b]||(o[b]=!0,c.push(b))}this._filterOptions[l]=c.sort()}}else{const t=this._filterableFields,n=this._data;for(let l=0;l<t.length;l++){const i=t[l];this._filterOptions[i]||(this._filterOptions[i]=[]);const o=this._filterOptions[i],c={};for(let d=0;d<o.length;d++)c[o[d]]=!0;for(let d=0;d<n.length;d++){const b=n[d][i];if(b!=null){const g=String(b);c[g]||(c[g]=!0,o.push(g))}}o.sort()}}},s.prototype._getUniqueValues=function(e){return(this._filterOptions[e]||[]).slice().sort()},s.prototype._updateFilterIndicators=function(){const e=this.ths;for(let t=0;t<e.length;t++){const n=e[t],l=n.getAttribute("data-ln-col");if(!l)continue;const i=n.querySelector("[data-ln-col-filter]");if(!i)continue;const o=this.currentFilters[l]&&this.currentFilters[l].length>0;i.classList.toggle("ln-filter-active",!!o)}},s.prototype._renderRows=function(){if(!this.tbody)return;const e=this._data,t=this._lastTotal,n=this._lastFiltered;if(t===0){this._disableVirtualScroll(),this._showEmptyState(this.name+"-empty");return}if(e.length===0||n===0){this._disableVirtualScroll(),this._showEmptyState(this.name+"-empty-filtered");return}e.length>200?(this._enableVirtualScroll(),this._renderVirtual()):(this._disableVirtualScroll(),this._renderAll())},s.prototype._renderAll=function(){const e=this._data,t=document.createDocumentFragment();for(let n=0;n<e.length;n++){const l=this._buildRow(e[n]);if(!l)break;t.appendChild(l)}this.tbody.textContent="",this.tbody.appendChild(t),this._selectable&&this._updateSelectAll()},s.prototype._buildRow=function(e){const t=k(this.dom,this.name+"-row","ln-data-table");if(!t)return null;const n=t.querySelector("[data-ln-row]")||t.firstElementChild;if(!n)return null;if(this._fillRow(n,e),n._lnRecord=e,e.id!=null&&n.setAttribute("data-ln-row-id",e.id),this._selectable&&e.id!=null&&this.selectedIds.has(String(e.id))){n.classList.add("ln-row-selected");const l=n.querySelector("[data-ln-row-select]");l&&(l.checked=!0)}return n},s.prototype._enableVirtualScroll=function(){if(this._virtual)return;this._virtual=!0,this._vStart=-1,this._vEnd=-1;const e=this;if(!this._rowHeight){const n=this._buildRow(this._data[0]);n&&(this.tbody.textContent="",this.tbody.appendChild(n),this._rowHeight=n.offsetHeight||40,this.tbody.textContent="")}this._scrollContainer=u(this.dom);const t=this._scrollContainer||window;this._scrollHandler=function(){e._rafId||(e._rafId=requestAnimationFrame(function(){e._rafId=null,e._renderVirtual()}))},t.addEventListener("scroll",this._scrollHandler,{passive:!0}),window.addEventListener("resize",this._scrollHandler,{passive:!0})},s.prototype._disableVirtualScroll=function(){this._virtual&&(this._virtual=!1,this._scrollHandler&&((this._scrollContainer||window).removeEventListener("scroll",this._scrollHandler),window.removeEventListener("resize",this._scrollHandler),this._scrollHandler=null),this._scrollContainer=null,this._rafId&&(cancelAnimationFrame(this._rafId),this._rafId=null),this._vStart=-1,this._vEnd=-1)},s.prototype._renderVirtual=function(){const e=this._data,t=e.length,n=this._rowHeight;if(!n||!t)return;const l=this.thead?this.thead.offsetHeight:0,i=this._scrollContainer;let o,c;if(i){const h=this.table.getBoundingClientRect(),_=i.getBoundingClientRect(),A=h.top-_.top+i.scrollTop+l;o=i.scrollTop-A,c=i.clientHeight}else{const A=this.table.getBoundingClientRect().top+window.scrollY+l;o=window.scrollY-A,c=window.innerHeight}let d=Math.max(0,Math.floor(o/n)-15);d=Math.min(d,t);const b=Math.min(d+Math.ceil(c/n)+30,t);if(d===this._vStart&&b===this._vEnd)return;this._vStart=d,this._vEnd=b;const g=this.ths.length||1,E=d*n,C=(t-b)*n,v=document.createDocumentFragment();if(E>0){const h=document.createElement("tr");h.className="ln-data-table__spacer",h.setAttribute("aria-hidden","true");const _=document.createElement("td");_.setAttribute("colspan",g),_.style.height=E+"px",h.appendChild(_),v.appendChild(h)}for(let h=d;h<b;h++){const _=this._buildRow(e[h]);_&&v.appendChild(_)}if(C>0){const h=document.createElement("tr");h.className="ln-data-table__spacer",h.setAttribute("aria-hidden","true");const _=document.createElement("td");_.setAttribute("colspan",g),_.style.height=C+"px",h.appendChild(_),v.appendChild(h)}this.tbody.textContent="",this.tbody.appendChild(v),this._selectable&&this._updateSelectAll()},s.prototype._fillRow=function(e,t){T(e,t);const n=e.querySelectorAll("[data-ln-cell-attr]");for(let l=0;l<n.length;l++){const i=n[l],o=i.getAttribute("data-ln-cell-attr").split(",");for(let c=0;c<o.length;c++){const d=o[c].trim().split(":");if(d.length!==2)continue;const b=d[0].trim(),g=d[1].trim();t[b]!=null&&i.setAttribute(g,t[b])}}},s.prototype._showEmptyState=function(e){const t=k(this.dom,e,"ln-data-table");this.tbody.textContent="",t&&this.tbody.appendChild(t)},s.prototype._updateFooter=function(){const e=this._lastTotal,t=this._lastFiltered,n=t<e;if(this._totalSpan&&(this._totalSpan.textContent=m(e)),this._filteredSpan&&(this._filteredSpan.textContent=n?m(t):""),this._filteredWrap&&this._filteredWrap.classList.toggle("hidden",!n),this._selectedSpan){const l=this.selectedIds.size;this._selectedSpan.textContent=l>0?m(l):"",this._selectedWrap&&this._selectedWrap.classList.toggle("hidden",l===0)}},s.prototype.destroy=function(){this.dom[r]&&(this.dom.removeEventListener("ln-data-table:set-data",this._onSetData),this.dom.removeEventListener("ln-data-table:set-loading",this._onSetLoading),this.thead&&(this.thead.removeEventListener("click",this._onSortClick),this.thead.removeEventListener("click",this._onFilterClick)),document.removeEventListener("click",this._onDocClick),document.removeEventListener("keydown",this._onKeydown),this._searchInput&&this._searchInput.removeEventListener("input",this._onSearchInput),this.tbody&&(this.tbody.removeEventListener("click",this._onRowClick),this.tbody.removeEventListener("click",this._onRowAction)),this._onSelectionChange&&this.tbody&&this.tbody.removeEventListener("change",this._onSelectionChange),this._selectAllCheckbox&&this._onSelectAll&&this._selectAllCheckbox.removeEventListener("change",this._onSelectAll),this.dom.removeEventListener("click",this._onClearAll),this._closeFilterDropdown(),this._disableVirtualScroll(),this._data=[],delete this.dom[r])};function p(e,t){const n=e[r];if(n&&t==="data-ln-data-table-selectable"){const l=e.hasAttribute("data-ln-data-table-selectable");l!==n._selectable&&(n._selectable=l,l?n._enableSelection():n._disableSelection())}}O(a,r,s,"ln-data-table",{extraAttributes:["data-ln-data-table-selectable"],onAttributeChange:p})})()})();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ── JS state: data table loading ──
|
|
2
|
+
// Loading-class added by JS while async data fetches; visual stays here
|
|
3
|
+
// because it's a transient JS-controlled state transition.
|
|
4
|
+
|
|
5
|
+
.ln-data-table--loading {
|
|
6
|
+
tbody {
|
|
7
|
+
opacity: 0.5;
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
}
|
|
10
|
+
}
|