@rozie-ui/data-table-lit 0.2.4 → 0.2.6

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.
@@ -42,6 +42,9 @@ export default class DetailPanel extends SignalWatcher(LitElement) {
42
42
  `;
43
43
  }
44
44
 
45
+ // Plain setup-once helper (NOT $computed — a $computed can't be aliased; the
46
+ // EditorSelect plain-function lesson). Build `[{ key, value }]` from the row's own
47
+ // enumerable keys, String-coercing each value. A null row yields an empty list.
45
48
  entries = () => {
46
49
  const r = this.row;
47
50
  if (!r) return [];
@@ -54,6 +54,8 @@ export default class EditorCheckbox extends SignalWatcher(LitElement) {
54
54
  `;
55
55
  }
56
56
 
57
+ // Immediate-commit-on-change: read .checked the global-filter way, coerce to a
58
+ // real boolean, and commit it directly.
57
59
  onChange = (e: any) => {
58
60
  this.commit && this.commit(!!(e && e.target ? e.target.checked : false));
59
61
  };
@@ -64,6 +64,9 @@ export default class EditorNumber extends SignalWatcher(LitElement) {
64
64
  this._draft.value = e && e.target ? e.target.value : '';
65
65
  };
66
66
 
67
+ // Coerce to a Number at commit time. Defensive guard: an empty/whitespace draft
68
+ // commits null rather than NaN (Number('') === 0 is a silent footgun); a
69
+ // non-numeric draft also commits null. Otherwise commit the coerced number.
67
70
  doCommit = () => {
68
71
  if (!this.commit) return;
69
72
  const raw = this._draft.value;
@@ -49,8 +49,6 @@ export default class EditorSelect extends SignalWatcher(LitElement) {
49
49
  // Seed the draft once from the incoming value (setup-once). Normalize null/undefined
50
50
  // to '' so the <select> binds to a string.
51
51
  this._draft.value = this.value != null ? String(this.value) : '';
52
-
53
- // Picking/arrow-cycling an option updates the draft only — no commit.
54
52
  }
55
53
 
56
54
  disconnectedCallback(): void {
@@ -71,10 +69,12 @@ export default class EditorSelect extends SignalWatcher(LitElement) {
71
69
  `;
72
70
  }
73
71
 
72
+ // Picking/arrow-cycling an option updates the draft only — no commit.
74
73
  onChange = (e: any) => {
75
74
  this._draft.value = e && e.target ? e.target.value : '';
76
75
  };
77
76
 
77
+ // commit/cancel are Function props (default null) — guard before calling.
78
78
  doCommit = () => {
79
79
  this.commit && this.commit(this._draft.value);
80
80
  };
package/src/EditorText.ts CHANGED
@@ -50,9 +50,6 @@ private __rozieFirstUpdateDone = false;
50
50
  // template). Normalize null/undefined to '' so the input value binds to a string.
51
51
  this._draft.value = this.value != null ? String(this.value) : '';
52
52
 
53
- // Untyped handler param neutralizes to `any`, so reading e.target.value typechecks
54
- // ×6 (the global-filter idiom). Never inline `$data.x = $event.target.value`.
55
-
56
53
  if (this.autofocus) this._refInputEl?.focus();
57
54
  }
58
55
 
@@ -79,10 +76,13 @@ private __rozieFirstUpdateDone = false;
79
76
  `;
80
77
  }
81
78
 
79
+ // Untyped handler param neutralizes to `any`, so reading e.target.value typechecks
80
+ // ×6 (the global-filter idiom). Never inline `$data.x = $event.target.value`.
82
81
  onInput = (e: any) => {
83
82
  this._draft.value = e && e.target ? e.target.value : '';
84
83
  };
85
84
 
85
+ // commit/cancel are Function props (default null) — guard before calling.
86
86
  doCommit = () => {
87
87
  this.commit && this.commit(this._draft.value);
88
88
  };
@@ -41,8 +41,6 @@ export default class FilterNumberRange extends SignalWatcher(LitElement) {
41
41
  // Seed both drafts once at setup from the incoming [min,max] tuple (setup-once).
42
42
  this._minDraft.value = Array.isArray(this.value) && this.value[0] != null ? String(this.value[0]) : '';
43
43
  this._maxDraft.value = Array.isArray(this.value) && this.value[1] != null ? String(this.value[1]) : '';
44
-
45
- // Untyped handler params neutralize to `any` (the global-filter idiom).
46
44
  }
47
45
 
48
46
  disconnectedCallback(): void {
@@ -65,6 +63,7 @@ export default class FilterNumberRange extends SignalWatcher(LitElement) {
65
63
  `;
66
64
  }
67
65
 
66
+ // Untyped handler params neutralize to `any` (the global-filter idiom).
68
67
  onMinInput = (e: any) => {
69
68
  this._minDraft.value = e && e.target ? e.target.value : '';
70
69
  };
@@ -73,10 +72,14 @@ export default class FilterNumberRange extends SignalWatcher(LitElement) {
73
72
  this._maxDraft.value = e && e.target ? e.target.value : '';
74
73
  };
75
74
 
75
+ // Plain string-coercion functions for the placeholders (NOT $computed — the
76
+ // EditorSelect/listbox lesson; opaque slot-scope props rejected by strict leaf tsc).
76
77
  minPlaceholder = () => Array.isArray(this.minMax) && this.minMax[0] != null ? String(this.minMax[0]) : '';
77
78
 
78
79
  maxPlaceholder = () => Array.isArray(this.minMax) && this.minMax[1] != null ? String(this.minMax[1]) : '';
79
80
 
81
+ // Convert a draft to a Number or undefined (empty string → undefined so a
82
+ // one-sided range works). Both undefined → clear the filter.
80
83
  applyRange = () => {
81
84
  const minNum = this._minDraft.value === '' ? undefined : Number(this._minDraft.value);
82
85
  const maxNum = this._maxDraft.value === '' ? undefined : Number(this._maxDraft.value);
@@ -55,8 +55,14 @@ export default class FilterSelect extends SignalWatcher(LitElement) {
55
55
  `;
56
56
  }
57
57
 
58
+ // The <select> value binding coerced to a string. $props.value is typed `unknown`
59
+ // (opaque slot-scope), which the strict bundled-leaf tsc rejects against the native
60
+ // select `value` type on React/Solid — the fix is a plain function returning a
61
+ // string (uniform ×6, NOT a $computed; the EditorSelect/listbox value lesson).
58
62
  selectValue = () => this.value != null ? String(this.value) : '';
59
63
 
64
+ // Immediate-apply-on-change: read the selected value the global-filter way. An
65
+ // empty value (the leading "All" option) clears the column filter.
60
66
  onChange = (e: any) => {
61
67
  const v = e && e.target ? e.target.value : '';
62
68
  if (v === '') {
package/src/FilterText.ts CHANGED
@@ -35,9 +35,6 @@ export default class FilterText extends SignalWatcher(LitElement) {
35
35
  // Seed the draft once at setup from the incoming value (setup-once, NOT in the
36
36
  // template). Normalize null/undefined to '' so the input value binds to a string.
37
37
  this._draft.value = this.value != null ? String(this.value) : '';
38
-
39
- // Untyped handler param neutralizes to `any`, so reading e.target.value typechecks
40
- // ×6 (the global-filter idiom). Never inline `$data.x = $event.target.value`.
41
38
  }
42
39
 
43
40
  disconnectedCallback(): void {
@@ -56,10 +53,13 @@ export default class FilterText extends SignalWatcher(LitElement) {
56
53
  `;
57
54
  }
58
55
 
56
+ // Untyped handler param neutralizes to `any`, so reading e.target.value typechecks
57
+ // ×6 (the global-filter idiom). Never inline `$data.x = $event.target.value`.
59
58
  onInput = (e: any) => {
60
59
  this._draft.value = e && e.target ? e.target.value : '';
61
60
  };
62
61
 
62
+ // setFilter is a Function prop (default null) — guard before calling.
63
63
  applyFilter = () => {
64
64
  this.setFilter && this.setFilter(this.columnId, this._draft.value);
65
65
  };
package/src/GroupBar.ts CHANGED
@@ -123,34 +123,49 @@ export default class GroupBar extends SignalWatcher(LitElement) {
123
123
  `;
124
124
  }
125
125
 
126
+ // Untyped handler params neutralize to `any` so the native drag-event shapes
127
+ // (dataTransfer / preventDefault) typecheck across all six strict leaves — the
128
+ // global-filter idiom (see FilterText.rozie). NEVER annotate these params.
129
+ // A palette CHIP started dragging → this is an ADD-a-new-column drag.
126
130
  onChipDragStart = (e: any, id: any) => {
127
131
  this._draggingId.value = id;
128
132
  this._dragKind.value = 'chip';
129
133
  if (e && e.dataTransfer) e.dataTransfer.setData('text/plain', id);
130
134
  };
131
135
 
136
+ // An active TOKEN started dragging → this is a REORDER drag.
132
137
  onTokenDragStart = (e: any, gk: any) => {
133
138
  this._draggingId.value = gk;
134
139
  this._dragKind.value = 'token';
135
140
  if (e && e.dataTransfer) e.dataTransfer.setData('text/plain', gk);
136
141
  };
137
142
 
143
+ // MUST preventDefault — native HTML5 DnD never fires @drop on a zone that does not
144
+ // cancel the dragover default. Also raises the drop-target highlight.
138
145
  onDragOver = (e: any) => {
139
146
  if (e) e.preventDefault();
140
147
  this._isOver.value = true;
141
148
  };
142
149
 
150
+ // While reordering, record the token under the pointer as the insertion anchor
151
+ // (we drop BEFORE it). preventDefault so the zone still accepts the drop. Ignored
152
+ // for chip drags — those just append at the end.
143
153
  onTokenDragOver = (e: any, gk: any) => {
144
154
  if (e) e.preventDefault();
145
155
  if (this._dragKind.value === 'token') this._dropKey.value = gk;
146
156
  };
147
157
 
158
+ // Clear the highlight only on a REAL leave: dragleave ALSO fires when the pointer
159
+ // crosses onto a child token, so ignore leaves whose relatedTarget is still inside
160
+ // the zone (prevents flicker as you hover over existing grouping tokens).
148
161
  onDragLeave = (e: any) => {
149
162
  if (e && e.currentTarget && e.relatedTarget && e.currentTarget.contains(e.relatedTarget)) return;
150
163
  this._isOver.value = false;
151
164
  this._dropKey.value = '';
152
165
  };
153
166
 
167
+ // Single reset for all ephemeral drag bookkeeping — called on drop AND on dragend
168
+ // (so an aborted drag, dropped outside the zone, still clears the marker/highlight).
154
169
  resetDrag = () => {
155
170
  this._draggingId.value = '';
156
171
  this._dragKind.value = '';
@@ -199,6 +214,9 @@ export default class GroupBar extends SignalWatcher(LitElement) {
199
214
  this.clearGrouping && this.clearGrouping();
200
215
  };
201
216
 
217
+ // Resolve a grouping key to its column's friendly label (falls back to the raw
218
+ // key). Used for both the token text and the remove button's aria-label so the
219
+ // bar reads in human terms, not internal column ids. Untyped like the handlers.
202
220
  labelFor = (key: any) => {
203
221
  const col = this.groupableColumns.find((c: any) => c.id === key);
204
222
  return col && col.label || key;
@@ -24,6 +24,13 @@
24
24
  */
25
25
  .rozie-data-table-wrap,
26
26
  .rozie-data-table {
27
+ /* NOTE (see file-head comment): most of the tokens below are declared but
28
+ never read via a CSS var() inside THIS component's own scoped <style> —
29
+ that is by design. This is the public surface; the "Wire the PUBLIC
30
+ tokens..." block further down assigns each one onto the short internal
31
+ --rdt-* custom property the component actually reads. A future audit
32
+ must not delete these as dead — they are the intended public API. */
33
+
27
34
  /* ── palette (the few values most consumers will set) ───────────────────── */
28
35
  --rozie-data-table-fg: inherit;
29
36
  --rozie-data-table-accent: #0066cc;
@@ -62,6 +69,14 @@
62
69
  /* ── disabled pagination controls ──────────────────────────────────────── */
63
70
  --rozie-data-table-disabled-opacity: 0.4;
64
71
 
72
+ /* ── virtual scroll (row windowing, req-3/D-06) ─────────────────────────── */
73
+ /* Read with NO fallback at its one use site (`max-height: var(--rozie-data-table-max-height);`
74
+ on `.rdt-scroll`) — when unset the property is invalid at computed-value
75
+ time and the rule behaves as if `max-height` were never declared there,
76
+ i.e. `none` (unbounded). Declaring `none` here is the render-neutral
77
+ equivalent of that unset behavior, made explicit and overridable. */
78
+ --rozie-data-table-max-height: none;
79
+
65
80
  /* ─────────────────────────────────────────────────────────────────────────
66
81
  * Wire the PUBLIC tokens onto the INTERNAL `--rdt-*` the component reads. A
67
82
  * bridge only needs to remap the public tokens above; these passthroughs do