flexdesk 0.4.3 → 0.4.4

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 CHANGED
@@ -163,6 +163,18 @@ 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
+ ### Column sizing
167
+
168
+ `fitColumns(columns, avail)` (from `@flexdesk/widgets`) turns per-column
169
+ measurements into widths. Pass each column's widest cell (`natural`), the width
170
+ most cells fit (`typical`), the header label (`header`) and any user-dragged
171
+ width (`pinned`). Short columns such as ids, dates and statuses keep their full
172
+ values, and free-text columns (`text`, or anything 240px and wider) take the
173
+ rest. When space runs short, short columns lose their rare outliers first,
174
+ then text shrinks to a floor. The widths are fractional and add up to `avail`
175
+ exactly whenever the floors fit. `DataTable` does not use it yet; an embedder
176
+ that measures its own columns can.
177
+
166
178
  ## The host port
167
179
 
168
180
  FlexDesk never touches `pywebview`, `fetch`, `localStorage` or the filesystem. It
package/dist/widgets.js CHANGED
@@ -1661,6 +1661,103 @@ var AutocompleteField = class {
1661
1661
  }
1662
1662
  };
1663
1663
 
1664
+ // src/ui/components/column_fit.js
1665
+ var COLUMN_FIT = Object.freeze({
1666
+ /** Absolute floor, and the drag-resize minimum. */
1667
+ FLOOR: 40,
1668
+ /** Sub-pixel safety against an ellipsis on content that exactly fits. */
1669
+ PAD: 2,
1670
+ /** A column whose full width reaches this is free text. */
1671
+ TEXT_AT: 240,
1672
+ /** A text column's starting share when room allows it. */
1673
+ TEXT_MIN: 200,
1674
+ /** How far a text column shrinks before short columns go below `typical`. */
1675
+ TEXT_FLOOR: 120,
1676
+ /** How far a short column shrinks before the table scrolls. */
1677
+ SHORT_FLOOR: 56
1678
+ });
1679
+ function fitColumns(columns, avail) {
1680
+ const { FLOOR, PAD, TEXT_AT, TEXT_MIN, TEXT_FLOOR, SHORT_FLOOR } = COLUMN_FIT;
1681
+ const n = columns.length;
1682
+ const cols = columns.map((c) => {
1683
+ const header = Math.max(0, Number(c.header) || 0);
1684
+ const natural = Math.max(0, Number(c.natural) || 0);
1685
+ const full = Math.max(FLOOR, Math.ceil(Math.max(natural, header) + PAD));
1686
+ const typical = Math.min(full, Math.max(FLOOR, Math.ceil(Math.max(
1687
+ Number.isFinite(c.typical) ? c.typical : natural,
1688
+ header
1689
+ ) + PAD)));
1690
+ const pinned = Number.isFinite(c.pinned) ? Math.max(FLOOR, Math.round(c.pinned)) : null;
1691
+ const text2 = pinned == null && (typeof c.text === "boolean" ? c.text : full >= TEXT_AT);
1692
+ return { full, typical, pinned, text: text2 };
1693
+ });
1694
+ const text = [];
1695
+ const short = [];
1696
+ cols.forEach((c, i) => {
1697
+ if (c.pinned == null) (c.text ? text : short).push(i);
1698
+ });
1699
+ if (!(avail > 1)) return cols.map((c) => c.pinned != null ? c.pinned : c.full);
1700
+ const widths = cols.map((c) => {
1701
+ if (c.pinned != null) return c.pinned;
1702
+ return c.text ? Math.min(c.full, TEXT_MIN) : c.full;
1703
+ });
1704
+ const total = () => widths.reduce((a, b) => a + b, 0);
1705
+ let slack = avail - total();
1706
+ if (slack >= 0) {
1707
+ slack = raise(widths, text, (i) => cols[i].full, slack);
1708
+ if (slack > 0) {
1709
+ if (text.length) text.forEach((i) => {
1710
+ widths[i] += slack / text.length;
1711
+ });
1712
+ else widths[short.length ? short[short.length - 1] : n - 1] += slack;
1713
+ }
1714
+ return widths;
1715
+ }
1716
+ const steps = [
1717
+ [short, (i) => cols[i].typical],
1718
+ [text, () => TEXT_FLOOR],
1719
+ [short, (i) => Math.min(cols[i].typical, SHORT_FLOOR)],
1720
+ [text, () => FLOOR * 2]
1721
+ ];
1722
+ for (const [idxs, low] of steps) {
1723
+ const over = total() - avail;
1724
+ if (over <= 1e-6) break;
1725
+ lowerTo(widths, idxs, low, over);
1726
+ }
1727
+ return widths;
1728
+ }
1729
+ function raise(widths, idxs, target, slack) {
1730
+ if (!(slack > 0)) return slack;
1731
+ const wants = idxs.map((i) => Math.max(0, target(i) - widths[i]));
1732
+ const need = wants.reduce((a, b) => a + b, 0);
1733
+ if (!(need > 0)) return slack;
1734
+ const give = Math.min(slack, need);
1735
+ idxs.forEach((i, k) => {
1736
+ widths[i] += give * wants[k] / need;
1737
+ });
1738
+ return slack - give;
1739
+ }
1740
+ function lowerTo(widths, idxs, low, over) {
1741
+ const floorOf = (i) => Math.min(widths[i], Math.max(0, low(i)));
1742
+ const cand = idxs.filter((i) => widths[i] > floorOf(i));
1743
+ if (!cand.length || over <= 0) return;
1744
+ const floors = new Map(cand.map((i) => [i, floorOf(i)]));
1745
+ const capacity = cand.reduce((a, i) => a + widths[i] - floors.get(i), 0);
1746
+ if (capacity <= over) {
1747
+ for (const i of cand) widths[i] = floors.get(i);
1748
+ return;
1749
+ }
1750
+ const cutAt = (level) => cand.reduce((a, i) => a + Math.max(0, widths[i] - Math.max(floors.get(i), level)), 0);
1751
+ let lo = 0;
1752
+ let hi = Math.max(...cand.map((i) => widths[i]));
1753
+ for (let k = 0; k < 64; k++) {
1754
+ const mid = (lo + hi) / 2;
1755
+ if (cutAt(mid) > over) lo = mid;
1756
+ else hi = mid;
1757
+ }
1758
+ for (const i of cand) widths[i] = Math.min(widths[i], Math.max(floors.get(i), hi));
1759
+ }
1760
+
1664
1761
  // src/ui/components/computing_status_window.js
1665
1762
  var _activeWindow = null;
1666
1763
  function showComputingWindow({ title = "Computing...", message = "", icon = "hourglass_top", onCancel } = {}) {
@@ -5400,6 +5497,7 @@ if (typeof window !== "undefined") {
5400
5497
  export {
5401
5498
  ActionDropdown,
5402
5499
  AutocompleteField,
5500
+ COLUMN_FIT,
5403
5501
  ComponentBase,
5404
5502
  ControllerBase,
5405
5503
  DEFAULT_TYPES,
@@ -5429,6 +5527,7 @@ export {
5429
5527
  createTreeItem,
5430
5528
  createTreeNode,
5431
5529
  filterTree,
5530
+ fitColumns,
5432
5531
  helpCategories,
5433
5532
  helpCopy,
5434
5533
  helpProvider,