@livenetworks/ashlar 1.4.1 → 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.
- package/js/ln-data-table/ln-data-table.js +1 -1
- package/js/ln-data-table/src/ln-data-table.js +0 -16
- package/js/ln-filter/README.md +99 -99
- package/js/ln-table/ln-table-sort.js +1 -1
- package/js/ln-table/src/ln-table-sort.js +156 -168
- package/package.json +3 -2
- package/scss/components/_filter.scss +25 -0
- package/scss/components/_ln-table.scss +1 -2
- package/scss/components/_table.scss +55 -1
- package/scss/config/mixins/_diagnostics.scss +37 -0
- package/scss/config/mixins/_filter.scss +49 -0
- package/scss/config/mixins/_index.scss +2 -1
- package/scss/config/mixins/_ln-table.scss +10 -91
- package/scss/config/mixins/_table.scss +202 -33
- package/scss/ln-ashlar-dev.scss +21 -0
- package/scss/ln-ashlar.scss +1 -1
- package/scss/components/_data-table.scss +0 -83
- package/scss/config/mixins/_data-table.scss +0 -346
|
@@ -1,168 +1,156 @@
|
|
|
1
|
-
import { guardBody, dispatch } from '../../ln-core';
|
|
2
|
-
import { persistGet, persistSet } from '../../ln-core';
|
|
3
|
-
|
|
4
|
-
(function () {
|
|
5
|
-
const DOM_ATTRIBUTE = 'lnTableSort';
|
|
6
|
-
const SORT_ATTR = 'data-ln-sort';
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
if (window[DOM_ATTRIBUTE] !== undefined) return;
|
|
10
|
-
|
|
11
|
-
// ─── Constructor ───────────────────────────────────────────
|
|
12
|
-
// Auto-initializes any <table> that contains th[data-ln-sort] headers.
|
|
13
|
-
|
|
14
|
-
function constructor(domRoot) {
|
|
15
|
-
findElements(domRoot);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Local findElements — intentional divergence from ln-core helper: tag selector ('table') + two-arg constructor (table, ths).
|
|
19
|
-
function findElements(root) {
|
|
20
|
-
const tables = Array.from(root.querySelectorAll('table'));
|
|
21
|
-
if (root.tagName === 'TABLE') tables.push(root);
|
|
22
|
-
|
|
23
|
-
tables.forEach(function (table) {
|
|
24
|
-
if (table[DOM_ATTRIBUTE]) return;
|
|
25
|
-
const ths = Array.from(table.querySelectorAll('th[' + SORT_ATTR + ']'));
|
|
26
|
-
if (ths.length) table[DOM_ATTRIBUTE] = new _component(table, ths);
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ─── Component ─────────────────────────────────────────────
|
|
31
|
-
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} else {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
window[DOM_ATTRIBUTE] = constructor;
|
|
159
|
-
_domObserver();
|
|
160
|
-
|
|
161
|
-
if (document.readyState === 'loading') {
|
|
162
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
163
|
-
constructor(document.body);
|
|
164
|
-
});
|
|
165
|
-
} else {
|
|
166
|
-
constructor(document.body);
|
|
167
|
-
}
|
|
168
|
-
})();
|
|
1
|
+
import { guardBody, dispatch } from '../../ln-core';
|
|
2
|
+
import { persistGet, persistSet } from '../../ln-core';
|
|
3
|
+
|
|
4
|
+
(function () {
|
|
5
|
+
const DOM_ATTRIBUTE = 'lnTableSort';
|
|
6
|
+
const SORT_ATTR = 'data-ln-sort';
|
|
7
|
+
const BUTTON_HOOK = 'data-ln-table-sort';
|
|
8
|
+
|
|
9
|
+
if (window[DOM_ATTRIBUTE] !== undefined) return;
|
|
10
|
+
|
|
11
|
+
// ─── Constructor ───────────────────────────────────────────
|
|
12
|
+
// Auto-initializes any <table> that contains th[data-ln-sort] headers.
|
|
13
|
+
|
|
14
|
+
function constructor(domRoot) {
|
|
15
|
+
findElements(domRoot);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Local findElements — intentional divergence from ln-core helper: tag selector ('table') + two-arg constructor (table, ths).
|
|
19
|
+
function findElements(root) {
|
|
20
|
+
const tables = Array.from(root.querySelectorAll('table'));
|
|
21
|
+
if (root.tagName === 'TABLE') tables.push(root);
|
|
22
|
+
|
|
23
|
+
tables.forEach(function (table) {
|
|
24
|
+
if (table[DOM_ATTRIBUTE]) return;
|
|
25
|
+
const ths = Array.from(table.querySelectorAll('th[' + SORT_ATTR + ']'));
|
|
26
|
+
if (ths.length) table[DOM_ATTRIBUTE] = new _component(table, ths);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Component ─────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
function _component(table, ths) {
|
|
33
|
+
this.table = table;
|
|
34
|
+
this.ths = ths;
|
|
35
|
+
this._col = -1;
|
|
36
|
+
this._dir = null;
|
|
37
|
+
const self = this;
|
|
38
|
+
|
|
39
|
+
ths.forEach(function (th, index) {
|
|
40
|
+
if (th[DOM_ATTRIBUTE + 'Bound']) return;
|
|
41
|
+
th[DOM_ATTRIBUTE + 'Bound'] = true;
|
|
42
|
+
|
|
43
|
+
// D1/Option A: bind click to the sort button, not the whole <th>.
|
|
44
|
+
// Columns without a [data-ln-table-sort] button skip binding.
|
|
45
|
+
const btn = th.querySelector('[' + BUTTON_HOOK + ']');
|
|
46
|
+
if (!btn) return;
|
|
47
|
+
|
|
48
|
+
btn._lnSortClick = function () {
|
|
49
|
+
self._handleClick(index, th);
|
|
50
|
+
};
|
|
51
|
+
btn.addEventListener('click', btn._lnSortClick);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ─── Restore persisted sort ───────────────────────────────
|
|
55
|
+
const persistRoot = table.closest('[data-ln-table][data-ln-persist]');
|
|
56
|
+
if (persistRoot) {
|
|
57
|
+
const saved = persistGet('table-sort', persistRoot);
|
|
58
|
+
if (saved && saved.dir && saved.col >= 0 && saved.col < ths.length) {
|
|
59
|
+
// Programmatically trigger the saved sort
|
|
60
|
+
this._handleClick(saved.col, ths[saved.col]);
|
|
61
|
+
if (saved.dir === 'desc') {
|
|
62
|
+
this._handleClick(saved.col, ths[saved.col]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_component.prototype._handleClick = function (colIndex, th) {
|
|
71
|
+
let newDir;
|
|
72
|
+
|
|
73
|
+
if (this._col !== colIndex) {
|
|
74
|
+
newDir = 'asc';
|
|
75
|
+
} else if (this._dir === 'asc') {
|
|
76
|
+
newDir = 'desc';
|
|
77
|
+
} else if (this._dir === 'desc') {
|
|
78
|
+
newDir = null;
|
|
79
|
+
} else {
|
|
80
|
+
newDir = 'asc';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// JS-2: use state classes on <th>; CSS owns icon visibility.
|
|
84
|
+
this.ths.forEach(function (t) {
|
|
85
|
+
t.classList.remove('ln-sort-asc', 'ln-sort-desc');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (newDir === null) {
|
|
89
|
+
this._col = -1;
|
|
90
|
+
this._dir = null;
|
|
91
|
+
} else {
|
|
92
|
+
this._col = colIndex;
|
|
93
|
+
this._dir = newDir;
|
|
94
|
+
th.classList.add(newDir === 'asc' ? 'ln-sort-asc' : 'ln-sort-desc');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
dispatch(this.table, 'ln-table:sort', {
|
|
98
|
+
column: colIndex,
|
|
99
|
+
sortType: th.getAttribute(SORT_ATTR),
|
|
100
|
+
direction: newDir
|
|
101
|
+
});
|
|
102
|
+
const persistRoot = this.table.closest('[data-ln-table][data-ln-persist]');
|
|
103
|
+
if (persistRoot) {
|
|
104
|
+
if (newDir === null) {
|
|
105
|
+
persistSet('table-sort', persistRoot, null);
|
|
106
|
+
} else {
|
|
107
|
+
persistSet('table-sort', persistRoot, { col: colIndex, dir: newDir });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
_component.prototype.destroy = function () {
|
|
113
|
+
if (!this.table[DOM_ATTRIBUTE]) return;
|
|
114
|
+
this.ths.forEach(function (th) {
|
|
115
|
+
const btn = th.querySelector('[' + BUTTON_HOOK + ']');
|
|
116
|
+
if (btn && btn._lnSortClick) {
|
|
117
|
+
btn.removeEventListener('click', btn._lnSortClick);
|
|
118
|
+
delete btn._lnSortClick;
|
|
119
|
+
}
|
|
120
|
+
delete th[DOM_ATTRIBUTE + 'Bound'];
|
|
121
|
+
});
|
|
122
|
+
delete this.table[DOM_ATTRIBUTE];
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// ─── DOM Observer ──────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
function _domObserver() {
|
|
128
|
+
guardBody(function () {
|
|
129
|
+
const observer = new MutationObserver(function (mutations) {
|
|
130
|
+
mutations.forEach(function (mutation) {
|
|
131
|
+
if (mutation.type === 'childList') {
|
|
132
|
+
mutation.addedNodes.forEach(function (node) {
|
|
133
|
+
if (node.nodeType === 1) findElements(node);
|
|
134
|
+
});
|
|
135
|
+
} else if (mutation.type === 'attributes') {
|
|
136
|
+
findElements(mutation.target);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [SORT_ATTR] });
|
|
141
|
+
}, 'ln-table-sort');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── Init ──────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
window[DOM_ATTRIBUTE] = constructor;
|
|
147
|
+
_domObserver();
|
|
148
|
+
|
|
149
|
+
if (document.readyState === 'loading') {
|
|
150
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
151
|
+
constructor(document.body);
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
constructor(document.body);
|
|
155
|
+
}
|
|
156
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livenetworks/ashlar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "LiveNetworks unified frontend library - CSS framework and vanilla JS components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Live Networks",
|
|
@@ -32,8 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"dev": "node scripts/build.mjs --watch",
|
|
35
|
-
"build": "node scripts/build.mjs && npm run build:demos",
|
|
35
|
+
"build": "node scripts/build.mjs && npm run build:dev-css && npm run build:demos",
|
|
36
36
|
"build:demos": "npm run build:demo-index && npm run build:demo-admin && npm run build:demo-docuflow",
|
|
37
|
+
"build:dev-css": "sass scss/ln-ashlar-dev.scss demo/dist/ln-ashlar-dev.css --no-source-map --style=compressed",
|
|
37
38
|
"build:demo-index": "sass demo/index.scss demo/index.css --no-source-map --style=compressed",
|
|
38
39
|
"build:demo-admin": "sass demo/admin/src/admin.scss demo/admin/dist/admin.css --no-source-map --style=compressed && cp demo/admin/src/demo.js demo/admin/dist/demo.js && node demo/admin/src/build-pages.mjs",
|
|
39
40
|
"build:demo-docuflow": "sass demo/docuflow/docuflow.scss demo/docuflow/dist/docuflow.css --no-source-map --style=compressed"
|
|
@@ -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,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';
|