Skip to content

Commit 68138c9

Browse files
author
Xie, Ziyu
committed
Pre-release v2.0.0-beta.2
Provide [rzContainment]
1 parent 00474a9 commit 68138c9

File tree

10 files changed

+201
-12
lines changed

10 files changed

+201
-12
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
6. [Events](#events)
1717

1818
# Getting Started
19-
angular2-draggable has angular (ver >= 4.x) directives that make the DOM element draggable and resizable.
19+
angular2-draggable has angular directives that make the DOM element draggable and resizable.
2020
+ `ngDraggable`
21+
+ v2.x requires Angular 6
22+
+ v1.4.2 requires Angular 4
23+
2124
+ `ngResizable`
25+
+ provided since v2.0, requires Angular 6
2226

2327
# Latest Update
24-
+ 2018.06.27: 2.0.0-beta.2
25-
+ ngResizable: Provide `[rzAspectRatio]`, whether the element should be constrained to a specific aspect ratio.
28+
+ 2018.07.02: 2.0.0-beta.2
29+
+ ngResizable: Provide `[rzAspectRatio]`, whether the element should be constrained to a specific aspect ratio. [demo](https://xieziyu.github.io/angular2-draggable/#/resizable/aspect-ratio)
30+
+ ngResizable: Provide `[rzContainment]`, constrains resizing to within the bounds of the specified element or region. [demo](https://xieziyu.github.io/angular2-draggable/#/resizable/containment)
2631

2732
+ 2018.06.26: 2.0.0-beta.1
2833
+ ngResizable: Provide `(rzStart)`, `(rzResizing)`, `(rzStop)` event emitters
@@ -157,6 +162,7 @@ Well you can use both directives concurrently if you wish:
157162
| ngResizable | boolean | `true` | You can toggle the resizable capability by setting `true` or `false` |
158163
| rzHandles | string | `"e,s,se"` | Which handles can be used for resizing. Optional types in `"n,e,s,w,se,sw,ne,nw"` or `"all"` |
159164
| rzAspectRatio | boolean\|number | false | `boolean`: Whether the element should be constrained to a specific aspect ratio. `number`: Force the element to maintain a specific aspect ratio during resizing (width/height) |
165+
| rzContainment | Selector\|string\|Element | null | Constrains resizing to within the bounds of the specified element or region. It accepts an HTMLElement, `'parent'` or a valid CSS selector string such as '#id' |
160166

161167
## CSS:
162168
+ When `ngDraggable` is enabled on some element, `ng-draggable` class is automatically assigned to it. You can use it to customize the pointer style. For example:

projects/angular2-draggable/src/lib/angular-resizable.directive.ts

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
2020
private _handles: { [key: string]: ResizeHandle } = {};
2121
private _handleType: string[] = [];
2222
private _handleResizing: ResizeHandle = null;
23-
2423
private _aspectRatio = 0;
24+
private _containment: HTMLElement = null;
2525
private _origMousePos: Position = null;
2626

2727
/** Original Size and Position */
@@ -36,6 +36,8 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
3636
private _initSize: Size = null;
3737
private _initPos: Position = null;
3838

39+
private _bounding: any = null;
40+
3941
/** Disables the resizable if set to false. */
4042
@Input() set ngResizable(v: any) {
4143
if (v !== undefined && v !== null && v !== '') {
@@ -59,7 +61,17 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
5961
* boolean: When set to true, the element will maintain its original aspect ratio.
6062
* number: Force the element to maintain a specific aspect ratio during resizing.
6163
*/
62-
@Input() rzAspectRatio: boolean|number = false;
64+
@Input() rzAspectRatio: boolean | number = false;
65+
66+
/**
67+
* Constrains resizing to within the bounds of the specified element or region.
68+
* Multiple types supported:
69+
* Selector: The resizable element will be contained to the bounding box of the first element found by the selector.
70+
* If no element is found, no containment will be set.
71+
* Element: The resizable element will be contained to the bounding box of this element.
72+
* String: Possible values: "parent".
73+
*/
74+
@Input() rzContainment: string | HTMLElement = null;
6375

6476
/** emitted when start resizing */
6577
@Output() rzStart = new EventEmitter<IResizeEvent>();
@@ -70,7 +82,7 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
7082
/** emitted when stop resizing */
7183
@Output() rzStop = new EventEmitter<IResizeEvent>();
7284

73-
constructor(private el: ElementRef, private renderer: Renderer2) { }
85+
constructor(private el: ElementRef<HTMLElement>, private renderer: Renderer2) { }
7486

7587
ngOnChanges(changes: SimpleChanges) {
7688
if (changes['rzHandles'] && !changes['rzHandles'].isFirstChange()) {
@@ -80,6 +92,10 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
8092
if (changes['rzAspectRatio'] && !changes['rzAspectRatio'].isFirstChange()) {
8193
this.updateAspectRatio();
8294
}
95+
96+
if (changes['rzContainment'] && !changes['rzContainment'].isFirstChange()) {
97+
this.updateContainment();
98+
}
8399
}
84100

85101
ngOnInit() {
@@ -88,6 +104,7 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
88104

89105
ngOnDestroy() {
90106
this.removeHandles();
107+
this._containment = null;
91108
}
92109

93110
ngAfterViewInit() {
@@ -97,6 +114,7 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
97114
this._currSize = Size.copy(this._initSize);
98115
this._currPos = Position.copy(this._initPos);
99116
this.updateAspectRatio();
117+
this.updateContainment();
100118
}
101119

102120
/** A method to reset size */
@@ -106,7 +124,7 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
106124
this.doResize();
107125
}
108126

109-
/** A method to reset size */
127+
/** A method to get current status */
110128
public getStatus() {
111129
if (!this._currPos || !this._currSize) {
112130
return null;
@@ -152,6 +170,24 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
152170
}
153171
}
154172

173+
/** Use it to update containment */
174+
private updateContainment() {
175+
if (!this.rzContainment) {
176+
this._containment = null;
177+
return;
178+
}
179+
180+
if (typeof this.rzContainment === 'string') {
181+
if (this.rzContainment === 'parent') {
182+
this._containment = this.el.nativeElement.parentElement;
183+
} else {
184+
this._containment = document.querySelector<HTMLElement>(this.rzContainment);
185+
}
186+
} else {
187+
this._containment = this.rzContainment;
188+
}
189+
}
190+
155191
/** Use it to create handle divs */
156192
private createHandles() {
157193
if (!this.rzHandles) {
@@ -226,6 +262,9 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
226262
this._origPos = Position.getCurrent(elm); // x: left, y: top
227263
this._currSize = Size.copy(this._origSize);
228264
this._currPos = Position.copy(this._origPos);
265+
if (this._containment) {
266+
this.getBounding();
267+
}
229268
this.startResize(handle);
230269
}
231270
}
@@ -240,6 +279,9 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
240279
this._origMousePos = null;
241280
this._origSize = null;
242281
this._origPos = null;
282+
if (this._containment) {
283+
this.resetBounding();
284+
}
243285
}
244286
}
245287

@@ -286,8 +328,21 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
286328

287329
if (this._handleResizing.type.match(/n/)) {
288330
// n, ne, nw
289-
this._currSize.height = this._origSize.height - p.y;
290331
this._currPos.y = this._origPos.y + p.y;
332+
this._currSize.height = this._origSize.height - p.y;
333+
334+
// check bounds
335+
if (this._containment) {
336+
if (this._currPos.y < 0) {
337+
this._currPos.y = 0;
338+
this._currSize.height = this._origSize.height + this._origPos.y;
339+
}
340+
}
341+
342+
if (this._currSize.height < 1) {
343+
this._currSize.height = 1;
344+
this._currPos.y = this._origPos.y + (this._origSize.height - 1);
345+
}
291346

292347
// aspect ratio
293348
this.adjustByRatio('h');
@@ -310,10 +365,24 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
310365
this._currSize.width = this._origSize.width - p.x;
311366
this._currPos.x = this._origPos.x + p.x;
312367

368+
// check bounds
369+
if (this._containment) {
370+
if (this._currPos.x < 0) {
371+
this._currPos.x = 0;
372+
this._currSize.width = this._origSize.width + this._origPos.x;
373+
}
374+
}
375+
376+
if (this._currSize.width < 1) {
377+
this._currSize.width = 1;
378+
this._currPos.x = this._origPos.x + (this._origSize.width - 1);
379+
}
380+
313381
// aspect ratio
314382
this.adjustByRatio('w');
315383
}
316384

385+
this.checkBounds();
317386
this.doResize();
318387
}
319388

@@ -334,4 +403,46 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
334403
}
335404
}
336405
}
406+
407+
private checkBounds() {
408+
if (this._containment) {
409+
const container = this._containment;
410+
const maxWidth = this._bounding.width - this._bounding.pr - this.el.nativeElement.offsetLeft;
411+
const maxHeight = this._bounding.height - this._bounding.pb - this.el.nativeElement.offsetTop;
412+
413+
if (this._currSize.width > maxWidth) {
414+
this._currSize.width = maxWidth;
415+
}
416+
417+
if (this._currSize.height > maxHeight) {
418+
this._currSize.height = maxHeight;
419+
}
420+
}
421+
}
422+
423+
private getBounding() {
424+
const el = this._containment;
425+
const computed = window.getComputedStyle(el);
426+
if (computed) {
427+
let p = computed.getPropertyValue('position');
428+
429+
this._bounding = {};
430+
this._bounding.width = el.clientWidth;
431+
this._bounding.height = el.clientHeight;
432+
this._bounding.pr = parseInt(computed.getPropertyValue('padding-right'), 10);
433+
this._bounding.pb = parseInt(computed.getPropertyValue('padding-bottom'), 10);
434+
this._bounding.position = computed.getPropertyValue('position');
435+
436+
if (p === 'static') {
437+
this.renderer.setStyle(el, 'position', 'relative');
438+
}
439+
}
440+
}
441+
442+
private resetBounding() {
443+
if (this._bounding && this._bounding.position === 'static') {
444+
this.renderer.setStyle(this._containment, 'position', 'relative');
445+
}
446+
this._bounding = null;
447+
}
337448
}

src/app/_nav.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ export const navigation = [
7575
text: 'new'
7676
}
7777
},
78+
{
79+
name: 'Containment',
80+
url: '/resizable/containment',
81+
icon: 'fa fa-window-close',
82+
badge: {
83+
variant: 'success',
84+
text: 'new'
85+
}
86+
},
7887
]
7988
}
8089
];

src/app/views/resizable-demo/resizable-demo-routing.module.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router';
33
import { ResizeDefaultDemoComponent } from './resize-default-demo/resize-default-demo.component';
44
import { ResizeEventDemoComponent } from './resize-event-demo/resize-event-demo.component';
55
import { ResizeAspectRatioDemoComponent } from './resize-aspect-ratio-demo/resize-aspect-ratio-demo.component';
6+
import { ResizeContainmentDemoComponent } from './resize-containment-demo/resize-containment-demo.component';
67

78

89
const routes: Routes = [
@@ -27,6 +28,13 @@ const routes: Routes = [
2728
title: 'Aspect Ratio'
2829
}
2930
},
31+
{
32+
path: 'containment',
33+
component: ResizeContainmentDemoComponent,
34+
data: {
35+
title: 'Containment'
36+
}
37+
}
3038
];
3139

3240
@NgModule({

src/app/views/resizable-demo/resizable-demo.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ResizableDemoRoutingModule } from './resizable-demo-routing.module';
66
import { ResizeDefaultDemoComponent } from './resize-default-demo/resize-default-demo.component';
77
import { ResizeEventDemoComponent } from './resize-event-demo/resize-event-demo.component';
88
import { ResizeAspectRatioDemoComponent } from './resize-aspect-ratio-demo/resize-aspect-ratio-demo.component';
9+
import { ResizeContainmentDemoComponent } from './resize-containment-demo/resize-containment-demo.component';
910

1011
@NgModule({
1112
imports: [
@@ -16,7 +17,8 @@ import { ResizeAspectRatioDemoComponent } from './resize-aspect-ratio-demo/resiz
1617
declarations: [
1718
ResizeDefaultDemoComponent,
1819
ResizeEventDemoComponent,
19-
ResizeAspectRatioDemoComponent
20+
ResizeAspectRatioDemoComponent,
21+
ResizeContainmentDemoComponent
2022
]
2123
})
2224
export class ResizableDemoModule { }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<div class="row">
2+
<div class="col" style="min-height: 300px;">
3+
<div id="container" class="widget-container">
4+
<h4 class="widget-header">Containment</h4>
5+
<!-- rzContainment could be a CSS selector string, 'parent' or an Element object -->
6+
<div ngResizable class="resizable-widget" rzContainment="#container" rzHandles="all">
7+
<h4 class="widget-header">Resizable</h4>
8+
</div>
9+
</div>
10+
</div>
11+
</div>
12+
13+
<!-- Documents -->
14+
<div class="row mt-4">
15+
<div class="col">
16+
<tabset>
17+
<tab heading="html">
18+
<markdown [data]="demo_html | language: 'html'"></markdown>
19+
</tab>
20+
<tab heading="component">
21+
<markdown [data]="demo_ts | language: 'typescript'"></markdown>
22+
</tab>
23+
</tabset>
24+
</div>
25+
</div>

src/app/views/resizable-demo/resize-containment-demo/resize-containment-demo.component.scss

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Component, OnInit } from '@angular/core';
2+
3+
declare const require: any;
4+
5+
@Component({
6+
selector: 'app-resize-containment-demo',
7+
templateUrl: './resize-containment-demo.component.html',
8+
styleUrls: ['./resize-containment-demo.component.scss']
9+
})
10+
export class ResizeContainmentDemoComponent implements OnInit {
11+
demo_html = require('!!html-loader!./resize-containment-demo.component.html');
12+
demo_ts = require('!!raw-loader!./resize-containment-demo.component.ts');
13+
14+
constructor() { }
15+
16+
ngOnInit() {
17+
}
18+
19+
}

src/assets/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#### New
44
+ ngResizable: Provide `[rzAspectRatio]`, whether the element should be constrained to a specific aspect ratio.
5+
+ ngResizable: Provide `[rzContainment]`, constrains resizing to within the bounds of the specified element or region.
56

67
## 2.0.0-beta.1 (2018-06-26)
78

src/scss/_resizable.scss

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
.resizable-widget {
22
width: 200px;
33
height: 200px;
4-
background-color: #fff;
5-
border: solid 1px #869fac;
4+
background-color: $white;
5+
border: solid 1px $gray-400;
66
padding: 0.5em;
77
}
88

99
.widget-header {
1010
text-align: center;
11-
border: 1px solid #869fac;
11+
border: 1px solid $gray-400;
1212
background: #e9e9e9;
1313
color: #333333;
1414
font-weight: bold;
1515
padding: 0.25em;
16+
}
17+
18+
.widget-container {
19+
width: 300px;
20+
height: 300px;
21+
padding: 0.5em;
22+
background-color: $cyan;
23+
border: solid 1px $gray-400;
1624
}

0 commit comments

Comments
 (0)