flexdesk 0.6.0 → 0.6.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/README.md +33 -0
- package/css/base.css +33 -0
- package/css/flexdesk.css +33 -0
- package/dist/charts.js +2 -2
- package/dist/{chunk-KH2GJTKR.js → chunk-IYV5JVYB.js} +29 -1
- package/dist/chunk-IYV5JVYB.js.map +7 -0
- package/dist/{chunk-CNXP3SIN.js → chunk-ZMCRDOCF.js} +2 -2
- package/dist/flexdesk.css +33 -0
- package/dist/flow.js +1 -1
- package/dist/tiles.js +2 -2
- package/dist/widgets.js +81 -1
- package/dist/widgets.js.map +3 -3
- package/package.json +1 -1
- package/src/ui/components/data_table.js +40 -0
- package/src/ui/utils/scroll_lock.js +111 -0
- package/dist/chunk-KH2GJTKR.js.map +0 -7
- /package/dist/{chunk-CNXP3SIN.js.map → chunk-ZMCRDOCF.js.map} +0 -0
package/README.md
CHANGED
|
@@ -321,6 +321,39 @@ behaves as it did in 0.4, except for these:
|
|
|
321
321
|
to it if it is in a cascade layer, or has zero specificity and loads before
|
|
322
322
|
`flexdesk.css`. A rule for the cells, the rows or the table does not.
|
|
323
323
|
|
|
324
|
+
### Comparing two tables: `diff` and `createScrollLock`
|
|
325
|
+
|
|
326
|
+
A side-by-side comparison is two `DataTable`s drawn from ONE aligned row list:
|
|
327
|
+
the rows as they are on one side, as they were on the other, and a gap where
|
|
328
|
+
a side has no row. Both options are off until you set them.
|
|
329
|
+
|
|
330
|
+
```js
|
|
331
|
+
import { DataTable, createScrollLock } from '@flexdesk/widgets';
|
|
332
|
+
|
|
333
|
+
const lock = createScrollLock({ axis: 'y' }); // 'x', 'y' or 'both'
|
|
334
|
+
const side = (host, pick) => DataTable.mount(host, {
|
|
335
|
+
headers, rows: aligned.map((r) => pick(r) ?? blank), pagination: false,
|
|
336
|
+
scrollLock: lock,
|
|
337
|
+
diff: (_row, i) => ({ status: statusOf(aligned[i], pick), cells: changedCols(aligned[i]) }),
|
|
338
|
+
});
|
|
339
|
+
side(left, (r) => r.now);
|
|
340
|
+
side(right, (r) => r.then);
|
|
341
|
+
lock.setEnabled(false); // scroll each side on its own; true brings them level
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
- `diff: (row, rowIdx) => {status, cells?}` draws the row
|
|
345
|
+
`twm-dt-diff twm-dt-diff--<status>`, for `added`, `removed`, `changed`,
|
|
346
|
+
`same` or `gap`, and each column index in `cells` (an array or a Set)
|
|
347
|
+
`twm-dt-diff__cell--changed`. `null` or any other status draws the row as
|
|
348
|
+
without `diff`. A `gap` row's cells are drawn empty, whatever its values and
|
|
349
|
+
`renderCell`, so the two sides keep their rows level. `updateRow` asks again.
|
|
350
|
+
- `scrollLock` takes a group from `createScrollLock()`. The table's body box
|
|
351
|
+
joins it on every render and leaves on the next render and on `dispose()`.
|
|
352
|
+
Any element can join with `lock.attach(el)`, which returns its detach. A
|
|
353
|
+
box that joins starts at the group's last position, and the echo of a
|
|
354
|
+
position the lock itself wrote moves nobody back. The lock copies pixels, so
|
|
355
|
+
it lines rows up only when both sides draw rows of the same height.
|
|
356
|
+
|
|
324
357
|
### Scrollbars
|
|
325
358
|
|
|
326
359
|
`installAutoScrollbars(root)` (from `@flexdesk/widgets`) replaces the browser's
|
package/css/base.css
CHANGED
|
@@ -686,6 +686,39 @@ body.twm-dt-col-resizing {
|
|
|
686
686
|
color: var(--text-dim, #666);
|
|
687
687
|
font-style: italic;
|
|
688
688
|
}
|
|
689
|
+
/* 0.6.1 `diff` — one side of a comparison. A row that is NEW on this side is
|
|
690
|
+
* green, one that is GONE on this side red, a changed row carries an amber
|
|
691
|
+
* left edge and its changed cells an amber fill, and a GAP — the place a row
|
|
692
|
+
* holds on the other side — is hatched, so the eye reads it as "nothing here"
|
|
693
|
+
* rather than as an empty row. (0,3,2) and up, for `twm-dt-cell--null`'s
|
|
694
|
+
* reason above; a selected row keeps its own colour (`:not(.selected)`). */
|
|
695
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--added:not(.selected) > td {
|
|
696
|
+
background: color-mix(in srgb, var(--color-success, #3fb950) 16%, transparent);
|
|
697
|
+
}
|
|
698
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--removed:not(.selected) > td {
|
|
699
|
+
background: color-mix(in srgb, var(--color-danger, #c74e4e) 18%, transparent);
|
|
700
|
+
}
|
|
701
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--removed > td {
|
|
702
|
+
text-decoration: line-through;
|
|
703
|
+
text-decoration-color: color-mix(in srgb, var(--color-danger, #c74e4e) 70%, transparent);
|
|
704
|
+
}
|
|
705
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--added > td:first-child {
|
|
706
|
+
box-shadow: inset 3px 0 0 0 var(--color-success, #3fb950);
|
|
707
|
+
}
|
|
708
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--removed > td:first-child {
|
|
709
|
+
box-shadow: inset 3px 0 0 0 var(--color-danger, #c74e4e);
|
|
710
|
+
}
|
|
711
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--changed > td:first-child {
|
|
712
|
+
box-shadow: inset 3px 0 0 0 var(--color-warning, #ff9800);
|
|
713
|
+
}
|
|
714
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff:not(.selected) > td.twm-dt-diff__cell--changed {
|
|
715
|
+
background: color-mix(in srgb, var(--color-warning, #ff9800) 22%, transparent);
|
|
716
|
+
}
|
|
717
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--gap > td.twm-dt-diff__gap {
|
|
718
|
+
background: repeating-linear-gradient(135deg,
|
|
719
|
+
transparent 0 6px,
|
|
720
|
+
color-mix(in srgb, var(--text-muted, #888) 14%, transparent) 6px 7px);
|
|
721
|
+
}
|
|
689
722
|
/* `emptyState` and `setError` with no rows: the box's own words, which may
|
|
690
723
|
* wrap — muted for an empty state, red for a failure, in both densities. Quiet
|
|
691
724
|
* while loading — an empty table that is still loading is not empty. */
|
package/css/flexdesk.css
CHANGED
|
@@ -713,6 +713,39 @@ body.twm-dt-col-resizing {
|
|
|
713
713
|
color: var(--text-dim, #666);
|
|
714
714
|
font-style: italic;
|
|
715
715
|
}
|
|
716
|
+
/* 0.6.1 `diff` — one side of a comparison. A row that is NEW on this side is
|
|
717
|
+
* green, one that is GONE on this side red, a changed row carries an amber
|
|
718
|
+
* left edge and its changed cells an amber fill, and a GAP — the place a row
|
|
719
|
+
* holds on the other side — is hatched, so the eye reads it as "nothing here"
|
|
720
|
+
* rather than as an empty row. (0,3,2) and up, for `twm-dt-cell--null`'s
|
|
721
|
+
* reason above; a selected row keeps its own colour (`:not(.selected)`). */
|
|
722
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--added:not(.selected) > td {
|
|
723
|
+
background: color-mix(in srgb, var(--color-success, #3fb950) 16%, transparent);
|
|
724
|
+
}
|
|
725
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--removed:not(.selected) > td {
|
|
726
|
+
background: color-mix(in srgb, var(--color-danger, #c74e4e) 18%, transparent);
|
|
727
|
+
}
|
|
728
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--removed > td {
|
|
729
|
+
text-decoration: line-through;
|
|
730
|
+
text-decoration-color: color-mix(in srgb, var(--color-danger, #c74e4e) 70%, transparent);
|
|
731
|
+
}
|
|
732
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--added > td:first-child {
|
|
733
|
+
box-shadow: inset 3px 0 0 0 var(--color-success, #3fb950);
|
|
734
|
+
}
|
|
735
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--removed > td:first-child {
|
|
736
|
+
box-shadow: inset 3px 0 0 0 var(--color-danger, #c74e4e);
|
|
737
|
+
}
|
|
738
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--changed > td:first-child {
|
|
739
|
+
box-shadow: inset 3px 0 0 0 var(--color-warning, #ff9800);
|
|
740
|
+
}
|
|
741
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff:not(.selected) > td.twm-dt-diff__cell--changed {
|
|
742
|
+
background: color-mix(in srgb, var(--color-warning, #ff9800) 22%, transparent);
|
|
743
|
+
}
|
|
744
|
+
.twm-data-table-component .twm-preview-table tbody tr.twm-dt-diff--gap > td.twm-dt-diff__gap {
|
|
745
|
+
background: repeating-linear-gradient(135deg,
|
|
746
|
+
transparent 0 6px,
|
|
747
|
+
color-mix(in srgb, var(--text-muted, #888) 14%, transparent) 6px 7px);
|
|
748
|
+
}
|
|
716
749
|
/* `emptyState` and `setError` with no rows: the box's own words, which may
|
|
717
750
|
* wrap — muted for an empty state, red for a failure, in both densities. Quiet
|
|
718
751
|
* while loading — an empty table that is still loading is not empty. */
|
package/dist/charts.js
CHANGED
|
@@ -39,10 +39,10 @@ import {
|
|
|
39
39
|
setPlotlySource,
|
|
40
40
|
uid,
|
|
41
41
|
updateChart
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-ZMCRDOCF.js";
|
|
43
43
|
import {
|
|
44
44
|
createRafResizeObserver
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-IYV5JVYB.js";
|
|
46
46
|
import {
|
|
47
47
|
ManagedWindow
|
|
48
48
|
} from "./chunk-6JSOVNID.js";
|
|
@@ -360,6 +360,7 @@ function matchDurationFilter(ms, filterText, shown) {
|
|
|
360
360
|
|
|
361
361
|
// src/ui/components/data_table.js
|
|
362
362
|
var DEFAULT_PAGE_SIZE = 100;
|
|
363
|
+
var DIFF_STATUSES = /* @__PURE__ */ new Set(["added", "removed", "changed", "same", "gap"]);
|
|
363
364
|
var TYPED_COLUMNS = /* @__PURE__ */ new Set(["date", "datetime", "duration"]);
|
|
364
365
|
var DESELECTING_CLICKS = /* @__PURE__ */ new WeakSet();
|
|
365
366
|
var AUTO_DISPOSE = /* @__PURE__ */ new Map();
|
|
@@ -531,6 +532,8 @@ var DataTable = class {
|
|
|
531
532
|
rowClass: null,
|
|
532
533
|
rowAttrs: null,
|
|
533
534
|
cellClass: null,
|
|
535
|
+
diff: null,
|
|
536
|
+
scrollLock: null,
|
|
534
537
|
rowIcon: null,
|
|
535
538
|
emptyState: void 0,
|
|
536
539
|
nullDisplay: null,
|
|
@@ -1009,6 +1012,9 @@ var DataTable = class {
|
|
|
1009
1012
|
}
|
|
1010
1013
|
this._tableWrapEl = tableWrap;
|
|
1011
1014
|
this.container.appendChild(this._wrapperEl);
|
|
1015
|
+
if (this.config.scrollLock && typeof this.config.scrollLock.attach === "function") {
|
|
1016
|
+
this._disposers.push(this.config.scrollLock.attach(tableWrap));
|
|
1017
|
+
}
|
|
1012
1018
|
if (this.config.autoDispose) autoDisposeWatch(this);
|
|
1013
1019
|
if (prevScroll && this._state.offset === this._renderedOffset) {
|
|
1014
1020
|
tableWrap.scrollTop = prevScroll.top;
|
|
@@ -2411,11 +2417,20 @@ var DataTable = class {
|
|
|
2411
2417
|
tr.appendChild(td);
|
|
2412
2418
|
}
|
|
2413
2419
|
const { cellClass, nullDisplay } = this.config;
|
|
2420
|
+
const diff = this._diffOf(row, globalIdx);
|
|
2421
|
+
const gap = diff?.status === "gap";
|
|
2422
|
+
const changedCells = diff?.cells ? diff.cells instanceof Set ? diff.cells : new Set(diff.cells) : null;
|
|
2414
2423
|
for (let colIdx = 0; colIdx < row.length; colIdx++) {
|
|
2415
2424
|
const td = document.createElement("td");
|
|
2416
2425
|
td.className = this._columnTypes[colIdx] || "text";
|
|
2417
2426
|
const value = row[colIdx];
|
|
2418
2427
|
let handled = false;
|
|
2428
|
+
if (gap) {
|
|
2429
|
+
td.classList.add("twm-dt-diff__gap");
|
|
2430
|
+
tr.appendChild(td);
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
if (changedCells?.has(colIdx)) td.classList.add("twm-dt-diff__cell--changed");
|
|
2419
2434
|
if (this.config.renderCell) {
|
|
2420
2435
|
handled = !!this.config.renderCell(td, value, colIdx, globalIdx, row);
|
|
2421
2436
|
if (!handled) {
|
|
@@ -2682,6 +2697,14 @@ var DataTable = class {
|
|
|
2682
2697
|
}
|
|
2683
2698
|
return null;
|
|
2684
2699
|
}
|
|
2700
|
+
/** 0.6.1. The `diff` answer for one row, or `null` when there is none or
|
|
2701
|
+
* its status is not one this component draws. */
|
|
2702
|
+
_diffOf(row, rowIdx) {
|
|
2703
|
+
if (typeof this.config.diff !== "function") return null;
|
|
2704
|
+
const answer = this.config.diff(row, rowIdx);
|
|
2705
|
+
if (!answer || !DIFF_STATUSES.has(answer.status)) return null;
|
|
2706
|
+
return answer;
|
|
2707
|
+
}
|
|
2685
2708
|
/** Everything `rowClass`, `rowAttrs` and the open mark put on a `<tr>`,
|
|
2686
2709
|
* taken off again first — so `updateRow` cannot leave last state's class
|
|
2687
2710
|
* behind. */
|
|
@@ -2696,6 +2719,11 @@ var DataTable = class {
|
|
|
2696
2719
|
const added = this._addClasses(tr, rowClass(row, rowIdx));
|
|
2697
2720
|
tr.__twmClasses = added.filter((c) => c !== "data-preview-row" && c !== "selected");
|
|
2698
2721
|
}
|
|
2722
|
+
for (const c of [...tr.classList]) {
|
|
2723
|
+
if (c === "twm-dt-diff" || c.startsWith("twm-dt-diff--")) tr.classList.remove(c);
|
|
2724
|
+
}
|
|
2725
|
+
const diff = this._diffOf(row, rowIdx);
|
|
2726
|
+
if (diff) tr.classList.add("twm-dt-diff", `twm-dt-diff--${diff.status}`);
|
|
2699
2727
|
if (tr.__twmAttrs) {
|
|
2700
2728
|
for (const name of tr.__twmAttrs) tr.removeAttribute(name);
|
|
2701
2729
|
tr.__twmAttrs = null;
|
|
@@ -3699,4 +3727,4 @@ export {
|
|
|
3699
3727
|
matchDurationFilter,
|
|
3700
3728
|
DataTable
|
|
3701
3729
|
};
|
|
3702
|
-
//# sourceMappingURL=chunk-
|
|
3730
|
+
//# sourceMappingURL=chunk-IYV5JVYB.js.map
|