argus-table 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/argus-table.js +38 -13
- package/dist/argus-table.js.map +1 -1
- package/dist/cdn/argus-table.js +260 -434
- package/dist/cdn/argus-table.js.map +1 -1
- package/dist/controllers/pin-scroll-controller.js +78 -0
- package/dist/controllers/pin-scroll-controller.js.map +1 -0
- package/dist/custom-elements.json +61 -1
- package/dist/render/body.js +0 -1
- package/dist/render/body.js.map +1 -1
- package/dist/render/header.js +0 -1
- package/dist/render/header.js.map +1 -1
- package/dist/styles/base.js +1 -225
- package/dist/styles/base.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/controllers/pin-scroll-controller.ts
|
|
2
|
+
/** How long after the last sideways scroll pinned cells stay sticky. */
|
|
3
|
+
var SETTLE_MS = 150;
|
|
4
|
+
/**
|
|
5
|
+
* Keeps pinned body cells in place without making them sticky while the table
|
|
6
|
+
* only scrolls up and down.
|
|
7
|
+
*
|
|
8
|
+
* Safari gives every sticky element a compositing layer of its own and moves
|
|
9
|
+
* each one on every scroll frame; with a sticky cell per pinned column per row
|
|
10
|
+
* that is dozens of layers, and vertical scrolling stutters (ADR 0001, "Pinned
|
|
11
|
+
* columns in Safari"). At rest, pinned body cells are plain cells shifted by the
|
|
12
|
+
* horizontal scroll offset (`--_sx`, and `--_max-x` for the end side), which
|
|
13
|
+
* only changes when the table scrolls sideways. While it does, the scroller
|
|
14
|
+
* carries `data-hscroll` and the cells are sticky, so they don't lag behind
|
|
15
|
+
* the scroll; once it settles they go back to the transform, at the same place.
|
|
16
|
+
* It also sizes the pinned edges' shadow overlays (`.pin-shadow`) to the
|
|
17
|
+
* scroller's visible area.
|
|
18
|
+
*/
|
|
19
|
+
var PinScrollController = class {
|
|
20
|
+
#scroller;
|
|
21
|
+
#attached;
|
|
22
|
+
#left = 0;
|
|
23
|
+
#timer;
|
|
24
|
+
#resize;
|
|
25
|
+
constructor(host, scroller) {
|
|
26
|
+
this.#scroller = scroller;
|
|
27
|
+
host.addController(this);
|
|
28
|
+
}
|
|
29
|
+
hostConnected() {
|
|
30
|
+
this.#attach();
|
|
31
|
+
}
|
|
32
|
+
hostUpdated() {
|
|
33
|
+
this.#attach();
|
|
34
|
+
}
|
|
35
|
+
hostDisconnected() {
|
|
36
|
+
this.#attached?.removeEventListener("scroll", this.#onScroll);
|
|
37
|
+
this.#resize?.disconnect();
|
|
38
|
+
clearTimeout(this.#timer);
|
|
39
|
+
this.#attached = void 0;
|
|
40
|
+
}
|
|
41
|
+
/** Re-reads how far the table scrolls sideways: call when column widths change. */
|
|
42
|
+
measure() {
|
|
43
|
+
const s = this.#attached;
|
|
44
|
+
if (!s) return;
|
|
45
|
+
s.style.setProperty("--_max-x", `${String(Math.max(0, s.scrollWidth - s.clientWidth))}px`);
|
|
46
|
+
for (const shadow of s.parentNode?.querySelectorAll(":scope > .pin-shadow") ?? []) {
|
|
47
|
+
shadow.style.setProperty("--_view-h", `${String(s.clientHeight)}px`);
|
|
48
|
+
shadow.style.setProperty("--_sb-x", `${String(s.offsetWidth - s.clientWidth)}px`);
|
|
49
|
+
}
|
|
50
|
+
this.#left = s.scrollLeft;
|
|
51
|
+
s.style.setProperty("--_sx", `${String(this.#left)}px`);
|
|
52
|
+
}
|
|
53
|
+
#attach() {
|
|
54
|
+
const s = this.#scroller();
|
|
55
|
+
if (!s || s === this.#attached) return;
|
|
56
|
+
this.#attached = s;
|
|
57
|
+
s.addEventListener("scroll", this.#onScroll, { passive: true });
|
|
58
|
+
this.#resize ??= new ResizeObserver(() => {
|
|
59
|
+
this.measure();
|
|
60
|
+
});
|
|
61
|
+
this.#resize.observe(s);
|
|
62
|
+
this.measure();
|
|
63
|
+
}
|
|
64
|
+
#onScroll = () => {
|
|
65
|
+
const s = this.#attached;
|
|
66
|
+
if (!s || s.scrollLeft === this.#left) return;
|
|
67
|
+
this.#left = s.scrollLeft;
|
|
68
|
+
s.toggleAttribute("data-hscroll", true);
|
|
69
|
+
clearTimeout(this.#timer);
|
|
70
|
+
this.#timer = setTimeout(() => {
|
|
71
|
+
this.measure();
|
|
72
|
+
s.removeAttribute("data-hscroll");
|
|
73
|
+
}, SETTLE_MS);
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
export { PinScrollController };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=pin-scroll-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-scroll-controller.js","names":[],"sources":["../../src/controllers/pin-scroll-controller.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/** How long after the last sideways scroll pinned cells stay sticky. */\nconst SETTLE_MS = 150;\n\n/**\n * Keeps pinned body cells in place without making them sticky while the table\n * only scrolls up and down.\n *\n * Safari gives every sticky element a compositing layer of its own and moves\n * each one on every scroll frame; with a sticky cell per pinned column per row\n * that is dozens of layers, and vertical scrolling stutters (ADR 0001, \"Pinned\n * columns in Safari\"). At rest, pinned body cells are plain cells shifted by the\n * horizontal scroll offset (`--_sx`, and `--_max-x` for the end side), which\n * only changes when the table scrolls sideways. While it does, the scroller\n * carries `data-hscroll` and the cells are sticky, so they don't lag behind\n * the scroll; once it settles they go back to the transform, at the same place.\n * It also sizes the pinned edges' shadow overlays (`.pin-shadow`) to the\n * scroller's visible area.\n */\nexport class PinScrollController implements ReactiveController {\n readonly #scroller: () => HTMLElement | null;\n #attached: HTMLElement | undefined;\n #left = 0;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #resize: ResizeObserver | undefined;\n\n constructor(host: ReactiveControllerHost, scroller: () => HTMLElement | null) {\n this.#scroller = scroller;\n host.addController(this);\n }\n\n hostConnected(): void {\n // After a move in the DOM; before the first render there is no scroller yet.\n this.#attach();\n }\n\n hostUpdated(): void {\n this.#attach();\n }\n\n hostDisconnected(): void {\n this.#attached?.removeEventListener('scroll', this.#onScroll);\n this.#resize?.disconnect();\n clearTimeout(this.#timer);\n this.#attached = undefined;\n }\n\n /** Re-reads how far the table scrolls sideways: call when column widths change. */\n measure(): void {\n const s = this.#attached;\n if (!s) return;\n s.style.setProperty('--_max-x', `${String(Math.max(0, s.scrollWidth - s.clientWidth))}px`);\n // The edge shadows sit over the scroller, clear of its scrollbars.\n for (const shadow of s.parentNode?.querySelectorAll<HTMLElement>(':scope > .pin-shadow') ??\n []) {\n shadow.style.setProperty('--_view-h', `${String(s.clientHeight)}px`);\n shadow.style.setProperty('--_sb-x', `${String(s.offsetWidth - s.clientWidth)}px`);\n }\n this.#left = s.scrollLeft;\n s.style.setProperty('--_sx', `${String(this.#left)}px`);\n }\n\n #attach(): void {\n const s = this.#scroller();\n if (!s || s === this.#attached) return;\n this.#attached = s;\n s.addEventListener('scroll', this.#onScroll, { passive: true });\n this.#resize ??= new ResizeObserver(() => {\n this.measure();\n });\n this.#resize.observe(s);\n this.measure();\n }\n\n readonly #onScroll = () => {\n const s = this.#attached;\n if (!s || s.scrollLeft === this.#left) return;\n this.#left = s.scrollLeft;\n s.toggleAttribute('data-hscroll', true);\n clearTimeout(this.#timer);\n this.#timer = setTimeout(() => {\n this.measure();\n s.removeAttribute('data-hscroll');\n }, SETTLE_MS);\n };\n}\n"],"mappings":";;AAGA,IAAM,YAAY;;;;;;;;;;;;;;;;AAiBlB,IAAa,sBAAb,MAA+D;CAC7D;CACA;CACA,QAAQ;CACR;CACA;CAEA,YAAY,MAA8B,UAAoC;EAC5E,KAAK,YAAY;EACjB,KAAK,cAAc,IAAI;CACzB;CAEA,gBAAsB;EAEpB,KAAK,QAAQ;CACf;CAEA,cAAoB;EAClB,KAAK,QAAQ;CACf;CAEA,mBAAyB;EACvB,KAAK,WAAW,oBAAoB,UAAU,KAAK,SAAS;EAC5D,KAAK,SAAS,WAAW;EACzB,aAAa,KAAK,MAAM;EACxB,KAAK,YAAY,KAAA;CACnB;;CAGA,UAAgB;EACd,MAAM,IAAI,KAAK;EACf,IAAI,CAAC,GAAG;EACR,EAAE,MAAM,YAAY,YAAY,GAAG,OAAO,KAAK,IAAI,GAAG,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,GAAG;EAEzF,KAAK,MAAM,UAAU,EAAE,YAAY,iBAA8B,sBAAsB,KACrF,CAAC,GAAG;GACJ,OAAO,MAAM,YAAY,aAAa,GAAG,OAAO,EAAE,YAAY,EAAE,GAAG;GACnE,OAAO,MAAM,YAAY,WAAW,GAAG,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,GAAG;EAClF;EACA,KAAK,QAAQ,EAAE;EACf,EAAE,MAAM,YAAY,SAAS,GAAG,OAAO,KAAK,KAAK,EAAE,GAAG;CACxD;CAEA,UAAgB;EACd,MAAM,IAAI,KAAK,UAAU;EACzB,IAAI,CAAC,KAAK,MAAM,KAAK,WAAW;EAChC,KAAK,YAAY;EACjB,EAAE,iBAAiB,UAAU,KAAK,WAAW,EAAE,SAAS,KAAK,CAAC;EAC9D,KAAK,YAAY,IAAI,qBAAqB;GACxC,KAAK,QAAQ;EACf,CAAC;EACD,KAAK,QAAQ,QAAQ,CAAC;EACtB,KAAK,QAAQ;CACf;CAEA,kBAA2B;EACzB,MAAM,IAAI,KAAK;EACf,IAAI,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO;EACvC,KAAK,QAAQ,EAAE;EACf,EAAE,gBAAgB,gBAAgB,IAAI;EACtC,aAAa,KAAK,MAAM;EACxB,KAAK,SAAS,iBAAiB;GAC7B,KAAK,QAAQ;GACb,EAAE,gBAAgB,cAAc;EAClC,GAAG,SAAS;CACd;AACF"}
|
|
@@ -613,6 +613,66 @@
|
|
|
613
613
|
}
|
|
614
614
|
]
|
|
615
615
|
},
|
|
616
|
+
{
|
|
617
|
+
"kind": "javascript-module",
|
|
618
|
+
"path": "src/controllers/pin-scroll-controller.ts",
|
|
619
|
+
"declarations": [
|
|
620
|
+
{
|
|
621
|
+
"kind": "class",
|
|
622
|
+
"description": "Keeps pinned body cells in place without making them sticky while the table\nonly scrolls up and down.\n\nSafari gives every sticky element a compositing layer of its own and moves\neach one on every scroll frame; with a sticky cell per pinned column per row\nthat is dozens of layers, and vertical scrolling stutters (ADR 0001, \"Pinned\ncolumns in Safari\"). At rest, pinned body cells are plain cells shifted by the\nhorizontal scroll offset (`--_sx`, and `--_max-x` for the end side), which\nonly changes when the table scrolls sideways. While it does, the scroller\ncarries `data-hscroll` and the cells are sticky, so they don't lag behind\nthe scroll; once it settles they go back to the transform, at the same place.\nIt also sizes the pinned edges' shadow overlays (`.pin-shadow`) to the\nscroller's visible area.",
|
|
623
|
+
"name": "PinScrollController",
|
|
624
|
+
"members": [
|
|
625
|
+
{
|
|
626
|
+
"kind": "method",
|
|
627
|
+
"name": "hostConnected",
|
|
628
|
+
"return": {
|
|
629
|
+
"type": {
|
|
630
|
+
"text": "void"
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
"kind": "method",
|
|
636
|
+
"name": "hostUpdated",
|
|
637
|
+
"return": {
|
|
638
|
+
"type": {
|
|
639
|
+
"text": "void"
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
"kind": "method",
|
|
645
|
+
"name": "hostDisconnected",
|
|
646
|
+
"return": {
|
|
647
|
+
"type": {
|
|
648
|
+
"text": "void"
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
"kind": "method",
|
|
654
|
+
"name": "measure",
|
|
655
|
+
"return": {
|
|
656
|
+
"type": {
|
|
657
|
+
"text": "void"
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
"description": "Re-reads how far the table scrolls sideways: call when column widths change."
|
|
661
|
+
}
|
|
662
|
+
]
|
|
663
|
+
}
|
|
664
|
+
],
|
|
665
|
+
"exports": [
|
|
666
|
+
{
|
|
667
|
+
"kind": "js",
|
|
668
|
+
"name": "PinScrollController",
|
|
669
|
+
"declaration": {
|
|
670
|
+
"name": "PinScrollController",
|
|
671
|
+
"module": "src/controllers/pin-scroll-controller.ts"
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
]
|
|
675
|
+
},
|
|
616
676
|
{
|
|
617
677
|
"kind": "javascript-module",
|
|
618
678
|
"path": "src/controllers/resize-drag.ts",
|
|
@@ -1112,7 +1172,7 @@
|
|
|
1112
1172
|
{
|
|
1113
1173
|
"kind": "variable",
|
|
1114
1174
|
"name": "baseStyles",
|
|
1115
|
-
"default": "css` @layer argus-base { :host { display: block; /* A table that virtualizes needs a bounded height; set your own on the element. */ block-size: 400px; box-sizing: border-box; contain: content; color-scheme: light dark; color: var(--argus-fg, CanvasText); background: var(--argus-bg, Canvas); font: var(--argus-font, inherit); --_row-height: var(--argus-row-height, 40px); --_pad: var(--argus-cell-padding-inline, 12px); --_border: var(--argus-border-width, 1px) solid var(--argus-border-color, light-dark(#e5e7eb, #374151)); /* 1 in LTR, -1 in RTL: mirrors the pinned-edge shadow. */ --_dir: 1; } :host(:dir(rtl)) { --_dir: -1; } :host([density='compact']) { --_row-height: var(--argus-row-height, 32px); --_pad: var(--argus-cell-padding-inline, 8px); } :host([density='spacious']) { --_row-height: var(--argus-row-height, 52px); --_pad: var(--argus-cell-padding-inline, 16px); } :host([hidden]) { display: none; } [part~='table'] { position: relative; block-size: 100%; overflow: auto; overscroll-behavior: contain; /* A cell moved to by keyboard scrolls clear of the sticky header and pinned columns. */ scroll-padding-block-start: var(--_row-height); scroll-padding-inline: var(--_pin-start, 0) var(--_pin-end, 0); } .row { display: grid; grid-template-columns: var(--_cols); inline-size: var(--_width); min-inline-size: 100%; block-size: var(--_row-height); box-sizing: border-box; } [part~='header'] { position: sticky; inset-block-start: 0; z-index: 2; } [part~='body'] { position: relative; contain: layout; } [part~='row'] { position: absolute; inset-block-start: 0; inset-inline-start: 0; --_row-bg: var(--argus-bg, Canvas); --_row-surface: var(--_row-bg); background: var(--_row-surface); } [part~='row-odd'] { --_row-bg: var(--argus-row-stripe-bg, var(--argus-bg, Canvas)); } [part~='row']:hover { --_row-surface: var(--argus-row-hover-bg, var(--_row-bg)); } [part~='cell'], [part~='header-cell'] { display: flex; align-items: center; min-inline-size: 0; box-sizing: border-box; padding-inline: var(--_pad); overflow: hidden; white-space: nowrap; border-block-end: var(--_border); } [part~='header-cell'] { background: var(--argus-header-bg, light-dark(#f9fafb, #111827)); color: var(--argus-header-fg, inherit); font-weight: 600; } [part~='header-cell'] { position: relative; } [part~='resize-handle'] { position: absolute; inset-block: 0; inset-inline-end: 0; inline-size: 8px; z-index: 1; cursor: col-resize; touch-action: none; } [part~='resize-handle']::after { content: ''; position: absolute; inset-block: 25%; inset-inline-end: 0; inline-size: 2px; background: var(--argus-resize-handle-color, light-dark(#9ca3af, #6b7280)); opacity: 0; } [part~='header-cell']:hover [part~='resize-handle']::after, [part~='header-cell']:focus-visible [part~='resize-handle']::after, [part~='resize-handle'][data-active]::after { opacity: 1; } [data-sort-col] { cursor: pointer; user-select: none; } [part~='header-cell']:focus-visible, [part~='cell']:focus-visible { outline: var(--argus-focus-ring, 2px solid Highlight); outline-offset: -2px; } /* A small chevron after the header content; only the sorted column shows it. */ .sort-indicator { flex: none; inline-size: 0.35em; block-size: 0.35em; margin-inline-start: 0.4em; /* Physical sides on purpose: the chevron is rotated, so it must not mirror in RTL. */ border-right: 0.12em solid currentColor; border-bottom: 0.12em solid currentColor; visibility: hidden; } [aria-sort] .sort-indicator { visibility: visible; } [aria-sort='ascending'] .sort-indicator { transform: translateY(0.1em) rotate(-135deg); } [aria-sort='descending'] .sort-indicator { transform: translateY(-0.1em) rotate(45deg); } [data-align='end'] .sort-indicator { order: -1; margin-inline: 0 0.4em; } /* * Pinned columns: sticky inside the scroll container, with offsets from the * table model. Logical insets make RTL work without extra rules. */ [data-pin] { position: sticky; z-index: 1;
|
|
1175
|
+
"default": "css` @layer argus-base { :host { display: block; /* A table that virtualizes needs a bounded height; set your own on the element. */ block-size: 400px; box-sizing: border-box; contain: content; color-scheme: light dark; color: var(--argus-fg, CanvasText); background: var(--argus-bg, Canvas); font: var(--argus-font, inherit); --_row-height: var(--argus-row-height, 40px); --_pad: var(--argus-cell-padding-inline, 12px); --_border: var(--argus-border-width, 1px) solid var(--argus-border-color, light-dark(#e5e7eb, #374151)); /* 1 in LTR, -1 in RTL: mirrors the pinned-edge shadow. */ --_dir: 1; } :host(:dir(rtl)) { --_dir: -1; } :host([density='compact']) { --_row-height: var(--argus-row-height, 32px); --_pad: var(--argus-cell-padding-inline, 8px); } :host([density='spacious']) { --_row-height: var(--argus-row-height, 52px); --_pad: var(--argus-cell-padding-inline, 16px); } :host([hidden]) { display: none; } [part~='table'] { position: relative; block-size: 100%; overflow: auto; overscroll-behavior: contain; /* A cell moved to by keyboard scrolls clear of the sticky header and pinned columns. */ scroll-padding-block-start: var(--_row-height); scroll-padding-inline: var(--_pin-start, 0) var(--_pin-end, 0); } .row { display: grid; grid-template-columns: var(--_cols); inline-size: var(--_width); min-inline-size: 100%; block-size: var(--_row-height); box-sizing: border-box; } [part~='header'] { position: sticky; inset-block-start: 0; z-index: 2; } [part~='body'] { position: relative; contain: layout; } [part~='row'] { position: absolute; inset-block-start: 0; inset-inline-start: 0; --_row-bg: var(--argus-bg, Canvas); --_row-surface: var(--_row-bg); background: var(--_row-surface); } [part~='row-odd'] { --_row-bg: var(--argus-row-stripe-bg, var(--argus-bg, Canvas)); } [part~='row']:hover { --_row-surface: var(--argus-row-hover-bg, var(--_row-bg)); } [part~='cell'], [part~='header-cell'] { display: flex; align-items: center; min-inline-size: 0; box-sizing: border-box; padding-inline: var(--_pad); overflow: hidden; white-space: nowrap; border-block-end: var(--_border); } [part~='header-cell'] { background: var(--argus-header-bg, light-dark(#f9fafb, #111827)); color: var(--argus-header-fg, inherit); font-weight: 600; } [part~='header-cell'] { position: relative; } [part~='resize-handle'] { position: absolute; inset-block: 0; inset-inline-end: 0; inline-size: 8px; z-index: 1; cursor: col-resize; touch-action: none; } [part~='resize-handle']::after { content: ''; position: absolute; inset-block: 25%; inset-inline-end: 0; inline-size: 2px; background: var(--argus-resize-handle-color, light-dark(#9ca3af, #6b7280)); opacity: 0; } [part~='header-cell']:hover [part~='resize-handle']::after, [part~='header-cell']:focus-visible [part~='resize-handle']::after, [part~='resize-handle'][data-active]::after { opacity: 1; } [data-sort-col] { cursor: pointer; user-select: none; } [part~='header-cell']:focus-visible, [part~='cell']:focus-visible { outline: var(--argus-focus-ring, 2px solid Highlight); outline-offset: -2px; } /* A small chevron after the header content; only the sorted column shows it. */ .sort-indicator { flex: none; inline-size: 0.35em; block-size: 0.35em; margin-inline-start: 0.4em; /* Physical sides on purpose: the chevron is rotated, so it must not mirror in RTL. */ border-right: 0.12em solid currentColor; border-bottom: 0.12em solid currentColor; visibility: hidden; } [aria-sort] .sort-indicator { visibility: visible; } [aria-sort='ascending'] .sort-indicator { transform: translateY(0.1em) rotate(-135deg); } [aria-sort='descending'] .sort-indicator { transform: translateY(-0.1em) rotate(45deg); } [data-align='end'] .sort-indicator { order: -1; margin-inline: 0 0.4em; } /* * Pinned columns: sticky inside the scroll container, with offsets from the * table model. Logical insets make RTL work without extra rules. */ [data-pin] { position: sticky; z-index: 1; } [data-pin='start'] { inset-inline-start: var(--_pin-offset); } [data-pin='end'] { inset-inline-end: var(--_pin-offset); } /* * Pinned body cells are sticky only while the table scrolls sideways * (data-hscroll on the scroller). At rest they are plain cells shifted by the * horizontal scroll offset, which vertical scrolling never changes: a sticky * cell per row is a compositing layer each, and Safari stutters moving them * (PinScrollController). Both place the cells at the same spot. */ [part~='row'] > [data-pin] { position: relative; inset-inline: auto; } [part~='row'] > [data-pin='start'] { transform: translateX(var(--_sx, 0px)); } [part~='row'] > [data-pin='end'] { transform: translateX(calc(var(--_sx, 0px) - var(--_dir) * var(--_max-x, 0px))); } [data-hscroll] [part~='row'] > [data-pin] { position: sticky; transform: none; } [data-hscroll] [part~='row'] > [data-pin='start'] { inset-inline-start: var(--_pin-offset); } [data-hscroll] [part~='row'] > [data-pin='end'] { inset-inline-end: var(--_pin-offset); } [part~='cell-pinned'] { background: var(--_row-surface); } /* * The edge shadow, once per pinned side (.pin-shadow, beside the scroller). * --argus-pinned-shadow is written for a shadow falling toward the end side; * the start edge in RTL and the end edge in LTR mirror it. */ .pin-shadow { position: absolute; inset-block-start: 0; block-size: min(var(--_view-h, 100%), var(--_content-h, 100%)); z-index: 3; pointer-events: none; } .pin-shadow[data-side='start'] { inset-inline-start: 0; } .pin-shadow[data-side='end'] { inset-inline-end: var(--_sb-x, 0px); } .pin-shadow::after { content: ''; position: absolute; inset: 0; box-shadow: var( --argus-pinned-shadow, 4px 0 6px -4px light-dark(rgb(0 0 0 / 0.2), rgb(0 0 0 / 0.6)) ); transform: scaleX(var(--_dir)); } .pin-shadow[data-side='end']::after { transform: scaleX(calc(-1 * var(--_dir))); } [data-align='center'] { justify-content: center; text-align: center; } [data-align='end'] { justify-content: flex-end; text-align: end; } ::slotted(*) { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; } [part~='empty'] { display: flex; align-items: center; justify-content: center; min-block-size: calc(100% - var(--_row-height)); padding: var(--_pad); box-sizing: border-box; } /* Measures the resolved row height in px for the virtualizer. */ .probe { position: absolute; inset-block-start: 0; inline-size: 0; block-size: var(--_row-height); visibility: hidden; pointer-events: none; } } `",
|
|
1116
1176
|
"description": "Base styles. Everything sits in `@layer argus-base`, and every value reads a\npublic token with a neutral fallback, so themes and page CSS always win.\n`--_*` properties are private."
|
|
1117
1177
|
}
|
|
1118
1178
|
],
|
package/dist/render/body.js
CHANGED
package/dist/render/body.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body.js","names":[],"sources":["../../src/render/body.ts"],"sourcesContent":["import { html, nothing } from 'lit';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport type { ColumnView } from '@argus-table/core';\nimport type { WindowRow } from '../controllers/virtual-controller.js';\nimport type { CellContent } from '../types.js';\nimport { cellParts, cellSlot, pinStyle } from './content.js';\n\ntype Columns<T> = readonly ColumnView<T, CellContent>[];\n\n/** The cell holding the grid's tab stop: a row index and a column id. */\nexport interface ActiveCell {\n index: number;\n col: string;\n}\n\n/**\n * The virtualized body. Rows are keyed by pool slot, so a row entering the window\n * reuses the DOM of one that left (ADR 0001), and they stay in pool order: screen\n * readers get the position from `aria-rowindex` (the header row is 1).\n */\nexport function renderBody<T>(\n rows: readonly WindowRow[],\n columns: Columns<T>,\n totalSize: number,\n active: ActiveCell | undefined,\n) {\n return html`<div\n part=\"body\"\n role=\"rowgroup\"\n style=${styleMap({ blockSize: `${String(totalSize)}px` })}\n >\n ${repeat(\n rows,\n (r) => r.slot,\n (r) => renderRow(r, columns, r.index === active?.index ? active.col : undefined),\n )}\n </div>`;\n}\n\n/**\n * One body row. Every cell is focusable (`tabindex=-1`) so clicks focus it; the\n * active one is the grid's single tab stop (roving tabindex).\n */\nfunction renderRow<T>(row: WindowRow, columns: Columns<T>, activeCol: string | undefined) {\n return html`<div\n part=${row.index % 2 === 0 ? 'row row-even' : 'row row-odd'}\n class=\"row\"\n role=\"row\"\n aria-rowindex=${row.index + 2}\n style=${styleMap({ transform: `translateY(${String(row.start)}px)` })}\n >\n ${repeat(\n columns,\n (c) => c.id,\n (c) =>\n html`<div\n part=${cellParts(c)}\n role=\"gridcell\"\n aria-colindex=${c.colIndex}\n tabindex=${c.id === activeCol ? 0 : -1}\n data-col=${c.id}\n data-align=${c.align}\n data-pin=${c.pin ?? nothing}\n
|
|
1
|
+
{"version":3,"file":"body.js","names":[],"sources":["../../src/render/body.ts"],"sourcesContent":["import { html, nothing } from 'lit';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport type { ColumnView } from '@argus-table/core';\nimport type { WindowRow } from '../controllers/virtual-controller.js';\nimport type { CellContent } from '../types.js';\nimport { cellParts, cellSlot, pinStyle } from './content.js';\n\ntype Columns<T> = readonly ColumnView<T, CellContent>[];\n\n/** The cell holding the grid's tab stop: a row index and a column id. */\nexport interface ActiveCell {\n index: number;\n col: string;\n}\n\n/**\n * The virtualized body. Rows are keyed by pool slot, so a row entering the window\n * reuses the DOM of one that left (ADR 0001), and they stay in pool order: screen\n * readers get the position from `aria-rowindex` (the header row is 1).\n */\nexport function renderBody<T>(\n rows: readonly WindowRow[],\n columns: Columns<T>,\n totalSize: number,\n active: ActiveCell | undefined,\n) {\n return html`<div\n part=\"body\"\n role=\"rowgroup\"\n style=${styleMap({ blockSize: `${String(totalSize)}px` })}\n >\n ${repeat(\n rows,\n (r) => r.slot,\n (r) => renderRow(r, columns, r.index === active?.index ? active.col : undefined),\n )}\n </div>`;\n}\n\n/**\n * One body row. Every cell is focusable (`tabindex=-1`) so clicks focus it; the\n * active one is the grid's single tab stop (roving tabindex).\n */\nfunction renderRow<T>(row: WindowRow, columns: Columns<T>, activeCol: string | undefined) {\n return html`<div\n part=${row.index % 2 === 0 ? 'row row-even' : 'row row-odd'}\n class=\"row\"\n role=\"row\"\n aria-rowindex=${row.index + 2}\n style=${styleMap({ transform: `translateY(${String(row.start)}px)` })}\n >\n ${repeat(\n columns,\n (c) => c.id,\n (c) =>\n html`<div\n part=${cellParts(c)}\n role=\"gridcell\"\n aria-colindex=${c.colIndex}\n tabindex=${c.id === activeCol ? 0 : -1}\n data-col=${c.id}\n data-align=${c.align}\n data-pin=${c.pin ?? nothing}\n style=${pinStyle(c) ?? nothing}\n >\n <slot name=${cellSlot(row.slot, c.colIndex)}></slot>\n </div>`,\n )}\n </div>`;\n}\n"],"mappings":";;;;;;;;;;AAqBA,SAAgB,WACd,MACA,SACA,WACA,QACA;CACA,OAAO,IAAI;;;YAGD,SAAS,EAAE,WAAW,GAAG,OAAO,SAAS,EAAE,IAAI,CAAC,EAAE;;MAExD,OACA,OACC,MAAM,EAAE,OACR,MAAM,UAAU,GAAG,SAAS,EAAE,UAAU,QAAQ,QAAQ,OAAO,MAAM,KAAA,CAAS,CACjF,EAAE;;AAEN;;;;;AAMA,SAAS,UAAa,KAAgB,SAAqB,WAA+B;CACxF,OAAO,IAAI;WACF,IAAI,QAAQ,MAAM,IAAI,iBAAiB,cAAc;;;oBAG5C,IAAI,QAAQ,EAAE;YACtB,SAAS,EAAE,WAAW,cAAc,OAAO,IAAI,KAAK,EAAE,KAAK,CAAC,EAAE;;MAEpE,OACA,UACC,MAAM,EAAE,KACR,MACC,IAAI;iBACK,UAAU,CAAC,EAAE;;0BAEJ,EAAE,SAAS;qBAChB,EAAE,OAAO,YAAY,IAAI,GAAG;qBAC5B,EAAE,GAAG;uBACH,EAAE,MAAM;qBACV,EAAE,OAAO,QAAQ;kBACpB,SAAS,CAAC,KAAK,QAAQ;;uBAElB,SAAS,IAAI,MAAM,EAAE,QAAQ,EAAE;eAElD,EAAE;;AAEN"}
|
package/dist/render/header.js
CHANGED
|
@@ -31,7 +31,6 @@ function renderHeader(columns, handlers, activeCol) {
|
|
|
31
31
|
tabindex=${c.id === activeCol ? 0 : -1}
|
|
32
32
|
data-align=${c.align}
|
|
33
33
|
data-pin=${c.pin ?? nothing}
|
|
34
|
-
?data-pin-edge=${c.pinEdge}
|
|
35
34
|
style=${pinStyle(c) ?? nothing}
|
|
36
35
|
>
|
|
37
36
|
<slot name=${headerSlot(c.colIndex)}></slot>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.js","names":[],"sources":["../../src/render/header.ts"],"sourcesContent":["import { html, nothing } from 'lit';\nimport { repeat } from 'lit/directives/repeat.js';\nimport type { ColumnView } from '@argus-table/core';\nimport type { CellContent } from '../types.js';\nimport { ariaSort, headerSlot, pinStyle } from './content.js';\n\nexport interface HeaderHandlers {\n click: (event: MouseEvent) => void;\n pointerdown: (event: PointerEvent) => void;\n}\n\n/**\n * The sticky header row. Header content is slotted from the light DOM. Pinned\n * header cells use the same private `data-pin` hooks as body cells, but no parts.\n * Every header carries `data-col` (its column id) and sortable ones also\n * `data-sort-col`; the element's delegated handlers read them. Headers take part\n * in the grid's roving tabindex: the active one is the tab stop (`activeCol`).\n * Resizable columns get a `resize-handle` on the header's inline-end edge. It is\n * not focusable: the focused header resizes with Alt+Arrow.\n */\nexport function renderHeader<T>(\n columns: readonly ColumnView<T, CellContent>[],\n handlers: HeaderHandlers,\n activeCol: string | undefined,\n) {\n return html`<div part=\"header\" role=\"rowgroup\">\n <div\n part=\"header-row\"\n class=\"row\"\n role=\"row\"\n aria-rowindex=\"1\"\n @click=${handlers.click}\n @pointerdown=${handlers.pointerdown}\n >\n ${repeat(\n columns,\n (c) => c.id,\n (c) =>\n html`<div\n part=${c.sorted ? 'header-cell header-cell-sorted' : 'header-cell'}\n role=\"columnheader\"\n aria-colindex=${c.colIndex}\n aria-sort=${ariaSort(c.sorted) ?? nothing}\n data-col=${c.id}\n data-sort-col=${c.sortable ? c.id : nothing}\n tabindex=${c.id === activeCol ? 0 : -1}\n data-align=${c.align}\n data-pin=${c.pin ?? nothing}\n
|
|
1
|
+
{"version":3,"file":"header.js","names":[],"sources":["../../src/render/header.ts"],"sourcesContent":["import { html, nothing } from 'lit';\nimport { repeat } from 'lit/directives/repeat.js';\nimport type { ColumnView } from '@argus-table/core';\nimport type { CellContent } from '../types.js';\nimport { ariaSort, headerSlot, pinStyle } from './content.js';\n\nexport interface HeaderHandlers {\n click: (event: MouseEvent) => void;\n pointerdown: (event: PointerEvent) => void;\n}\n\n/**\n * The sticky header row. Header content is slotted from the light DOM. Pinned\n * header cells use the same private `data-pin` hooks as body cells, but no parts.\n * Every header carries `data-col` (its column id) and sortable ones also\n * `data-sort-col`; the element's delegated handlers read them. Headers take part\n * in the grid's roving tabindex: the active one is the tab stop (`activeCol`).\n * Resizable columns get a `resize-handle` on the header's inline-end edge. It is\n * not focusable: the focused header resizes with Alt+Arrow.\n */\nexport function renderHeader<T>(\n columns: readonly ColumnView<T, CellContent>[],\n handlers: HeaderHandlers,\n activeCol: string | undefined,\n) {\n return html`<div part=\"header\" role=\"rowgroup\">\n <div\n part=\"header-row\"\n class=\"row\"\n role=\"row\"\n aria-rowindex=\"1\"\n @click=${handlers.click}\n @pointerdown=${handlers.pointerdown}\n >\n ${repeat(\n columns,\n (c) => c.id,\n (c) =>\n html`<div\n part=${c.sorted ? 'header-cell header-cell-sorted' : 'header-cell'}\n role=\"columnheader\"\n aria-colindex=${c.colIndex}\n aria-sort=${ariaSort(c.sorted) ?? nothing}\n data-col=${c.id}\n data-sort-col=${c.sortable ? c.id : nothing}\n tabindex=${c.id === activeCol ? 0 : -1}\n data-align=${c.align}\n data-pin=${c.pin ?? nothing}\n style=${pinStyle(c) ?? nothing}\n >\n <slot name=${headerSlot(c.colIndex)}></slot>\n ${c.sortable ? html`<span class=\"sort-indicator\" aria-hidden=\"true\"></span>` : nothing}\n ${c.resizable ? resizeHandle(c) : nothing}\n </div>`,\n )}\n </div>\n </div>`;\n}\n\n/**\n * A vertical separator with the column's width as its value. A flex column has no\n * fixed width until it is resized, so it reports none.\n */\nfunction resizeHandle<T>(c: ColumnView<T, CellContent>) {\n return html`<div\n part=\"resize-handle\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize column\"\n aria-valuenow=${c.flex === undefined ? Math.round(c.width) : nothing}\n aria-valuemin=${c.minWidth}\n aria-valuemax=${Number.isFinite(c.maxWidth) ? c.maxWidth : nothing}\n data-resize-col=${c.id}\n ></div>`;\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,SAAgB,aACd,SACA,UACA,WACA;CACA,OAAO,IAAI;;;;;;eAME,SAAS,MAAM;qBACT,SAAS,YAAY;;QAElC,OACA,UACC,MAAM,EAAE,KACR,MACC,IAAI;mBACK,EAAE,SAAS,mCAAmC,cAAc;;4BAEnD,EAAE,SAAS;wBACf,SAAS,EAAE,MAAM,KAAK,QAAQ;uBAC/B,EAAE,GAAG;4BACA,EAAE,WAAW,EAAE,KAAK,QAAQ;uBACjC,EAAE,OAAO,YAAY,IAAI,GAAG;yBAC1B,EAAE,MAAM;uBACV,EAAE,OAAO,QAAQ;oBACpB,SAAS,CAAC,KAAK,QAAQ;;yBAElB,WAAW,EAAE,QAAQ,EAAE;cAClC,EAAE,WAAW,IAAI,4DAA4D,QAAQ;cACrF,EAAE,YAAY,aAAa,CAAC,IAAI,QAAQ;iBAEhD,EAAE;;;AAGR;;;;;AAMA,SAAS,aAAgB,GAA+B;CACtD,OAAO,IAAI;;;;;oBAKO,EAAE,SAAS,KAAA,IAAY,KAAK,MAAM,EAAE,KAAK,IAAI,QAAQ;oBACrD,EAAE,SAAS;oBACX,OAAO,SAAS,EAAE,QAAQ,IAAI,EAAE,WAAW,QAAQ;sBACjD,EAAE,GAAG;;AAE3B"}
|
package/dist/styles/base.js
CHANGED
|
@@ -5,231 +5,7 @@ import { css } from "lit";
|
|
|
5
5
|
* public token with a neutral fallback, so themes and page CSS always win.
|
|
6
6
|
* `--_*` properties are private.
|
|
7
7
|
*/
|
|
8
|
-
var baseStyles = css
|
|
9
|
-
@layer argus-base {
|
|
10
|
-
:host {
|
|
11
|
-
display: block;
|
|
12
|
-
/* A table that virtualizes needs a bounded height; set your own on the element. */
|
|
13
|
-
block-size: 400px;
|
|
14
|
-
box-sizing: border-box;
|
|
15
|
-
contain: content;
|
|
16
|
-
color-scheme: light dark;
|
|
17
|
-
color: var(--argus-fg, CanvasText);
|
|
18
|
-
background: var(--argus-bg, Canvas);
|
|
19
|
-
font: var(--argus-font, inherit);
|
|
20
|
-
--_row-height: var(--argus-row-height, 40px);
|
|
21
|
-
--_pad: var(--argus-cell-padding-inline, 12px);
|
|
22
|
-
--_border: var(--argus-border-width, 1px) solid
|
|
23
|
-
var(--argus-border-color, light-dark(#e5e7eb, #374151));
|
|
24
|
-
/* 1 in LTR, -1 in RTL: mirrors the pinned-edge shadow. */
|
|
25
|
-
--_dir: 1;
|
|
26
|
-
}
|
|
27
|
-
:host(:dir(rtl)) {
|
|
28
|
-
--_dir: -1;
|
|
29
|
-
}
|
|
30
|
-
:host([density='compact']) {
|
|
31
|
-
--_row-height: var(--argus-row-height, 32px);
|
|
32
|
-
--_pad: var(--argus-cell-padding-inline, 8px);
|
|
33
|
-
}
|
|
34
|
-
:host([density='spacious']) {
|
|
35
|
-
--_row-height: var(--argus-row-height, 52px);
|
|
36
|
-
--_pad: var(--argus-cell-padding-inline, 16px);
|
|
37
|
-
}
|
|
38
|
-
:host([hidden]) {
|
|
39
|
-
display: none;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
[part~='table'] {
|
|
43
|
-
position: relative;
|
|
44
|
-
block-size: 100%;
|
|
45
|
-
overflow: auto;
|
|
46
|
-
overscroll-behavior: contain;
|
|
47
|
-
/* A cell moved to by keyboard scrolls clear of the sticky header and pinned columns. */
|
|
48
|
-
scroll-padding-block-start: var(--_row-height);
|
|
49
|
-
scroll-padding-inline: var(--_pin-start, 0) var(--_pin-end, 0);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.row {
|
|
53
|
-
display: grid;
|
|
54
|
-
grid-template-columns: var(--_cols);
|
|
55
|
-
inline-size: var(--_width);
|
|
56
|
-
min-inline-size: 100%;
|
|
57
|
-
block-size: var(--_row-height);
|
|
58
|
-
box-sizing: border-box;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
[part~='header'] {
|
|
62
|
-
position: sticky;
|
|
63
|
-
inset-block-start: 0;
|
|
64
|
-
z-index: 2;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
[part~='body'] {
|
|
68
|
-
position: relative;
|
|
69
|
-
contain: layout;
|
|
70
|
-
}
|
|
71
|
-
[part~='row'] {
|
|
72
|
-
position: absolute;
|
|
73
|
-
inset-block-start: 0;
|
|
74
|
-
inset-inline-start: 0;
|
|
75
|
-
--_row-bg: var(--argus-bg, Canvas);
|
|
76
|
-
--_row-surface: var(--_row-bg);
|
|
77
|
-
background: var(--_row-surface);
|
|
78
|
-
}
|
|
79
|
-
[part~='row-odd'] {
|
|
80
|
-
--_row-bg: var(--argus-row-stripe-bg, var(--argus-bg, Canvas));
|
|
81
|
-
}
|
|
82
|
-
[part~='row']:hover {
|
|
83
|
-
--_row-surface: var(--argus-row-hover-bg, var(--_row-bg));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
[part~='cell'],
|
|
87
|
-
[part~='header-cell'] {
|
|
88
|
-
display: flex;
|
|
89
|
-
align-items: center;
|
|
90
|
-
min-inline-size: 0;
|
|
91
|
-
box-sizing: border-box;
|
|
92
|
-
padding-inline: var(--_pad);
|
|
93
|
-
overflow: hidden;
|
|
94
|
-
white-space: nowrap;
|
|
95
|
-
border-block-end: var(--_border);
|
|
96
|
-
}
|
|
97
|
-
[part~='header-cell'] {
|
|
98
|
-
background: var(--argus-header-bg, light-dark(#f9fafb, #111827));
|
|
99
|
-
color: var(--argus-header-fg, inherit);
|
|
100
|
-
font-weight: 600;
|
|
101
|
-
}
|
|
102
|
-
[part~='header-cell'] {
|
|
103
|
-
position: relative;
|
|
104
|
-
}
|
|
105
|
-
[part~='resize-handle'] {
|
|
106
|
-
position: absolute;
|
|
107
|
-
inset-block: 0;
|
|
108
|
-
inset-inline-end: 0;
|
|
109
|
-
inline-size: 8px;
|
|
110
|
-
z-index: 1;
|
|
111
|
-
cursor: col-resize;
|
|
112
|
-
touch-action: none;
|
|
113
|
-
}
|
|
114
|
-
[part~='resize-handle']::after {
|
|
115
|
-
content: '';
|
|
116
|
-
position: absolute;
|
|
117
|
-
inset-block: 25%;
|
|
118
|
-
inset-inline-end: 0;
|
|
119
|
-
inline-size: 2px;
|
|
120
|
-
background: var(--argus-resize-handle-color, light-dark(#9ca3af, #6b7280));
|
|
121
|
-
opacity: 0;
|
|
122
|
-
}
|
|
123
|
-
[part~='header-cell']:hover [part~='resize-handle']::after,
|
|
124
|
-
[part~='header-cell']:focus-visible [part~='resize-handle']::after,
|
|
125
|
-
[part~='resize-handle'][data-active]::after {
|
|
126
|
-
opacity: 1;
|
|
127
|
-
}
|
|
128
|
-
[data-sort-col] {
|
|
129
|
-
cursor: pointer;
|
|
130
|
-
user-select: none;
|
|
131
|
-
}
|
|
132
|
-
[part~='header-cell']:focus-visible,
|
|
133
|
-
[part~='cell']:focus-visible {
|
|
134
|
-
outline: var(--argus-focus-ring, 2px solid Highlight);
|
|
135
|
-
outline-offset: -2px;
|
|
136
|
-
}
|
|
137
|
-
/* A small chevron after the header content; only the sorted column shows it. */
|
|
138
|
-
.sort-indicator {
|
|
139
|
-
flex: none;
|
|
140
|
-
inline-size: 0.35em;
|
|
141
|
-
block-size: 0.35em;
|
|
142
|
-
margin-inline-start: 0.4em;
|
|
143
|
-
/* Physical sides on purpose: the chevron is rotated, so it must not mirror in RTL. */
|
|
144
|
-
border-right: 0.12em solid currentColor;
|
|
145
|
-
border-bottom: 0.12em solid currentColor;
|
|
146
|
-
visibility: hidden;
|
|
147
|
-
}
|
|
148
|
-
[aria-sort] .sort-indicator {
|
|
149
|
-
visibility: visible;
|
|
150
|
-
}
|
|
151
|
-
[aria-sort='ascending'] .sort-indicator {
|
|
152
|
-
transform: translateY(0.1em) rotate(-135deg);
|
|
153
|
-
}
|
|
154
|
-
[aria-sort='descending'] .sort-indicator {
|
|
155
|
-
transform: translateY(-0.1em) rotate(45deg);
|
|
156
|
-
}
|
|
157
|
-
[data-align='end'] .sort-indicator {
|
|
158
|
-
order: -1;
|
|
159
|
-
margin-inline: 0 0.4em;
|
|
160
|
-
}
|
|
161
|
-
/*
|
|
162
|
-
* Pinned columns: sticky inside the scroll container, with offsets from the
|
|
163
|
-
* table model. Logical insets make RTL work without extra rules.
|
|
164
|
-
*/
|
|
165
|
-
[data-pin] {
|
|
166
|
-
position: sticky;
|
|
167
|
-
z-index: 1;
|
|
168
|
-
/* Visible so the edge shadow isn't clipped; slotted content still clips itself. */
|
|
169
|
-
overflow: visible;
|
|
170
|
-
}
|
|
171
|
-
[data-pin='start'] {
|
|
172
|
-
inset-inline-start: var(--_pin-offset);
|
|
173
|
-
}
|
|
174
|
-
[data-pin='end'] {
|
|
175
|
-
inset-inline-end: var(--_pin-offset);
|
|
176
|
-
}
|
|
177
|
-
[part~='cell-pinned'] {
|
|
178
|
-
background: var(--_row-surface);
|
|
179
|
-
}
|
|
180
|
-
/*
|
|
181
|
-
* The edge shadow. --argus-pinned-shadow is written for a shadow falling
|
|
182
|
-
* toward the end side; the start edge in RTL and the end edge in LTR mirror it.
|
|
183
|
-
*/
|
|
184
|
-
[data-pin-edge]::after {
|
|
185
|
-
content: '';
|
|
186
|
-
position: absolute;
|
|
187
|
-
inset: 0;
|
|
188
|
-
pointer-events: none;
|
|
189
|
-
box-shadow: var(
|
|
190
|
-
--argus-pinned-shadow,
|
|
191
|
-
4px 0 6px -4px light-dark(rgb(0 0 0 / 0.2), rgb(0 0 0 / 0.6))
|
|
192
|
-
);
|
|
193
|
-
transform: scaleX(var(--_dir));
|
|
194
|
-
}
|
|
195
|
-
[data-pin='end'][data-pin-edge]::after {
|
|
196
|
-
transform: scaleX(calc(-1 * var(--_dir)));
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
[data-align='center'] {
|
|
200
|
-
justify-content: center;
|
|
201
|
-
text-align: center;
|
|
202
|
-
}
|
|
203
|
-
[data-align='end'] {
|
|
204
|
-
justify-content: flex-end;
|
|
205
|
-
text-align: end;
|
|
206
|
-
}
|
|
207
|
-
::slotted(*) {
|
|
208
|
-
min-inline-size: 0;
|
|
209
|
-
overflow: hidden;
|
|
210
|
-
text-overflow: ellipsis;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
[part~='empty'] {
|
|
214
|
-
display: flex;
|
|
215
|
-
align-items: center;
|
|
216
|
-
justify-content: center;
|
|
217
|
-
min-block-size: calc(100% - var(--_row-height));
|
|
218
|
-
padding: var(--_pad);
|
|
219
|
-
box-sizing: border-box;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/* Measures the resolved row height in px for the virtualizer. */
|
|
223
|
-
.probe {
|
|
224
|
-
position: absolute;
|
|
225
|
-
inset-block-start: 0;
|
|
226
|
-
inline-size: 0;
|
|
227
|
-
block-size: var(--_row-height);
|
|
228
|
-
visibility: hidden;
|
|
229
|
-
pointer-events: none;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
`;
|
|
8
|
+
var baseStyles = css`@layer argus-base{:host{box-sizing:border-box;contain:content;color-scheme:light dark;block-size:400px;color:var(--argus-fg,CanvasText);background:var(--argus-bg,Canvas);font:var(--argus-font,inherit);--_row-height:var(--argus-row-height,40px);--_pad:var(--argus-cell-padding-inline,12px);--_border:var(--argus-border-width,1px) solid var(--argus-border-color,light-dark(#e5e7eb,#374151));--_dir:1;display:block}:host(:dir(rtl)){--_dir:-1}:host([density=compact]){--_row-height:var(--argus-row-height,32px);--_pad:var(--argus-cell-padding-inline,8px)}:host([density=spacious]){--_row-height:var(--argus-row-height,52px);--_pad:var(--argus-cell-padding-inline,16px)}:host([hidden]){display:none}[part~=table]{overscroll-behavior:contain;block-size:100%;scroll-padding-block-start:var(--_row-height);scroll-padding-inline:var(--_pin-start,0) var(--_pin-end,0);position:relative;overflow:auto}.row{grid-template-columns:var(--_cols);inline-size:var(--_width);min-inline-size:100%;block-size:var(--_row-height);box-sizing:border-box;display:grid}[part~=header]{z-index:2;position:sticky;inset-block-start:0}[part~=body]{contain:layout;position:relative}[part~=row]{--_row-bg:var(--argus-bg,Canvas);--_row-surface:var(--_row-bg);background:var(--_row-surface);position:absolute;inset-block-start:0;inset-inline-start:0}[part~=row-odd]{--_row-bg:var(--argus-row-stripe-bg,var(--argus-bg,Canvas))}[part~=row]:hover{--_row-surface:var(--argus-row-hover-bg,var(--_row-bg))}[part~=cell],[part~=header-cell]{box-sizing:border-box;min-inline-size:0;padding-inline:var(--_pad);white-space:nowrap;border-block-end:var(--_border);align-items:center;display:flex;overflow:hidden}[part~=header-cell]{background:var(--argus-header-bg,light-dark(#f9fafb,#111827));color:var(--argus-header-fg,inherit);font-weight:600;position:relative}[part~=resize-handle]{z-index:1;cursor:col-resize;touch-action:none;inline-size:8px;position:absolute;inset-block:0;inset-inline-end:0}[part~=resize-handle]:after{content:"";background:var(--argus-resize-handle-color,light-dark(#9ca3af,#6b7280));opacity:0;inline-size:2px;position:absolute;inset-block:25%;inset-inline-end:0}[part~=header-cell]:hover [part~=resize-handle]:after,[part~=header-cell]:focus-visible [part~=resize-handle]:after,[part~=resize-handle][data-active]:after{opacity:1}[data-sort-col]{cursor:pointer;user-select:none}[part~=header-cell]:focus-visible,[part~=cell]:focus-visible{outline:var(--argus-focus-ring,2px solid Highlight);outline-offset:-2px}.sort-indicator{visibility:hidden;border-bottom:.12em solid;border-right:.12em solid;flex:none;block-size:.35em;inline-size:.35em;margin-inline-start:.4em}[aria-sort] .sort-indicator{visibility:visible}[aria-sort=ascending] .sort-indicator{transform:translateY(.1em)rotate(-135deg)}[aria-sort=descending] .sort-indicator{transform:translateY(-.1em)rotate(45deg)}[data-align=end] .sort-indicator{order:-1;margin-inline:0 .4em}[data-pin]{z-index:1;position:sticky}[data-pin=start]{inset-inline-start:var(--_pin-offset)}[data-pin=end]{inset-inline-end:var(--_pin-offset)}[part~=row]>[data-pin]{position:relative;inset-inline:auto}[part~=row]>[data-pin=start]{transform:translateX(var(--_sx,0px))}[part~=row]>[data-pin=end]{transform:translateX(calc(var(--_sx,0px) - var(--_dir) * var(--_max-x,0px)))}[data-hscroll] [part~=row]>[data-pin]{position:sticky;transform:none}[data-hscroll] [part~=row]>[data-pin=start]{inset-inline-start:var(--_pin-offset)}[data-hscroll] [part~=row]>[data-pin=end]{inset-inline-end:var(--_pin-offset)}[part~=cell-pinned]{background:var(--_row-surface)}.pin-shadow{block-size:min(var(--_view-h,100%), var(--_content-h,100%));z-index:3;pointer-events:none;position:absolute;inset-block-start:0}.pin-shadow[data-side=start]{inset-inline-start:0}.pin-shadow[data-side=end]{inset-inline-end:var(--_sb-x,0px)}.pin-shadow:after{content:"";box-shadow:var(--argus-pinned-shadow,4px 0 6px -4px light-dark(#0003,#0009));transform:scaleX(var(--_dir));position:absolute;inset:0}.pin-shadow[data-side=end]:after{transform:scaleX(calc(-1 * var(--_dir)))}[data-align=center]{text-align:center;justify-content:center}[data-align=end]{text-align:end;justify-content:flex-end}::slotted(*){text-overflow:ellipsis;min-inline-size:0;overflow:hidden}[part~=empty]{min-block-size:calc(100% - var(--_row-height));padding:var(--_pad);box-sizing:border-box;justify-content:center;align-items:center;display:flex}.probe{inline-size:0;block-size:var(--_row-height);visibility:hidden;pointer-events:none;position:absolute;inset-block-start:0}}`;
|
|
233
9
|
export { baseStyles };
|
|
234
10
|
|
|
235
11
|
//# sourceMappingURL=base.js.map
|
package/dist/styles/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../src/styles/base.ts"],"sourcesContent":["import { css } from 'lit';\n\n/**\n * Base styles. Everything sits in `@layer argus-base`, and every value reads a\n * public token with a neutral fallback, so themes and page CSS always win.\n * `--_*` properties are private.\n */\nexport const baseStyles = css`\n @layer argus-base {\n :host {\n display: block;\n /* A table that virtualizes needs a bounded height; set your own on the element. */\n block-size: 400px;\n box-sizing: border-box;\n contain: content;\n color-scheme: light dark;\n color: var(--argus-fg, CanvasText);\n background: var(--argus-bg, Canvas);\n font: var(--argus-font, inherit);\n --_row-height: var(--argus-row-height, 40px);\n --_pad: var(--argus-cell-padding-inline, 12px);\n --_border: var(--argus-border-width, 1px) solid\n var(--argus-border-color, light-dark(#e5e7eb, #374151));\n /* 1 in LTR, -1 in RTL: mirrors the pinned-edge shadow. */\n --_dir: 1;\n }\n :host(:dir(rtl)) {\n --_dir: -1;\n }\n :host([density='compact']) {\n --_row-height: var(--argus-row-height, 32px);\n --_pad: var(--argus-cell-padding-inline, 8px);\n }\n :host([density='spacious']) {\n --_row-height: var(--argus-row-height, 52px);\n --_pad: var(--argus-cell-padding-inline, 16px);\n }\n :host([hidden]) {\n display: none;\n }\n\n [part~='table'] {\n position: relative;\n block-size: 100%;\n overflow: auto;\n overscroll-behavior: contain;\n /* A cell moved to by keyboard scrolls clear of the sticky header and pinned columns. */\n scroll-padding-block-start: var(--_row-height);\n scroll-padding-inline: var(--_pin-start, 0) var(--_pin-end, 0);\n }\n\n .row {\n display: grid;\n grid-template-columns: var(--_cols);\n inline-size: var(--_width);\n min-inline-size: 100%;\n block-size: var(--_row-height);\n box-sizing: border-box;\n }\n\n [part~='header'] {\n position: sticky;\n inset-block-start: 0;\n z-index: 2;\n }\n\n [part~='body'] {\n position: relative;\n contain: layout;\n }\n [part~='row'] {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n --_row-bg: var(--argus-bg, Canvas);\n --_row-surface: var(--_row-bg);\n background: var(--_row-surface);\n }\n [part~='row-odd'] {\n --_row-bg: var(--argus-row-stripe-bg, var(--argus-bg, Canvas));\n }\n [part~='row']:hover {\n --_row-surface: var(--argus-row-hover-bg, var(--_row-bg));\n }\n\n [part~='cell'],\n [part~='header-cell'] {\n display: flex;\n align-items: center;\n min-inline-size: 0;\n box-sizing: border-box;\n padding-inline: var(--_pad);\n overflow: hidden;\n white-space: nowrap;\n border-block-end: var(--_border);\n }\n [part~='header-cell'] {\n background: var(--argus-header-bg, light-dark(#f9fafb, #111827));\n color: var(--argus-header-fg, inherit);\n font-weight: 600;\n }\n [part~='header-cell'] {\n position: relative;\n }\n [part~='resize-handle'] {\n position: absolute;\n inset-block: 0;\n inset-inline-end: 0;\n inline-size: 8px;\n z-index: 1;\n cursor: col-resize;\n touch-action: none;\n }\n [part~='resize-handle']::after {\n content: '';\n position: absolute;\n inset-block: 25%;\n inset-inline-end: 0;\n inline-size: 2px;\n background: var(--argus-resize-handle-color, light-dark(#9ca3af, #6b7280));\n opacity: 0;\n }\n [part~='header-cell']:hover [part~='resize-handle']::after,\n [part~='header-cell']:focus-visible [part~='resize-handle']::after,\n [part~='resize-handle'][data-active]::after {\n opacity: 1;\n }\n [data-sort-col] {\n cursor: pointer;\n user-select: none;\n }\n [part~='header-cell']:focus-visible,\n [part~='cell']:focus-visible {\n outline: var(--argus-focus-ring, 2px solid Highlight);\n outline-offset: -2px;\n }\n /* A small chevron after the header content; only the sorted column shows it. */\n .sort-indicator {\n flex: none;\n inline-size: 0.35em;\n block-size: 0.35em;\n margin-inline-start: 0.4em;\n /* Physical sides on purpose: the chevron is rotated, so it must not mirror in RTL. */\n border-right: 0.12em solid currentColor;\n border-bottom: 0.12em solid currentColor;\n visibility: hidden;\n }\n [aria-sort] .sort-indicator {\n visibility: visible;\n }\n [aria-sort='ascending'] .sort-indicator {\n transform: translateY(0.1em) rotate(-135deg);\n }\n [aria-sort='descending'] .sort-indicator {\n transform: translateY(-0.1em) rotate(45deg);\n }\n [data-align='end'] .sort-indicator {\n order: -1;\n margin-inline: 0 0.4em;\n }\n /*\n * Pinned columns: sticky inside the scroll container, with offsets from the\n * table model. Logical insets make RTL work without extra rules.\n */\n [data-pin] {\n position: sticky;\n z-index: 1;\n
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../src/styles/base.ts"],"sourcesContent":["import { css } from 'lit';\n\n/**\n * Base styles. Everything sits in `@layer argus-base`, and every value reads a\n * public token with a neutral fallback, so themes and page CSS always win.\n * `--_*` properties are private.\n */\nexport const baseStyles = css`\n @layer argus-base {\n :host {\n display: block;\n /* A table that virtualizes needs a bounded height; set your own on the element. */\n block-size: 400px;\n box-sizing: border-box;\n contain: content;\n color-scheme: light dark;\n color: var(--argus-fg, CanvasText);\n background: var(--argus-bg, Canvas);\n font: var(--argus-font, inherit);\n --_row-height: var(--argus-row-height, 40px);\n --_pad: var(--argus-cell-padding-inline, 12px);\n --_border: var(--argus-border-width, 1px) solid\n var(--argus-border-color, light-dark(#e5e7eb, #374151));\n /* 1 in LTR, -1 in RTL: mirrors the pinned-edge shadow. */\n --_dir: 1;\n }\n :host(:dir(rtl)) {\n --_dir: -1;\n }\n :host([density='compact']) {\n --_row-height: var(--argus-row-height, 32px);\n --_pad: var(--argus-cell-padding-inline, 8px);\n }\n :host([density='spacious']) {\n --_row-height: var(--argus-row-height, 52px);\n --_pad: var(--argus-cell-padding-inline, 16px);\n }\n :host([hidden]) {\n display: none;\n }\n\n [part~='table'] {\n position: relative;\n block-size: 100%;\n overflow: auto;\n overscroll-behavior: contain;\n /* A cell moved to by keyboard scrolls clear of the sticky header and pinned columns. */\n scroll-padding-block-start: var(--_row-height);\n scroll-padding-inline: var(--_pin-start, 0) var(--_pin-end, 0);\n }\n\n .row {\n display: grid;\n grid-template-columns: var(--_cols);\n inline-size: var(--_width);\n min-inline-size: 100%;\n block-size: var(--_row-height);\n box-sizing: border-box;\n }\n\n [part~='header'] {\n position: sticky;\n inset-block-start: 0;\n z-index: 2;\n }\n\n [part~='body'] {\n position: relative;\n contain: layout;\n }\n [part~='row'] {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n --_row-bg: var(--argus-bg, Canvas);\n --_row-surface: var(--_row-bg);\n background: var(--_row-surface);\n }\n [part~='row-odd'] {\n --_row-bg: var(--argus-row-stripe-bg, var(--argus-bg, Canvas));\n }\n [part~='row']:hover {\n --_row-surface: var(--argus-row-hover-bg, var(--_row-bg));\n }\n\n [part~='cell'],\n [part~='header-cell'] {\n display: flex;\n align-items: center;\n min-inline-size: 0;\n box-sizing: border-box;\n padding-inline: var(--_pad);\n overflow: hidden;\n white-space: nowrap;\n border-block-end: var(--_border);\n }\n [part~='header-cell'] {\n background: var(--argus-header-bg, light-dark(#f9fafb, #111827));\n color: var(--argus-header-fg, inherit);\n font-weight: 600;\n }\n [part~='header-cell'] {\n position: relative;\n }\n [part~='resize-handle'] {\n position: absolute;\n inset-block: 0;\n inset-inline-end: 0;\n inline-size: 8px;\n z-index: 1;\n cursor: col-resize;\n touch-action: none;\n }\n [part~='resize-handle']::after {\n content: '';\n position: absolute;\n inset-block: 25%;\n inset-inline-end: 0;\n inline-size: 2px;\n background: var(--argus-resize-handle-color, light-dark(#9ca3af, #6b7280));\n opacity: 0;\n }\n [part~='header-cell']:hover [part~='resize-handle']::after,\n [part~='header-cell']:focus-visible [part~='resize-handle']::after,\n [part~='resize-handle'][data-active]::after {\n opacity: 1;\n }\n [data-sort-col] {\n cursor: pointer;\n user-select: none;\n }\n [part~='header-cell']:focus-visible,\n [part~='cell']:focus-visible {\n outline: var(--argus-focus-ring, 2px solid Highlight);\n outline-offset: -2px;\n }\n /* A small chevron after the header content; only the sorted column shows it. */\n .sort-indicator {\n flex: none;\n inline-size: 0.35em;\n block-size: 0.35em;\n margin-inline-start: 0.4em;\n /* Physical sides on purpose: the chevron is rotated, so it must not mirror in RTL. */\n border-right: 0.12em solid currentColor;\n border-bottom: 0.12em solid currentColor;\n visibility: hidden;\n }\n [aria-sort] .sort-indicator {\n visibility: visible;\n }\n [aria-sort='ascending'] .sort-indicator {\n transform: translateY(0.1em) rotate(-135deg);\n }\n [aria-sort='descending'] .sort-indicator {\n transform: translateY(-0.1em) rotate(45deg);\n }\n [data-align='end'] .sort-indicator {\n order: -1;\n margin-inline: 0 0.4em;\n }\n /*\n * Pinned columns: sticky inside the scroll container, with offsets from the\n * table model. Logical insets make RTL work without extra rules.\n */\n [data-pin] {\n position: sticky;\n z-index: 1;\n }\n [data-pin='start'] {\n inset-inline-start: var(--_pin-offset);\n }\n [data-pin='end'] {\n inset-inline-end: var(--_pin-offset);\n }\n /*\n * Pinned body cells are sticky only while the table scrolls sideways\n * (data-hscroll on the scroller). At rest they are plain cells shifted by the\n * horizontal scroll offset, which vertical scrolling never changes: a sticky\n * cell per row is a compositing layer each, and Safari stutters moving them\n * (PinScrollController). Both place the cells at the same spot.\n */\n [part~='row'] > [data-pin] {\n position: relative;\n inset-inline: auto;\n }\n [part~='row'] > [data-pin='start'] {\n transform: translateX(var(--_sx, 0px));\n }\n [part~='row'] > [data-pin='end'] {\n transform: translateX(calc(var(--_sx, 0px) - var(--_dir) * var(--_max-x, 0px)));\n }\n [data-hscroll] [part~='row'] > [data-pin] {\n position: sticky;\n transform: none;\n }\n [data-hscroll] [part~='row'] > [data-pin='start'] {\n inset-inline-start: var(--_pin-offset);\n }\n [data-hscroll] [part~='row'] > [data-pin='end'] {\n inset-inline-end: var(--_pin-offset);\n }\n [part~='cell-pinned'] {\n background: var(--_row-surface);\n }\n /*\n * The edge shadow, once per pinned side (.pin-shadow, beside the scroller).\n * --argus-pinned-shadow is written for a shadow falling toward the end side;\n * the start edge in RTL and the end edge in LTR mirror it.\n */\n .pin-shadow {\n position: absolute;\n inset-block-start: 0;\n block-size: min(var(--_view-h, 100%), var(--_content-h, 100%));\n z-index: 3;\n pointer-events: none;\n }\n .pin-shadow[data-side='start'] {\n inset-inline-start: 0;\n }\n .pin-shadow[data-side='end'] {\n inset-inline-end: var(--_sb-x, 0px);\n }\n .pin-shadow::after {\n content: '';\n position: absolute;\n inset: 0;\n box-shadow: var(\n --argus-pinned-shadow,\n 4px 0 6px -4px light-dark(rgb(0 0 0 / 0.2), rgb(0 0 0 / 0.6))\n );\n transform: scaleX(var(--_dir));\n }\n .pin-shadow[data-side='end']::after {\n transform: scaleX(calc(-1 * var(--_dir)));\n }\n\n [data-align='center'] {\n justify-content: center;\n text-align: center;\n }\n [data-align='end'] {\n justify-content: flex-end;\n text-align: end;\n }\n ::slotted(*) {\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n [part~='empty'] {\n display: flex;\n align-items: center;\n justify-content: center;\n min-block-size: calc(100% - var(--_row-height));\n padding: var(--_pad);\n box-sizing: border-box;\n }\n\n /* Measures the resolved row height in px for the virtualizer. */\n .probe {\n position: absolute;\n inset-block-start: 0;\n inline-size: 0;\n block-size: var(--_row-height);\n visibility: hidden;\n pointer-events: none;\n }\n }\n`;\n"],"mappings":";;;;;;;AAOA,IAAa,aAAa,GAAG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "argus-table",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "A framework-agnostic data table web component built with Lit and TanStack.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
41
41
|
"@size-limit/file": "^14.3.0",
|
|
42
42
|
"@vitest/browser-playwright": "^5.1.0",
|
|
43
|
+
"lightningcss": "1.33.0",
|
|
43
44
|
"playwright": "1.63.0",
|
|
44
45
|
"publint": "^0.3.25",
|
|
45
46
|
"rollup": "~4.64.12",
|