@livenetworks/ashlar 1.4.0 → 1.5.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.
Files changed (44) hide show
  1. package/js/ln-accordion/ln-accordion.js +1 -1
  2. package/js/ln-api-connector/ln-api-connector.js +1 -1
  3. package/js/ln-autoresize/ln-autoresize.js +1 -1
  4. package/js/ln-autosave/ln-autosave.js +1 -1
  5. package/js/ln-circular-progress/ln-circular-progress.js +1 -1
  6. package/js/ln-confirm/ln-confirm.js +1 -1
  7. package/js/ln-core/helpers.js +6 -3
  8. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  9. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
  10. package/js/ln-data-store/ln-data-store.js +1 -1
  11. package/js/ln-data-table/ln-data-table.js +1 -1
  12. package/js/ln-data-table/src/ln-data-table.js +0 -16
  13. package/js/ln-date/ln-date.js +1 -1
  14. package/js/ln-dropdown/ln-dropdown.js +1 -1
  15. package/js/ln-filter/README.md +99 -99
  16. package/js/ln-filter/ln-filter.js +1 -1
  17. package/js/ln-form/ln-form.js +1 -1
  18. package/js/ln-modal/ln-modal.js +1 -1
  19. package/js/ln-number/ln-number.js +1 -1
  20. package/js/ln-popover/ln-popover.js +1 -1
  21. package/js/ln-search/ln-search.js +1 -1
  22. package/js/ln-sortable/ln-sortable.js +1 -1
  23. package/js/ln-table/ln-table-sort.js +1 -1
  24. package/js/ln-table/ln-table.js +1 -1
  25. package/js/ln-table/src/ln-table-sort.js +156 -168
  26. package/js/ln-tabs/ln-tabs.js +1 -1
  27. package/js/ln-time/ln-time.js +1 -1
  28. package/js/ln-toggle/ln-toggle.js +1 -1
  29. package/js/ln-tooltip/ln-tooltip.js +1 -1
  30. package/js/ln-translations/ln-translations.js +1 -1
  31. package/js/ln-validate/ln-validate.js +1 -1
  32. package/package.json +3 -2
  33. package/scss/components/_filter.scss +25 -0
  34. package/scss/components/_ln-table.scss +1 -2
  35. package/scss/components/_table.scss +55 -1
  36. package/scss/config/mixins/_diagnostics.scss +37 -0
  37. package/scss/config/mixins/_filter.scss +49 -0
  38. package/scss/config/mixins/_index.scss +2 -1
  39. package/scss/config/mixins/_ln-table.scss +10 -91
  40. package/scss/config/mixins/_table.scss +202 -33
  41. package/scss/ln-ashlar-dev.scss +21 -0
  42. package/scss/ln-ashlar.scss +1 -1
  43. package/scss/components/_data-table.scss +0 -83
  44. package/scss/config/mixins/_data-table.scss +0 -346
@@ -0,0 +1,25 @@
1
+ @use '../config/mixins' as *;
2
+
3
+ // Filter dropdown content chrome (search input + options list; NO clear button —
4
+ // the data-ln-filter-reset "All" sentinel is the reset affordance).
5
+ // Floating shell is supplied separately:
6
+ // • popover-driven (ln-filter static): @mixin popover on the [data-ln-popover] wrapper
7
+ // • ln-data-table dynamic clone: floating-panel + box below
8
+ .column-filter-dropdown {
9
+ @include filter-dropdown;
10
+ }
11
+
12
+ // ln-data-table appends its cloned dropdown directly into the <th> and
13
+ // manages its own open/close — it does NOT route through ln-popover, so it
14
+ // needs the floating shell applied here (the popover path gets it from
15
+ // @mixin popover instead).
16
+ [data-ln-data-table] .column-filter-dropdown {
17
+ @include floating-panel;
18
+ position: absolute;
19
+ top: 100%;
20
+ right: 0;
21
+ left: auto;
22
+ margin-top: var(--size-xs);
23
+ min-width: 14rem;
24
+ padding: var(--size-sm);
25
+ }
@@ -2,8 +2,6 @@
2
2
 
3
3
  [data-ln-table] {
4
4
  @include ln-table;
5
- @include ln-table-column-filter;
6
- @include ln-table-sort;
7
5
  }
8
6
 
9
7
  // In-thead toolbar — first thead row's colspan th.
@@ -58,3 +56,4 @@
58
56
  .ln-table__spacer {
59
57
  @include ln-table-spacer-row;
60
58
  }
59
+
@@ -2,7 +2,6 @@
2
2
 
3
3
  table {
4
4
  @include table-base;
5
- @include table-filter-button;
6
5
 
7
6
  tr.section-header td {
8
7
  @include table-section-header;
@@ -17,3 +16,58 @@ table {
17
16
  table { @include table-responsive; }
18
17
  }
19
18
  }
19
+
20
+ // ─── Shared Column Sort & Filter Buttons ────────────────────────
21
+ // CSS targets the unprefixed structural helper class on the button, NOT the data-ln-* JS
22
+ // hook. .table-sort / .table-filter are thin bindings that mirror the mixins; a consumer
23
+ // may instead @include table-sort / table-filter on their own selector (the class is a
24
+ // convenience, not a mandate — see "Naming & class architecture"). Behavior hooks
25
+ // (data-ln-col-sort / data-ln-table-sort / data-ln-popover-for / data-ln-col-filter) are
26
+ // the per-system functional layer and intentionally differ.
27
+
28
+ .table-sort { @include table-sort; }
29
+ th:hover .table-sort { opacity: 0.7; }
30
+
31
+ // Active sort — JS-state class on the <th>, set by sort JS (client) or server.
32
+ .ln-sort-asc .table-sort,
33
+ .ln-sort-desc .table-sort { @include table-sort-active; }
34
+
35
+ .ln-sort-asc .table-sort {
36
+ [data-ln-sort-icon="none"] { display: none; }
37
+ [data-ln-sort-icon="asc"] { display: inline-block; }
38
+ }
39
+ .ln-sort-desc .table-sort {
40
+ [data-ln-sort-icon="none"] { display: none; }
41
+ [data-ln-sort-icon="desc"] { display: inline-block; }
42
+ }
43
+
44
+ .table-filter { @include table-filter; }
45
+ th:hover .table-filter { opacity: 0.7; }
46
+
47
+ // Active filter — JS-state class set DIRECTLY on the button (self-selector), combined with
48
+ // the structural helper class. Fixes the latent ln-data-table bug: JS toggles
49
+ // .ln-filter-active on the button itself, but the old CSS used a descendant combinator
50
+ // (.ln-filter-active [data-ln-col-filter]) that never matched, so the dot never rendered.
51
+ // See D2.
52
+ .table-filter.ln-filter-active { @include table-filter-active; }
53
+
54
+
55
+
56
+ // ─── Data Table JS-State Classes ───────────────────────────────
57
+ // JS-added state classes only. Row chrome (clickable cursor/hover, action
58
+ // buttons, sticky header) is a CONSUMER concern — applied via semantic
59
+ // selectors in project SCSS, never keyed off the data-ln-* JS hooks.
60
+
61
+ .ln-row-selected {
62
+ @include table-row-selected;
63
+ }
64
+
65
+ .ln-row-focused {
66
+ @include table-row-focused;
67
+ }
68
+
69
+ // Virtual-scroll spacer
70
+ .ln-data-table__spacer {
71
+ @include table-spacer-row;
72
+ }
73
+
@@ -0,0 +1,37 @@
1
+ @use 'colors' as *;
2
+ @use 'spacing' as *;
3
+ @use 'position' as *;
4
+
5
+ // ─── DOM Diagnostics & Linter Mixins ───────────────────────────
6
+ // These mixins are for developer-only diagnostic overlays and validations.
7
+ // They should be completely isolated from production bundles.
8
+
9
+ // Generic block element DOM diagnostic error
10
+ // Applies a red dashed border and a prominent warning banner at the top.
11
+ @mixin dev-dom-error($message) {
12
+ outline: 2px dashed hsl(var(--color-error));
13
+ @include relative;
14
+
15
+ &::before {
16
+ content: " ⚠ #{$message}";
17
+ display: block;
18
+ background: color-mix(in srgb, hsl(var(--color-error)) 15%, transparent);
19
+ color: hsl(var(--color-error));
20
+ padding: var(--size-xs) var(--size-sm);
21
+ font-size: var(--text-xs, 0.75rem);
22
+ font-weight: bold;
23
+ border-bottom: 1px solid hsl(var(--color-strong));
24
+ }
25
+ }
26
+
27
+ // Generic inline/element-level DOM diagnostic warning
28
+ // Appends a red inline text warning adjacent to the targeted element.
29
+ @mixin dev-inline-error($message) {
30
+ &::after {
31
+ content: " ⚠ #{$message}";
32
+ color: hsl(var(--color-error));
33
+ font-weight: normal;
34
+ white-space: nowrap;
35
+ margin-inline-start: var(--size-xs, 0.5rem);
36
+ }
37
+ }
@@ -0,0 +1,49 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'interaction' as *;
4
+ @use 'transitions' as *;
5
+ @use 'form' as *;
6
+
7
+ // ─── Filter dropdown — content chrome only ─────────────────────────
8
+ // The floating shell (chrome, fixed positioning, min-width, show/hide)
9
+ // comes from `@mixin popover` on the popover-driven path, OR from
10
+ // `@mixin floating-panel` + scroll box on the ln-data-table dynamic
11
+ // path (see components/_filter.scss). This mixin styles ONLY the inner
12
+ // content: scrollable options box, search input, and the options list
13
+ // (pills via check-list).
14
+ //
15
+ // NOTE: there is intentionally NO clear-button block. The redundant
16
+ // "Clear Filter" button was removed library-wide — the `data-ln-filter-reset`
17
+ // "All" sentinel checkbox already performs the reset. Do not re-introduce a
18
+ // `[data-ln-filter-clear]` selector here.
19
+ //
20
+ // Tunables (consumer-overridable via --var with sane defaults):
21
+ // --filter-dropdown-max-height (default 20rem)
22
+ // --filter-dropdown-overflow-y (default auto)
23
+ // --filter-dropdown-text (default var(--text-xs)) ← sm density
24
+ // --filter-dropdown-item-pad-y (default var(--size-3xs))
25
+ //
26
+ // Usage:
27
+ // .column-filter-dropdown { @include filter-dropdown; }
28
+
29
+ @mixin filter-dropdown {
30
+ max-height: var(--filter-dropdown-max-height, 20rem);
31
+ overflow-y: var(--filter-dropdown-overflow-y, auto);
32
+ text-transform: none; // reset inherited header uppercase
33
+
34
+ [data-ln-filter-search] {
35
+ @include form-input;
36
+ font-size: var(--filter-dropdown-text, var(--text-xs));
37
+ --padding-y: var(--size-xs);
38
+ --padding-x: var(--size-sm);
39
+ width: 100%;
40
+ margin-bottom: var(--size-sm);
41
+ }
42
+
43
+ [data-ln-filter-options] {
44
+ @include check-list;
45
+ font-size: var(--filter-dropdown-text, var(--text-xs));
46
+
47
+ li { padding: var(--filter-dropdown-item-pad-y, var(--size-3xs)) 0; }
48
+ }
49
+ }
@@ -25,6 +25,7 @@
25
25
  @forward 'nav';
26
26
  @forward 'btn';
27
27
  @forward 'form';
28
+ @forward 'filter';
28
29
  @forward 'modal';
29
30
  @forward 'breadcrumbs';
30
31
  @forward 'loader';
@@ -53,6 +54,6 @@
53
54
  @forward 'toast';
54
55
  @forward 'upload';
55
56
  @forward 'translations';
56
- @forward 'data-table';
57
57
  @forward 'ln-table';
58
58
  @forward 'ajax';
59
+ @forward 'diagnostics';
@@ -21,8 +21,14 @@
21
21
  // [data-ln-table] { @include ln-table; }
22
22
  // .ln-table__toolbar { @include ln-table-toolbar; }
23
23
  //
24
- // State-driven CSS (spacer rows, sort icon .hidden toggle, content overflow
25
- // fix) stays in the co-located JS SCSS file.
24
+ // Sort/filter button styling is SHARED via scss/components/_table.scss
25
+ // (.table-sort / .table-filter structural helper classes + the :has() stack
26
+ // in @mixin table-base). No ln-table-specific sort/filter button mixins here.
27
+ // Under D1 (Option A) the <th> is no longer the sort click target, so no
28
+ // ln-table-specific cursor:pointer / select-none on the <th> is needed.
29
+ //
30
+ // State-driven CSS (spacer rows, content overflow fix) stays in
31
+ // the co-located JS SCSS file.
26
32
 
27
33
  @mixin ln-table {
28
34
  @include border;
@@ -126,95 +132,6 @@
126
132
  }
127
133
  }
128
134
 
129
- // ─── Column filter dropdowns in th ────────────────────────
130
-
131
- @mixin ln-table-column-filter {
132
- th [data-ln-dropdown] {
133
- @include inline-flex;
134
- @include items-center;
135
- }
136
-
137
- th [data-ln-dropdown] > button {
138
- @include flex;
139
- @include items-center;
140
- --gap: var(--size-xs);
141
- gap: var(--gap);
142
- background: none;
143
- border: none;
144
- color: inherit;
145
- font: inherit;
146
- @include cursor-pointer;
147
- --btn-padding-y: 0;
148
- --btn-padding-x: 0;
149
-
150
- .ln-icon-filter {
151
- opacity: 0.4;
152
- @include motion-safe {
153
- transition: opacity var(--transition-fast);
154
- }
155
- }
156
-
157
- &:hover .ln-icon-filter {
158
- opacity: 1;
159
- }
160
- }
161
-
162
- th [data-ln-dropdown-menu] button[data-ln-filter-key] {
163
- @include w-full;
164
- @include flex;
165
- @include items-center;
166
- @include text-left;
167
- --btn-padding-y: var(--size-xs);
168
- --btn-padding-x: var(--size-sm);
169
- @include text-sm;
170
- @include font-normal;
171
- background: none;
172
- border: none;
173
- border-radius: 0;
174
- color: var(--color-fg);
175
- @include cursor-pointer;
176
- @include transition-fast;
177
-
178
- &:hover {
179
- --color-bg: var(--bg-sunken);
180
- background: var(--color-bg);
181
- }
182
-
183
- &[data-active] {
184
- background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
185
- color: var(--color-accent);
186
- @include font-medium;
187
- }
188
- }
189
- }
190
-
191
- // ─── Sort indicators on th ────────────────────────────────
192
-
193
- @mixin ln-table-sort {
194
- th[data-ln-sort] {
195
- @include cursor-pointer;
196
- @include select-none;
197
-
198
- [data-ln-sort-icon] {
199
- @include size(0.875rem);
200
- --margin-inline: var(--size-xs);
201
- margin-left: var(--margin-inline);
202
- vertical-align: middle;
203
- opacity: 0.4;
204
- @include motion-safe {
205
- transition: opacity var(--transition-fast);
206
- }
207
- }
208
-
209
- &:hover [data-ln-sort-icon] {
210
- opacity: 0.7;
211
- }
212
-
213
- &[data-ln-sort-active] [data-ln-sort-icon] {
214
- opacity: 1;
215
- }
216
- }
217
- }
218
135
 
219
136
  // ─── Footer ───────────────────────────────────────────────
220
137
 
@@ -418,3 +335,5 @@
418
335
  background: transparent !important;
419
336
  }
420
337
  }
338
+
339
+
@@ -6,30 +6,59 @@
6
6
  @use 'borders' as *;
7
7
  @use 'transitions' as *;
8
8
  @use 'position' as *;
9
+ @use 'motion' as *;
10
+ @use 'interaction' as *;
11
+ @use 'form' as *;
12
+ @use 'btn' as *;
13
+ @use 'card' as *;
14
+ @use 'ln-table';
9
15
 
10
16
  // ─── Base table ─────────────────────────────────────────────────
11
17
 
12
- @mixin table-base {
18
+ @mixin table-base($sticky: false) {
13
19
  @include w-full;
14
20
  background: var(--color-bg);
15
21
  --radius: var(--radius-lg);
16
22
  border-radius: var(--radius);
17
23
  // `overflow: clip` not `overflow: hidden` — clips at rounded corners
18
24
  // without establishing a scroll container, so sticky descendants
19
- // (e.g. `@mixin data-table` thead/tfoot) bind to the outer
20
- // `.app-main > section` scroll surface instead of being trapped
21
- // inside the <table> element. Same rationale as `@mixin section-card`
22
- // and `@mixin ln-table`.
25
+ // bind to the outer scroll surface instead of being trapped inside.
23
26
  overflow: clip;
24
27
  --shadow: var(--shadow-resting);
25
28
  box-shadow: var(--shadow);
26
- border-collapse: collapse;
29
+
30
+ @if $sticky {
31
+ @include relative;
32
+ border-collapse: separate;
33
+ border-spacing: 0;
34
+ } @else {
35
+ border-collapse: collapse;
36
+ }
27
37
  font-variant-numeric: tabular-nums;
28
38
 
39
+ // Row hover transition color. Sticky/enhanced tables use a relative lightness calculation
40
+ // to make the hovered row visually distinct from --bg-sunken chrome headers/footers.
41
+ $bg-hover: var(--bg-sunken);
42
+ @if $sticky {
43
+ $bg-hover: hsl(from var(--bg-base) h s calc(l - 4));
44
+ }
45
+ --bg-row-hover: #{$bg-hover};
46
+
29
47
  thead {
30
48
  --color-bg: var(--bg-sunken);
31
49
  background: var(--color-bg);
32
50
  border-block-end: var(--border-block-end, var(--border-width-strong) solid var(--color-border));
51
+
52
+ @if $sticky {
53
+ @include sticky-top;
54
+ }
55
+ }
56
+
57
+ // Support for in-thead toolbar inside sticky tables
58
+ @if $sticky {
59
+ thead > tr:first-child:has(> th[colspan]) > th {
60
+ @include ln-table.ln-table-thead-toolbar;
61
+ }
33
62
  }
34
63
 
35
64
  th {
@@ -41,6 +70,54 @@
41
70
  letter-spacing: 0.4px;
42
71
  @include uppercase;
43
72
  color: var(--color-fg);
73
+ position: relative;
74
+ top: auto;
75
+ z-index: auto;
76
+
77
+ // Shared header-button stack — keyed off the unprefixed structural button classes,
78
+ // so table / ln-table / ln-data-table all share one positioning rule.
79
+ &:has(.table-sort):has(.table-filter) {
80
+ padding-inline-end: calc(var(--padding-x) + 2.5rem);
81
+
82
+ .table-filter {
83
+ position: absolute;
84
+ top: 50%;
85
+ transform: translateY(-50%);
86
+ right: var(--padding-x);
87
+ margin: 0;
88
+ }
89
+ .table-sort {
90
+ position: absolute;
91
+ top: 50%;
92
+ transform: translateY(-50%);
93
+ right: calc(var(--padding-x) + 1.25rem);
94
+ margin: 0;
95
+ }
96
+ }
97
+
98
+ &:has(.table-sort):not(:has(.table-filter)) {
99
+ padding-inline-end: calc(var(--padding-x) + 1.25rem);
100
+
101
+ .table-sort {
102
+ position: absolute;
103
+ top: 50%;
104
+ transform: translateY(-50%);
105
+ right: var(--padding-x);
106
+ margin: 0;
107
+ }
108
+ }
109
+
110
+ &:has(.table-filter):not(:has(.table-sort)) {
111
+ padding-inline-end: calc(var(--padding-x) + 1.25rem);
112
+
113
+ .table-filter {
114
+ position: absolute;
115
+ top: 50%;
116
+ transform: translateY(-50%);
117
+ right: var(--padding-x);
118
+ margin: 0;
119
+ }
120
+ }
44
121
  }
45
122
 
46
123
  tbody tr {
@@ -48,10 +125,7 @@
48
125
  min-height: var(--density-row-h);
49
126
 
50
127
  &:hover {
51
- // Default hover stays --bg-sunken for standalone tables. Data-table
52
- // re-binds --color-bg locally (see _data-table.scss) so its hover
53
- // disambiguates from chrome that also uses --bg-sunken.
54
- --color-bg: var(--bg-sunken);
128
+ --color-bg: var(--bg-row-hover);
55
129
  background: var(--color-bg);
56
130
  }
57
131
  }
@@ -83,6 +157,21 @@
83
157
  th.nowrap {
84
158
  @include whitespace-nowrap;
85
159
  }
160
+
161
+ // Sticky section footer inside sticky/enhanced tables
162
+ @if $sticky {
163
+ > footer {
164
+ padding: var(--padding-y) var(--padding-x);
165
+ border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
166
+ --color-bg: var(--bg-sunken);
167
+ background: var(--color-bg);
168
+ --color-fg: var(--fg-muted);
169
+ color: var(--color-fg);
170
+ font-size: var(--text-body-sm);
171
+ position: sticky;
172
+ z-index: var(--z-sticky);
173
+ }
174
+ }
86
175
  }
87
176
 
88
177
  // ─── Responsive (mobile stacked layout) ─────────────────────────
@@ -182,34 +271,114 @@
182
271
  }
183
272
  }
184
273
 
185
- // ─── Column filter button ──────────────────────────────────────
186
- // Button inside <th> that triggers a filter popover/dropdown.
187
- // Convention: add class="filter" to the trigger button.
188
- // ln-table.js sets data-ln-filter-active on the <th> when active.
189
-
190
- @mixin table-filter-button {
191
- th .filter {
192
- @include inline-flex;
193
- @include items-center;
194
- float: right;
195
- --btn-padding-y: var(--size-2xs);
196
- --btn-padding-x: var(--size-2xs);
197
- @include rounded-sm;
198
- opacity: 0.5;
274
+ // ─── Column sort & filter buttons ──────────────────────────────
199
275
 
200
- &:hover {
201
- opacity: 1;
202
- --color-bg: var(--bg-sunken);
203
- background: var(--color-bg);
204
- }
276
+ @mixin table-sort {
277
+ opacity: 0.4;
278
+ @include motion-safe {
279
+ transition: opacity 0.15s;
205
280
  }
281
+ @include cursor-pointer;
282
+ background: none;
283
+ border: none;
284
+ --btn-padding-y: var(--size-2xs);
285
+ --btn-padding-x: var(--size-2xs);
286
+ --margin-inline: var(--size-xs);
287
+ margin-inline-start: var(--margin-inline);
288
+ vertical-align: middle;
206
289
 
207
- th [data-ln-dropdown] {
208
- float: right;
290
+ [data-ln-sort-icon="asc"],
291
+ [data-ln-sort-icon="desc"] {
292
+ display: none;
209
293
  }
294
+ }
295
+
296
+ @mixin table-sort-active {
297
+ opacity: 1;
298
+ color: var(--color-accent);
299
+ }
210
300
 
211
- th[data-ln-filter-active] .filter {
301
+ @mixin table-filter {
302
+ opacity: 0.4;
303
+ @include motion-safe {
304
+ transition: opacity 0.15s;
305
+ }
306
+ @include cursor-pointer;
307
+ background: none;
308
+ border: none;
309
+ --btn-padding-y: var(--size-2xs);
310
+ --btn-padding-x: var(--size-2xs);
311
+ --margin-inline: var(--size-2xs);
312
+ margin-inline-start: var(--margin-inline);
313
+ vertical-align: middle;
314
+ @include relative;
315
+ }
316
+
317
+ @mixin table-filter-active {
318
+ opacity: 1;
319
+ color: var(--color-accent);
320
+
321
+ &::after {
322
+ content: '';
323
+ @include absolute;
324
+ top: 0;
325
+ right: 0;
326
+ width: 0.375rem;
327
+ height: 0.375rem;
328
+ border-radius: var(--radius-full);
329
+ background: var(--color-accent);
330
+ }
331
+ }
332
+
333
+
334
+
335
+
336
+
337
+ @mixin table-row-action {
338
+ background: none;
339
+ border: none;
340
+ --shadow: none;
341
+ --btn-padding-y: var(--size-2xs);
342
+ --btn-padding-x: var(--size-2xs);
343
+ opacity: 0.5;
344
+ @include motion-safe {
345
+ transition: opacity 0.15s;
346
+ }
347
+
348
+ &:hover:not(:disabled) {
349
+ background: none;
212
350
  opacity: 1;
213
- color: var(--color-accent);
214
351
  }
215
352
  }
353
+
354
+ @mixin table-row-action-delete {
355
+ --color-primary: var(--color-error);
356
+ color: hsl(var(--color-primary));
357
+ }
358
+
359
+ @mixin table-row {
360
+ cursor: pointer;
361
+
362
+ &:hover {
363
+ --color-bg: var(--bg-sunken);
364
+ background: var(--color-bg);
365
+ }
366
+ }
367
+
368
+ @mixin table-row-selected {
369
+ background: var(--color-accent-tint);
370
+ }
371
+
372
+ @mixin table-row-focused {
373
+ --color-accent-focus: hsl(from var(--color-accent) h s calc(l + 8));
374
+ outline: 2px solid var(--color-accent-focus);
375
+ outline-offset: -2px;
376
+ }
377
+
378
+ @mixin table-spacer-row {
379
+ td {
380
+ padding: 0 !important;
381
+ border: none !important;
382
+ }
383
+ }
384
+
@@ -0,0 +1,21 @@
1
+ // ==========================================================================
2
+ // Dev & Validation Diagnostics — ln-ashlar
3
+ // ==========================================================================
4
+ // This stylesheet contains diagnostic CSS tools to validate correct HTML
5
+ // structure, missing integration attributes, and accessibility warnings during
6
+ // development. Do not compile or deploy this file to production.
7
+ // ==========================================================================
8
+
9
+ @use 'config/mixins' as *;
10
+
11
+ // 1. Table Sort: missing sort button warning
12
+ // Detects when a th[data-ln-sort] is sortable but doesn't contain a <button data-ln-table-sort>
13
+ th[data-ln-sort]:not(:has([data-ln-table-sort])) {
14
+ @include dev-inline-error("missing sort button");
15
+ }
16
+
17
+ // 2. Table Integration: missing ID warning
18
+ // Warns if a table wrapper [data-ln-table] is missing an ID, which breaks search/filter integration
19
+ [data-ln-table]:not([id]) {
20
+ @include dev-dom-error("[data-ln-table] is missing a required 'id' attribute for filter mapping.");
21
+ }
@@ -34,6 +34,7 @@
34
34
  @use 'components/scrollbar';
35
35
  @use 'components/tooltip';
36
36
  @use 'components/popover';
37
+ @use 'components/filter';
37
38
  @use 'components/chip';
38
39
  @use 'components/loader';
39
40
  @use 'components/progress';
@@ -45,7 +46,6 @@
45
46
  @use 'components/toast';
46
47
  @use 'components/upload';
47
48
  @use 'components/translations';
48
- @use 'components/data-table';
49
49
  @use 'components/ln-table';
50
50
  @use 'components/ajax';
51
51