@ex-grid/core 0.2.0-beta.3 → 0.2.0-rc.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/dist/index.js CHANGED
@@ -857,7 +857,23 @@ var findPanelLabelIds = Object.freeze({
857
857
  /** The refused cells past those named, their count at `{count}`. */
858
858
  replaceRefusedMore: "find-replace-refused-more",
859
859
  /** Why a match in a column that is not Editable cannot be written. */
860
- notEditable: "find-not-editable"
860
+ notEditable: "find-not-editable",
861
+ /** The Find All button (ADR-0470 §1). */
862
+ findAll: "find-all",
863
+ /** The list of a Find All's matches: its accessible name. */
864
+ findAllList: "find-all-list",
865
+ /** What a Find All that matched several cells says, their count at `{count}`. */
866
+ findAllFound: "find-all-found",
867
+ /** What a Find All that matched one cell says. */
868
+ findAllFoundOne: "find-all-found-one",
869
+ /** What a Find All that matched nothing says. */
870
+ findAllNone: "find-all-none",
871
+ /** Where a match in a row is, its row's number from 1 at `{row}` and its column's header at `{column}`. */
872
+ findAllInRow: "find-all-in-row",
873
+ /** Where a match in a row is, by the Consumer's label for the row at `{row}` and its column's header at `{column}`. */
874
+ findAllInLabelledRow: "find-all-in-labelled-row",
875
+ /** Where a match in a header is, the header's text at `{column}`. */
876
+ findAllInHeader: "find-all-in-header"
861
877
  });
862
878
  var findPanelWords = Object.freeze([
863
879
  [findPanelLabelIds.title, "Find"],
@@ -879,7 +895,15 @@ var findPanelWords = Object.freeze([
879
895
  [findPanelLabelIds.replaceRefused, "Nothing was replaced."],
880
896
  [findPanelLabelIds.replaceRefusedCell, "Row {row}, {column}: {reason}."],
881
897
  [findPanelLabelIds.replaceRefusedMore, "And {count} more."],
882
- [findPanelLabelIds.notEditable, "this column cannot be edited"]
898
+ [findPanelLabelIds.notEditable, "this column cannot be edited"],
899
+ [findPanelLabelIds.findAll, "Find All"],
900
+ [findPanelLabelIds.findAllList, "Matches"],
901
+ [findPanelLabelIds.findAllFound, "{count} cells found"],
902
+ [findPanelLabelIds.findAllFoundOne, "1 cell found"],
903
+ [findPanelLabelIds.findAllNone, "No cells found"],
904
+ [findPanelLabelIds.findAllInRow, "Row {row}, {column}"],
905
+ [findPanelLabelIds.findAllInLabelledRow, "{row}, {column}"],
906
+ [findPanelLabelIds.findAllInHeader, "Header {column}"]
883
907
  ]);
884
908
  var refusedCellsNamed = 5;
885
909
  function findOutcomeText(outcome, words2) {
@@ -5169,6 +5193,16 @@ var GridMetrics = class _GridMetrics {
5169
5193
  get fillHandleSizePx() {
5170
5194
  return Math.max(5, roundHalfToEven(this.rowHeightPx * 0.3));
5171
5195
  }
5196
+ /** The side of a Reference Outline's corner square (ADR-0480): 5px, as Excel draws it, in the Reference's colour
5197
+ * with a 1px margin of the ground, centred on the outline's corner. */
5198
+ get referenceHandleSizePx() {
5199
+ return 5;
5200
+ }
5201
+ /** How wide the strip along a Reference Outline's line is that a press grabs it by (ADR-0480): 5px, centred on
5202
+ * the line, wider than the line so that it can be pressed. */
5203
+ get referenceGripPx() {
5204
+ return 5;
5205
+ }
5172
5206
  /** A cell's horizontal padding on one side; a cell pays it twice. */
5173
5207
  get cellPaddingXPx() {
5174
5208
  return this.cellMetrics.cellHorizontalPaddingPx;
@@ -6974,6 +7008,187 @@ function beyond(position, first, last) {
6974
7008
  return position < first ? position - first : 0;
6975
7009
  }
6976
7010
 
7011
+ // src/paint/selection-styles.ts
7012
+ var outlineSideStyles = Object.freeze(Array.from({ length: 16 }, (_, flags) => ((flags & 1) === 0 ? "" : "; --ex-outline-in-t: 1") + ((flags & 2) === 0 ? "" : "; --ex-outline-in-l: 1") + ((flags & 4) === 0 ? "" : "; --ex-outline-heading-t: 1") + ((flags & 8) === 0 ? "" : "; --ex-outline-heading-l: 1")));
7013
+ var flushPx = 0.01;
7014
+ function cellRange(cell2) {
7015
+ return selectionRange(cell2.row, cell2.column, 1, 1);
7016
+ }
7017
+ function box(range, focus, columns, rowHeightPx, painted, pinnedLayer, cover) {
7018
+ if (rangeCellCount(range) === 1 && rangeContains(range, focus)) return void 0;
7019
+ const style = whole3(range, columns, rowHeightPx, painted, pinnedLayer, rowHeightPx);
7020
+ const side = sideOf(range, columns, pinnedLayer);
7021
+ if (style === void 0 || side === void 0) return void 0;
7022
+ return style + outlineSides(range.topRow, side.first, columns, pinnedLayer, cover, true);
7023
+ }
7024
+ function tint(range, focus, columns, rowHeightPx, painted, pinnedLayer) {
7025
+ const side = sideOf(range, columns, pinnedLayer);
7026
+ if (side === void 0 || focusAlone(range, side, focus)) return void 0;
7027
+ const rows = clip(range, painted);
7028
+ if (rows === void 0) return void 0;
7029
+ const leftPx = columns.offsetPxOf(side.first);
7030
+ let style = rect(leftPx, columns.offsetPxOf(side.last + 1) - leftPx, rows, rowHeightPx, painted.start);
7031
+ if (rangeContains(range, focus) && sideOf(cellRange(focus), columns, pinnedLayer) !== void 0 && focus.row >= rows.top && focus.row < rows.top + rows.count) {
7032
+ const holeLeftPx = columns.offsetPxOf(focus.column) - leftPx;
7033
+ const holeRightPx = columns.offsetPxOf(focus.column + 1) - leftPx;
7034
+ const holeTopPx = (focus.row - rows.top) * rowHeightPx;
7035
+ const holeBottomPx = holeTopPx + rowHeightPx;
7036
+ style += `; --ex-range-hole: polygon(evenodd, 0 0, 100% 0, 100% 100%, 0 100%, 0 0, ${holeLeftPx}px ${holeTopPx}px, ${holeRightPx}px ${holeTopPx}px, ${holeRightPx}px ${holeBottomPx}px, ${holeLeftPx}px ${holeBottomPx}px, ${holeLeftPx}px ${holeTopPx}px)`;
7037
+ }
7038
+ return style;
7039
+ }
7040
+ function tinted(range, focus, columns, painted, pinnedLayer) {
7041
+ const side = sideOf(range, columns, pinnedLayer);
7042
+ return side !== void 0 && !focusAlone(range, side, focus) && clip(range, painted) !== void 0;
7043
+ }
7044
+ function reaches(range, columns, painted, pinnedLayer) {
7045
+ return sideOf(range, columns, pinnedLayer) !== void 0 && clip(range, painted) !== void 0;
7046
+ }
7047
+ function whole3(range, columns, rowHeightPx, painted, pinnedLayer, outsidePx = 0) {
7048
+ const rows = clip(range, painted);
7049
+ if (sideOf(range, columns, pinnedLayer) === void 0 || rows === void 0) return void 0;
7050
+ const leftPx = columns.offsetPxOf(range.leftColumn);
7051
+ const rightPx = columns.offsetPxOf(rightColumn(range) + 1);
7052
+ let style = rect(leftPx, rightPx - leftPx, rows, rowHeightPx, painted.start);
7053
+ if (sideOf(range, columns, !pinnedLayer) !== void 0) {
7054
+ const boundaryPx = columns.offsetPxOf(columns.pinnedCount);
7055
+ const o = outsidePx > 0 ? `${-outsidePx}px` : "0";
7056
+ style += pinnedLayer ? `; clip-path: inset(${o} ${rightPx - boundaryPx}px ${o} ${o})` : `; clip-path: inset(${o} ${o} ${o} ${boundaryPx - leftPx}px)`;
7057
+ }
7058
+ return style;
7059
+ }
7060
+ function copied(range, columns, rowHeightPx, painted, pinnedLayer, cover) {
7061
+ const style = whole3(range, columns, rowHeightPx, painted, pinnedLayer, rowHeightPx);
7062
+ const side = sideOf(range, columns, pinnedLayer);
7063
+ if (style === void 0 || side === void 0) return void 0;
7064
+ return style + outlineSides(range.topRow, side.first, columns, pinnedLayer, cover, false);
7065
+ }
7066
+ function focusStyle(focus, columns, rowHeightPx, painted, pinnedLayer, cover) {
7067
+ const style = part(cellRange(focus), columns, rowHeightPx, painted, pinnedLayer);
7068
+ if (style === void 0) return void 0;
7069
+ return style + outlineSides(focus.row, focus.column, columns, pinnedLayer, cover, true);
7070
+ }
7071
+ function gripResizes(kind) {
7072
+ return kind === "topLeft" || kind === "topRight" || kind === "bottomLeft" || kind === "bottomRight";
7073
+ }
7074
+ var referenceSquareMarginPx = 1;
7075
+ function gripCursor(kind) {
7076
+ if (kind === "topLeft" || kind === "bottomRight") return "nwse";
7077
+ return kind === "topRight" || kind === "bottomLeft" ? "nesw" : "move";
7078
+ }
7079
+ function referenceGrips(range, columns, rowHeightPx, painted, pinnedLayer, gripPx, squarePx, readableLeftPx, named3 = range) {
7080
+ const side = sideOf(range, columns, pinnedLayer);
7081
+ const rows = clip(range, painted);
7082
+ if (side === void 0 || rows === void 0) return [];
7083
+ const rowPainted = (row) => row >= painted.start - 1 && row <= painted.start + painted.count;
7084
+ const hasLeft = sideOf(cellRange({ row: range.topRow, column: range.leftColumn }), columns, pinnedLayer) !== void 0 && range.leftColumn === named3.leftColumn;
7085
+ const hasRight = sideOf(cellRange({ row: range.topRow, column: rightColumn(range) }), columns, pinnedLayer) !== void 0 && rightColumn(range) === rightColumn(named3);
7086
+ const hasTop = rowPainted(range.topRow) && range.topRow === named3.topRow;
7087
+ const hasBottom = rowPainted(bottomRow(range)) && bottomRow(range) === bottomRow(named3);
7088
+ const leftX = columns.offsetPxOf(range.leftColumn);
7089
+ const rightX = columns.offsetPxOf(rightColumn(range) + 1);
7090
+ const topY = (range.topRow - painted.start) * rowHeightPx;
7091
+ const bottomY = (bottomRow(range) + 1 - painted.start) * rowHeightPx;
7092
+ const spanLeft = Math.max(columns.offsetPxOf(side.first), readableLeftPx);
7093
+ const spanRight = columns.offsetPxOf(side.last + 1);
7094
+ const spanTop = (rows.top - painted.start) * rowHeightPx;
7095
+ const spanBottom = spanTop + rows.count * rowHeightPx;
7096
+ const half = gripPx / 2;
7097
+ const grips = [];
7098
+ const strip = (kind, leftPx, topPx, widthPx, heightPx) => {
7099
+ const clippedLeft = Math.max(leftPx, readableLeftPx);
7100
+ const clippedWidth = widthPx - (clippedLeft - leftPx);
7101
+ if (clippedWidth > 0 && heightPx > 0) grips.push(Object.freeze({ kind, leftPx: clippedLeft, topPx, widthPx: clippedWidth, heightPx }));
7102
+ };
7103
+ if (hasTop) strip("top", spanLeft, topY - half, spanRight - spanLeft, gripPx);
7104
+ if (hasBottom) strip("bottom", spanLeft, bottomY - half, spanRight - spanLeft, gripPx);
7105
+ if (hasLeft) strip("left", leftX - half, spanTop, gripPx, spanBottom - spanTop);
7106
+ if (hasRight) strip("right", rightX - half, spanTop, gripPx, spanBottom - spanTop);
7107
+ const squareBox = squarePx + 2 * referenceSquareMarginPx;
7108
+ const square = (kind, x, y) => {
7109
+ if (x < readableLeftPx) return;
7110
+ grips.push(Object.freeze({ kind, leftPx: x - squareBox / 2, topPx: y - squareBox / 2, widthPx: squareBox, heightPx: squareBox }));
7111
+ };
7112
+ if (hasTop && hasLeft) square("topLeft", leftX, topY);
7113
+ if (hasTop && hasRight) square("topRight", rightX, topY);
7114
+ if (hasBottom && hasLeft) square("bottomLeft", leftX, bottomY);
7115
+ if (hasBottom && hasRight) square("bottomRight", rightX, bottomY);
7116
+ return grips;
7117
+ }
7118
+ function scrollable(range, columns, rowHeightPx, painted) {
7119
+ return part(range, columns, rowHeightPx, painted, false);
7120
+ }
7121
+ function pinned(range, columns, rowHeightPx, painted) {
7122
+ return part(range, columns, rowHeightPx, painted, true);
7123
+ }
7124
+ function handle(range, corner, columns, rowHeightPx, painted, sizePx, pinnedLayer, readable) {
7125
+ const cell2 = handleCell(range, corner);
7126
+ if (sideOf(cellRange(cell2), columns, pinnedLayer) === void 0) return void 0;
7127
+ const heldAtTheTop = corner === "topRight";
7128
+ if (!heldAtTheTop && cell2.row < painted.start - 1 || cell2.row > painted.start + painted.count) return void 0;
7129
+ const [leftPx, topPx] = handleCentrePx(range, corner, columns, rowHeightPx, painted.start, sizePx, readable);
7130
+ return `left: ${leftPx - sizePx / 2}px; top: ${topPx - sizePx / 2}px; width: ${sizePx}px; height: ${sizePx}px`;
7131
+ }
7132
+ function handleCell(range, corner) {
7133
+ switch (corner) {
7134
+ case "topRight":
7135
+ return Object.freeze({ row: range.topRow, column: rightColumn(range) });
7136
+ case "bottomLeft":
7137
+ return Object.freeze({ row: bottomRow(range), column: range.leftColumn });
7138
+ case "bottomRight":
7139
+ return Object.freeze({ row: bottomRow(range), column: rightColumn(range) });
7140
+ default:
7141
+ return unreachable(corner);
7142
+ }
7143
+ }
7144
+ function handleCentrePx(range, corner, columns, rowHeightPx, firstPaintedRow, sizePx, readable) {
7145
+ const half = sizePx / 2;
7146
+ switch (corner) {
7147
+ case "topRight":
7148
+ return [columns.offsetPxOf(rightColumn(range) + 1), Math.max((range.topRow - firstPaintedRow) * rowHeightPx, readable.topPx) + half];
7149
+ case "bottomLeft":
7150
+ return [
7151
+ (range.leftColumn < columns.pinnedCount ? columns.offsetPxOf(range.leftColumn) : Math.max(columns.offsetPxOf(range.leftColumn), readable.leftPx)) + half,
7152
+ (bottomRow(range) + 1 - firstPaintedRow) * rowHeightPx
7153
+ ];
7154
+ case "bottomRight":
7155
+ return [columns.offsetPxOf(rightColumn(range) + 1), (bottomRow(range) + 1 - firstPaintedRow) * rowHeightPx];
7156
+ default:
7157
+ return unreachable(corner);
7158
+ }
7159
+ }
7160
+ function outlineSides(topRow, firstColumn, columns, pinnedLayer, cover, headingMark) {
7161
+ const top = topRow < cover.firstOpenRow;
7162
+ const left = pinnedLayer ? columns.offsetPxOf(firstColumn) <= columns.leadWidthPx + flushPx : columns.offsetPxOf(firstColumn) - cover.scrollLeftPx <= columns.pinnedWidthPx + flushPx;
7163
+ const headingTop = top && headingMark && cover.hasHeader;
7164
+ const headingLeft = left && headingMark && columns.leadWidthPx > 0 && (pinnedLayer || columns.pinnedCount === 0);
7165
+ const flags = (top && !headingTop ? 1 : 0) | (left && !headingLeft ? 2 : 0) | (headingTop ? 4 : 0) | (headingLeft ? 8 : 0);
7166
+ return itemAt(outlineSideStyles, flags);
7167
+ }
7168
+ function focusAlone(range, side, focus) {
7169
+ return range.rowCount === 1 && side.first === side.last && focus.row === range.topRow && focus.column === side.first;
7170
+ }
7171
+ function part(range, columns, rowHeightPx, painted, pinnedLayer) {
7172
+ const side = sideOf(range, columns, pinnedLayer);
7173
+ const rows = clip(range, painted);
7174
+ if (side === void 0 || rows === void 0) return void 0;
7175
+ return rect(columns.offsetPxOf(side.first), columns.offsetPxOf(side.last + 1) - columns.offsetPxOf(side.first), rows, rowHeightPx, painted.start);
7176
+ }
7177
+ function sideOf(range, columns, pinnedLayer) {
7178
+ const first = pinnedLayer ? range.leftColumn : Math.max(range.leftColumn, columns.pinnedCount);
7179
+ const last = pinnedLayer ? Math.min(rightColumn(range), columns.pinnedCount - 1) : rightColumn(range);
7180
+ return first > last ? void 0 : { first, last };
7181
+ }
7182
+ function clip(range, painted) {
7183
+ const top = Math.max(range.topRow, painted.start - 1);
7184
+ const bottom = Math.min(bottomRow(range), painted.start + painted.count);
7185
+ return bottom < top ? void 0 : { top, count: bottom - top + 1 };
7186
+ }
7187
+ function rect(leftPx, widthPx, rows, rowHeightPx, firstPaintedRow) {
7188
+ const topPx = (rows.top - firstPaintedRow) * rowHeightPx;
7189
+ return `left: ${leftPx}px; top: ${topPx}px; width: ${widthPx}px; height: ${rows.count * rowHeightPx}px`;
7190
+ }
7191
+
6977
7192
  // src/query/sort.ts
6978
7193
  function nextSort(current, column) {
6979
7194
  if (column === "") throw new TypeError("A sort names a column.");
@@ -7840,6 +8055,81 @@ var InMemoryRowMarks = class {
7840
8055
  }
7841
8056
  };
7842
8057
 
8058
+ // src/finding/find-all.ts
8059
+ function findAllPage(total, matches3, rowSequenceVersion) {
8060
+ if (!Number.isInteger(total) || total < 0) throw new RangeError(`A Find All counts 0 matches or more, not ${total}.`);
8061
+ if (matches3.length > total) throw new RangeError(`A Find All page holds ${matches3.length} matches of a list of ${total}.`);
8062
+ return Object.freeze({
8063
+ total,
8064
+ rowSequenceVersion,
8065
+ matches: Object.freeze(matches3.map(({ row, column, text: text3, rowLabel }) => Object.freeze(rowLabel === void 0 ? { row, column, text: text3 } : { row, column, text: text3, rowLabel })))
8066
+ });
8067
+ }
8068
+ var FindAllMatches = class _FindAllMatches {
8069
+ #rows;
8070
+ #columns;
8071
+ #texts;
8072
+ #ordinals;
8073
+ #rowLabel;
8074
+ constructor(rows, columns, texts, ordinals, rowLabel) {
8075
+ this.#rows = rows;
8076
+ this.#columns = columns;
8077
+ this.#texts = texts;
8078
+ this.#ordinals = ordinals;
8079
+ this.#rowLabel = rowLabel;
8080
+ }
8081
+ /**
8082
+ * Walks `rows` once (ADR-0470 §2), by rows and across a row in the request's column order. `textOf` answers a
8083
+ * column's displayed text by name, or `undefined` for a column with no text to search or one not known, which is
8084
+ * skipped, as a step skips it. An empty pattern matches nothing. `rowLabel`, where given, names each match's row in
8085
+ * the Consumer's words.
8086
+ */
8087
+ static of(rows, request, textOf4, rowLabel) {
8088
+ const columns = Object.freeze([...request.columns]);
8089
+ const texts = columns.map(textOf4);
8090
+ const matches3 = matcherOf(request);
8091
+ const ordinals = [];
8092
+ if (request.text !== "") {
8093
+ const columnCount = columns.length;
8094
+ rows.forEach((item, row) => {
8095
+ texts.forEach((text3, column) => {
8096
+ if (text3 === void 0) return;
8097
+ if (request.scope !== void 0 && !inScope(request.scope, row, column)) return;
8098
+ if (matches3(text3(item))) ordinals.push(row * columnCount + column);
8099
+ });
8100
+ });
8101
+ }
8102
+ return new _FindAllMatches(rows, columns, texts, Object.freeze(ordinals), rowLabel);
8103
+ }
8104
+ /** How many cells match. */
8105
+ get total() {
8106
+ return this.#ordinals.length;
8107
+ }
8108
+ /** The entries from `start`, `count` of them or every one left, under `rowSequenceVersion`. A range past the end
8109
+ * holds none; a range outside the list's positions is refused. */
8110
+ page(start, count, rowSequenceVersion) {
8111
+ if (!Number.isInteger(start) || start < 0) throw new RangeError(`A Find All page starts at an entry at or after 0, not ${start}.`);
8112
+ if (!Number.isInteger(count) || count < 1) throw new RangeError(`A Find All page asks for 1 entry or more, not ${count}.`);
8113
+ const columnCount = this.#columns.length;
8114
+ const matches3 = this.#ordinals.slice(start, start + count).map((ordinal2) => {
8115
+ const row = Math.floor(ordinal2 / columnCount);
8116
+ const column = ordinal2 % columnCount;
8117
+ const text3 = itemAt(this.#texts, column);
8118
+ if (text3 === void 0) throw new Error("Internal: a Find All match in a column with no text.");
8119
+ const item = itemAt(this.#rows, row);
8120
+ const match = { row, column: itemAt(this.#columns, column), text: text3(item) };
8121
+ if (this.#rowLabel === void 0) return match;
8122
+ const label = this.#rowLabel(item);
8123
+ if (typeof label !== "string") throw new TypeError(`findAllRowLabel answers text for every row, not ${typeof label} (ADR-0470 \xA73).`);
8124
+ return { ...match, rowLabel: label };
8125
+ });
8126
+ return findAllPage(this.total, matches3, rowSequenceVersion);
8127
+ }
8128
+ };
8129
+ function findAll(rows, rowSequenceVersion, request, textOf4) {
8130
+ return FindAllMatches.of(rows, request, textOf4).page(request.start, request.count, rowSequenceVersion);
8131
+ }
8132
+
7843
8133
  // src/source/row-key.ts
7844
8134
  function keyOf2(rowKey, row, which2) {
7845
8135
  return checkedKey(rowKey(row), which2);
@@ -8560,8 +8850,13 @@ var InMemoryGridSource = class _InMemoryGridSource {
8560
8850
  #marks;
8561
8851
  #columns;
8562
8852
  #state;
8563
- constructor(rows, { rowKey, clock = systemClock }) {
8853
+ // The last Find All's matches, kept while the result, the columns and the question stand, so a list scrolled
8854
+ // through is walked once (ADR-0470 §2).
8855
+ #findAllRowLabel;
8856
+ #findAll;
8857
+ constructor(rows, { rowKey, clock = systemClock, findAllRowLabel }) {
8564
8858
  this.#rowKey = rowKey;
8859
+ this.#findAllRowLabel = findAllRowLabel;
8565
8860
  let window2;
8566
8861
  if (rowKey === void 0) {
8567
8862
  this.#rows = Object.freeze([...rows]);
@@ -8806,6 +9101,24 @@ var InMemoryGridSource = class _InMemoryGridSource {
8806
9101
  const columns = this.#queryColumns();
8807
9102
  return findStep(this.#state.window, request, (name) => textsOf(columns, name, textFor));
8808
9103
  }
9104
+ /**
9105
+ * The reference Find All (ADR-0470): `findAll` over the current result, matching each column's displayed text as
9106
+ * `find` does. The matches are found once for a question and read a page at a time while the result stands. A
9107
+ * request read under another version is answered anyway, under this source's: the grid discards it by the
9108
+ * version.
9109
+ */
9110
+ async findAll(request, signal) {
9111
+ signal.throwIfAborted();
9112
+ const columns = this.#queryColumns();
9113
+ const { window: window2, rowSequenceVersion } = this.#state;
9114
+ const key2 = JSON.stringify([request.text, request.matchCase, request.wholeCell, request.scope, request.columns]);
9115
+ let held2 = this.#findAll;
9116
+ if (held2?.key !== key2 || held2.window !== window2 || held2.columns !== columns) {
9117
+ held2 = { key: key2, window: window2, columns, matches: FindAllMatches.of(window2, request, (name) => textsOf(columns, name, textFor), this.#findAllRowLabel) };
9118
+ this.#findAll = held2;
9119
+ }
9120
+ return held2.matches.page(request.start, request.count, rowSequenceVersion);
9121
+ }
8809
9122
  /**
8810
9123
  * The reference Selection Summary (ADR-0130): `summaryOf` over the current result, reading each column's
8811
9124
  * value. A column this source was not told about, or one with no value of its own, is blank. It never
@@ -9425,172 +9738,38 @@ var ClipboardLedger = class _ClipboardLedger {
9425
9738
  const write = this.#pending.shift() ?? null;
9426
9739
  this.#heard.set(change, write);
9427
9740
  return write;
9428
- }
9429
- };
9430
-
9431
- // src/dom/column-styles.ts
9432
- var ColumnStyles = class {
9433
- /** The geometry the styles are made from. */
9434
- geometry;
9435
- #cells = [];
9436
- #pinnedCells = [];
9437
- #gaps = [];
9438
- constructor(geometry) {
9439
- this.geometry = geometry;
9440
- }
9441
- /** A scrollable cell's width. */
9442
- cell(column) {
9443
- return this.#cells[column] ??= `width: ${this.geometry.widthPxOf(column)}px`;
9444
- }
9445
- /** A pinned cell's width and its own offset: a sticky cell still occupies its place in the flow, so it
9446
- * sticks exactly where it would otherwise have been. */
9447
- pinnedCell(column) {
9448
- return this.#pinnedCells[column] ??= `width: ${this.geometry.widthPxOf(column)}px; left: ${this.geometry.offsetPxOf(column)}px`;
9449
- }
9450
- /** The spacer standing for the scrollable columns virtualised away to the left (ADR-0004). The pinned
9451
- * block is already in the flow ahead of it, so its width comes off. */
9452
- gap(firstScrollable) {
9453
- return this.#gaps[firstScrollable] ??= `width: ${Math.max(0, this.geometry.offsetPxOf(firstScrollable) - this.geometry.pinnedWidthPx)}px`;
9454
- }
9455
- /** Whether any scrollable column lies to the left of `firstScrollable`, so a gap stands for it. */
9456
- hasGap(firstScrollable) {
9457
- return this.geometry.offsetPxOf(firstScrollable) > this.geometry.pinnedWidthPx;
9458
- }
9459
- };
9460
-
9461
- // src/paint/selection-styles.ts
9462
- var outlineSideStyles = Object.freeze(Array.from({ length: 16 }, (_, flags) => ((flags & 1) === 0 ? "" : "; --ex-outline-in-t: 1") + ((flags & 2) === 0 ? "" : "; --ex-outline-in-l: 1") + ((flags & 4) === 0 ? "" : "; --ex-outline-heading-t: 1") + ((flags & 8) === 0 ? "" : "; --ex-outline-heading-l: 1")));
9463
- var flushPx = 0.01;
9464
- function cellRange(cell2) {
9465
- return selectionRange(cell2.row, cell2.column, 1, 1);
9466
- }
9467
- function box(range, focus, columns, rowHeightPx, painted, pinnedLayer, cover) {
9468
- if (rangeCellCount(range) === 1 && rangeContains(range, focus)) return void 0;
9469
- const style = whole3(range, columns, rowHeightPx, painted, pinnedLayer, rowHeightPx);
9470
- const side = sideOf(range, columns, pinnedLayer);
9471
- if (style === void 0 || side === void 0) return void 0;
9472
- return style + outlineSides(range.topRow, side.first, columns, pinnedLayer, cover, true);
9473
- }
9474
- function tint(range, focus, columns, rowHeightPx, painted, pinnedLayer) {
9475
- const side = sideOf(range, columns, pinnedLayer);
9476
- if (side === void 0 || focusAlone(range, side, focus)) return void 0;
9477
- const rows = clip(range, painted);
9478
- if (rows === void 0) return void 0;
9479
- const leftPx = columns.offsetPxOf(side.first);
9480
- let style = rect(leftPx, columns.offsetPxOf(side.last + 1) - leftPx, rows, rowHeightPx, painted.start);
9481
- if (rangeContains(range, focus) && sideOf(cellRange(focus), columns, pinnedLayer) !== void 0 && focus.row >= rows.top && focus.row < rows.top + rows.count) {
9482
- const holeLeftPx = columns.offsetPxOf(focus.column) - leftPx;
9483
- const holeRightPx = columns.offsetPxOf(focus.column + 1) - leftPx;
9484
- const holeTopPx = (focus.row - rows.top) * rowHeightPx;
9485
- const holeBottomPx = holeTopPx + rowHeightPx;
9486
- style += `; --ex-range-hole: polygon(evenodd, 0 0, 100% 0, 100% 100%, 0 100%, 0 0, ${holeLeftPx}px ${holeTopPx}px, ${holeRightPx}px ${holeTopPx}px, ${holeRightPx}px ${holeBottomPx}px, ${holeLeftPx}px ${holeBottomPx}px, ${holeLeftPx}px ${holeTopPx}px)`;
9487
- }
9488
- return style;
9489
- }
9490
- function tinted(range, focus, columns, painted, pinnedLayer) {
9491
- const side = sideOf(range, columns, pinnedLayer);
9492
- return side !== void 0 && !focusAlone(range, side, focus) && clip(range, painted) !== void 0;
9493
- }
9494
- function reaches(range, columns, painted, pinnedLayer) {
9495
- return sideOf(range, columns, pinnedLayer) !== void 0 && clip(range, painted) !== void 0;
9496
- }
9497
- function whole3(range, columns, rowHeightPx, painted, pinnedLayer, outsidePx = 0) {
9498
- const rows = clip(range, painted);
9499
- if (sideOf(range, columns, pinnedLayer) === void 0 || rows === void 0) return void 0;
9500
- const leftPx = columns.offsetPxOf(range.leftColumn);
9501
- const rightPx = columns.offsetPxOf(rightColumn(range) + 1);
9502
- let style = rect(leftPx, rightPx - leftPx, rows, rowHeightPx, painted.start);
9503
- if (sideOf(range, columns, !pinnedLayer) !== void 0) {
9504
- const boundaryPx = columns.offsetPxOf(columns.pinnedCount);
9505
- const o = outsidePx > 0 ? `${-outsidePx}px` : "0";
9506
- style += pinnedLayer ? `; clip-path: inset(${o} ${rightPx - boundaryPx}px ${o} ${o})` : `; clip-path: inset(${o} ${o} ${o} ${boundaryPx - leftPx}px)`;
9507
- }
9508
- return style;
9509
- }
9510
- function copied(range, columns, rowHeightPx, painted, pinnedLayer, cover) {
9511
- const style = whole3(range, columns, rowHeightPx, painted, pinnedLayer, rowHeightPx);
9512
- const side = sideOf(range, columns, pinnedLayer);
9513
- if (style === void 0 || side === void 0) return void 0;
9514
- return style + outlineSides(range.topRow, side.first, columns, pinnedLayer, cover, false);
9515
- }
9516
- function focusStyle(focus, columns, rowHeightPx, painted, pinnedLayer, cover) {
9517
- const style = part(cellRange(focus), columns, rowHeightPx, painted, pinnedLayer);
9518
- if (style === void 0) return void 0;
9519
- return style + outlineSides(focus.row, focus.column, columns, pinnedLayer, cover, true);
9520
- }
9521
- function scrollable(range, columns, rowHeightPx, painted) {
9522
- return part(range, columns, rowHeightPx, painted, false);
9523
- }
9524
- function pinned(range, columns, rowHeightPx, painted) {
9525
- return part(range, columns, rowHeightPx, painted, true);
9526
- }
9527
- function handle(range, corner, columns, rowHeightPx, painted, sizePx, pinnedLayer, readable) {
9528
- const cell2 = handleCell(range, corner);
9529
- if (sideOf(cellRange(cell2), columns, pinnedLayer) === void 0) return void 0;
9530
- const heldAtTheTop = corner === "topRight";
9531
- if (!heldAtTheTop && cell2.row < painted.start - 1 || cell2.row > painted.start + painted.count) return void 0;
9532
- const [leftPx, topPx] = handleCentrePx(range, corner, columns, rowHeightPx, painted.start, sizePx, readable);
9533
- return `left: ${leftPx - sizePx / 2}px; top: ${topPx - sizePx / 2}px; width: ${sizePx}px; height: ${sizePx}px`;
9534
- }
9535
- function handleCell(range, corner) {
9536
- switch (corner) {
9537
- case "topRight":
9538
- return Object.freeze({ row: range.topRow, column: rightColumn(range) });
9539
- case "bottomLeft":
9540
- return Object.freeze({ row: bottomRow(range), column: range.leftColumn });
9541
- case "bottomRight":
9542
- return Object.freeze({ row: bottomRow(range), column: rightColumn(range) });
9543
- default:
9544
- return unreachable(corner);
9545
- }
9546
- }
9547
- function handleCentrePx(range, corner, columns, rowHeightPx, firstPaintedRow, sizePx, readable) {
9548
- const half = sizePx / 2;
9549
- switch (corner) {
9550
- case "topRight":
9551
- return [columns.offsetPxOf(rightColumn(range) + 1), Math.max((range.topRow - firstPaintedRow) * rowHeightPx, readable.topPx) + half];
9552
- case "bottomLeft":
9553
- return [
9554
- (range.leftColumn < columns.pinnedCount ? columns.offsetPxOf(range.leftColumn) : Math.max(columns.offsetPxOf(range.leftColumn), readable.leftPx)) + half,
9555
- (bottomRow(range) + 1 - firstPaintedRow) * rowHeightPx
9556
- ];
9557
- case "bottomRight":
9558
- return [columns.offsetPxOf(rightColumn(range) + 1), (bottomRow(range) + 1 - firstPaintedRow) * rowHeightPx];
9559
- default:
9560
- return unreachable(corner);
9561
- }
9562
- }
9563
- function outlineSides(topRow, firstColumn, columns, pinnedLayer, cover, headingMark) {
9564
- const top = topRow < cover.firstOpenRow;
9565
- const left = pinnedLayer ? columns.offsetPxOf(firstColumn) <= columns.leadWidthPx + flushPx : columns.offsetPxOf(firstColumn) - cover.scrollLeftPx <= columns.pinnedWidthPx + flushPx;
9566
- const headingTop = top && headingMark && cover.hasHeader;
9567
- const headingLeft = left && headingMark && columns.leadWidthPx > 0 && (pinnedLayer || columns.pinnedCount === 0);
9568
- const flags = (top && !headingTop ? 1 : 0) | (left && !headingLeft ? 2 : 0) | (headingTop ? 4 : 0) | (headingLeft ? 8 : 0);
9569
- return itemAt(outlineSideStyles, flags);
9570
- }
9571
- function focusAlone(range, side, focus) {
9572
- return range.rowCount === 1 && side.first === side.last && focus.row === range.topRow && focus.column === side.first;
9573
- }
9574
- function part(range, columns, rowHeightPx, painted, pinnedLayer) {
9575
- const side = sideOf(range, columns, pinnedLayer);
9576
- const rows = clip(range, painted);
9577
- if (side === void 0 || rows === void 0) return void 0;
9578
- return rect(columns.offsetPxOf(side.first), columns.offsetPxOf(side.last + 1) - columns.offsetPxOf(side.first), rows, rowHeightPx, painted.start);
9579
- }
9580
- function sideOf(range, columns, pinnedLayer) {
9581
- const first = pinnedLayer ? range.leftColumn : Math.max(range.leftColumn, columns.pinnedCount);
9582
- const last = pinnedLayer ? Math.min(rightColumn(range), columns.pinnedCount - 1) : rightColumn(range);
9583
- return first > last ? void 0 : { first, last };
9584
- }
9585
- function clip(range, painted) {
9586
- const top = Math.max(range.topRow, painted.start - 1);
9587
- const bottom = Math.min(bottomRow(range), painted.start + painted.count);
9588
- return bottom < top ? void 0 : { top, count: bottom - top + 1 };
9589
- }
9590
- function rect(leftPx, widthPx, rows, rowHeightPx, firstPaintedRow) {
9591
- const topPx = (rows.top - firstPaintedRow) * rowHeightPx;
9592
- return `left: ${leftPx}px; top: ${topPx}px; width: ${widthPx}px; height: ${rows.count * rowHeightPx}px`;
9593
- }
9741
+ }
9742
+ };
9743
+
9744
+ // src/dom/column-styles.ts
9745
+ var ColumnStyles = class {
9746
+ /** The geometry the styles are made from. */
9747
+ geometry;
9748
+ #cells = [];
9749
+ #pinnedCells = [];
9750
+ #gaps = [];
9751
+ constructor(geometry) {
9752
+ this.geometry = geometry;
9753
+ }
9754
+ /** A scrollable cell's width. */
9755
+ cell(column) {
9756
+ return this.#cells[column] ??= `width: ${this.geometry.widthPxOf(column)}px`;
9757
+ }
9758
+ /** A pinned cell's width and its own offset: a sticky cell still occupies its place in the flow, so it
9759
+ * sticks exactly where it would otherwise have been. */
9760
+ pinnedCell(column) {
9761
+ return this.#pinnedCells[column] ??= `width: ${this.geometry.widthPxOf(column)}px; left: ${this.geometry.offsetPxOf(column)}px`;
9762
+ }
9763
+ /** The spacer standing for the scrollable columns virtualised away to the left (ADR-0004). The pinned
9764
+ * block is already in the flow ahead of it, so its width comes off. */
9765
+ gap(firstScrollable) {
9766
+ return this.#gaps[firstScrollable] ??= `width: ${Math.max(0, this.geometry.offsetPxOf(firstScrollable) - this.geometry.pinnedWidthPx)}px`;
9767
+ }
9768
+ /** Whether any scrollable column lies to the left of `firstScrollable`, so a gap stands for it. */
9769
+ hasGap(firstScrollable) {
9770
+ return this.geometry.offsetPxOf(firstScrollable) > this.geometry.pinnedWidthPx;
9771
+ }
9772
+ };
9594
9773
 
9595
9774
  // src/dom/cell-editing.ts
9596
9775
  var waitingStyle = "left: 0px; top: 0px; width: 0px; height: 0px";
@@ -10278,6 +10457,8 @@ var FindPanel = class {
10278
10457
  #replace;
10279
10458
  // Whether a Replace All is out with the Consumer.
10280
10459
  #waiting = false;
10460
+ #findAllButton;
10461
+ #findAllPart;
10281
10462
  constructor(doc, held2, words2, answers) {
10282
10463
  this.#doc = doc;
10283
10464
  this.#held = held2;
@@ -10293,7 +10474,12 @@ var FindPanel = class {
10293
10474
  });
10294
10475
  const actions = doc.createElement("div");
10295
10476
  actions.className = "ex-popover-actions";
10477
+ this.#findAllButton = this.#button("ex-find-all-button", findPanelLabelIds.findAll, () => {
10478
+ answers.findAll();
10479
+ });
10480
+ this.#findAllButton.hidden = true;
10296
10481
  actions.append(
10482
+ this.#findAllButton,
10297
10483
  this.#button("ex-find-previous", findPanelLabelIds.previous, () => {
10298
10484
  answers.step(true);
10299
10485
  }),
@@ -10364,6 +10550,15 @@ var FindPanel = class {
10364
10550
  say(text3) {
10365
10551
  if (this.#outcome.textContent !== text3) this.#outcome.textContent = text3;
10366
10552
  }
10553
+ offerFindAll(offered) {
10554
+ this.#findAllButton.hidden = !offered;
10555
+ }
10556
+ showFindAll(part2) {
10557
+ if (part2 === this.#findAllPart) return;
10558
+ this.#findAllPart?.remove();
10559
+ this.#findAllPart = part2;
10560
+ if (part2 !== void 0) this.#outcome.after(part2);
10561
+ }
10367
10562
  focusField() {
10368
10563
  this.#field.focus({ preventScroll: true });
10369
10564
  this.#field.select();
@@ -10848,6 +11043,9 @@ var SlottedFindPanel = class {
10848
11043
  #replaceShown = false;
10849
11044
  #waiting = false;
10850
11045
  #outcome = "";
11046
+ #findAllShown = false;
11047
+ #findAllPart;
11048
+ #body;
10851
11049
  #view;
10852
11050
  constructor(doc, mount, held2, label, answers, takeFocus) {
10853
11051
  this.#held = held2;
@@ -10863,6 +11061,7 @@ var SlottedFindPanel = class {
10863
11061
  slot.className = "ex-find-slot";
10864
11062
  form.append(slot);
10865
11063
  body.append(form);
11064
+ this.#body = body;
10866
11065
  const toField = () => {
10867
11066
  this.#request(false);
10868
11067
  };
@@ -10929,6 +11128,17 @@ var SlottedFindPanel = class {
10929
11128
  replaceAll: act(() => {
10930
11129
  answers.replaceAll();
10931
11130
  }),
11131
+ findAll: act(() => {
11132
+ answers.findAll();
11133
+ }),
11134
+ listFindAll: act((list) => {
11135
+ answers.listFindAll(list);
11136
+ }, (list) => {
11137
+ if (typeof list !== "function") throw new TypeError("listFindAll is handed a function that answers the list, asked in its turn (ADR-0470 \xA77).");
11138
+ }),
11139
+ dropFindAll: act(() => {
11140
+ answers.dropFindAll();
11141
+ }),
10932
11142
  close: act(() => {
10933
11143
  answers.close();
10934
11144
  }),
@@ -10960,6 +11170,18 @@ var SlottedFindPanel = class {
10960
11170
  this.#outcome = text3;
10961
11171
  this.#changed();
10962
11172
  }
11173
+ offerFindAll(offered) {
11174
+ if (offered === this.#findAllShown) return;
11175
+ this.#findAllShown = offered;
11176
+ this.#changed();
11177
+ }
11178
+ /** The list stands under the contents, in the core's own place, whichever Chrome draws them (ADR-0470 §3). */
11179
+ showFindAll(part2) {
11180
+ if (part2 === this.#findAllPart) return;
11181
+ this.#findAllPart?.remove();
11182
+ this.#findAllPart = part2;
11183
+ if (part2 !== void 0) this.#body.append(part2);
11184
+ }
10963
11185
  /** Mounts the contents with the next request for the field, whose text is selected. */
10964
11186
  focusField() {
10965
11187
  this.#request(true);
@@ -10982,7 +11204,8 @@ var SlottedFindPanel = class {
10982
11204
  replaceShown: this.#replaceShown,
10983
11205
  replaceText: this.#held.replaceText,
10984
11206
  replaceWaiting: this.#waiting,
10985
- outcome: this.#outcome
11207
+ outcome: this.#outcome,
11208
+ findAllShown: this.#findAllShown
10986
11209
  });
10987
11210
  return this.#view;
10988
11211
  }
@@ -11168,6 +11391,169 @@ var BarField = class _BarField {
11168
11391
  }
11169
11392
  };
11170
11393
 
11394
+ // src/dom/find-all-list.ts
11395
+ var findAllVisibleLines = 8;
11396
+ var lists = 0;
11397
+ var FindAllList = class {
11398
+ /** The part's box, `.ex-find-all`, which the panel places under its contents. */
11399
+ element;
11400
+ /** The listbox, which takes the keyboard for the list's keys. */
11401
+ list;
11402
+ #countLine;
11403
+ #doc;
11404
+ #model;
11405
+ #lineHeightPx;
11406
+ #spacer;
11407
+ #id;
11408
+ #scrollTopPx = 0;
11409
+ // The active line last brought into view: a line is revealed as it becomes active, never again while it stays so,
11410
+ // so the user scrolls the list away from it freely.
11411
+ #revealed;
11412
+ // The lines drawn, by index, so a refresh rewrites only what changed.
11413
+ #drawn = /* @__PURE__ */ new Map();
11414
+ constructor(doc, model, lineHeightPx) {
11415
+ if (!(lineHeightPx > 0)) throw new RangeError(`A Find All list's lines are taller than 0px, not ${lineHeightPx}.`);
11416
+ this.#doc = doc;
11417
+ this.#model = model;
11418
+ this.#lineHeightPx = lineHeightPx;
11419
+ lists += 1;
11420
+ this.#id = `ex-find-all-${lists}`;
11421
+ this.element = doc.createElement("div");
11422
+ this.element.className = "ex-find-all";
11423
+ this.#countLine = doc.createElement("div");
11424
+ this.#countLine.className = "ex-find-all-count";
11425
+ this.#countLine.setAttribute("role", "status");
11426
+ const element = doc.createElement("div");
11427
+ element.className = "ex-find-all-list";
11428
+ element.setAttribute("role", "listbox");
11429
+ element.setAttribute("aria-label", model.label);
11430
+ element.tabIndex = 0;
11431
+ element.setAttribute("data-ex-claims", "keydown click");
11432
+ element.style.height = `${findAllVisibleLines * lineHeightPx}px`;
11433
+ this.#spacer = doc.createElement("div");
11434
+ this.#spacer.className = "ex-find-all-spacer";
11435
+ element.append(this.#spacer);
11436
+ element.addEventListener("scroll", () => {
11437
+ if (element.scrollTop === this.#scrollTopPx) return;
11438
+ this.#scrollTopPx = element.scrollTop;
11439
+ this.refresh();
11440
+ });
11441
+ element.addEventListener("click", (event) => {
11442
+ const line = event.target instanceof Element ? event.target.closest(".ex-find-all-line") : null;
11443
+ if (!(line instanceof HTMLElement) || !element.contains(line)) return;
11444
+ const index = Number(line.dataset["exIndex"]);
11445
+ if (!Number.isInteger(index)) throw new Error("Internal: a Find All line without its index.");
11446
+ model.answered();
11447
+ model.choose(index);
11448
+ });
11449
+ element.addEventListener("keydown", (event) => {
11450
+ if (event.ctrlKey || event.altKey || event.metaKey || event.isComposing) return;
11451
+ const target = this.#keyTarget(event.key);
11452
+ if (target === void 0) return;
11453
+ event.preventDefault();
11454
+ model.answered();
11455
+ if (target >= 0) model.choose(target);
11456
+ });
11457
+ this.list = element;
11458
+ this.element.append(this.#countLine, element);
11459
+ this.refresh();
11460
+ }
11461
+ /** What the count line says: how many cells were found, or why the list went (ADR-0470 §5). */
11462
+ say(text3) {
11463
+ if (this.#countLine.textContent !== text3) this.#countLine.textContent = text3;
11464
+ }
11465
+ // The line a key moves to (ADR-0470 §4), read or not yet, -1 for a key the list takes that moves nowhere, or
11466
+ // `undefined` for a key that is not the list's.
11467
+ #keyTarget(key2) {
11468
+ const count = this.#model.count();
11469
+ if (!["ArrowDown", "ArrowUp", "Home", "End", "Enter", "PageDown", "PageUp"].includes(key2)) return void 0;
11470
+ if (count === void 0 || count === 0) return -1;
11471
+ const active = this.#model.active();
11472
+ const last = count - 1;
11473
+ switch (key2) {
11474
+ case "ArrowDown":
11475
+ return active === void 0 ? 0 : Math.min(last, active + 1);
11476
+ case "ArrowUp":
11477
+ return active === void 0 ? 0 : Math.max(0, active - 1);
11478
+ case "PageDown":
11479
+ return active === void 0 ? 0 : Math.min(last, active + findAllVisibleLines);
11480
+ case "PageUp":
11481
+ return active === void 0 ? 0 : Math.max(0, active - findAllVisibleLines);
11482
+ case "Home":
11483
+ return 0;
11484
+ case "End":
11485
+ return last;
11486
+ default:
11487
+ return active === void 0 ? -1 : active;
11488
+ }
11489
+ }
11490
+ /** Draws the lines in view as the model now holds them, and brings the active line into view. */
11491
+ refresh() {
11492
+ const count = this.#model.count();
11493
+ const lines = count === void 0 ? 0 : count;
11494
+ this.#spacer.style.height = `${lines * this.#lineHeightPx}px`;
11495
+ const active = this.#model.active();
11496
+ if (active !== this.#revealed) {
11497
+ this.#revealed = active;
11498
+ if (active !== void 0 && active < lines) this.#reveal(active);
11499
+ }
11500
+ const first = Math.min(Math.max(0, lines - 1), Math.floor(this.#scrollTopPx / this.#lineHeightPx));
11501
+ const last = Math.min(lines - 1, first + findAllVisibleLines);
11502
+ for (const [index, line] of this.#drawn) {
11503
+ if (index < first || index > last) {
11504
+ line.remove();
11505
+ this.#drawn.delete(index);
11506
+ }
11507
+ }
11508
+ for (let index = first; index <= last; index++) this.#draw(index, index === active);
11509
+ this.list.hidden = lines === 0;
11510
+ if (active !== void 0 && active < lines) this.list.setAttribute("aria-activedescendant", `${this.#id}-${active}`);
11511
+ else this.list.removeAttribute("aria-activedescendant");
11512
+ if (lines > 0) this.#model.inView(first, last);
11513
+ }
11514
+ // The active line scrolled into view, as far as it must, as the grid reveals its Focus.
11515
+ #reveal(index) {
11516
+ const top = index * this.#lineHeightPx;
11517
+ const room = findAllVisibleLines * this.#lineHeightPx;
11518
+ let scrollTop = this.#scrollTopPx;
11519
+ if (top < scrollTop) scrollTop = top;
11520
+ else if (top + this.#lineHeightPx > scrollTop + room) scrollTop = top + this.#lineHeightPx - room;
11521
+ if (scrollTop === this.#scrollTopPx) return;
11522
+ this.#scrollTopPx = scrollTop;
11523
+ this.list.scrollTop = scrollTop;
11524
+ }
11525
+ #draw(index, active) {
11526
+ let line = this.#drawn.get(index);
11527
+ if (line === void 0) {
11528
+ line = this.#doc.createElement("div");
11529
+ line.className = "ex-find-all-line";
11530
+ line.id = `${this.#id}-${index}`;
11531
+ line.setAttribute("role", "option");
11532
+ line.dataset["exIndex"] = String(index);
11533
+ line.style.top = `${index * this.#lineHeightPx}px`;
11534
+ line.style.height = `${this.#lineHeightPx}px`;
11535
+ const where2 = this.#doc.createElement("span");
11536
+ where2.className = "ex-find-all-where";
11537
+ const text4 = this.#doc.createElement("span");
11538
+ text4.className = "ex-find-all-text";
11539
+ line.append(where2, text4);
11540
+ this.#spacer.append(line);
11541
+ this.#drawn.set(index, line);
11542
+ }
11543
+ const read = this.#model.line(index);
11544
+ const [where, text3] = [line.children[0], line.children[1]];
11545
+ if (where === void 0 || text3 === void 0) throw new Error("Internal: a Find All line without its two parts.");
11546
+ const whereText = read === void 0 ? "" : read.where;
11547
+ const shownText = read === void 0 ? "" : read.text;
11548
+ if (where.textContent !== whereText) where.textContent = whereText;
11549
+ if (text3.textContent !== shownText) text3.textContent = shownText;
11550
+ if (read === void 0) line.setAttribute("aria-busy", "true");
11551
+ else line.removeAttribute("aria-busy");
11552
+ line.setAttribute("aria-selected", active ? "true" : "false");
11553
+ line.classList.toggle("ex-find-all-active", active);
11554
+ }
11555
+ };
11556
+
11171
11557
  // src/dom/grid-marks.ts
11172
11558
  var headerChecked = { none: "false", some: "mixed", all: "true" };
11173
11559
  var GridMarks = class {
@@ -11725,10 +12111,11 @@ function namesTheStartOfTheRow(action2) {
11725
12111
  // src/dom/selection-layers.ts
11726
12112
  var pieceOf = (className, style) => style === void 0 ? [] : [{ className, style }];
11727
12113
  var nothing2 = Object.freeze({ shade: void 0, pinned: Object.freeze([]), scrollable: Object.freeze([]) });
12114
+ var noBands = Object.freeze({ hoverRow: void 0, focusRow: false, focusColumn: false, rowCount: 0 });
11728
12115
  var noFill = Object.freeze({ outline: void 0, handle: void 0 });
11729
12116
  var noOutlines = Object.freeze({ references: Object.freeze([]), columns: Object.freeze([]), point: void 0, pointDashes: void 0 });
11730
12117
  var outlineClass = ([, colour]) => `ex-reference-outline ex-reference-${String(colour)}`;
11731
- function selectionLayers(selection, copied2, columns, rowHeightPx, painted, cover, bands = { hoverRow: void 0, focusRow: false }, fill = noFill, outlines = noOutlines) {
12118
+ function selectionLayers(selection, copied2, columns, rowHeightPx, painted, cover, bands = noBands, fill = noFill, outlines = noOutlines) {
11732
12119
  const dashes2 = (pinnedLayer) => copied2.flatMap((range) => pieceOf("ex-copied-range", copied(range, columns, rowHeightPx, painted, pinnedLayer, cover)));
11733
12120
  const bandOf = (row) => ({ topRow: row, leftColumn: 0, rowCount: 1, columnCount: columns.count });
11734
12121
  const fillPieces = (pinnedLayer) => [
@@ -11744,12 +12131,24 @@ function selectionLayers(selection, copied2, columns, rowHeightPx, painted, cove
11744
12131
  // Point's outline is one more rectangle over them; the Selection and the Focus beneath it do not move.
11745
12132
  ...withReferences && outlines.point !== void 0 ? pieceOf(outlines.point.className, pinnedLayer ? pinned(outlines.point.range, columns, rowHeightPx, painted) : scrollable(outlines.point.range, columns, rowHeightPx, painted)) : [],
11746
12133
  // What a press outside the grid wrote for is dashed, Selection or none, cut at the boundary as a column is.
11747
- ...outlines.pointDashes === void 0 ? [] : pieceOf("ex-point-dashes", pinnedLayer ? pinned(outlines.pointDashes, columns, rowHeightPx, painted) : scrollable(outlines.pointDashes, columns, rowHeightPx, painted))
12134
+ ...outlines.pointDashes === void 0 ? [] : pieceOf("ex-point-dashes", pinnedLayer ? pinned(outlines.pointDashes, columns, rowHeightPx, painted) : scrollable(outlines.pointDashes, columns, rowHeightPx, painted)),
12135
+ // The grips last, over everything a layer holds, a later outline's over an earlier's (ADR-0480).
12136
+ ...withReferences && outlines.grips !== void 0 ? gripPieces(outlines.grips, pinnedLayer) : []
11748
12137
  ];
12138
+ const gripPieces = (grips, pinnedLayer) => grips.outlines.flatMap(({ outline, range, named: named3, colour }) => referenceGrips(range, columns, rowHeightPx, painted, pinnedLayer, grips.gripPx, grips.squarePx, pinnedLayer ? grips.pinnedLeftPx : grips.scrollableLeftPx, named3).map(({ kind, leftPx, topPx, widthPx, heightPx }) => ({
12139
+ className: `ex-reference-grip ex-reference-grip-${kind}${gripResizes(kind) ? " ex-reference-square" : ""} ex-reference-${String(colour)}`,
12140
+ style: `left: ${String(leftPx)}px; top: ${String(topPx)}px; width: ${String(widthPx)}px; height: ${String(heightPx)}px${gripResizes(kind) ? `; border-width: ${String(referenceSquareMarginPx)}px` : ""}`,
12141
+ grip: { outline, kind, leftPx, topPx }
12142
+ })));
11749
12143
  const focus = selection.focus;
12144
+ const columnBandsOf = (at) => !bands.focusRow ? [selectionRange(0, at.column, bands.rowCount, 1)] : [
12145
+ ...at.row > 0 ? [selectionRange(0, at.column, at.row, 1)] : [],
12146
+ ...at.row + 1 < bands.rowCount ? [selectionRange(at.row + 1, at.column, bands.rowCount - at.row - 1, 1)] : []
12147
+ ];
11750
12148
  const rowBands = columns.count === 0 ? [] : [
11751
12149
  ...bands.hoverRow === void 0 ? [] : [{ className: "ex-hover-row", range: bandOf(bands.hoverRow) }],
11752
- ...bands.focusRow && focus !== void 0 ? [{ className: "ex-focus-row", range: bandOf(focus.row) }] : []
12150
+ ...bands.focusRow && focus !== void 0 ? [{ className: "ex-focus-row", range: bandOf(focus.row) }] : [],
12151
+ ...bands.focusColumn && focus !== void 0 && bands.rowCount > 0 ? columnBandsOf(focus).map((range) => ({ className: "ex-focus-column", range })) : []
11753
12152
  ];
11754
12153
  const pinnedBands = rowBands.flatMap(({ className, range }) => pieceOf(className, pinned(range, columns, rowHeightPx, painted)));
11755
12154
  const scrollableBands = rowBands.flatMap(({ className, range }) => pieceOf(className, scrollable(range, columns, rowHeightPx, painted)));
@@ -12082,7 +12481,7 @@ function boxClass(status) {
12082
12481
 
12083
12482
  // src/dom/shipped-stylesheet.ts
12084
12483
  var stylesheetName = "ex-grid.css";
12085
- var stylesheetStamp = "670feeb20f929018c24b2d39d2543ffa85fe4ea73b627ca17aa58cec983a3c8f";
12484
+ var stylesheetStamp = "63b729dd8e7c4d0d507d810eee14b23894982022d2efbaec94f42280fcca9d7b";
12086
12485
 
12087
12486
  // src/dom/cell-paint.ts
12088
12487
  var CellPaint = class _CellPaint {
@@ -13373,7 +13772,7 @@ function colourOfKey(keys, key2) {
13373
13772
 
13374
13773
  // src/dom/reference-outlines.ts
13375
13774
  function referenceOutlineOptions(given2) {
13376
- const { referencesIn, onReferenceKeyColoursChanged, outlinedColumns } = given2;
13775
+ const { referencesIn, onReferenceKeyColoursChanged, outlinedColumns, moveReference } = given2;
13377
13776
  if (referencesIn !== void 0 && typeof referencesIn !== "function") {
13378
13777
  throw new TypeError("referencesIn is a function of the editor's text, answering its References (ADR-0057).");
13379
13778
  }
@@ -13383,11 +13782,15 @@ function referenceOutlineOptions(given2) {
13383
13782
  if (outlinedColumns !== void 0 && !Array.isArray(outlinedColumns)) {
13384
13783
  throw new TypeError("outlinedColumns is a list of columns, each named with its colour (ADR-0057).");
13385
13784
  }
13785
+ if (moveReference !== void 0 && typeof moveReference !== "function") {
13786
+ throw new TypeError("moveReference is a function of a Reference's text and the range it is to name (ADR-0480).");
13787
+ }
13386
13788
  const asked = given2.outlinedColumns;
13387
13789
  return {
13388
13790
  referencesIn: given2.referencesIn,
13389
13791
  onReferenceKeyColoursChanged: given2.onReferenceKeyColoursChanged,
13390
- outlinedColumns: asked === void 0 ? void 0 : Object.freeze(asked.map(outlinedColumn))
13792
+ outlinedColumns: asked === void 0 ? void 0 : Object.freeze(asked.map(outlinedColumn)),
13793
+ moveReference: given2.moveReference
13391
13794
  };
13392
13795
  }
13393
13796
  function outlinedColumn(asked, i) {
@@ -13750,6 +14153,13 @@ var MatchMarks = class {
13750
14153
  return shown3 !== void 0 && this.rule === void 0 ? this.#matcher(shown3) : this.#judge(row, column);
13751
14154
  }
13752
14155
  };
14156
+ var findAllPageSize = 100;
14157
+ function isFindAllList(value) {
14158
+ return typeof value === "object" && value !== null && "count" in value && typeof value.count === "number" && "line" in value && typeof value.line === "function" && "choose" in value && typeof value.choose === "function";
14159
+ }
14160
+ function isFindAllLine(value) {
14161
+ return typeof value === "object" && value !== null && "where" in value && typeof value.where === "string" && "text" in value && typeof value.text === "string";
14162
+ }
13753
14163
  var userWriteCellsFollowed = 1024;
13754
14164
  var userWritesKept = 64;
13755
14165
  function checkedPointedAt(given2) {
@@ -13762,6 +14172,16 @@ function checkedPointedAt(given2) {
13762
14172
  function sameDashesAsked(was, now) {
13763
14173
  return was.dashes === now.dashes && was.columns === now.columns && was.window === now.window && was.windowStart === now.windowStart && was.start === now.start && was.count === now.count && was.totalRows === now.totalRows;
13764
14174
  }
14175
+ function oppositeCorner(range, kind) {
14176
+ const top = kind === "bottomLeft" || kind === "bottomRight";
14177
+ const left = kind === "topRight" || kind === "bottomRight";
14178
+ return cell(top ? range.topRow : bottomRow(range), left ? range.leftColumn : rightColumn(range));
14179
+ }
14180
+ function edgeCellOf(range, kind, at) {
14181
+ const row = kind === "top" ? range.topRow : kind === "bottom" ? bottomRow(range) : clamp(at.row, range.topRow, bottomRow(range));
14182
+ const column = kind === "left" ? range.leftColumn : kind === "right" ? rightColumn(range) : clamp(at.column, range.leftColumn, rightColumn(range));
14183
+ return cell(row, column);
14184
+ }
13765
14185
  function toneOf(column, value) {
13766
14186
  const tone = defined(column.tone, "a tone rule")(value);
13767
14187
  if (tone !== "none" && tone !== "positive" && tone !== "negative") {
@@ -13923,6 +14343,7 @@ var GridView = class {
13923
14343
  #handlePointer;
13924
14344
  #handleOutline;
13925
14345
  #highlightFocusRow = false;
14346
+ #highlightFocusColumn = false;
13926
14347
  #highlightHoverRow = false;
13927
14348
  #stripeRows = false;
13928
14349
  // The pager (ADR-0015): the rows a page holds while one stands, and the page shown, transient view state as the
@@ -14185,6 +14606,8 @@ var GridView = class {
14185
14606
  #findHeld = { text: "", matchCase: false, wholeCell: false, replaceText: "" };
14186
14607
  #findOutcome = { kind: "none" };
14187
14608
  #findStepping;
14609
+ // The list of the last Find All while it stands (ADR-0470).
14610
+ #findAll;
14188
14611
  #replacing = 0;
14189
14612
  // A Pending Commit (ADR-0380 section 3), and the stamps given so far. `#input` is the last input the root heard
14190
14613
  // ahead of everything in it: while its dispatch is under way, it is the gesture a commit made in its handler
@@ -14239,7 +14662,12 @@ var GridView = class {
14239
14662
  #topRowOwed;
14240
14663
  #layerInputs;
14241
14664
  // The Reference Outline options (ADR-0057), and the colouring, columns and key colours they make.
14242
- #referenceOptions = { referencesIn: void 0, onReferenceKeyColoursChanged: void 0, outlinedColumns: void 0 };
14665
+ #referenceOptions = {
14666
+ referencesIn: void 0,
14667
+ onReferenceKeyColoursChanged: void 0,
14668
+ outlinedColumns: void 0,
14669
+ moveReference: void 0
14670
+ };
14243
14671
  #referenceOutlines = new ReferenceOutlines();
14244
14672
  // Point (ADR-0051): the options, and the pointing outline as a Focus and an Extent of its own, a one-range
14245
14673
  // selection moved by the transitions the Selection's own use; the span of the text its Reference occupies, and
@@ -14257,6 +14685,10 @@ var GridView = class {
14257
14685
  #pointLength = 0;
14258
14686
  #pointWritten;
14259
14687
  #pointDragging = false;
14688
+ // A Reference Outline's grip drag (ADR-0480): what it was pressed on and against, and what it has reached. The
14689
+ // grips as last drawn, each element with what it is, so a press on one is read without the layout.
14690
+ #gripDrag;
14691
+ #gripElements = /* @__PURE__ */ new WeakMap();
14260
14692
  // Whether what Point wrote was handed in from outside (ADR-0058): no outline stands over this grid's cells for
14261
14693
  // it, and F4 and the arrows change nothing while it stands. With it, the edit as it was before that write.
14262
14694
  #pointedFromOutside = false;
@@ -14903,10 +15335,13 @@ var GridView = class {
14903
15335
  const { showFillHandle, onFill } = options;
14904
15336
  if (showFillHandle !== void 0 && typeof showFillHandle !== "boolean") throw new TypeError("showFillHandle is true or false.");
14905
15337
  if (onFill !== void 0 && typeof onFill !== "function") throw new TypeError("onFill is a function of the fill's intent.");
14906
- const { highlightFocusRow, highlightHoverRow } = options;
15338
+ const { highlightFocusRow, highlightFocusColumn, highlightHoverRow } = options;
14907
15339
  if (highlightFocusRow !== void 0 && typeof highlightFocusRow !== "boolean") {
14908
15340
  throw new TypeError("highlightFocusRow is true or false.");
14909
15341
  }
15342
+ if (highlightFocusColumn !== void 0 && typeof highlightFocusColumn !== "boolean") {
15343
+ throw new TypeError("highlightFocusColumn is true or false.");
15344
+ }
14910
15345
  if (highlightHoverRow !== void 0 && typeof highlightHoverRow !== "boolean") {
14911
15346
  throw new TypeError("highlightHoverRow is true or false.");
14912
15347
  }
@@ -15102,6 +15537,7 @@ var GridView = class {
15102
15537
  showFillHandle: options.showFillHandle === true,
15103
15538
  onFill: options.onFill,
15104
15539
  highlightFocusRow: options.highlightFocusRow === true,
15540
+ highlightFocusColumn: options.highlightFocusColumn === true,
15105
15541
  highlightHoverRow: (options.highlightHoverRow ?? options.presentationDefaults?.highlightHoverRow) === true,
15106
15542
  cellMessage: options.cellMessage,
15107
15543
  declaredKeys,
@@ -15318,6 +15754,7 @@ var GridView = class {
15318
15754
  this.#fillHeard = taken.onFill;
15319
15755
  if (!taken.showFillHandle && this.#handleDrag !== void 0) this.#setDragging(false);
15320
15756
  this.#highlightFocusRow = taken.highlightFocusRow;
15757
+ this.#highlightFocusColumn = taken.highlightFocusColumn;
15321
15758
  if (taken.highlightHoverRow !== this.#highlightHoverRow) {
15322
15759
  this.#highlightHoverRow = taken.highlightHoverRow;
15323
15760
  if (taken.highlightHoverRow) {
@@ -15907,6 +16344,10 @@ var GridView = class {
15907
16344
  this.#taken(event);
15908
16345
  return;
15909
16346
  }
16347
+ if (escape && !event.repeat && this.#gripDrag !== void 0 && this.#cancelGripDrag()) {
16348
+ this.#taken(event);
16349
+ return;
16350
+ }
15910
16351
  if (popover !== void 0 && escape && (onGrid || inPopover) && !innerPopupOpen(popover)) {
15911
16352
  this.#taken(event);
15912
16353
  this.#closePopover(true);
@@ -16392,12 +16833,20 @@ var GridView = class {
16392
16833
  }
16393
16834
  this.#pressHandedOver = false;
16394
16835
  this.#secondaryPress = event.target === this.#viewport && (event.button === 2 || event.button === 0 && event.ctrlKey) ? { at: this.#cellUnder(event), leftPx: event.offsetX - this.#scrollLeftPx, topPx: this.#viewportYOf(event) } : void 0;
16395
- if (event.button !== 0 || event.target !== this.#viewport) return;
16396
- const grabbed = event.shiftKey || event.ctrlKey || event.metaKey ? void 0 : this.#fillHandleUnder(event);
16836
+ const grip = event.button === 0 && event.target instanceof Element ? this.#gripElements.get(event.target) : void 0;
16837
+ if (event.button !== 0 || grip === void 0 && event.target !== this.#viewport) return;
16838
+ const modified = event.shiftKey || event.ctrlKey || event.metaKey || event.altKey;
16839
+ if (grip !== void 0 && !modified && this.#pressGrip(grip.grip, this.#cellAtGrip(event, grip, true))) {
16840
+ this.#answered += 1;
16841
+ event.preventDefault();
16842
+ this.#invalidate();
16843
+ return;
16844
+ }
16845
+ const grabbed = grip !== void 0 || event.shiftKey || event.ctrlKey || event.metaKey ? void 0 : this.#fillHandleUnder(event);
16397
16846
  this.#reconcileSelection();
16398
- const at = this.#cellUnder(event);
16847
+ const at = grip === void 0 ? this.#cellUnder(event) : this.#cellAtGrip(event, grip);
16399
16848
  if (at === void 0) return;
16400
- if (this.#inRowHeadings(event)) {
16849
+ if (grip === void 0 && this.#inRowHeadings(event)) {
16401
16850
  this.#pressRowHeading(event, at.row);
16402
16851
  return;
16403
16852
  }
@@ -16441,6 +16890,12 @@ var GridView = class {
16441
16890
  if (event.target !== this.#viewport) return;
16442
16891
  const at = this.#cellUnder(event);
16443
16892
  if (at === void 0) return;
16893
+ if (this.#gripDrag !== void 0) {
16894
+ this.#moveGripDrag(at);
16895
+ const gripY = this.#viewportYOf(event);
16896
+ if (gripY !== void 0) this.#updateEdgeBand(event.offsetX - this.#scrollLeftPx, gripY);
16897
+ return;
16898
+ }
16444
16899
  if (this.#pointDragging) {
16445
16900
  this.#pointDrag(at);
16446
16901
  return;
@@ -16463,6 +16918,10 @@ var GridView = class {
16463
16918
  // would hear every drag (ADR-0018).
16464
16919
  #onDragEnd = (event) => {
16465
16920
  if (this.#headingDrag && this.#headingHears(event)) this.#moveHeadingDrag(event);
16921
+ if (this.#gripDrag !== void 0 && event.target === this.#viewport) {
16922
+ const at = this.#cellUnder(event);
16923
+ if (at !== void 0) this.#moveGripDrag(at);
16924
+ }
16466
16925
  if (this.#handleDrag !== void 0) {
16467
16926
  this.#finishHandleDrag(event.target === this.#viewport ? this.#cellUnder(event) : void 0);
16468
16927
  return;
@@ -17971,7 +18430,7 @@ var GridView = class {
17971
18430
  const edit = this.#edit;
17972
18431
  if (edit === void 0) return;
17973
18432
  this.#edit = void 0;
17974
- if (this.#endPointing()) this.#setDragging(false);
18433
+ if (this.#endPointing() || this.#gripDrag !== void 0) this.#setDragging(false);
17975
18434
  this.#closeCompletion();
17976
18435
  this.#completionSeen = void 0;
17977
18436
  this.#tellPointState();
@@ -18199,6 +18658,185 @@ var GridView = class {
18199
18658
  this.#pointer = pointer.extendTo(at, this.#extent);
18200
18659
  this.#writePointedReference(edit, false);
18201
18660
  }
18661
+ // ---- Dragging a Reference Outline (ADR-0480) ---------------------------------------------------------------
18662
+ // Whether the open edit's outlines carry grips: a Reference can be dragged while an edit is open whose References
18663
+ // the Consumer answers and rewrites, its commit not waiting on a verdict, and pointed at from outside by no one.
18664
+ #gripsShown() {
18665
+ const { referencesIn, moveReference } = this.#referenceOptions;
18666
+ return this.#edit !== void 0 && referencesIn !== void 0 && moveReference !== void 0 && this.#pending === void 0 && !this.#pointedAtNow();
18667
+ }
18668
+ // The outlines that carry grips, by their place among the colouring's outlines, as they are drawn on the grid.
18669
+ #gripPaint(colouring, rowCount, columnCount) {
18670
+ const visible = this.#visible;
18671
+ const geometry = this.#geometry;
18672
+ if (visible === void 0 || geometry === void 0) return void 0;
18673
+ const outlines = colouring.outlines.flatMap(([range, colour], outline) => {
18674
+ const drawn = withinExtent(range, { rowCount, columnCount });
18675
+ return drawn === void 0 ? [] : [{ outline, range: drawn, named: range, colour }];
18676
+ });
18677
+ if (outlines.length === 0) return void 0;
18678
+ const readable = this.#readableEdge(visible, geometry);
18679
+ return {
18680
+ outlines,
18681
+ gripPx: this.#metrics.referenceGripPx,
18682
+ squarePx: this.#metrics.referenceHandleSizePx,
18683
+ pinnedLeftPx: this.#styles.geometry.leadWidthPx,
18684
+ scrollableLeftPx: readable.leftPx
18685
+ };
18686
+ }
18687
+ // The cell under a press on a grip, from where the grip was drawn and the press's offset inside it: a pinned
18688
+ // layer's coordinates are the Viewport's less the scroll offset, as it is held against the Viewport's left edge.
18689
+ // `along` reads an edge's strip only along its edge: across it, half the strip lies outside the range, past the
18690
+ // last row or column where the range ends there, and the drag holds the edge's own row or column (edgeCellOf),
18691
+ // so the axis across is not read and is answered as 0.
18692
+ #cellAtGrip(event, drawn, along = false) {
18693
+ const geometry = this.#geometry;
18694
+ const visible = this.#visible;
18695
+ if (geometry === void 0 || visible === void 0) return void 0;
18696
+ const x = drawn.grip.leftPx + event.offsetX + (drawn.pinned ? this.#scrollLeftPx : 0);
18697
+ const row = geometry.rowAt(drawn.grip.topPx + event.offsetY + geometry.offsetPxOf(visible.start));
18698
+ const column = this.#styles.geometry.columnAt(x, this.#scrollLeftPx);
18699
+ const { kind } = drawn.grip;
18700
+ if (along && (kind === "left" || kind === "right")) return row === void 0 ? void 0 : cell(row, 0);
18701
+ if (along && (kind === "top" || kind === "bottom")) return column === void 0 ? void 0 : cell(0, column);
18702
+ return row === void 0 || column === void 0 ? void 0 : cell(row, column);
18703
+ }
18704
+ /**
18705
+ * A press on a grip (ADR-0480, section 3): an edge holds the cell of the range nearest the press, a square the
18706
+ * opposite corner, and the drag begins; the text is not touched until the range reached changes. Taken against
18707
+ * the colouring the grips were drawn for: an outline gone, or text changed since, refuses the press, which does
18708
+ * nothing. Whether the press was the grip's.
18709
+ */
18710
+ #pressGrip(grip, at) {
18711
+ const edit = this.#edit;
18712
+ const moveReference = this.#referenceOptions.moveReference;
18713
+ if (edit === void 0 || moveReference === void 0 || !this.#gripsShown()) return false;
18714
+ const text3 = this.#editorText(edit);
18715
+ const colouring = this.#referenceOutlines.asked;
18716
+ const named3 = colouring.references.filter((reference) => reference.range !== void 0)[grip.outline];
18717
+ const from = colouring.outlines[grip.outline]?.[0];
18718
+ const resizes = gripResizes(grip.kind);
18719
+ if (this.#referenceOutlines.askedFor !== text3 || named3 === void 0 || from === void 0 || withinExtent(from, this.#extent) === void 0 || !resizes && at === void 0) return true;
18720
+ const surface = this.#surfaceOf(edit);
18721
+ const held2 = resizes ? oppositeCorner(from, grip.kind) : edgeCellOf(from, grip.kind, defined(at, "the cell an edge was pressed on"));
18722
+ this.#gripDrag = {
18723
+ edit,
18724
+ resizes,
18725
+ from,
18726
+ held: held2,
18727
+ start: named3.start,
18728
+ length: named3.length,
18729
+ written: text3,
18730
+ reached: from,
18731
+ before: {
18732
+ text: text3,
18733
+ selectionStart: surface?.selectionStart ?? -1,
18734
+ selectionEnd: surface?.selectionEnd ?? -1,
18735
+ // oxlint-disable-line ex-grid/no-stand-in-default -- ADR-0480: a caret not known is put back as not known
18736
+ mode: edit.mode,
18737
+ pointer: this.#pointer,
18738
+ pointStart: this.#pointStart,
18739
+ pointLength: this.#pointLength,
18740
+ pointWritten: this.#pointWritten
18741
+ }
18742
+ };
18743
+ this.#root.setAttribute("data-ex-reference-drag", gripCursor(grip.kind));
18744
+ this.#setDragging(true);
18745
+ if (surface !== void 0 && this.#root.ownerDocument.activeElement !== surface) surface.focus({ preventScroll: true });
18746
+ return true;
18747
+ }
18748
+ /**
18749
+ * The drag reached `at` (ADR-0480, section 3): a move takes the range by as many rows and columns as `at` lies
18750
+ * from the cell held, kept inside the grid, or where it reached past the grid when pressed, no further past it
18751
+ * than it was; a resize spans the held corner and `at`. Where the range reached
18752
+ * changed, the Reference is rewritten in place by the Consumer's answer, the caret and Point's span moved with the
18753
+ * text around them. Text changed meanwhile by anything but the drag ends it: the span it holds is no longer the
18754
+ * Reference's. Whether anything changed.
18755
+ */
18756
+ #moveGripDrag(at) {
18757
+ const drag = this.#gripDrag;
18758
+ const edit = this.#edit;
18759
+ const moveReference = this.#referenceOptions.moveReference;
18760
+ if (drag === void 0) return false;
18761
+ if (edit === void 0 || edit !== drag.edit || moveReference === void 0 || this.#editorText(edit) !== drag.written) {
18762
+ this.#setDragging(false);
18763
+ return false;
18764
+ }
18765
+ const extent = this.#extent;
18766
+ const { from, held: held2 } = drag;
18767
+ const reached = drag.resizes ? fromCorners(held2, at) : selectionRange(
18768
+ clamp(from.topRow + at.row - held2.row, 0, Math.max(extent.rowCount - from.rowCount, from.topRow)),
18769
+ clamp(from.leftColumn + at.column - held2.column, 0, Math.max(extent.columnCount - from.columnCount, from.leftColumn)),
18770
+ from.rowCount,
18771
+ from.columnCount
18772
+ );
18773
+ if (sameRange(reached, drag.reached)) return false;
18774
+ const answer = moveReference(drag.written.slice(drag.start, drag.start + drag.length), reached);
18775
+ if (typeof answer !== "string" || answer === "") {
18776
+ throw new TypeError("moveReference answered no text for a dragged Reference: a Reference is a non-empty string (ADR-0480).");
18777
+ }
18778
+ const [text3] = replaceSpan(drag.written, drag.start, drag.length, answer);
18779
+ const end = drag.start + drag.length;
18780
+ const delta = answer.length - drag.length;
18781
+ const moved = (place) => place < 0 || place <= drag.start ? place : place >= end ? place + delta : drag.start + answer.length;
18782
+ const surface = this.#surfaceOf(edit);
18783
+ const selectionStart = moved(surface?.selectionStart ?? -1);
18784
+ const selectionEnd = moved(surface?.selectionEnd ?? -1);
18785
+ if (this.#pointWritten !== void 0 && this.#pointingContinues(edit)) {
18786
+ if (this.#pointStart === drag.start && this.#pointer !== void 0) {
18787
+ this.#pointLength = answer.length;
18788
+ this.#pointer = Selection.empty.click(cell(reached.topRow, reached.leftColumn), extent).extendTo(cell(bottomRow(reached), rightColumn(reached)), extent);
18789
+ } else if (this.#pointStart >= end) {
18790
+ this.#pointStart += delta;
18791
+ }
18792
+ this.#pointWritten = text3;
18793
+ }
18794
+ this.#writeEditorText(edit, text3);
18795
+ if (this.#formulaBarDeclared.shown) this.#bar.showText(text3);
18796
+ if (surface !== void 0 && selectionStart >= 0) surface.setSelectionRange(selectionStart, Math.max(selectionStart, selectionEnd));
18797
+ drag.length = answer.length;
18798
+ drag.written = text3;
18799
+ drag.reached = reached;
18800
+ this.#closeCompletion();
18801
+ this.#askReferences();
18802
+ this.#tellPointState();
18803
+ this.#invalidate();
18804
+ return true;
18805
+ }
18806
+ // Escape mid-drag (ADR-0480, section 6): the edit as the press found it, its text, caret, mode and pointing, and
18807
+ // the drag over. The edit stays open. Text typed since the drag last wrote has ended the drag already, as the
18808
+ // next move would find (#moveGripDrag): the drag is over, nothing is put back, and the Escape is not the drag's.
18809
+ // Whether the Escape was the drag's.
18810
+ #cancelGripDrag() {
18811
+ const drag = this.#gripDrag;
18812
+ const edit = this.#edit;
18813
+ this.#setDragging(false);
18814
+ if (drag === void 0 || edit === void 0 || edit !== drag.edit || this.#editorText(edit) !== drag.written) return false;
18815
+ const { before } = drag;
18816
+ if (this.#editorText(edit) !== before.text) {
18817
+ this.#writeEditorText(edit, before.text);
18818
+ if (this.#formulaBarDeclared.shown) this.#bar.showText(before.text);
18819
+ const surface = this.#surfaceOf(edit);
18820
+ if (surface !== void 0 && before.selectionStart >= 0) surface.setSelectionRange(before.selectionStart, before.selectionEnd);
18821
+ }
18822
+ edit.mode = before.mode;
18823
+ if (before.pointWritten !== void 0) {
18824
+ this.#pointer = before.pointer;
18825
+ this.#pointStart = before.pointStart;
18826
+ this.#pointLength = before.pointLength;
18827
+ this.#pointWritten = before.pointWritten;
18828
+ }
18829
+ this.#askReferences();
18830
+ this.#tellPointState();
18831
+ this.#invalidate();
18832
+ return true;
18833
+ }
18834
+ // The drag is over, wherever it reached: the root's cursor and the grips' pointer events come back.
18835
+ #endGripDrag() {
18836
+ if (this.#gripDrag === void 0) return;
18837
+ this.#gripDrag = void 0;
18838
+ this.#root.removeAttribute("data-ex-reference-drag");
18839
+ }
18202
18840
  // F2 moves between the states (ADR-0010), and where pointing is declared between Caret and Point: from Caret it
18203
18841
  // points where the Consumer says a Reference can go at the caret, and from Point it moves the caret again, the
18204
18842
  // outline gone and its Reference left written (ADR-0051).
@@ -19601,6 +20239,7 @@ var GridView = class {
19601
20239
  if (popover.kind === "find") {
19602
20240
  this.#findStepping?.abort();
19603
20241
  this.#findStepping = void 0;
20242
+ this.#dropFindAll();
19604
20243
  }
19605
20244
  const active = doc.activeElement;
19606
20245
  const ours = active === null || active === doc.body || active === doc.documentElement || this.#root.contains(active);
@@ -19980,7 +20619,17 @@ var GridView = class {
19980
20619
  replaceAll: () => {
19981
20620
  this.#replaceAll();
19982
20621
  },
20622
+ findAll: () => {
20623
+ this.#findAllStart();
20624
+ },
20625
+ listFindAll: (list) => {
20626
+ this.#findAllOwn(list);
20627
+ },
20628
+ dropFindAll: () => {
20629
+ this.#dropFindAll();
20630
+ },
19983
20631
  heldChanged: () => {
20632
+ this.#findAllHeldChanged();
19984
20633
  this.#invalidate();
19985
20634
  },
19986
20635
  answered: this.#answer
@@ -20543,6 +21192,7 @@ var GridView = class {
20543
21192
  return;
20544
21193
  }
20545
21194
  this.#askSummaryAgain();
21195
+ this.#dropFindAll();
20546
21196
  this.#setFindOutcome({ kind: "replaced", count: result.count });
20547
21197
  }
20548
21198
  // A Replace that wrote nothing (ADR-0332 §2): the panel names the cells and why, and the Consumer is told.
@@ -20550,6 +21200,395 @@ var GridView = class {
20550
21200
  this.#setFindOutcome({ kind: "replaceRefused", cells });
20551
21201
  this.#onRefused?.({ kind: "find", reason: "replaceRefused" });
20552
21202
  }
21203
+ // ---- Find All (ADR-0470) -----------------------------------------------------------------------------------
21204
+ // Whether Find All can be asked (ADR-0470 §1): the source lists every match beside finding the next.
21205
+ get #canFindAll() {
21206
+ return this.#source.find !== void 0 && this.#source.findAll !== void 0;
21207
+ }
21208
+ /**
21209
+ * Find All (ADR-0470): the list of every match, asked of the source a page at a time under the version its
21210
+ * positions are read in, with the headers the grid searches itself listed first (ADR-0333). Its first page is an
21211
+ * answer out, as a step's is: the inputs made meanwhile wait for it, and an Escape cancels it (ADR-0451 section 3).
21212
+ * The Focus does not move: the list is the whole set, and a line chosen from it moves the Focus there.
21213
+ */
21214
+ #findAllStart() {
21215
+ if (this.#stepInTurn(() => {
21216
+ this.#findAllStart();
21217
+ })) return;
21218
+ const popover = this.#popover;
21219
+ const source = this.#source;
21220
+ if (popover?.kind !== "find" || this.#findHeld.text === "" || !this.#canFindAll) return;
21221
+ this.#dropFindAll();
21222
+ this.#reconcileSelection();
21223
+ const selection = this.#held.selection;
21224
+ const scope = !selection.isEmpty && selection.cellCount > 1 ? selection.ranges : void 0;
21225
+ const pattern = this.#findPattern();
21226
+ const headers = scope === void 0 ? this.#findHeaders() : [];
21227
+ const request = Object.freeze({
21228
+ ...pattern,
21229
+ scope,
21230
+ columns: Object.freeze(this.#columns.map(({ column }) => column.name)),
21231
+ rowSequenceVersion: this.#state.rowSequenceVersion
21232
+ });
21233
+ const list = {
21234
+ source,
21235
+ sequence: this.#sequence(),
21236
+ request,
21237
+ columnCount: request.columns.length,
21238
+ headers: matchingHeaders(headers, pattern).map((index) => itemAt(headers, index)),
21239
+ rowTotal: void 0,
21240
+ pages: /* @__PURE__ */ new Map(),
21241
+ asking: /* @__PURE__ */ new Map(),
21242
+ active: void 0,
21243
+ activeFor: void 0,
21244
+ view: void 0,
21245
+ gone: void 0,
21246
+ part: void 0,
21247
+ own: void 0
21248
+ };
21249
+ this.#showFindAll(popover.panel, list);
21250
+ this.#askFindAllPage(list, 0, "first");
21251
+ }
21252
+ /**
21253
+ * Find All with a list the find panel slot's contents hold themselves (ADR-0470 §7), as ExSheet's over its
21254
+ * Workbook: asked in its turn, drawn as the grid's own list is, its count and lines the contents', a choice taken in
21255
+ * its turn and handed back to them. It goes when the text or an option changes or the panel closes, never with the
21256
+ * order: what the contents list is theirs to drop.
21257
+ */
21258
+ #findAllOwn(made4) {
21259
+ if (this.#stepInTurn(() => {
21260
+ this.#findAllOwn(made4);
21261
+ })) return;
21262
+ const popover = this.#popover;
21263
+ if (popover?.kind !== "find" || this.#findHeld.text === "") return;
21264
+ this.#dropFindAll();
21265
+ const listed2 = made4();
21266
+ if (!isFindAllList(listed2)) throw new TypeError("listFindAll's function answers a list: { count, line, choose } (ADR-0470 \xA77).");
21267
+ if (!Number.isInteger(listed2.count) || listed2.count < 0) {
21268
+ throw new RangeError(`listFindAll's list holds ${String(listed2.count)} lines: a count is a whole number from 0 (ADR-0470 \xA77).`);
21269
+ }
21270
+ const request = Object.freeze({
21271
+ ...this.#findPattern(),
21272
+ columns: Object.freeze(this.#columns.map(({ column }) => column.name)),
21273
+ rowSequenceVersion: this.#state.rowSequenceVersion
21274
+ });
21275
+ const list = {
21276
+ source: this.#source,
21277
+ sequence: this.#sequence(),
21278
+ request,
21279
+ columnCount: request.columns.length,
21280
+ headers: [],
21281
+ rowTotal: listed2.count,
21282
+ pages: /* @__PURE__ */ new Map(),
21283
+ asking: /* @__PURE__ */ new Map(),
21284
+ active: void 0,
21285
+ activeFor: void 0,
21286
+ view: void 0,
21287
+ gone: void 0,
21288
+ part: void 0,
21289
+ own: listed2
21290
+ };
21291
+ this.#showFindAll(popover.panel, list);
21292
+ this.#invalidate();
21293
+ }
21294
+ // The list's part, made and placed under the panel's contents.
21295
+ #showFindAll(panel, list) {
21296
+ this.#findAll = list;
21297
+ const part2 = new FindAllList(this.#root.ownerDocument, {
21298
+ count: () => this.#findAllCount(list),
21299
+ line: (index) => this.#findAllLine(list, index),
21300
+ active: () => list.active,
21301
+ inView: (first, last) => {
21302
+ this.#findAllInView(list, first, last);
21303
+ },
21304
+ choose: (index) => {
21305
+ this.#findAllChoose(list, index);
21306
+ },
21307
+ answered: this.#answer,
21308
+ label: this.#labelOf(findPanelLabelIds.findAllList)
21309
+ }, this.#metrics.rowHeightPx);
21310
+ list.part = part2;
21311
+ panel.showFindAll(part2.element);
21312
+ this.#sayFindAll(list);
21313
+ }
21314
+ // The lines the list holds: the headers matched and every row's match, none once it has gone, or `undefined`
21315
+ // while the first page is out.
21316
+ #findAllCount(list) {
21317
+ if (list.gone !== void 0) return 0;
21318
+ if (list.own !== void 0) return list.own.count;
21319
+ return list.rowTotal === void 0 ? void 0 : list.headers.length + list.rowTotal;
21320
+ }
21321
+ // Line `index` in the grid's words (ADR-0470 §3), or `undefined` while its page is not read.
21322
+ #findAllLine(list, index) {
21323
+ if (list.own !== void 0) {
21324
+ const line = list.own.line(index);
21325
+ if (!isFindAllLine(line)) throw new TypeError(`listFindAll's list answered line ${String(index)} as something other than { where, text } (ADR-0470 \xA77).`);
21326
+ return line;
21327
+ }
21328
+ const header = list.headers[index];
21329
+ if (header !== void 0) {
21330
+ return { where: filledWords(this.#labelOf(findPanelLabelIds.findAllInHeader), { column: header.text }), text: header.text };
21331
+ }
21332
+ const match = this.#findAllMatchAt(list, index - list.headers.length);
21333
+ if (match === void 0) return void 0;
21334
+ const resolved = this.#columns.find(({ column: column2 }) => column2.name === match.column);
21335
+ const column = resolved === void 0 ? match.column : headerOf(resolved);
21336
+ const where = match.rowLabel === void 0 ? filledWords(this.#labelOf(findPanelLabelIds.findAllInRow), { row: String(match.row + 1), column }) : filledWords(this.#labelOf(findPanelLabelIds.findAllInLabelledRow), { row: match.rowLabel, column });
21337
+ return { where, text: match.text };
21338
+ }
21339
+ // The row match at `at` among the rows' matches, where its page has been read.
21340
+ #findAllMatchAt(list, at) {
21341
+ if (at < 0) return void 0;
21342
+ return list.pages.get(Math.floor(at / findAllPageSize))?.[at % findAllPageSize];
21343
+ }
21344
+ // What the count line says (ADR-0470 §3, §5).
21345
+ #sayFindAll(list) {
21346
+ const count = this.#findAllCount(list);
21347
+ const text3 = list.gone !== void 0 ? this.#labelOf(list.gone === "orderChanged" ? findPanelLabelIds.orderChanged : findPanelLabelIds.sourceChanged) : count === void 0 ? "" : count === 0 ? this.#labelOf(findPanelLabelIds.findAllNone) : count === 1 ? this.#labelOf(findPanelLabelIds.findAllFoundOne) : filledWords(this.#labelOf(findPanelLabelIds.findAllFound), { count: String(count) });
21348
+ list.part?.say(text3);
21349
+ }
21350
+ // The list's lines from `first` to `last` are in view: their pages are asked, and the next one ahead, one at a time
21351
+ // (ADR-0470 §2): a page asked while another is out waits for it.
21352
+ #findAllInView(list, first, last) {
21353
+ list.view = { first, last };
21354
+ if (this.#findAll !== list || list.own !== void 0 || list.gone !== void 0 || list.rowTotal === void 0 || list.asking.size > 0) return;
21355
+ const rows = list.rowTotal;
21356
+ const from = Math.max(0, first - list.headers.length);
21357
+ const to = Math.min(rows - 1, last - list.headers.length + findAllVisibleLines);
21358
+ for (let page = Math.floor(from / findAllPageSize); page <= Math.floor(to / findAllPageSize); page++) {
21359
+ if (!list.pages.has(page)) {
21360
+ this.#askFindAllPage(list, page, void 0);
21361
+ return;
21362
+ }
21363
+ }
21364
+ }
21365
+ /**
21366
+ * One page of the list asked of the source. The first, and one a choice waits on, is an answer out in the Input
21367
+ * Order (ADR-0451 section 3): the inputs made meanwhile wait for it, and an Escape cancels it. `then` runs once it
21368
+ * has landed, before those inputs.
21369
+ */
21370
+ #askFindAllPage(list, page, pended, then) {
21371
+ const { source } = list;
21372
+ const controller = new AbortController();
21373
+ list.asking.set(page, controller);
21374
+ const request = Object.freeze({ ...list.request, start: page * findAllPageSize, count: findAllPageSize });
21375
+ if (pended !== void 0) this.#invalidate();
21376
+ const asked = Promise.resolve().then(() => {
21377
+ if (source.findAll === void 0) throw new Error("Internal: a Find All page asked of a source that lists nothing.");
21378
+ return source.findAll(request, controller.signal);
21379
+ });
21380
+ const next2 = () => {
21381
+ const { view } = list;
21382
+ if (this.#findAll === list && list.gone === void 0 && view !== void 0) this.#findAllInView(list, view.first, view.last);
21383
+ };
21384
+ const landed = asked.then((answer) => {
21385
+ if (this.#destroyed || list.asking.get(page) !== controller) return;
21386
+ list.asking.delete(page);
21387
+ if (pended !== void 0) this.#invalidate();
21388
+ if (this.#findAll === list && list.gone === void 0) this.#findAllAnswered(list, page, request, answer);
21389
+ then?.();
21390
+ next2();
21391
+ }, (error) => {
21392
+ if (this.#destroyed || list.asking.get(page) !== controller) return;
21393
+ list.asking.delete(page);
21394
+ if (pended !== void 0) this.#invalidate();
21395
+ if (error instanceof DOMException && error.name === "AbortError") {
21396
+ if (pended === "first" && this.#findAll === list) this.#dropFindAll();
21397
+ else next2();
21398
+ return;
21399
+ }
21400
+ throw error;
21401
+ });
21402
+ if (pended === void 0) return;
21403
+ this.#pendStep(landed, controller, () => {
21404
+ if (list.asking.get(page) !== controller) return;
21405
+ list.asking.delete(page);
21406
+ if (pended === "first" && this.#findAll === list) this.#dropFindAll();
21407
+ this.#invalidate();
21408
+ });
21409
+ }
21410
+ // A page's answer, judged against what it was asked under (ADR-0470 §2).
21411
+ #findAllAnswered(list, page, request, answer) {
21412
+ const given2 = answer;
21413
+ if (typeof given2 !== "object" || given2 === null || !Array.isArray(given2.matches) || typeof given2.total !== "number" || typeof given2.rowSequenceVersion !== "number") {
21414
+ throw new TypeError("The source's findAll answered something other than a Find All page: { total, matches, rowSequenceVersion }, as findAllPage makes one (ADR-0470).");
21415
+ }
21416
+ if (list.source !== this.#source) {
21417
+ this.#findAllGone(list, "sourceChanged");
21418
+ return;
21419
+ }
21420
+ if (answer.rowSequenceVersion !== request.rowSequenceVersion || list.sequence !== this.#sequence()) {
21421
+ this.#findAllGone(list, "orderChanged");
21422
+ return;
21423
+ }
21424
+ const { total, matches: matches3 } = answer;
21425
+ if (!Number.isInteger(total) || total < 0) throw new RangeError(`The Find All answer counts ${total} matches: a count is a whole number from 0 (ADR-0470).`);
21426
+ if (list.rowTotal !== void 0 && total !== list.rowTotal) {
21427
+ this.#findAllGone(list, "orderChanged");
21428
+ return;
21429
+ }
21430
+ const owed = Math.max(0, Math.min(request.count, total - request.start));
21431
+ if (matches3.length !== owed) {
21432
+ throw new RangeError(`The Find All answer holds ${matches3.length} matches for the entries ${request.start} on of a list of ${total}: it owes ${owed} (ADR-0470 \xA72).`);
21433
+ }
21434
+ let before = -1;
21435
+ for (const match of matches3) {
21436
+ const column = request.columns.indexOf(match.column);
21437
+ const ordinal2 = match.row * list.columnCount + column;
21438
+ if (column < 0 || !Number.isInteger(match.row) || match.row < 0 || match.row >= this.#totalRows || typeof match.text !== "string" || match.rowLabel !== void 0 && typeof match.rowLabel !== "string" || request.scope !== void 0 && !inScope(request.scope, match.row, column) || ordinal2 <= before) {
21439
+ throw new RangeError(`The Find All answer names row ${match.row}, column '${match.column}', outside the request or out of its order: it asked about the columns [${request.columns.join(", ")}] of a result of ${this.#totalRows} rows, by rows (ADR-0470 \xA72).`);
21440
+ }
21441
+ before = ordinal2;
21442
+ }
21443
+ list.rowTotal = total;
21444
+ list.pages.set(page, matches3);
21445
+ this.#sayFindAll(list);
21446
+ list.part?.refresh();
21447
+ }
21448
+ /**
21449
+ * A line chosen from the list (ADR-0470 §4): the Focus moves to its cell and reveals it, as a step that found it
21450
+ * does, or a header becomes the current match. Taken in its turn, against what the list was read under: under
21451
+ * another version or source, the list goes and says so, and nothing moves. A line whose page is not yet read, as
21452
+ * End's in a long list, waits for its page as an answer out, and is chosen once it is in.
21453
+ */
21454
+ #findAllChoose(list, index) {
21455
+ if (this.#stepInTurn(() => {
21456
+ this.#findAllChoose(list, index);
21457
+ })) return;
21458
+ this.#findAllChooseNow(list, index, true);
21459
+ }
21460
+ #findAllChooseNow(list, index, mayAsk) {
21461
+ if (this.#findAll !== list || list.gone !== void 0) return;
21462
+ if (list.own !== void 0) {
21463
+ if (index < 0 || index >= list.own.count) return;
21464
+ const moved = list.own.choose(index);
21465
+ if (typeof moved !== "boolean") throw new TypeError(`listFindAll's choose answers whether it moved, not ${typeof moved} (ADR-0470 \xA77).`);
21466
+ if (moved && this.#findAll === list) {
21467
+ list.active = index;
21468
+ list.activeFor = this.#findCurrent ?? this.#findCurrentHeader;
21469
+ }
21470
+ if (this.#findAll === list) list.part?.refresh();
21471
+ this.#invalidate();
21472
+ return;
21473
+ }
21474
+ if (list.source !== this.#source) {
21475
+ this.#findAllGone(list, "sourceChanged");
21476
+ return;
21477
+ }
21478
+ if (list.sequence !== this.#sequence()) {
21479
+ this.#findAllGone(list, "orderChanged");
21480
+ return;
21481
+ }
21482
+ const header = list.headers[index];
21483
+ const match = header === void 0 ? this.#findAllMatchAt(list, index - list.headers.length) : void 0;
21484
+ if (header === void 0 && match === void 0) {
21485
+ const count = this.#findAllCount(list);
21486
+ if (!mayAsk || count === void 0 || index < list.headers.length || index >= count) return;
21487
+ for (const controller of list.asking.values()) controller.abort();
21488
+ list.asking.clear();
21489
+ this.#askFindAllPage(list, Math.floor((index - list.headers.length) / findAllPageSize), "choice", () => {
21490
+ this.#findAllChooseNow(list, index, false);
21491
+ });
21492
+ return;
21493
+ }
21494
+ this.#findStepping?.abort();
21495
+ this.#findStepping = void 0;
21496
+ this.#reconcileSelection();
21497
+ if (header !== void 0) {
21498
+ const now = this.#findHeaders().find(({ id }) => id === header.id);
21499
+ if (now === void 0) {
21500
+ this.#findAllGone(list, "orderChanged");
21501
+ return;
21502
+ }
21503
+ list.active = index;
21504
+ this.#landOnHeader(now);
21505
+ list.activeFor = this.#findCurrentHeader;
21506
+ } else if (match !== void 0) {
21507
+ const column = this.#columns.findIndex(({ column: declared }) => declared.name === match.column);
21508
+ if (column < 0) {
21509
+ this.#findAllGone(list, "orderChanged");
21510
+ return;
21511
+ }
21512
+ list.active = index;
21513
+ const found = cell(match.row, column);
21514
+ this.#findCurrent = { cell: found, version: this.#sequence() };
21515
+ list.activeFor = this.#findCurrent;
21516
+ this.#findCurrentHeader = void 0;
21517
+ this.#setFindOutcome({ kind: "found" });
21518
+ const held2 = this.#held.selection;
21519
+ this.#apply(list.request.scope !== void 0 && held2.cellCount > 1 && held2.contains(found) ? held2.focusOn(found) : held2.click(found, this.#extent));
21520
+ this.#revealOwed = { target: found, extending: void 0, rowStart: false, stepRows: 0 };
21521
+ this.#revealHeaderOwed = void 0;
21522
+ this.#followArmed = { focus: found, target: found };
21523
+ }
21524
+ list.part?.refresh();
21525
+ this.#invalidate();
21526
+ }
21527
+ // In each pass: the list's current line follows the current match, which a step, a Replace or a placed match may
21528
+ // have moved since the line was chosen (ADR-0470 §4): the line of the new one where its page is read, else none.
21529
+ // A list of the Chrome's own keeps the line it chose: what its contents' current match is, the contents know.
21530
+ #findAllFollowCurrent() {
21531
+ const list = this.#findAll;
21532
+ if (list === void 0 || list.own !== void 0 || list.gone !== void 0) return;
21533
+ const current = this.#findCurrent ?? this.#findCurrentHeader;
21534
+ if (current === list.activeFor) return;
21535
+ list.activeFor = current;
21536
+ list.active = this.#findAllLineOf(list);
21537
+ list.part?.refresh();
21538
+ }
21539
+ // The line the current match stands on among what the list has read, or none.
21540
+ #findAllLineOf(list) {
21541
+ const header = this.#findCurrentHeader;
21542
+ if (header !== void 0) {
21543
+ const at = list.headers.findIndex(({ id }) => id === header.id);
21544
+ return at < 0 ? void 0 : at;
21545
+ }
21546
+ const current = this.#findCurrent;
21547
+ if (current === void 0 || current.version !== list.sequence) return void 0;
21548
+ const column = this.#columns[current.cell.column]?.column.name;
21549
+ for (const [page, matches3] of list.pages) {
21550
+ const at = matches3.findIndex((match) => match.row === current.cell.row && match.column === column);
21551
+ if (at >= 0) return list.headers.length + page * findAllPageSize + at;
21552
+ }
21553
+ return void 0;
21554
+ }
21555
+ // The list read under another order or from another source (ADR-0470 §5): its lines go, the count line says why,
21556
+ // and nothing is asked again.
21557
+ #findAllGone(list, reason) {
21558
+ if (list.gone !== void 0) return;
21559
+ list.gone = reason;
21560
+ for (const controller of list.asking.values()) controller.abort();
21561
+ list.asking.clear();
21562
+ list.pages.clear();
21563
+ list.active = void 0;
21564
+ this.#sayFindAll(list);
21565
+ list.part?.refresh();
21566
+ }
21567
+ // The list taken away (ADR-0470 §5): a new Find All, a new search, the panel closed, a Replace All.
21568
+ #dropFindAll() {
21569
+ const list = this.#findAll;
21570
+ if (list === void 0) return;
21571
+ this.#findAll = void 0;
21572
+ for (const controller of list.asking.values()) controller.abort();
21573
+ list.asking.clear();
21574
+ const popover = this.#popover;
21575
+ if (popover?.kind === "find") popover.panel.showFindAll(void 0);
21576
+ else list.part?.element.remove();
21577
+ }
21578
+ // In each pass: a list whose order or source has moved since it was read goes (ADR-0470 §5).
21579
+ #forgetFindAllOffSequence() {
21580
+ const list = this.#findAll;
21581
+ if (list === void 0 || list.own !== void 0 || list.gone !== void 0) return;
21582
+ if (list.source !== this.#source) this.#findAllGone(list, "sourceChanged");
21583
+ else if (list.sequence !== this.#sequence()) this.#findAllGone(list, "orderChanged");
21584
+ }
21585
+ // The search the panel holds changed: a list of another search goes, so it never lists what the field no longer asks.
21586
+ #findAllHeldChanged() {
21587
+ const list = this.#findAll;
21588
+ if (list === void 0) return;
21589
+ const { text: text3, matchCase, wholeCell } = this.#findHeld;
21590
+ if (list.request.text !== text3 || list.request.matchCase !== matchCase || list.request.wholeCell !== wholeCell) this.#dropFindAll();
21591
+ }
20553
21592
  // ---- The clipboard (ADR-0005) ------------------------------------------------------------------------
20554
21593
  // A copy the browser's command asked for. Refused, the clipboard stays as it was: the default copy of a
20555
21594
  // user-select: none root carries nothing, so the event is left alone. Approved, a Selection the Window
@@ -21070,6 +22109,7 @@ var GridView = class {
21070
22109
  if (!dragging) {
21071
22110
  this.#forgetHeadingDrag();
21072
22111
  this.#endHandleDrag();
22112
+ this.#endGripDrag();
21073
22113
  this.#pointDragging = false;
21074
22114
  this.#pointedDrag = void 0;
21075
22115
  }
@@ -21694,7 +22734,7 @@ var GridView = class {
21694
22734
  if (row !== void 0 && column !== void 0) {
21695
22735
  const extent = this.#extent;
21696
22736
  const current = this.#held.selection;
21697
- changed = this.#handleDrag !== void 0 ? this.#moveHandlePointer(cell(row, column)) : this.#apply(!this.#headingDrag ? current.extendTo(cell(row, column), extent) : this.#headingAxis === "rows" ? current.extendToRow(row, extent, this.#firstVisibleColumn) : current.extendToColumn(column, extent, this.#firstVisibleRow));
22737
+ changed = this.#gripDrag !== void 0 ? this.#moveGripDrag(cell(row, column)) : this.#handleDrag !== void 0 ? this.#moveHandlePointer(cell(row, column)) : this.#apply(!this.#headingDrag ? current.extendTo(cell(row, column), extent) : this.#headingAxis === "rows" ? current.extendToRow(row, extent, this.#firstVisibleColumn) : current.extendToColumn(column, extent, this.#firstVisibleRow));
21698
22738
  }
21699
22739
  if (moved || changed) this.#invalidate();
21700
22740
  };
@@ -21951,13 +22991,18 @@ var GridView = class {
21951
22991
  // A row the source has since taken away has no band; the next movement finds the row there now.
21952
22992
  hoverRow: this.#hoverRow !== void 0 && this.#hoverRow < this.#totalRows ? this.#hoverRow : void 0,
21953
22993
  focusRow: this.#highlightFocusRow,
22994
+ focusColumn: this.#highlightFocusColumn,
21954
22995
  // Each held, so a pass that changed neither text nor columns finds them the same.
21955
22996
  colouring: this.#colouring(),
21956
22997
  extentRows: this.#totalRows,
21957
22998
  extentColumns: this.#columns.length,
21958
22999
  pointer: this.#edit === void 0 ? void 0 : this.#pointer,
21959
23000
  outlinedColumns: this.#referenceOutlines.columns(this.#outlinedColumnsBoth(), this.#columnNames, this.#totalRows),
21960
- pointDashes: this.#pointDashesRange()
23001
+ pointDashes: this.#pointDashesRange(),
23002
+ // The grips (ADR-0480): drawn while a Reference can be dragged, held at the edges of what covers them.
23003
+ grips: this.#gripsShown(),
23004
+ pinnedWidthPx: this.#styles.geometry.pinnedWidthPx,
23005
+ leadWidthPx: this.#styles.geometry.leadWidthPx
21961
23006
  };
21962
23007
  const was = this.#layerInputs;
21963
23008
  if (was === void 0 || Object.entries(inputs).some(([name, value]) => was[name] !== value)) {
@@ -21969,7 +23014,7 @@ var GridView = class {
21969
23014
  inputs.rowHeightPx,
21970
23015
  visible,
21971
23016
  { firstOpenRow, scrollLeftPx: this.#scrollLeftPx, hasHeader: true },
21972
- { hoverRow: inputs.hoverRow, focusRow: inputs.focusRow },
23017
+ { hoverRow: inputs.hoverRow, focusRow: inputs.focusRow, focusColumn: inputs.focusColumn, rowCount: inputs.extentRows },
21973
23018
  this.#fillPaint(handle2, readable),
21974
23019
  {
21975
23020
  references: inputs.colouring.outlines.flatMap(([range, colour]) => {
@@ -21978,17 +23023,23 @@ var GridView = class {
21978
23023
  }),
21979
23024
  columns: inputs.outlinedColumns,
21980
23025
  point: this.#pointPaint(inputs.pointer, inputs.colouring),
21981
- pointDashes: inputs.pointDashes
23026
+ pointDashes: inputs.pointDashes,
23027
+ grips: inputs.grips ? this.#gripPaint(inputs.colouring, inputs.extentRows, inputs.extentColumns) : void 0
21982
23028
  }
21983
23029
  );
21984
23030
  const signature = JSON.stringify(pieces);
21985
23031
  if (signature !== this.#paintedLayers) {
21986
23032
  this.#paintedLayers = signature;
21987
23033
  const fill = (layer, held2) => {
21988
- layer.replaceChildren(...held2.map(({ className, style }) => {
23034
+ const pinned2 = layer !== this.#selectionLayer;
23035
+ layer.replaceChildren(...held2.map(({ className, style, grip }) => {
21989
23036
  const made4 = this.#root.ownerDocument.createElement("div");
21990
23037
  made4.className = className;
21991
23038
  made4.setAttribute("style", style);
23039
+ if (grip !== void 0) {
23040
+ made4.setAttribute("data-ex-grip", grip.kind);
23041
+ this.#gripElements.set(made4, { grip, pinned: pinned2 });
23042
+ }
21992
23043
  return made4;
21993
23044
  }));
21994
23045
  };
@@ -22099,6 +23150,9 @@ var GridView = class {
22099
23150
  this.#write(this.#scroller, "scroller", "style", this.#scrollerStyle());
22100
23151
  this.#write(this.#spacer, "spacer", "style", `width: ${geometry.totalWidthPx}px; height: ${this.#bandHeightPx + scrollHeightPx}px`);
22101
23152
  this.#forgetCurrentMatchOffFocus();
23153
+ this.#forgetFindAllOffSequence();
23154
+ this.#findAllFollowCurrent();
23155
+ if (this.#popover?.kind === "find") this.#popover.panel.offerFindAll(this.#canFindAll);
22102
23156
  this.#paintHeader();
22103
23157
  this.#paintViewport();
22104
23158
  this.#paintStatus();
@@ -23842,8 +24896,11 @@ function wireFindPanel(contents, context, answered2 = false) {
23842
24896
  case "replaceAll":
23843
24897
  context.replaceAll();
23844
24898
  break;
24899
+ case "findAll":
24900
+ context.findAll();
24901
+ break;
23845
24902
  default:
23846
- throw new RangeError(`A find panel's button is marked ${findPanelAttributes.step}="${marked.value}": it is next, previous, replace or replaceAll.`);
24903
+ throw new RangeError(`A find panel's button is marked ${findPanelAttributes.step}="${marked.value}": it is next, previous, replace, replaceAll or findAll.`);
23847
24904
  }
23848
24905
  };
23849
24906
  contents.addEventListener("input", input);
@@ -23891,9 +24948,9 @@ var completionAttributes = Object.freeze({
23891
24948
  candidate: "data-ex-candidate"
23892
24949
  });
23893
24950
  function wireEditorCompletion(contents, context, current) {
23894
- const lists = contents.querySelectorAll(`[${completionAttributes.list}]`);
23895
- if (lists.length > 1) throw new Error(`The completion's contents drew ${lists.length} lists marked ${completionAttributes.list}: mark the one holding the candidates.`);
23896
- const list = lists[0];
24951
+ const lists2 = contents.querySelectorAll(`[${completionAttributes.list}]`);
24952
+ if (lists2.length > 1) throw new Error(`The completion's contents drew ${lists2.length} lists marked ${completionAttributes.list}: mark the one holding the candidates.`);
24953
+ const list = lists2[0];
23897
24954
  if (list !== void 0) {
23898
24955
  list.id = context.listId;
23899
24956
  list.setAttribute("role", "listbox");
@@ -23963,6 +25020,14 @@ var japaneseGridWordTable = /* @__PURE__ */ new Map([
23963
25020
  ["find-replace-refused-cell", "{row} \u884C\u76EE\u3001{column}: {reason}\u3002"],
23964
25021
  ["find-replace-refused-more", "\u307B\u304B {count} \u4EF6\u3002"],
23965
25022
  ["find-not-editable", "\u3053\u306E\u5217\u306F\u7DE8\u96C6\u3067\u304D\u307E\u305B\u3093"],
25023
+ ["find-all", "\u3059\u3079\u3066\u691C\u7D22"],
25024
+ ["find-all-list", "\u691C\u7D22\u7D50\u679C"],
25025
+ ["find-all-found", "{count} \u500B\u306E\u30BB\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F"],
25026
+ ["find-all-found-one", "1 \u500B\u306E\u30BB\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F"],
25027
+ ["find-all-none", "\u4E00\u81F4\u3059\u308B\u30BB\u30EB\u306F\u3042\u308A\u307E\u305B\u3093"],
25028
+ ["find-all-in-row", "{row} \u884C\u76EE\u3001{column}"],
25029
+ ["find-all-in-labelled-row", "{row}\u3001{column}"],
25030
+ ["find-all-in-header", "\u898B\u51FA\u3057 {column}"],
23966
25031
  ["filter-ok", "OK"],
23967
25032
  ["filter-cancel", "\u30AD\u30E3\u30F3\u30BB\u30EB"],
23968
25033
  ["filter-search", "\u691C\u7D22"],
@@ -24592,7 +25657,7 @@ var FetchingGridSource = class _FetchingGridSource {
24592
25657
  #named;
24593
25658
  constructor(fetch, options) {
24594
25659
  if (typeof fetch !== "function") throw new TypeError("GridSource.fetch takes a function that answers a Query with a page.");
24595
- const { readAheadRows = defaultReadAheadRows, distinctValues, find, summarize, rowKey, marks: marks2, onFetchFailed, clock = systemClock } = options;
25660
+ const { readAheadRows = defaultReadAheadRows, distinctValues, find, findAll: findAll2, summarize, rowKey, marks: marks2, onFetchFailed, clock = systemClock } = options;
24596
25661
  if (!Number.isSafeInteger(readAheadRows) || readAheadRows < 0) {
24597
25662
  throw new RangeError(`readAheadRows is a whole number of at least 0, not ${readAheadRows}.`);
24598
25663
  }
@@ -24618,6 +25683,13 @@ var FetchingGridSource = class _FetchingGridSource {
24618
25683
  return find(request, this.#inForce(), signal);
24619
25684
  };
24620
25685
  }
25686
+ if (findAll2 !== void 0) {
25687
+ if (find === void 0) throw new TypeError("A Find All is offered beside a Find: give `find` with `findAll` (ADR-0470 \xA71).");
25688
+ this.findAll = async (request, signal) => {
25689
+ this.#requireUndisposed();
25690
+ return findAll2(request, this.#inForce(), signal);
25691
+ };
25692
+ }
24621
25693
  if (summarize !== void 0) {
24622
25694
  this.summarize = async (request, signal) => {
24623
25695
  this.#requireUndisposed();
@@ -27270,6 +28342,7 @@ export {
27270
28342
  EditorHint,
27271
28343
  FetchingGridSource,
27272
28344
  FetchingRowMarks,
28345
+ FindAllMatches,
27273
28346
  FormatCode,
27274
28347
  GlyphWidthTable,
27275
28348
  GridMetrics,
@@ -27308,6 +28381,8 @@ export {
27308
28381
  exactDecimalDigits,
27309
28382
  filterPanelAttributes,
27310
28383
  filterPanelLabelIds,
28384
+ findAll,
28385
+ findAllPage,
27311
28386
  findMatches,
27312
28387
  findOutcomeText,
27313
28388
  findPanelAttributes,