Skip to content

Commit

Permalink
Pre-release v2.0.0-beta.2
Browse files Browse the repository at this point in the history
Provide [rzContainment]
  • Loading branch information
Xie, Ziyu committed Jul 2, 2018
1 parent 00474a9 commit 68138c9
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 12 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@
6. [Events](#events)

# Getting Started
angular2-draggable has angular (ver >= 4.x) directives that make the DOM element draggable and resizable.
angular2-draggable has angular directives that make the DOM element draggable and resizable.
+ `ngDraggable`
+ v2.x requires Angular 6
+ v1.4.2 requires Angular 4

+ `ngResizable`
+ provided since v2.0, requires Angular 6

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

+ 2018.06.26: 2.0.0-beta.1
+ ngResizable: Provide `(rzStart)`, `(rzResizing)`, `(rzStop)` event emitters
Expand Down Expand Up @@ -157,6 +162,7 @@ Well you can use both directives concurrently if you wish:
| ngResizable | boolean | `true` | You can toggle the resizable capability by setting `true` or `false` |
| 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"` |
| 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) |
| 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' |

## CSS:
+ 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:
Expand Down
121 changes: 116 additions & 5 deletions projects/angular2-draggable/src/lib/angular-resizable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
private _handles: { [key: string]: ResizeHandle } = {};
private _handleType: string[] = [];
private _handleResizing: ResizeHandle = null;

private _aspectRatio = 0;
private _containment: HTMLElement = null;
private _origMousePos: Position = null;

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

private _bounding: any = null;

/** Disables the resizable if set to false. */
@Input() set ngResizable(v: any) {
if (v !== undefined && v !== null && v !== '') {
Expand All @@ -59,7 +61,17 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
* boolean: When set to true, the element will maintain its original aspect ratio.
* number: Force the element to maintain a specific aspect ratio during resizing.
*/
@Input() rzAspectRatio: boolean|number = false;
@Input() rzAspectRatio: boolean | number = false;

/**
* Constrains resizing to within the bounds of the specified element or region.
* Multiple types supported:
* Selector: The resizable element will be contained to the bounding box of the first element found by the selector.
* If no element is found, no containment will be set.
* Element: The resizable element will be contained to the bounding box of this element.
* String: Possible values: "parent".
*/
@Input() rzContainment: string | HTMLElement = null;

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

constructor(private el: ElementRef, private renderer: Renderer2) { }
constructor(private el: ElementRef<HTMLElement>, private renderer: Renderer2) { }

ngOnChanges(changes: SimpleChanges) {
if (changes['rzHandles'] && !changes['rzHandles'].isFirstChange()) {
Expand All @@ -80,6 +92,10 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
if (changes['rzAspectRatio'] && !changes['rzAspectRatio'].isFirstChange()) {
this.updateAspectRatio();
}

if (changes['rzContainment'] && !changes['rzContainment'].isFirstChange()) {
this.updateContainment();
}
}

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

ngOnDestroy() {
this.removeHandles();
this._containment = null;
}

ngAfterViewInit() {
Expand All @@ -97,6 +114,7 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
this._currSize = Size.copy(this._initSize);
this._currPos = Position.copy(this._initPos);
this.updateAspectRatio();
this.updateContainment();
}

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

/** A method to reset size */
/** A method to get current status */
public getStatus() {
if (!this._currPos || !this._currSize) {
return null;
Expand Down Expand Up @@ -152,6 +170,24 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
}
}

/** Use it to update containment */
private updateContainment() {
if (!this.rzContainment) {
this._containment = null;
return;
}

if (typeof this.rzContainment === 'string') {
if (this.rzContainment === 'parent') {
this._containment = this.el.nativeElement.parentElement;
} else {
this._containment = document.querySelector<HTMLElement>(this.rzContainment);
}
} else {
this._containment = this.rzContainment;
}
}

/** Use it to create handle divs */
private createHandles() {
if (!this.rzHandles) {
Expand Down Expand Up @@ -226,6 +262,9 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
this._origPos = Position.getCurrent(elm); // x: left, y: top
this._currSize = Size.copy(this._origSize);
this._currPos = Position.copy(this._origPos);
if (this._containment) {
this.getBounding();
}
this.startResize(handle);
}
}
Expand All @@ -240,6 +279,9 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
this._origMousePos = null;
this._origSize = null;
this._origPos = null;
if (this._containment) {
this.resetBounding();
}
}
}

Expand Down Expand Up @@ -286,8 +328,21 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,

if (this._handleResizing.type.match(/n/)) {
// n, ne, nw
this._currSize.height = this._origSize.height - p.y;
this._currPos.y = this._origPos.y + p.y;
this._currSize.height = this._origSize.height - p.y;

// check bounds
if (this._containment) {
if (this._currPos.y < 0) {
this._currPos.y = 0;
this._currSize.height = this._origSize.height + this._origPos.y;
}
}

if (this._currSize.height < 1) {
this._currSize.height = 1;
this._currPos.y = this._origPos.y + (this._origSize.height - 1);
}

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

// check bounds
if (this._containment) {
if (this._currPos.x < 0) {
this._currPos.x = 0;
this._currSize.width = this._origSize.width + this._origPos.x;
}
}

if (this._currSize.width < 1) {
this._currSize.width = 1;
this._currPos.x = this._origPos.x + (this._origSize.width - 1);
}

// aspect ratio
this.adjustByRatio('w');
}

this.checkBounds();
this.doResize();
}

Expand All @@ -334,4 +403,46 @@ export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy,
}
}
}

private checkBounds() {
if (this._containment) {
const container = this._containment;
const maxWidth = this._bounding.width - this._bounding.pr - this.el.nativeElement.offsetLeft;
const maxHeight = this._bounding.height - this._bounding.pb - this.el.nativeElement.offsetTop;

if (this._currSize.width > maxWidth) {
this._currSize.width = maxWidth;
}

if (this._currSize.height > maxHeight) {
this._currSize.height = maxHeight;
}
}
}

private getBounding() {
const el = this._containment;
const computed = window.getComputedStyle(el);
if (computed) {
let p = computed.getPropertyValue('position');

this._bounding = {};
this._bounding.width = el.clientWidth;
this._bounding.height = el.clientHeight;
this._bounding.pr = parseInt(computed.getPropertyValue('padding-right'), 10);
this._bounding.pb = parseInt(computed.getPropertyValue('padding-bottom'), 10);
this._bounding.position = computed.getPropertyValue('position');

if (p === 'static') {
this.renderer.setStyle(el, 'position', 'relative');
}
}
}

private resetBounding() {
if (this._bounding && this._bounding.position === 'static') {
this.renderer.setStyle(this._containment, 'position', 'relative');
}
this._bounding = null;
}
}
9 changes: 9 additions & 0 deletions src/app/_nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ export const navigation = [
text: 'new'
}
},
{
name: 'Containment',
url: '/resizable/containment',
icon: 'fa fa-window-close',
badge: {
variant: 'success',
text: 'new'
}
},
]
}
];
8 changes: 8 additions & 0 deletions src/app/views/resizable-demo/resizable-demo-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router';
import { ResizeDefaultDemoComponent } from './resize-default-demo/resize-default-demo.component';
import { ResizeEventDemoComponent } from './resize-event-demo/resize-event-demo.component';
import { ResizeAspectRatioDemoComponent } from './resize-aspect-ratio-demo/resize-aspect-ratio-demo.component';
import { ResizeContainmentDemoComponent } from './resize-containment-demo/resize-containment-demo.component';


const routes: Routes = [
Expand All @@ -27,6 +28,13 @@ const routes: Routes = [
title: 'Aspect Ratio'
}
},
{
path: 'containment',
component: ResizeContainmentDemoComponent,
data: {
title: 'Containment'
}
}
];

@NgModule({
Expand Down
4 changes: 3 additions & 1 deletion src/app/views/resizable-demo/resizable-demo.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ResizableDemoRoutingModule } from './resizable-demo-routing.module';
import { ResizeDefaultDemoComponent } from './resize-default-demo/resize-default-demo.component';
import { ResizeEventDemoComponent } from './resize-event-demo/resize-event-demo.component';
import { ResizeAspectRatioDemoComponent } from './resize-aspect-ratio-demo/resize-aspect-ratio-demo.component';
import { ResizeContainmentDemoComponent } from './resize-containment-demo/resize-containment-demo.component';

@NgModule({
imports: [
Expand All @@ -16,7 +17,8 @@ import { ResizeAspectRatioDemoComponent } from './resize-aspect-ratio-demo/resiz
declarations: [
ResizeDefaultDemoComponent,
ResizeEventDemoComponent,
ResizeAspectRatioDemoComponent
ResizeAspectRatioDemoComponent,
ResizeContainmentDemoComponent
]
})
export class ResizableDemoModule { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="row">
<div class="col" style="min-height: 300px;">
<div id="container" class="widget-container">
<h4 class="widget-header">Containment</h4>
<!-- rzContainment could be a CSS selector string, 'parent' or an Element object -->
<div ngResizable class="resizable-widget" rzContainment="#container" rzHandles="all">
<h4 class="widget-header">Resizable</h4>
</div>
</div>
</div>
</div>

<!-- Documents -->
<div class="row mt-4">
<div class="col">
<tabset>
<tab heading="html">
<markdown [data]="demo_html | language: 'html'"></markdown>
</tab>
<tab heading="component">
<markdown [data]="demo_ts | language: 'typescript'"></markdown>
</tab>
</tabset>
</div>
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component, OnInit } from '@angular/core';

declare const require: any;

@Component({
selector: 'app-resize-containment-demo',
templateUrl: './resize-containment-demo.component.html',
styleUrls: ['./resize-containment-demo.component.scss']
})
export class ResizeContainmentDemoComponent implements OnInit {
demo_html = require('!!html-loader!./resize-containment-demo.component.html');
demo_ts = require('!!raw-loader!./resize-containment-demo.component.ts');

constructor() { }

ngOnInit() {
}

}
1 change: 1 addition & 0 deletions src/assets/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

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

Expand Down
14 changes: 11 additions & 3 deletions src/scss/_resizable.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
.resizable-widget {
width: 200px;
height: 200px;
background-color: #fff;
border: solid 1px #869fac;
background-color: $white;
border: solid 1px $gray-400;
padding: 0.5em;
}

.widget-header {
text-align: center;
border: 1px solid #869fac;
border: 1px solid $gray-400;
background: #e9e9e9;
color: #333333;
font-weight: bold;
padding: 0.25em;
}

.widget-container {
width: 300px;
height: 300px;
padding: 0.5em;
background-color: $cyan;
border: solid 1px $gray-400;
}

0 comments on commit 68138c9

Please sign in to comment.