1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | import * as go from '../release/go-module.js';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | export class ZoomSlider {
|
56 | private _diagram: go.Diagram;
|
57 | private _initialScale: number;
|
58 | private _diagramDiv: HTMLDivElement | null;
|
59 | private _sliderDiv: HTMLDivElement | null;
|
60 |
|
61 |
|
62 | private _size: number = 125;
|
63 | private _buttonSize: number = 25;
|
64 | private _alignment: go.Spot = go.Spot.BottomRight;
|
65 | private _alignmentFocus: go.Spot = go.Spot.BottomRight;
|
66 | private _orientation: string = 'vertical';
|
67 | private _opacity: number = .75;
|
68 |
|
69 |
|
70 | private updateOnViewportBoundsChanged: ((e: go.DiagramEvent) => void);
|
71 |
|
72 | /**
|
73 | * Constructs a ZoomSlider and sets up properties based on the options provided.
|
74 | * Also sets up change listeners on the Diagram so the ZoomSlider stays up-to-date.
|
75 | * @param {Diagram} diagram a reference to a GoJS Diagram
|
76 | * @param {Object=} options an optional JS Object describing options for the slider
|
77 | */
|
78 | constructor(diagram: go.Diagram, options?: { [index: string]: any}) {
|
79 | this._diagram = diagram;
|
80 | this._initialScale = diagram.scale;
|
81 | this._diagramDiv = diagram.div;
|
82 | this._sliderDiv = null;
|
83 |
|
84 | // Set properties based on options
|
85 | if (options !== undefined) {
|
86 | if (options.size !== undefined) this._size = options.size;
|
87 | if (options.buttonSize !== undefined) this._buttonSize = options.buttonSize;
|
88 | if (options.alignment !== undefined) this._alignment = options.alignment;
|
89 | if (options.alignmentFocus !== undefined) this._alignmentFocus = options.alignmentFocus;
|
90 | if (options.orientation !== undefined) this._orientation = options.orientation;
|
91 | if (options.opacity !== undefined) this._opacity = options.opacity;
|
92 | }
|
93 |
|
94 | // Prepare change listeners
|
95 | const self = this;
|
96 | this.updateOnViewportBoundsChanged = (e: go.DiagramEvent) => { self.scaleToValue(); };
|
97 |
|
98 | this.init();
|
99 | }
|
100 |
|
101 | |
102 |
|
103 |
|
104 | get diagram(): go.Diagram { return this._diagram; }
|
105 |
|
106 | |
107 |
|
108 |
|
109 |
|
110 | get size(): number { return this._size; }
|
111 | set size(val: number) {
|
112 | const old = this._size;
|
113 | if (old !== val) {
|
114 | this._size = val;
|
115 | this.resize();
|
116 | }
|
117 | }
|
118 |
|
119 | |
120 |
|
121 |
|
122 |
|
123 | get buttonSize(): number { return this._buttonSize; }
|
124 | set buttonSize(val: number) {
|
125 | const old = this._buttonSize;
|
126 | if (old !== val) {
|
127 | this._buttonSize = val;
|
128 | this.resize();
|
129 | }
|
130 | }
|
131 |
|
132 | |
133 |
|
134 |
|
135 |
|
136 | get alignment(): go.Spot { return this._alignment; }
|
137 | set alignment(val: go.Spot) {
|
138 | const old = this._alignment;
|
139 | if (old !== val) {
|
140 | this._alignment = val;
|
141 | this.realign();
|
142 | }
|
143 | }
|
144 |
|
145 | |
146 |
|
147 |
|
148 |
|
149 | get alignmentFocus(): go.Spot { return this._alignmentFocus; }
|
150 | set alignmentFocus(val: go.Spot) {
|
151 | const old = this._alignmentFocus;
|
152 | if (old !== val) {
|
153 | this._alignmentFocus = val;
|
154 | this.realign();
|
155 | }
|
156 | }
|
157 |
|
158 | |
159 |
|
160 |
|
161 |
|
162 |
|
163 | get orientation(): string { return this._orientation; }
|
164 | set orientation(val: string) {
|
165 | if (val !== 'horizontal' && val !== 'vertical') {
|
166 | throw new Error('Orientation must be "horizontal" or "vertical"');
|
167 | }
|
168 | const old = this._orientation;
|
169 | if (old !== val) {
|
170 | this._orientation = val;
|
171 | this.resize(true);
|
172 | }
|
173 | }
|
174 |
|
175 | |
176 |
|
177 |
|
178 |
|
179 | get opacity(): number { return this._opacity; }
|
180 | set opacity(val: number) {
|
181 | const old = this._opacity;
|
182 | if (old !== val) {
|
183 | this._opacity = val;
|
184 | if (this._sliderDiv !== null) {
|
185 | this._sliderDiv.style.opacity = val.toString();
|
186 | }
|
187 | }
|
188 | }
|
189 |
|
190 | |
191 |
|
192 |
|
193 |
|
194 | private init() {
|
195 |
|
196 | this.sliderDivSetup();
|
197 | this.resize(true);
|
198 |
|
199 |
|
200 | this.sliderListenerSetup();
|
201 | }
|
202 |
|
203 | |
204 |
|
205 |
|
206 |
|
207 | private sliderDivSetup() {
|
208 | this._sliderDiv = document.createElement('div');
|
209 | this._sliderDiv.className = 'zoomSlider';
|
210 |
|
211 |
|
212 | const zoomOutBtn = document.createElement('button');
|
213 | zoomOutBtn.id = 'zoomSliderOut';
|
214 | zoomOutBtn.className = 'zoomButton';
|
215 | zoomOutBtn.innerHTML = '-';
|
216 | this._sliderDiv.appendChild(zoomOutBtn);
|
217 |
|
218 | const zoomRangeContainer = document.createElement('div');
|
219 | zoomRangeContainer.id = 'zoomSliderRangeCtn';
|
220 | zoomRangeContainer.className = 'zoomRangeContainer';
|
221 | this._sliderDiv.appendChild(zoomRangeContainer);
|
222 |
|
223 | const zoomRangeInput = document.createElement('input');
|
224 | zoomRangeInput.id = 'zoomSliderRange';
|
225 | zoomRangeInput.className = 'zoomRangeInput';
|
226 | zoomRangeInput.type = 'range';
|
227 | zoomRangeInput.min = '-50';
|
228 | zoomRangeInput.max = '100';
|
229 | zoomRangeContainer.appendChild(zoomRangeInput);
|
230 |
|
231 | const zoomInBtn = document.createElement('button');
|
232 | zoomInBtn.id = 'zoomSliderIn';
|
233 | zoomInBtn.className = 'zoomButton';
|
234 | zoomInBtn.innerHTML = '+';
|
235 | this._sliderDiv.appendChild(zoomInBtn);
|
236 |
|
237 |
|
238 |
|
239 | if (this._diagramDiv !== null) {
|
240 | const diagramParent = this._diagramDiv.parentElement;
|
241 | if (diagramParent !== null) {
|
242 | diagramParent.appendChild(this._sliderDiv);
|
243 | }
|
244 | }
|
245 | }
|
246 |
|
247 | |
248 |
|
249 |
|
250 |
|
251 |
|
252 | private sliderListenerSetup() {
|
253 | const zoomOutBtn = document.getElementById('zoomSliderOut');
|
254 | const zoomInBtn = document.getElementById('zoomSliderIn');
|
255 | const zoomRangeInput = document.getElementById('zoomSliderRange') as HTMLInputElement;
|
256 |
|
257 | if (zoomOutBtn === null || zoomInBtn === null || zoomRangeInput === null) return;
|
258 |
|
259 |
|
260 | this.diagram.addDiagramListener('ViewportBoundsChanged', this.updateOnViewportBoundsChanged);
|
261 |
|
262 |
|
263 | const self = this;
|
264 | zoomOutBtn.onclick = function() {
|
265 | zoomRangeInput.stepDown();
|
266 | self.valueToScale();
|
267 | };
|
268 |
|
269 | zoomInBtn.onclick = function() {
|
270 | zoomRangeInput.stepUp();
|
271 | self.valueToScale();
|
272 | };
|
273 |
|
274 | const valChanged = function() {
|
275 | self.valueToScale();
|
276 | };
|
277 | zoomRangeInput.oninput = valChanged;
|
278 | zoomRangeInput.onchange = valChanged;
|
279 | }
|
280 |
|
281 | |
282 |
|
283 |
|
284 |
|
285 |
|
286 | private resize(reorient?: boolean) {
|
287 | let sliderWidth = 0;
|
288 | let sliderHeight = 0;
|
289 |
|
290 | const zoomOutBtn = document.getElementById('zoomSliderOut');
|
291 | const zoomInBtn = document.getElementById('zoomSliderIn');
|
292 | const zoomRangeContainer = document.getElementById('zoomSliderRangeCtn');
|
293 | const zoomRangeInput = document.getElementById('zoomSliderRange');
|
294 |
|
295 | if (this._sliderDiv === null || zoomOutBtn === null || zoomInBtn === null ||
|
296 | zoomRangeContainer === null || zoomRangeInput === null) return;
|
297 |
|
298 | if (this.orientation === 'horizontal') {
|
299 | sliderWidth = this.size;
|
300 | sliderHeight = this.buttonSize;
|
301 | const rangeWidth = sliderWidth - sliderHeight * 2;
|
302 |
|
303 | zoomOutBtn.style.width = sliderHeight + 'px';
|
304 | zoomOutBtn.style.height = sliderHeight + 'px';
|
305 |
|
306 | zoomRangeContainer.style.width = rangeWidth + 'px';
|
307 | zoomRangeContainer.style.height = sliderHeight + 'px';
|
308 |
|
309 | zoomRangeInput.style.width = rangeWidth + 'px';
|
310 | zoomRangeInput.style.height = sliderHeight + 'px';
|
311 | zoomRangeInput.style.transformOrigin = '';
|
312 |
|
313 | zoomInBtn.style.width = sliderHeight + 'px';
|
314 | zoomInBtn.style.height = sliderHeight + 'px';
|
315 | } else {
|
316 | sliderHeight = this.size;
|
317 | sliderWidth = this.buttonSize;
|
318 | const rangeHeight = sliderHeight - sliderWidth * 2;
|
319 |
|
320 | zoomInBtn.style.width = sliderWidth + 'px';
|
321 | zoomInBtn.style.height = sliderWidth + 'px';
|
322 |
|
323 | zoomRangeContainer.style.width = sliderWidth + 'px';
|
324 | zoomRangeContainer.style.height = rangeHeight + 'px';
|
325 |
|
326 | zoomRangeInput.style.width = rangeHeight + 'px';
|
327 | zoomRangeInput.style.height = sliderWidth + 'px';
|
328 | zoomRangeInput.style.transformOrigin = rangeHeight / 2 + 'px ' + rangeHeight / 2 + 'px';
|
329 |
|
330 | zoomOutBtn.style.width = sliderWidth + 'px';
|
331 | zoomOutBtn.style.height = sliderWidth + 'px';
|
332 | }
|
333 | this._sliderDiv.style.width = sliderWidth + 'px';
|
334 | this._sliderDiv.style.height = sliderHeight + 'px';
|
335 |
|
336 |
|
337 | if (reorient) {
|
338 | this.reorient();
|
339 | }
|
340 |
|
341 |
|
342 | this.realign();
|
343 | }
|
344 |
|
345 | |
346 |
|
347 |
|
348 |
|
349 | private reorient() {
|
350 | const zoomOutBtn = document.getElementById('zoomSliderOut');
|
351 | const zoomInBtn = document.getElementById('zoomSliderIn');
|
352 | const zoomRangeInput = document.getElementById('zoomSliderRange');
|
353 |
|
354 | if (this._sliderDiv === null || zoomOutBtn === null || zoomInBtn === null || zoomRangeInput === null) return;
|
355 |
|
356 |
|
357 | if (this.orientation === 'horizontal') {
|
358 | zoomRangeInput.style.transform = '';
|
359 | this._sliderDiv.insertBefore(zoomOutBtn, this._sliderDiv.firstChild);
|
360 | this._sliderDiv.appendChild(zoomInBtn);
|
361 | } else {
|
362 | zoomRangeInput.style.transform = 'rotate(-90deg)';
|
363 | this._sliderDiv.insertBefore(zoomInBtn, this._sliderDiv.firstChild);
|
364 | this._sliderDiv.appendChild(zoomOutBtn);
|
365 | }
|
366 | }
|
367 |
|
368 | |
369 |
|
370 |
|
371 |
|
372 | private realign() {
|
373 | if (this._diagramDiv === null || this._sliderDiv === null) return;
|
374 |
|
375 | let sliderWidth = 0;
|
376 | let sliderHeight = 0;
|
377 | if (this.orientation === 'horizontal') {
|
378 | sliderWidth = this.size;
|
379 | sliderHeight = this.buttonSize;
|
380 | } else {
|
381 | sliderHeight = this.size;
|
382 | sliderWidth = this.buttonSize;
|
383 | }
|
384 |
|
385 |
|
386 | const diagramParent = this._diagramDiv.parentElement;
|
387 | const diagramLoc = this._diagramDiv.getBoundingClientRect();
|
388 | if (diagramParent !== null) {
|
389 | const parentLoc = diagramParent.getBoundingClientRect();
|
390 |
|
391 | const top = diagramLoc.top - parentLoc.top +
|
392 | this.alignment.y * this._diagramDiv.clientHeight + this.alignment.offsetY -
|
393 | this.alignmentFocus.y * sliderHeight + this.alignmentFocus.offsetY;
|
394 |
|
395 | const left = diagramLoc.left - parentLoc.left +
|
396 | this.alignment.x * this._diagramDiv.clientWidth + this.alignment.offsetX -
|
397 | this.alignmentFocus.x * sliderWidth + this.alignmentFocus.offsetX;
|
398 |
|
399 | this._sliderDiv.style.top = top + 'px';
|
400 | this._sliderDiv.style.left = left + 'px';
|
401 | }
|
402 | }
|
403 |
|
404 | |
405 |
|
406 |
|
407 |
|
408 | private scaleToValue() {
|
409 | const slider = document.getElementById('zoomSliderRange') as HTMLInputElement;
|
410 | const diagram = this.diagram;
|
411 | const A = this._initialScale;
|
412 | const B = diagram.commandHandler.zoomFactor;
|
413 | const y1 = diagram.scale;
|
414 | slider.value = Math.round(Math.log(y1 / A) / Math.log(B)).toString();
|
415 | }
|
416 |
|
417 | |
418 |
|
419 |
|
420 |
|
421 | private valueToScale() {
|
422 | const slider = document.getElementById('zoomSliderRange') as HTMLInputElement;
|
423 | const diagram = this.diagram;
|
424 | const x = parseFloat(slider.value);
|
425 | const A = this._initialScale;
|
426 | const B = diagram.commandHandler.zoomFactor;
|
427 | diagram.scale = A * Math.pow(B, x);
|
428 | }
|
429 |
|
430 | |
431 |
|
432 |
|
433 | public remove() {
|
434 |
|
435 | this.diagram.removeDiagramListener('ViewportBoundsChanged', this.updateOnViewportBoundsChanged);
|
436 |
|
437 | if (this._sliderDiv !== null) {
|
438 | this._sliderDiv.innerHTML = '';
|
439 | if (this._sliderDiv.parentElement) {
|
440 | this._sliderDiv.parentElement.removeChild(this._sliderDiv);
|
441 | }
|
442 | this._sliderDiv = null;
|
443 | }
|
444 | }
|
445 | }
|