UNPKG

10.9 kBPlain TextView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4
5/*
6* This is an extension and not part of the main GoJS library.
7* Note that the API for this class may change with any version, even point releases.
8* If you intend to use an extension in production, you should copy the code to your own source directory.
9* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
10* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
11*/
12
13import * as go from '../release/go-module.js';
14
15/**
16 * The RowResizingTool class lets the user resize each row of a named Table Panel in a selected Part.
17 *
18 * If you want to experiment with this extension, try the <a href="../../extensionsJSM/ColumnResizing.html">Column Resizing</a> sample.
19 * @category Tool Extension
20 */
21export class RowResizingTool extends go.Tool {
22 private _handleArchetype: go.GraphObject;
23 private _tableName: string = 'TABLE';
24
25 // internal state
26 private _handle: go.GraphObject | null = null;
27 private _adornedTable: go.Panel | null = null;
28
29 /**
30 * Constructs a RowResizingTool and sets the handle and name of the tool.
31 */
32 constructor() {
33 super();
34 const h: go.Shape = new go.Shape();
35 h.geometryString = 'M0 0 H14 M0 2 H14';
36 h.desiredSize = new go.Size(14, 2);
37 h.cursor = 'row-resize';
38 h.geometryStretch = go.GraphObject.None;
39 h.background = 'rgba(255,255,255,0.5)';
40 h.stroke = 'rgba(30,144,255,0.5)';
41 this._handleArchetype = h;
42 this.name = 'RowResizing';
43 }
44
45 /**
46 * Gets or sets small GraphObject that is copied as a resize handle for each row.
47 * This tool expects that this object's {@link GraphObject#desiredSize} (a.k.a width and height) has been set to real numbers.
48 *
49 * The default value is a {@link Shape} that is a narrow rectangle.
50 */
51 get handleArchetype(): go.GraphObject { return this._handleArchetype; }
52 set handleArchetype(val: go.GraphObject) { this._handleArchetype = val; }
53
54 /**
55 * Gets or sets the name of the Table Panel to be resized.
56 *
57 * The default value is the name "TABLE".
58 */
59 get tableName(): string { return this._tableName; }
60 set tableName(val: string) { this._tableName = val; }
61
62 /**
63 * This read-only property returns the {@link GraphObject} that is the tool handle being dragged by the user.
64 * This will be contained by an {@link Adornment} whose category is "RowResizing".
65 * Its {@link Adornment#adornedObject} is the same as the {@link #adornedTable}.
66 */
67 get handle(): go.GraphObject | null { return this._handle; }
68
69 /**
70 * This read-only property returns the {@link Panel} of type {@link Panel.Table} whose rows are being resized.
71 * This must be contained within the selected {@link Part}.
72 */
73 get adornedTable(): go.Panel | null { return this._adornedTable; }
74
75 /**
76 * Show an {@link Adornment} with a resize handle at each row.
77 * Don't show anything if {@link #tableName} doesn't identify a {@link Panel}
78 * that has a {@link Panel#type} of type {@link Panel.Table}.
79 */
80 public updateAdornments(part: go.Part): void {
81 if (part === null || part instanceof go.Link) return; // this tool never applies to Links
82 if (part.isSelected && !this.diagram.isReadOnly) {
83 const selelt = part.findObject(this.tableName);
84 if (selelt instanceof go.Panel && selelt.actualBounds.isReal() && selelt.isVisibleObject() &&
85 part.actualBounds.isReal() && part.isVisible() &&
86 selelt.type === go.Panel.Table) {
87 const table = selelt;
88 let adornment = part.findAdornment(this.name);
89 if (adornment === null) {
90 adornment = this.makeAdornment(table);
91 part.addAdornment(this.name, adornment);
92 }
93 if (adornment !== null) {
94 const pad = table.padding as go.Margin;
95 const numrows = table.rowCount;
96 // update the position/alignment of each handle
97 adornment.elements.each((h) => {
98 if (!h.pickable) return;
99 const rowdef = table.getRowDefinition(h.row);
100 let hgt = rowdef.actual;
101 if (hgt > 0) hgt = rowdef.total;
102 let sep = 0;
103 // find next non-zero-height row's separatorStrokeWidth
104 let idx = h.row + 1;
105 while (idx < numrows && table.getRowDefinition(idx).actual === 0) idx++;
106 if (idx < numrows) {
107 sep = table.getRowDefinition(idx).separatorStrokeWidth;
108 if (isNaN(sep)) sep = table.defaultRowSeparatorStrokeWidth;
109 }
110 h.alignment = new go.Spot(0, 0, pad.left + h.width / 2, pad.top + rowdef.position + hgt + sep / 2);
111 });
112 adornment.locationObject.desiredSize = table.actualBounds.size;
113 adornment.location = table.getDocumentPoint(adornment.locationSpot);
114 adornment.angle = table.getDocumentAngle();
115 return;
116 }
117 }
118 }
119 part.removeAdornment(this.name);
120 }
121
122 /**
123 * @hidden @internal
124 * @param {Panel} table the Table Panel whose rows may be resized
125 * @return {Adornment}
126 */
127 public makeAdornment(table: go.Panel): go.Adornment {
128 // the Adornment is a Spot Panel holding resize handles
129 const adornment = new go.Adornment();
130 adornment.category = this.name;
131 adornment.adornedObject = table;
132 adornment.type = go.Panel.Spot;
133 adornment.locationObjectName = 'BLOCK';
134 // create the "main" element of the Spot Panel
135 const block = new go.TextBlock(); // doesn't matter much what this is
136 block.name = 'BLOCK';
137 block.pickable = false; // it's transparent and not pickable
138 adornment.add(block);
139 // now add resize handles for each row
140 for (let i = 0; i < table.rowCount; i++) {
141 const rowdef = table.getRowDefinition(i);
142 const h = this.makeHandle(table, rowdef);
143 if (h !== null) adornment.add(h);
144 }
145 return adornment;
146 }
147
148 /**
149 * @hidden @internal
150 * @param {Panel} table the Table Panel whose rows may be resized
151 * @param {RowColumnDefinition} coldef the row definition to be resized
152 * @return a copy of the {@link #handleArchetype}
153 */
154 public makeHandle(table: go.Panel, rowdef: go.RowColumnDefinition): go.GraphObject | null {
155 const h = this.handleArchetype;
156 if (h === null) return null;
157 const c = h.copy();
158 c.row = rowdef.index;
159 return c;
160 }
161
162
163 /**
164 * This tool may run when there is a mouse-down event on a "RowResizing" handle,
165 * the diagram is not read-only, the left mouse button is being used,
166 * and this tool's adornment's resize handle is at the current mouse point.
167 */
168 public canStart(): boolean {
169 if (!this.isEnabled) return false;
170
171 const diagram = this.diagram;
172 if (diagram.isReadOnly) return false;
173 if (!diagram.lastInput.left) return false;
174 const h = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
175 return (h !== null);
176 }
177
178 /**
179 * Find the {@link #handle}, ensure type {@link Panel.Table}, capture the mouse, and start a transaction.
180 *
181 * If the call to {@link Tool#findToolHandleAt} finds no "RowResizing" tool handle, this method returns without activating this tool.
182 */
183 public doActivate(): void {
184 const diagram = this.diagram;
185 this._handle = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
186 if (this.handle === null) return;
187 const panel = (this.handle.part as go.Adornment).adornedObject as go.Adornment;
188 if (!panel || panel.type !== go.Panel.Table) return;
189 this._adornedTable = panel;
190 diagram.isMouseCaptured = true;
191 this.startTransaction(this.name);
192 this.isActive = true;
193 }
194
195 /**
196 * Stop the current transaction and release the mouse.
197 */
198 public doDeactivate(): void {
199 this.stopTransaction();
200 this._handle = null;
201 this._adornedTable = null;
202 const diagram = this.diagram;
203 diagram.isMouseCaptured = false;
204 this.isActive = false;
205 }
206
207 /**
208 * Call {@link #resize} with a new size determined by the current mouse point.
209 * This determines the new bounds by calling {@link #computeResize}.
210 */
211 public doMouseMove(): void {
212 const diagram = this.diagram;
213 if (this.isActive) {
214 const newpt = this.computeResize(diagram.lastInput.documentPoint);
215 this.resize(newpt);
216 }
217 }
218
219 /**
220 * Call {@link #resize} with the final bounds based on the most recent mouse point, and commit the transaction.
221 * This determines the new bounds by calling {@link #computeResize}.
222 */
223 public doMouseUp(): void {
224 const diagram = this.diagram;
225 if (this.isActive) {
226 const newpt = this.computeResize(diagram.lastInput.documentPoint);
227 this.resize(newpt);
228 this.transactionResult = this.name; // success
229 }
230 this.stopTool();
231 }
232
233 /**
234 * Change the {@link RowColumnDefinition#height} of the row being resized
235 * to a value corresponding to the given mouse point.
236 * @param {Point} newPoint the value returned by the call to {@link #computeResize}
237 */
238 public resize(newPoint: go.Point): void {
239 const table = this.adornedTable;
240 if (table === null) return;
241 const h = this.handle;
242 if (h === null) return;
243 const pad = table.padding as go.Margin;
244 const numrows = table.rowCount;
245 const locpt = table.getLocalPoint(newPoint);
246 const rowdef = table.getRowDefinition(h.row);
247 let sep = 0;
248 let idx = h.row + 1;
249 while (idx < numrows && table.getRowDefinition(idx).actual === 0) idx++;
250 if (idx < numrows) {
251 sep = table.getRowDefinition(idx).separatorStrokeWidth;
252 if (isNaN(sep)) sep = table.defaultRowSeparatorStrokeWidth;
253 }
254 rowdef.height = Math.max(0, locpt.y - pad.top - rowdef.position - (rowdef.total - rowdef.actual) - sep / 2);
255 }
256
257
258 /**
259 * This can be overridden in order to customize the resizing process.
260 * @expose
261 * @param {Point} p the point where the handle is being dragged.
262 * @return {Point}
263 */
264 public computeResize(p: go.Point): go.Point {
265 return p;
266 }
267
268 /**
269 * Pressing the Delete key removes any column width setting and stops this tool.
270 */
271 public doKeyDown(): void {
272 if (!this.isActive) return;
273 const diagram = this.diagram;
274 const e = diagram.lastInput;
275 if (e.key === 'Del' || e.key === '\t') { // remove height setting
276 if (this.adornedTable !== null && this.handle !== null) {
277 const rowdef = this.adornedTable.getRowDefinition(this.handle.row);
278 rowdef.height = NaN;
279 this.transactionResult = this.name; // success
280 this.stopTool();
281 }
282 } else {
283 super.doKeyDown();
284 }
285 }
286
287}