flexdesk 0.4.6 → 0.5.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/README.md +140 -0
- package/css/base.css +123 -2
- package/css/flexdesk.css +127 -4
- package/css/overrides.css +4 -2
- package/css/tokens.css +14 -0
- package/dist/charts.js +3 -3
- package/dist/{chunk-ELXVW542.js → chunk-24MS7W5Y.js} +2 -2
- package/dist/{chunk-LH5TSOZW.js → chunk-6JSOVNID.js} +42 -2
- package/dist/{chunk-LH5TSOZW.js.map → chunk-6JSOVNID.js.map} +3 -3
- package/dist/{chunk-HWCX5I4R.js → chunk-CNXP3SIN.js} +3 -3
- package/dist/{chunk-CYTWMP2Y.js → chunk-KH2GJTKR.js} +1112 -32
- package/dist/chunk-KH2GJTKR.js.map +7 -0
- package/dist/flexdesk.css +127 -4
- package/dist/tiles.js +3 -3
- package/dist/tokens.css +14 -0
- package/dist/widgets.js +31 -5
- package/dist/widgets.js.map +1 -1
- package/dist/wm.js +79 -19
- package/dist/wm.js.map +3 -3
- package/package.json +1 -1
- package/src/tiling/landing_table.js +13 -5
- package/src/tiling/tile_breadcrumb.js +21 -0
- package/src/tiling/tile_renderer.js +11 -0
- package/src/tiling/wm.js +62 -18
- package/src/ui/components/data_table.js +961 -37
- package/src/ui/components/row_activation.js +70 -0
- package/src/ui/components/table_values.js +423 -0
- package/dist/chunk-CYTWMP2Y.js.map +0 -7
- /package/dist/{chunk-ELXVW542.js.map → chunk-24MS7W5Y.js.map} +0 -0
- /package/dist/{chunk-HWCX5I4R.js.map → chunk-CNXP3SIN.js.map} +0 -0
package/README.md
CHANGED
|
@@ -163,6 +163,22 @@ anywhere else, it walks the focused tile as before. With `backToOpenList`, a
|
|
|
163
163
|
record in a window closes onto an open list of its section, first among the
|
|
164
164
|
window's own tabs and then in any tile of its desktop.
|
|
165
165
|
|
|
166
|
+
**A page's own levels come first** (0.4.7). A page can have levels that are not
|
|
167
|
+
tabs, such as a section whose list opens one member in place of itself. Return
|
|
168
|
+
`navigateBack()` from the content factory's mount, and Backspace asks it before
|
|
169
|
+
anything above: it climbs one of the page's own levels and returns `true`. Only
|
|
170
|
+
a strict `true` counts. `false`, any other value, a missing function or a throw
|
|
171
|
+
(logged as a warning) all let Back walk the tab history, the open list and the
|
|
172
|
+
taxonomy exactly as before. It is asked in the same place as the rest of Back:
|
|
173
|
+
the window you last clicked in, else the focused tile. Return
|
|
174
|
+
`canNavigateBack()` as well, and `wm.canNavigateBack()` reports the page's own
|
|
175
|
+
levels, for that same window or tile. `TileRenderer.contentOf(leafId)` reads
|
|
176
|
+
what a tile's factory returned. To draw the page's levels, call
|
|
177
|
+
`renderBreadcrumb(container, segments)` from `@flexdesk/wm`, with
|
|
178
|
+
`segments = [{ label, icon?, onClick? }]`. It uses the tile breadcrumb's
|
|
179
|
+
markup, so it looks the same, and each `onClick` is yours: the last segment,
|
|
180
|
+
and any without `onClick`, is drawn as plain text.
|
|
181
|
+
|
|
166
182
|
### Column sizing
|
|
167
183
|
|
|
168
184
|
`fitColumns(columns, avail)` (from `@flexdesk/widgets`) turns per-column
|
|
@@ -175,6 +191,130 @@ then text shrinks to a floor. The widths are fractional and add up to `avail`
|
|
|
175
191
|
exactly whenever the floors fit. `DataTable` does not use it yet; an embedder
|
|
176
192
|
that measures its own columns can.
|
|
177
193
|
|
|
194
|
+
### Sorting a paged DataTable
|
|
195
|
+
|
|
196
|
+
A client-paged `DataTable` (no `onPageChange`) keeps its page when it is
|
|
197
|
+
sorted, so a sort on page 3 shows rows 201–300 of the new order. Pass
|
|
198
|
+
`resetPageOnSort: true` (0.4.7) to start every new order at the first page, a
|
|
199
|
+
header click included, the way a new filter already does. It is off by default
|
|
200
|
+
because keeping the page is what every earlier release did. Server-side paging
|
|
201
|
+
is not affected: its page is your `offset`, which a sort never changes.
|
|
202
|
+
|
|
203
|
+
### DataTable options in 0.5.0: lists that need no help
|
|
204
|
+
|
|
205
|
+
0.5.0 adds options for the ordinary list in an application: one that sits in a
|
|
206
|
+
page, opens a row, marks the open one, and sorts dates as dates. Every option
|
|
207
|
+
is off until you set it. A table that sets none of them looks and behaves as it
|
|
208
|
+
did in 0.4, apart from the few changes listed at the end of this section. This
|
|
209
|
+
was measured in headless Edge: 64 cells in 7 default tables, an empty and a
|
|
210
|
+
loading one among them, computed the same under 0.4.7 and 0.5.0.
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
import { DataTable } from '@flexdesk/widgets';
|
|
214
|
+
|
|
215
|
+
// House rules once, then one call per list: build AND draw.
|
|
216
|
+
const ListTable = DataTable.withDefaults({
|
|
217
|
+
mode: 'compact', sortable: true, pagination: false,
|
|
218
|
+
fitContent: true, firstColumn: 'plain', autoDispose: true,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const table = ListTable.mount(host, {
|
|
222
|
+
headers: ['Name', 'Role', 'Last seen', 'Took'],
|
|
223
|
+
rows, // values, not formatted text
|
|
224
|
+
columnTypes: [null, null, 'datetime', 'duration'],
|
|
225
|
+
getRowKey: (row) => row[0],
|
|
226
|
+
activeRow: openName,
|
|
227
|
+
onRowActivate: (i, row) => open(row), // one click opens
|
|
228
|
+
rowClass: (row) => (row[1] === 'Suspended' ? 'is-off' : null),
|
|
229
|
+
emptyState: 'Nobody yet.',
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Layout**
|
|
234
|
+
|
|
235
|
+
| Option | What it does |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `fitContent: true` | Makes the table as tall as its rows. By default the wrapper is `height: 100%`. In a parent without a height (an ordinary block in a page that scrolls) that resolves to nothing, so the header draws and the rows do not. |
|
|
238
|
+
| `maxHeight: 240` | Implies `fitContent` and caps the table at this height. A number is px; a string is used as CSS. Past the cap, the body scrolls under its header. |
|
|
239
|
+
| `firstColumn: 'plain'` | Styles the first column like the others. By default it is styled as a time column: 120–150px wide, grey, weight 500. |
|
|
240
|
+
| `--twm-dt-cell-pad-block`, `--twm-dt-cell-pad-inline`, `--twm-dt-cell-line-height` | Three CSS tokens. Set them on `:root` or on any ancestor to give both densities one row height. They are unset by default, and then each density keeps its own 0.4 values. In Edge, with the tokens set to `2px`, `6px` and `1.35`, normal and compact rows both measured 21.19px. Without them, normal rows measured 27px. The normal density reads the line-height token on the table's `thead`/`tbody` and the cells inherit it, so your own line-height for the cells, the rows or the table still wins, from a cascade layer too. |
|
|
241
|
+
|
|
242
|
+
**Opening a row, and which one is open**
|
|
243
|
+
|
|
244
|
+
| Option | What it does |
|
|
245
|
+
|---|---|
|
|
246
|
+
| `onRowActivate(rowIdx, row, ev)` | Opens a row. It fires after the selection has been updated (`onRowClick` still fires before it, as in 0.4). It does not fire for a press on a control in the row (a button, link, field, label, `<summary>`, editable text, or `[data-twm-action]`), for a click that ends a drag selecting text in the table, for the second click of a double-click, or for the Ctrl click that takes a `selectable: 'single'` table's row off the selection. `ev.type` tells you which gesture fired it. If your `onSelectionChange` or `onRowClick` redraws the table (`setData`, `render`), the row that was pressed still opens; `row` is that row as it was drawn. |
|
|
247
|
+
| `activateOn` | Which gestures open a row: `'click'` (the default), `'dblclick'`, `'enter'`, or an array of them. A pick list, where one click selects, uses `['dblclick', 'enter']`. Enter opens the selected row (the one row selected, or the selection's anchor), which is the row a double-click on it would open. If nothing is selected, Enter opens the active row. |
|
|
248
|
+
| `clickable` | Adds `twm-dt--clickable` to the table, which gives rows a pointer cursor and a hover, readonly tables included. It defaults to on exactly when `onRowActivate` is set. |
|
|
249
|
+
| `selectable: 'single'` | Lets a click select exactly one row. Shift and Ctrl do not extend the selection, Ctrl+A selects nothing, and `setSelection` keeps the last index it is given. |
|
|
250
|
+
| `getRowKey(row, idx)`, `activeRow`, `setActiveRow(key)`, `getActiveRow()` | Mark the open row: `twm-dt-row--active` and `aria-current="true"`, drawn as an accent on the left edge. The mark is not the selection. It survives `setData`, sorting, filtering and paging, and a right-click does not move it. |
|
|
251
|
+
|
|
252
|
+
The landing helper `attachLandingTableBehavior` now uses the same rule as
|
|
253
|
+
`onRowActivate` to decide whether a click opens a row. That changes three
|
|
254
|
+
things, with no option (see the list at the end of this section).
|
|
255
|
+
|
|
256
|
+
**Row state**
|
|
257
|
+
|
|
258
|
+
| Option | What it does |
|
|
259
|
+
|---|---|
|
|
260
|
+
| `rowClass(row, idx)`, `rowAttrs(row, idx)` | Add classes and attributes to the row's `<tr>`. They follow the row through sorts and filters, and `updateRow` asks again, so a class the row no longer earns is removed. |
|
|
261
|
+
| `cellClass(value, colIdx, row, idx)` | Adds classes to a cell, after its type class. |
|
|
262
|
+
| `rowIcon(row, idx)` | Draws an icon (`'name'` or `{ icon, title, tone }`) at the start of the first cell. It is drawn from an attribute, so the icon's name is not part of the cell's text, its tooltip or a copy. |
|
|
263
|
+
| `emptyState` | Text, a node, or a function, shown in place of `emptyMessage`. It is hidden while `setLoading(true)` is on. |
|
|
264
|
+
| `setError(message \| node \| Error \| null)` | Shows a failure inside the table. If there are rows on screen, they stay, under a `role="alert"` banner. If there are none, the failure takes the place of the empty row. `setData({ rows })` and `setError(null)` clear it. |
|
|
265
|
+
| `showRowNumbers: 'position'` | Numbers rows 1, 2, 3 in the order shown, after a sort, a filter, or on a later page. `true` still numbers rows by their original index. |
|
|
266
|
+
| `nullDisplay: 'NULL'` | Draws `null` and `undefined` as this text, adds `twm-dt-cell--null` to the cell, and copies the text as well. |
|
|
267
|
+
|
|
268
|
+
**Values, not text**
|
|
269
|
+
|
|
270
|
+
| Option | What it does |
|
|
271
|
+
|---|---|
|
|
272
|
+
| `columnTypes: [..., 'date' \| 'datetime' \| 'duration']` | Sorts and filters the column by its value: an ISO 8601 string, a `Date` or epoch ms for dates, and milliseconds or text such as `'3.2 s'` for durations. A date-only value such as `2026-10-05` is a calendar date, so the time zone never moves it. Any other text, and a date that does not exist (`2026-02-31`), is shown as it came and sorts last. Non-ISO text is not passed to `Date.parse`, because browsers read it differently (V8 reads `'12'` as a date in 2001). Only `columnTypes` makes a column a typed column. A `getColumnType` that returns `'date'` still sets only the cell's class name, as in 0.4. |
|
|
273
|
+
| `dateFormat`, `dateTimeFormat` | **Default ISO: `YYYY-MM-DD` and `YYYY-MM-DD HH:mm`.** Pass a pattern (`DD.MM.YYYY`, `D MMM YYYY, h:mm a`, with `[literal]` text) or a function `(date) => string`, for every column or per column index. |
|
|
274
|
+
| `durationFormat` | `'auto'` (`850 ms`, `3.2 s`, `2m 5s`, `1h 2m`), `'clock'` (`1:02:05`), or a function. |
|
|
275
|
+
| `dateTimeZone: 'UTC'` | Reads and draws typed dates in UTC instead of the local zone. |
|
|
276
|
+
| `sortValue(value, colIdx, row)` | Sets what a column sorts by, for every column or (as an object or array) per column. Return `undefined` to fall back to the column's own rule. |
|
|
277
|
+
|
|
278
|
+
The column filters work on values. On a date column, `2026-10` matches October,
|
|
279
|
+
`>2026-10` matches after October, `<=2026-10-05` includes the 5th, and
|
|
280
|
+
`2026-01..2026-03` matches January through the end of March. A date operand is
|
|
281
|
+
ISO with two-digit parts, so an operator over a half-typed one (`>20`,
|
|
282
|
+
`>2026-1`) filters nothing until it is complete. Text without an operator that
|
|
283
|
+
is not such a date (`5 Oct`, `Oct`, `2026-1`) is matched against the cell as
|
|
284
|
+
drawn, so a filter narrows as you type it. On a duration column, `>1s`,
|
|
285
|
+
`<500ms` and `1s..1m` work. `formatDate`, `formatDuration`, `parseDateValue`
|
|
286
|
+
and `parseDuration` are exported, so text drawn elsewhere can use the same
|
|
287
|
+
words as the table.
|
|
288
|
+
|
|
289
|
+
**Housekeeping**
|
|
290
|
+
|
|
291
|
+
| Option | What it does |
|
|
292
|
+
|---|---|
|
|
293
|
+
| `DataTable.mount(host, config)` | Builds the table and draws it. The constructor still draws nothing until `render()`. |
|
|
294
|
+
| `DataTable.withDefaults(defaults)` | Returns a subclass whose `defaults` sit under every config it is given. `instanceof DataTable` holds, and it can be narrowed again. |
|
|
295
|
+
| `autoDispose: true` | Disposes the table once it has been taken out of the document and is still out a task later. It lets go of everything `dispose()` does, but it removes only the table's own elements and never empties the host. By then you may have mounted the next list into the host or written your own message there, and that stays. Do not set it on a table you detach and attach again later, such as a cached tab, because a disposed table comes back empty. |
|
|
296
|
+
| `resetColumnWidths()` | Forgets every dragged, fitted or restored width, the persisted ones included, and measures the columns again. Use it after a zoom or a font change. |
|
|
297
|
+
|
|
298
|
+
**Changes that need no option.** A table that sets none of the options above
|
|
299
|
+
behaves as it did in 0.4, except for these:
|
|
300
|
+
|
|
301
|
+
- The copy menu's document listeners (click, scroll, Escape, resize) now exist
|
|
302
|
+
only while the menu is open. Before 0.5.0, a table that had ever been
|
|
303
|
+
right-clicked kept them until `dispose()`.
|
|
304
|
+
- `attachLandingTableBehavior` judges a click by the `onRowActivate` rule:
|
|
305
|
+
- A double-click opens the row once, not twice.
|
|
306
|
+
- A click while text in the host is selected does not open the row. That is
|
|
307
|
+
the click that ends a drag across a cell.
|
|
308
|
+
- A click on a `<label>`, a `<summary>` or editable (`contenteditable`) text
|
|
309
|
+
in a row does not open the row either. 0.4.7 skipped only buttons, links
|
|
310
|
+
and form fields. A `[data-twm-action]` control still runs its action.
|
|
311
|
+
- The CSS reads the new tokens with the 0.4 values as fallbacks, so the
|
|
312
|
+
computed styles are the same while the tokens are unset. One exception: the
|
|
313
|
+
normal density's line-height token is declared on the `thead` and `tbody`.
|
|
314
|
+
A line-height rule of yours aimed at those two elements themselves now loses
|
|
315
|
+
to it if it is in a cascade layer, or has zero specificity and loads before
|
|
316
|
+
`flexdesk.css`. A rule for the cells, the rows or the table does not.
|
|
317
|
+
|
|
178
318
|
### Scrollbars
|
|
179
319
|
|
|
180
320
|
`installAutoScrollbars(root)` (from `@flexdesk/widgets`) replaces the browser's
|
package/css/base.css
CHANGED
|
@@ -496,7 +496,9 @@
|
|
|
496
496
|
.twm-preview-table th,
|
|
497
497
|
.twm-preview-table td {
|
|
498
498
|
border-bottom: 1px solid #2a2a2a;
|
|
499
|
-
|
|
499
|
+
/* 0.5.0: the row-geometry tokens (tokens.css). Unset, these fall back to
|
|
500
|
+
* exactly the 0.4 padding. */
|
|
501
|
+
padding: var(--twm-dt-cell-pad-block, var(--space-sm)) var(--twm-dt-cell-pad-inline, 8px);
|
|
500
502
|
white-space: nowrap;
|
|
501
503
|
/* Never spill text past the column. Once columns are locked to fixed
|
|
502
504
|
* widths, over-long content is clipped with an ellipsis; the full
|
|
@@ -598,6 +600,125 @@ body.twm-dt-col-resizing {
|
|
|
598
600
|
.twm-preview-table:not(.twm-preview-table--readonly) tr:hover td {
|
|
599
601
|
background: rgba(255, 255, 255, 0.04);
|
|
600
602
|
}
|
|
603
|
+
/* ── 0.5.0 DataTable options. Each rule below matches only a class or an
|
|
604
|
+
* attribute a 0.5.0 option sets, so a table that asks for none of them is
|
|
605
|
+
* styled exactly as in 0.4. ───────────────────────────────────────────── */
|
|
606
|
+
|
|
607
|
+
/* The row-geometry tokens for the NORMAL density's line height. Compact
|
|
608
|
+
* declares its own on the cells (with the token in front), as 0.4 did; normal
|
|
609
|
+
* never declared one, so this one is NOT on the cells. It is on the ROW GROUPS,
|
|
610
|
+
* which only this component creates, and the cells inherit it. Unset, it
|
|
611
|
+
* computes to `unset` — inheritance from the table, which is exactly what no
|
|
612
|
+
* declaration gives. A declaration on the cells themselves, however weak
|
|
613
|
+
* (`:where()`), would beat any consumer rule for the cells that sits in a
|
|
614
|
+
* cascade LAYER (unlayered author styles outrank every layer, whatever their
|
|
615
|
+
* specificity) and any zero-specificity one loaded before this sheet; on the
|
|
616
|
+
* row groups, a consumer's rule for the cells, the rows or the table still
|
|
617
|
+
* wins, as it did in 0.4. */
|
|
618
|
+
:where(.twm-preview-table) > :where(thead, tbody, tfoot) {
|
|
619
|
+
line-height: var(--twm-dt-cell-line-height);
|
|
620
|
+
}
|
|
621
|
+
/* `firstColumn: 'plain'` — the first column is a column like the others, not
|
|
622
|
+
* the time column this component was built around (120–150px, grey, 500).
|
|
623
|
+
* One class more than both time rules, so the order of the two sheets cannot
|
|
624
|
+
* matter. The header keeps the header's weight and colour. */
|
|
625
|
+
.twm-preview-table.twm-dt--first-plain th:first-child,
|
|
626
|
+
.twm-preview-table.twm-dt--first-plain td:first-child {
|
|
627
|
+
width: auto;
|
|
628
|
+
min-width: 80px;
|
|
629
|
+
max-width: none;
|
|
630
|
+
}
|
|
631
|
+
.twm-preview-table.twm-dt--first-plain td:first-child {
|
|
632
|
+
font-weight: inherit;
|
|
633
|
+
color: inherit;
|
|
634
|
+
}
|
|
635
|
+
.twm-preview-table.twm-dt--first-plain th:first-child {
|
|
636
|
+
font-weight: 600;
|
|
637
|
+
color: #aaa;
|
|
638
|
+
}
|
|
639
|
+
/* `onRowActivate` / `clickable` — a row that opens looks like it: a pointer,
|
|
640
|
+
* and a hover even on a readonly table. Never over a selected row, whose
|
|
641
|
+
* colour has to survive the pointer (see the selection rule's note), and never
|
|
642
|
+
* over the empty or failed state's row, which opens nothing. */
|
|
643
|
+
.twm-preview-table.twm-dt--clickable tbody tr:not(.twm-dt-spacer):not(.data-table__empty-row) {
|
|
644
|
+
cursor: pointer;
|
|
645
|
+
}
|
|
646
|
+
.twm-preview-table.twm-dt--clickable tbody tr:not(.twm-dt-spacer):not(.data-table__empty-row):not(.selected):hover td {
|
|
647
|
+
background: var(--state-hover, rgba(255, 255, 255, 0.05));
|
|
648
|
+
}
|
|
649
|
+
/* `activeRow` — the row that is OPEN. A left edge, not a background: hover
|
|
650
|
+
* and selection keep theirs. */
|
|
651
|
+
.twm-preview-table tbody tr.twm-dt-row--active > td:first-child {
|
|
652
|
+
box-shadow: inset 2px 0 0 0 var(--accent-bright, #007acc);
|
|
653
|
+
}
|
|
654
|
+
/* `rowIcon` — drawn from `data-twm-icon` so the icon's NAME is not cell
|
|
655
|
+
* text (not in a copy, not in the clipped-cell tooltip). The span carries the
|
|
656
|
+
* icon font's class; the pseudo-element inherits its font and ligatures. Top
|
|
657
|
+
* aligned on a line height of 1, so it cannot make its row taller. */
|
|
658
|
+
.twm-preview-table td .twm-dt-row-icon {
|
|
659
|
+
display: inline-block;
|
|
660
|
+
font-size: 1.15em;
|
|
661
|
+
line-height: 1;
|
|
662
|
+
vertical-align: top;
|
|
663
|
+
margin-right: 4px;
|
|
664
|
+
opacity: 0.85;
|
|
665
|
+
}
|
|
666
|
+
.twm-preview-table td .twm-dt-row-icon::before {
|
|
667
|
+
content: attr(data-twm-icon);
|
|
668
|
+
}
|
|
669
|
+
.twm-preview-table td .twm-dt-row-icon--muted { color: var(--text-muted, #888); }
|
|
670
|
+
.twm-preview-table td .twm-dt-row-icon--info { color: var(--color-info, #2196f3); }
|
|
671
|
+
.twm-preview-table td .twm-dt-row-icon--success { color: var(--color-success, #3fb950); }
|
|
672
|
+
.twm-preview-table td .twm-dt-row-icon--warn { color: var(--color-warning, #ff9800); }
|
|
673
|
+
.twm-preview-table td .twm-dt-row-icon--danger { color: var(--color-danger, #c74e4e); }
|
|
674
|
+
/* THESE CELLS ARE OFTEN A ROW'S FIRST CELL, and the first cell is the time
|
|
675
|
+
* column's: `.twm-preview-table td:first-child` (0,2,1, grey and weight 500,
|
|
676
|
+
* declared twice, the second copy far below), `firstColumn: 'plain'` (0,3,1,
|
|
677
|
+
* `color: inherit`) and the compact density's cell padding (0,2,1, in
|
|
678
|
+
* overrides.css) all reach them. A rule of the same weight LOSES to whichever
|
|
679
|
+
* comes later — which drew a failure in an empty list grey, exactly like an
|
|
680
|
+
* empty state, and gave both 2px of padding under compact. So each rule below
|
|
681
|
+
* is (0,3,2) or more: the wrapper and `tbody` in front of the cell's class,
|
|
682
|
+
* which no first-column or density rule matches, and the order of the sheets
|
|
683
|
+
* cannot matter. */
|
|
684
|
+
/* `nullDisplay` — a NULL reads as one, not as the text "NULL". */
|
|
685
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-dt-cell--null {
|
|
686
|
+
color: var(--text-dim, #666);
|
|
687
|
+
font-style: italic;
|
|
688
|
+
}
|
|
689
|
+
/* `emptyState` and `setError` with no rows: the box's own words, which may
|
|
690
|
+
* wrap — muted for an empty state, red for a failure, in both densities. Quiet
|
|
691
|
+
* while loading — an empty table that is still loading is not empty. */
|
|
692
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-data-table__empty-cell.twm-data-table__empty-cell--state,
|
|
693
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-data-table__empty-cell.twm-data-table__empty-cell--error {
|
|
694
|
+
text-align: center;
|
|
695
|
+
padding: 16px;
|
|
696
|
+
white-space: normal;
|
|
697
|
+
font-weight: normal;
|
|
698
|
+
color: var(--text-muted, #888);
|
|
699
|
+
}
|
|
700
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-data-table__empty-cell.twm-data-table__empty-cell--error {
|
|
701
|
+
color: var(--color-danger, #c74e4e);
|
|
702
|
+
}
|
|
703
|
+
.twm-data-table-component--loading .twm-preview-table td.twm-data-table__empty-cell--state {
|
|
704
|
+
visibility: hidden;
|
|
705
|
+
}
|
|
706
|
+
/* `setError` with rows: a banner over the rows that are KEPT. */
|
|
707
|
+
.twm-data-table__error {
|
|
708
|
+
flex: 0 0 auto;
|
|
709
|
+
display: flex;
|
|
710
|
+
align-items: center;
|
|
711
|
+
gap: 6px;
|
|
712
|
+
padding: 4px 8px;
|
|
713
|
+
font-size: var(--font-size-sm, 12px);
|
|
714
|
+
color: var(--color-danger, #c74e4e);
|
|
715
|
+
background: rgba(199, 78, 78, 0.12);
|
|
716
|
+
border-bottom: 1px solid rgba(199, 78, 78, 0.4);
|
|
717
|
+
}
|
|
718
|
+
.twm-data-table__error .twm-data-table__error-icon {
|
|
719
|
+
font-size: 16px;
|
|
720
|
+
line-height: 1;
|
|
721
|
+
}
|
|
601
722
|
/* Wizard mode: uniform column widths, no selection */
|
|
602
723
|
.twm-preview-table-wrap--wizard .twm-preview-table {
|
|
603
724
|
table-layout: auto;
|
|
@@ -1508,7 +1629,7 @@ body.twm-window-maximized .twm-resize-handle {
|
|
|
1508
1629
|
}
|
|
1509
1630
|
.twm-preview-table th, .twm-preview-table td {
|
|
1510
1631
|
border-bottom: 1px solid #2a2a2a;
|
|
1511
|
-
padding: var(--space-sm) 8px;
|
|
1632
|
+
padding: var(--twm-dt-cell-pad-block, var(--space-sm)) var(--twm-dt-cell-pad-inline, 8px);
|
|
1512
1633
|
white-space: nowrap;
|
|
1513
1634
|
}
|
|
1514
1635
|
.twm-preview-table th {
|
package/css/flexdesk.css
CHANGED
|
@@ -504,7 +504,9 @@
|
|
|
504
504
|
.twm-preview-table th,
|
|
505
505
|
.twm-preview-table td {
|
|
506
506
|
border-bottom: 1px solid #2a2a2a;
|
|
507
|
-
|
|
507
|
+
/* 0.5.0: the row-geometry tokens (tokens.css). Unset, these fall back to
|
|
508
|
+
* exactly the 0.4 padding. */
|
|
509
|
+
padding: var(--twm-dt-cell-pad-block, var(--space-sm)) var(--twm-dt-cell-pad-inline, 8px);
|
|
508
510
|
white-space: nowrap;
|
|
509
511
|
/* Never spill text past the column. Once columns are locked to fixed
|
|
510
512
|
* widths, over-long content is clipped with an ellipsis; the full
|
|
@@ -625,6 +627,125 @@ body.twm-dt-col-resizing {
|
|
|
625
627
|
.twm-preview-table tbody tr.selected td {
|
|
626
628
|
background: var(--state-selected, rgba(0, 122, 204, 0.15));
|
|
627
629
|
}
|
|
630
|
+
/* ── 0.5.0 DataTable options. Each rule below matches only a class or an
|
|
631
|
+
* attribute a 0.5.0 option sets, so a table that asks for none of them is
|
|
632
|
+
* styled exactly as in 0.4. ───────────────────────────────────────────── */
|
|
633
|
+
|
|
634
|
+
/* The row-geometry tokens for the NORMAL density's line height. Compact
|
|
635
|
+
* declares its own on the cells (with the token in front), as 0.4 did; normal
|
|
636
|
+
* never declared one, so this one is NOT on the cells. It is on the ROW GROUPS,
|
|
637
|
+
* which only this component creates, and the cells inherit it. Unset, it
|
|
638
|
+
* computes to `unset` — inheritance from the table, which is exactly what no
|
|
639
|
+
* declaration gives. A declaration on the cells themselves, however weak
|
|
640
|
+
* (`:where()`), would beat any consumer rule for the cells that sits in a
|
|
641
|
+
* cascade LAYER (unlayered author styles outrank every layer, whatever their
|
|
642
|
+
* specificity) and any zero-specificity one loaded before this sheet; on the
|
|
643
|
+
* row groups, a consumer's rule for the cells, the rows or the table still
|
|
644
|
+
* wins, as it did in 0.4. */
|
|
645
|
+
:where(.twm-preview-table) > :where(thead, tbody, tfoot) {
|
|
646
|
+
line-height: var(--twm-dt-cell-line-height);
|
|
647
|
+
}
|
|
648
|
+
/* `firstColumn: 'plain'` — the first column is a column like the others, not
|
|
649
|
+
* the time column this component was built around (120–150px, grey, 500).
|
|
650
|
+
* One class more than both time rules, so the order of the two sheets cannot
|
|
651
|
+
* matter. The header keeps the header's weight and colour. */
|
|
652
|
+
.twm-preview-table.twm-dt--first-plain th:first-child,
|
|
653
|
+
.twm-preview-table.twm-dt--first-plain td:first-child {
|
|
654
|
+
width: auto;
|
|
655
|
+
min-width: 80px;
|
|
656
|
+
max-width: none;
|
|
657
|
+
}
|
|
658
|
+
.twm-preview-table.twm-dt--first-plain td:first-child {
|
|
659
|
+
font-weight: inherit;
|
|
660
|
+
color: inherit;
|
|
661
|
+
}
|
|
662
|
+
.twm-preview-table.twm-dt--first-plain th:first-child {
|
|
663
|
+
font-weight: 600;
|
|
664
|
+
color: #aaa;
|
|
665
|
+
}
|
|
666
|
+
/* `onRowActivate` / `clickable` — a row that opens looks like it: a pointer,
|
|
667
|
+
* and a hover even on a readonly table. Never over a selected row, whose
|
|
668
|
+
* colour has to survive the pointer (see the selection rule's note), and never
|
|
669
|
+
* over the empty or failed state's row, which opens nothing. */
|
|
670
|
+
.twm-preview-table.twm-dt--clickable tbody tr:not(.twm-dt-spacer):not(.data-table__empty-row) {
|
|
671
|
+
cursor: pointer;
|
|
672
|
+
}
|
|
673
|
+
.twm-preview-table.twm-dt--clickable tbody tr:not(.twm-dt-spacer):not(.data-table__empty-row):not(.selected):hover td {
|
|
674
|
+
background: var(--state-hover, rgba(255, 255, 255, 0.05));
|
|
675
|
+
}
|
|
676
|
+
/* `activeRow` — the row that is OPEN. A left edge, not a background: hover
|
|
677
|
+
* and selection keep theirs. */
|
|
678
|
+
.twm-preview-table tbody tr.twm-dt-row--active > td:first-child {
|
|
679
|
+
box-shadow: inset 2px 0 0 0 var(--accent-bright, #007acc);
|
|
680
|
+
}
|
|
681
|
+
/* `rowIcon` — drawn from `data-twm-icon` so the icon's NAME is not cell
|
|
682
|
+
* text (not in a copy, not in the clipped-cell tooltip). The span carries the
|
|
683
|
+
* icon font's class; the pseudo-element inherits its font and ligatures. Top
|
|
684
|
+
* aligned on a line height of 1, so it cannot make its row taller. */
|
|
685
|
+
.twm-preview-table td .twm-dt-row-icon {
|
|
686
|
+
display: inline-block;
|
|
687
|
+
font-size: 1.15em;
|
|
688
|
+
line-height: 1;
|
|
689
|
+
vertical-align: top;
|
|
690
|
+
margin-right: 4px;
|
|
691
|
+
opacity: 0.85;
|
|
692
|
+
}
|
|
693
|
+
.twm-preview-table td .twm-dt-row-icon::before {
|
|
694
|
+
content: attr(data-twm-icon);
|
|
695
|
+
}
|
|
696
|
+
.twm-preview-table td .twm-dt-row-icon--muted { color: var(--text-muted, #888); }
|
|
697
|
+
.twm-preview-table td .twm-dt-row-icon--info { color: var(--color-info, #2196f3); }
|
|
698
|
+
.twm-preview-table td .twm-dt-row-icon--success { color: var(--color-success, #3fb950); }
|
|
699
|
+
.twm-preview-table td .twm-dt-row-icon--warn { color: var(--color-warning, #ff9800); }
|
|
700
|
+
.twm-preview-table td .twm-dt-row-icon--danger { color: var(--color-danger, #c74e4e); }
|
|
701
|
+
/* THESE CELLS ARE OFTEN A ROW'S FIRST CELL, and the first cell is the time
|
|
702
|
+
* column's: `.twm-preview-table td:first-child` (0,2,1, grey and weight 500,
|
|
703
|
+
* declared twice, the second copy far below), `firstColumn: 'plain'` (0,3,1,
|
|
704
|
+
* `color: inherit`) and the compact density's cell padding (0,2,1, in
|
|
705
|
+
* overrides.css) all reach them. A rule of the same weight LOSES to whichever
|
|
706
|
+
* comes later — which drew a failure in an empty list grey, exactly like an
|
|
707
|
+
* empty state, and gave both 2px of padding under compact. So each rule below
|
|
708
|
+
* is (0,3,2) or more: the wrapper and `tbody` in front of the cell's class,
|
|
709
|
+
* which no first-column or density rule matches, and the order of the sheets
|
|
710
|
+
* cannot matter. */
|
|
711
|
+
/* `nullDisplay` — a NULL reads as one, not as the text "NULL". */
|
|
712
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-dt-cell--null {
|
|
713
|
+
color: var(--text-dim, #666);
|
|
714
|
+
font-style: italic;
|
|
715
|
+
}
|
|
716
|
+
/* `emptyState` and `setError` with no rows: the box's own words, which may
|
|
717
|
+
* wrap — muted for an empty state, red for a failure, in both densities. Quiet
|
|
718
|
+
* while loading — an empty table that is still loading is not empty. */
|
|
719
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-data-table__empty-cell.twm-data-table__empty-cell--state,
|
|
720
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-data-table__empty-cell.twm-data-table__empty-cell--error {
|
|
721
|
+
text-align: center;
|
|
722
|
+
padding: 16px;
|
|
723
|
+
white-space: normal;
|
|
724
|
+
font-weight: normal;
|
|
725
|
+
color: var(--text-muted, #888);
|
|
726
|
+
}
|
|
727
|
+
.twm-data-table-component .twm-preview-table tbody td.twm-data-table__empty-cell.twm-data-table__empty-cell--error {
|
|
728
|
+
color: var(--color-danger, #c74e4e);
|
|
729
|
+
}
|
|
730
|
+
.twm-data-table-component--loading .twm-preview-table td.twm-data-table__empty-cell--state {
|
|
731
|
+
visibility: hidden;
|
|
732
|
+
}
|
|
733
|
+
/* `setError` with rows: a banner over the rows that are KEPT. */
|
|
734
|
+
.twm-data-table__error {
|
|
735
|
+
flex: 0 0 auto;
|
|
736
|
+
display: flex;
|
|
737
|
+
align-items: center;
|
|
738
|
+
gap: 6px;
|
|
739
|
+
padding: 4px 8px;
|
|
740
|
+
font-size: var(--font-size-sm, 12px);
|
|
741
|
+
color: var(--color-danger, #c74e4e);
|
|
742
|
+
background: rgba(199, 78, 78, 0.12);
|
|
743
|
+
border-bottom: 1px solid rgba(199, 78, 78, 0.4);
|
|
744
|
+
}
|
|
745
|
+
.twm-data-table__error .twm-data-table__error-icon {
|
|
746
|
+
font-size: 16px;
|
|
747
|
+
line-height: 1;
|
|
748
|
+
}
|
|
628
749
|
/* Wizard mode: uniform column widths, no selection */
|
|
629
750
|
.twm-preview-table-wrap--wizard .twm-preview-table {
|
|
630
751
|
table-layout: auto;
|
|
@@ -1606,7 +1727,7 @@ body.twm-window-maximized .twm-resize-handle {
|
|
|
1606
1727
|
}
|
|
1607
1728
|
.twm-preview-table th, .twm-preview-table td {
|
|
1608
1729
|
border-bottom: 1px solid #2a2a2a;
|
|
1609
|
-
padding: var(--space-sm) 8px;
|
|
1730
|
+
padding: var(--twm-dt-cell-pad-block, var(--space-sm)) var(--twm-dt-cell-pad-inline, 8px);
|
|
1610
1731
|
white-space: nowrap;
|
|
1611
1732
|
}
|
|
1612
1733
|
.twm-preview-table th {
|
|
@@ -6617,8 +6738,10 @@ tr:hover .twm-row-action { opacity: 1; }
|
|
|
6617
6738
|
}
|
|
6618
6739
|
.twm-data-table-component--compact .twm-preview-table th,
|
|
6619
6740
|
.twm-data-table-component--compact .twm-preview-table td {
|
|
6620
|
-
|
|
6621
|
-
|
|
6741
|
+
/* 0.5.0: the row-geometry tokens (tokens.css), falling back to exactly
|
|
6742
|
+
* the 0.4 compact values. */
|
|
6743
|
+
padding: var(--twm-dt-cell-pad-block, 2px) var(--twm-dt-cell-pad-inline, 6px);
|
|
6744
|
+
line-height: var(--twm-dt-cell-line-height, 1.35);
|
|
6622
6745
|
}
|
|
6623
6746
|
.twm-data-table-component--compact .twm-pagination-controls {
|
|
6624
6747
|
padding: 1px 6px !important;
|
package/css/overrides.css
CHANGED
|
@@ -1873,8 +1873,10 @@ tr:hover .twm-row-action { opacity: 1; }
|
|
|
1873
1873
|
}
|
|
1874
1874
|
.twm-data-table-component--compact .twm-preview-table th,
|
|
1875
1875
|
.twm-data-table-component--compact .twm-preview-table td {
|
|
1876
|
-
|
|
1877
|
-
|
|
1876
|
+
/* 0.5.0: the row-geometry tokens (tokens.css), falling back to exactly
|
|
1877
|
+
* the 0.4 compact values. */
|
|
1878
|
+
padding: var(--twm-dt-cell-pad-block, 2px) var(--twm-dt-cell-pad-inline, 6px);
|
|
1879
|
+
line-height: var(--twm-dt-cell-line-height, 1.35);
|
|
1878
1880
|
}
|
|
1879
1881
|
.twm-data-table-component--compact .twm-pagination-controls {
|
|
1880
1882
|
padding: 1px 6px !important;
|
package/css/tokens.css
CHANGED
|
@@ -179,4 +179,18 @@
|
|
|
179
179
|
--ea-text-muted: var(--text-muted);
|
|
180
180
|
--ea-text-dim: var(--text-dim);
|
|
181
181
|
--ea-accent: var(--accent-bright);
|
|
182
|
+
|
|
183
|
+
/* ── DataTable row geometry (0.5.0) — DELIBERATELY UNSET ──────
|
|
184
|
+
Three tokens make every DataTable's rows one height, whatever its
|
|
185
|
+
density. Set them (on :root, or on any ancestor of the tables) and
|
|
186
|
+
both the normal and the compact density read them:
|
|
187
|
+
|
|
188
|
+
--twm-dt-cell-pad-block: 2px; the cell's top and bottom padding
|
|
189
|
+
--twm-dt-cell-pad-inline: 6px; its left and right padding
|
|
190
|
+
--twm-dt-cell-line-height: 1.35; the line height in a cell
|
|
191
|
+
|
|
192
|
+
They are NOT defined here, and must not be: each density falls back
|
|
193
|
+
to its OWN 0.4 values when a token is unset (normal: 6px 8px and an
|
|
194
|
+
inherited line height; compact: 2px 6px and 1.35), and a default here
|
|
195
|
+
would replace both densities' values with one. */
|
|
182
196
|
}
|
package/dist/charts.js
CHANGED
|
@@ -39,13 +39,13 @@ import {
|
|
|
39
39
|
setPlotlySource,
|
|
40
40
|
uid,
|
|
41
41
|
updateChart
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-CNXP3SIN.js";
|
|
43
43
|
import {
|
|
44
44
|
createRafResizeObserver
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-KH2GJTKR.js";
|
|
46
46
|
import {
|
|
47
47
|
ManagedWindow
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-6JSOVNID.js";
|
|
49
49
|
import "./chunk-FL5KFNQH.js";
|
|
50
50
|
import "./chunk-JYWURG5T.js";
|
|
51
51
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ManagedWindow
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6JSOVNID.js";
|
|
4
4
|
|
|
5
5
|
// src/help/help_registry.js
|
|
6
6
|
var EMPTY_PROVIDER = Object.freeze({
|
|
@@ -973,4 +973,4 @@ export {
|
|
|
973
973
|
showContextMenu,
|
|
974
974
|
hideContextMenu
|
|
975
975
|
};
|
|
976
|
-
//# sourceMappingURL=chunk-
|
|
976
|
+
//# sourceMappingURL=chunk-24MS7W5Y.js.map
|
|
@@ -1231,7 +1231,47 @@ var ManagedWindow = class {
|
|
|
1231
1231
|
}
|
|
1232
1232
|
};
|
|
1233
1233
|
|
|
1234
|
+
// src/ui/components/row_activation.js
|
|
1235
|
+
var ROW_CONTROL_SELECTOR = [
|
|
1236
|
+
"button",
|
|
1237
|
+
"a",
|
|
1238
|
+
"input",
|
|
1239
|
+
"select",
|
|
1240
|
+
"textarea",
|
|
1241
|
+
"label",
|
|
1242
|
+
"summary",
|
|
1243
|
+
'[contenteditable=""]',
|
|
1244
|
+
'[contenteditable="true"]',
|
|
1245
|
+
"[data-twm-action]"
|
|
1246
|
+
].join(", ");
|
|
1247
|
+
function isRowControl(target, scope = null) {
|
|
1248
|
+
const hit = target?.closest?.(ROW_CONTROL_SELECTOR);
|
|
1249
|
+
return !!hit && (!scope || scope.contains(hit));
|
|
1250
|
+
}
|
|
1251
|
+
function endsTextSelection(target, scope = null) {
|
|
1252
|
+
const doc = target?.ownerDocument || (typeof document !== "undefined" ? document : null);
|
|
1253
|
+
const sel = doc?.getSelection?.();
|
|
1254
|
+
if (!sel || sel.isCollapsed || !String(sel)) return false;
|
|
1255
|
+
if (!scope) return true;
|
|
1256
|
+
const inside = (node) => !!node && scope.contains(node);
|
|
1257
|
+
return inside(sel.anchorNode) || inside(sel.focusNode);
|
|
1258
|
+
}
|
|
1259
|
+
function isRowActivation(ev, gesture = "click", { scope = null } = {}) {
|
|
1260
|
+
const target = ev?.target;
|
|
1261
|
+
if (!target?.closest) return false;
|
|
1262
|
+
if (isRowControl(target, scope)) return false;
|
|
1263
|
+
if (gesture === "click") {
|
|
1264
|
+
if ((ev.detail ?? 0) > 1) return false;
|
|
1265
|
+
if (endsTextSelection(target, scope)) return false;
|
|
1266
|
+
}
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1234
1270
|
export {
|
|
1235
|
-
ManagedWindow
|
|
1271
|
+
ManagedWindow,
|
|
1272
|
+
ROW_CONTROL_SELECTOR,
|
|
1273
|
+
isRowControl,
|
|
1274
|
+
endsTextSelection,
|
|
1275
|
+
isRowActivation
|
|
1236
1276
|
};
|
|
1237
|
-
//# sourceMappingURL=chunk-
|
|
1277
|
+
//# sourceMappingURL=chunk-6JSOVNID.js.map
|