├── src
├── assets
│ ├── .gitkeep
│ ├── demo.pdf
│ ├── fonts
│ │ └── RobotoMono-Regular.ttf
│ └── logo.svg
├── app
│ ├── layout
│ │ ├── app-menu
│ │ │ ├── app-menu.component.scss
│ │ │ ├── app-menu.component.ts
│ │ │ └── app-menu.component.html
│ │ └── layout.module.ts
│ ├── pages
│ │ ├── demo-resizable
│ │ │ ├── resizable-grid
│ │ │ │ ├── resizable-grid.component.scss
│ │ │ │ ├── resizable-grid.component.ts
│ │ │ │ └── resizable-grid.component.html
│ │ │ ├── resizable-basic
│ │ │ │ ├── resizable-basic.component.scss
│ │ │ │ ├── resizable-basic.component.html
│ │ │ │ └── resizable-basic.component.ts
│ │ │ ├── resizable-events
│ │ │ │ ├── resizable-events.component.scss
│ │ │ │ ├── resizable-events.component.html
│ │ │ │ └── resizable-events.component.ts
│ │ │ ├── resizable-layout
│ │ │ │ ├── resizable-layout.component.scss
│ │ │ │ ├── resizable-layout.component.ts
│ │ │ │ └── resizable-layout.component.html
│ │ │ ├── resizable-min-max
│ │ │ │ ├── resizable-min-max.component.scss
│ │ │ │ ├── resizable-min-max.component.html
│ │ │ │ └── resizable-min-max.component.ts
│ │ │ ├── resizable-containment
│ │ │ │ ├── resizable-containment.component.scss
│ │ │ │ ├── resizable-containment.component.html
│ │ │ │ └── resizable-containment.component.ts
│ │ │ ├── demo-resizable-routing.module.ts
│ │ │ ├── resizable-iframe
│ │ │ │ ├── resizable-iframe.component.scss
│ │ │ │ ├── resizable-iframe.component.html
│ │ │ │ └── resizable-iframe.component.ts
│ │ │ └── demo-resizable.module.ts
│ │ ├── demo-draggable
│ │ │ ├── draggable-basic
│ │ │ │ ├── draggable-basic.component.scss
│ │ │ │ ├── draggable-basic.component.html
│ │ │ │ └── draggable-basic.component.ts
│ │ │ ├── draggable-events
│ │ │ │ ├── draggable-events.component.scss
│ │ │ │ ├── draggable-events.component.html
│ │ │ │ └── draggable-events.component.ts
│ │ │ ├── draggable-layout
│ │ │ │ ├── draggable-layout.component.scss
│ │ │ │ ├── draggable-layout.component.ts
│ │ │ │ └── draggable-layout.component.html
│ │ │ ├── draggable-boundary
│ │ │ │ ├── draggable-boundary.component.scss
│ │ │ │ ├── draggable-boundary.component.ts
│ │ │ │ └── draggable-boundary.component.html
│ │ │ ├── draggable-methods
│ │ │ │ ├── draggable-methods.component.scss
│ │ │ │ ├── draggable-methods.component.html
│ │ │ │ └── draggable-methods.component.ts
│ │ │ ├── draggable-options
│ │ │ │ ├── draggable-options.component.scss
│ │ │ │ ├── draggable-options.component.ts
│ │ │ │ └── draggable-options.component.html
│ │ │ ├── draggable-swap
│ │ │ │ ├── draggable-swap.component.scss
│ │ │ │ ├── draggable-swap.component.html
│ │ │ │ └── draggable-swap.component.ts
│ │ │ ├── demo-draggable-routing.module.ts
│ │ │ ├── draggable-grid
│ │ │ │ ├── draggable-grid.component.scss
│ │ │ │ ├── draggable-grid.component.ts
│ │ │ │ └── draggable-grid.component.html
│ │ │ └── demo-draggable.module.ts
│ │ └── welcome
│ │ │ ├── welcome.component.scss
│ │ │ ├── welcome-routing.module.ts
│ │ │ ├── welcome.module.ts
│ │ │ ├── welcome.component.ts
│ │ │ └── welcome.component.html
│ ├── shared
│ │ ├── code-block
│ │ │ ├── code-block.component.scss
│ │ │ ├── code-block.component.html
│ │ │ └── code-block.component.ts
│ │ ├── icons-provider.module.ts
│ │ ├── shared.module.ts
│ │ └── ng-zorro-custom.module.ts
│ ├── app.component.ts
│ ├── menus.ts
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.module.ts
│ └── app.component.scss
├── styles.scss
├── main.ts
├── index.html
├── styles
│ ├── _resizable.scss
│ └── _custom.scss
└── prism-material-dark.css
├── .vscode
├── extensions.json
├── settings.json
├── launch.json
└── tasks.json
├── projects
└── angular2-draggable
│ ├── src
│ ├── scss
│ │ ├── _variables.scss
│ │ └── resizable.scss
│ ├── public-api.ts
│ └── lib
│ │ ├── models
│ │ ├── resize-handle-type.ts
│ │ ├── resize-event.ts
│ │ ├── size.ts
│ │ └── position.ts
│ │ ├── angular-draggable.module.ts
│ │ ├── widgets
│ │ ├── helper-block.ts
│ │ └── resize-handle.ts
│ │ ├── angular-draggable.directive.ts
│ │ └── angular-resizable.directive.ts
│ ├── ng-package.json
│ ├── tsconfig.lib.prod.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.lib.json
│ ├── package.json
│ └── LICENSE
├── .prettierrc.json
├── tsconfig.app.json
├── tsconfig.spec.json
├── .editorconfig
├── .prettierignore
├── scripts
└── build-scss.js
├── .gitignore
├── .github
└── workflows
│ └── ci.yaml
├── LICENSE
├── tsconfig.json
├── .eslintrc.json
├── package.json
├── angular.json
├── README.md
└── CHANGELOG.md
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/layout/app-menu/app-menu.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-grid/resizable-grid.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-basic/draggable-basic.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-events/draggable-events.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-layout/draggable-layout.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-basic/resizable-basic.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-events/resizable-events.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-layout/resizable-layout.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-boundary/draggable-boundary.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-methods/draggable-methods.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-options/draggable-options.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-min-max/resizable-min-max.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-containment/resizable-containment.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/shared/code-block/code-block.component.scss:
--------------------------------------------------------------------------------
1 | .code-block {
2 | margin-top: 16px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/demo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xieziyu/angular2-draggable/HEAD/src/assets/demo.pdf
--------------------------------------------------------------------------------
/src/app/pages/welcome/welcome.component.scss:
--------------------------------------------------------------------------------
1 | section.jumbotron {
2 | background-color: #fff !important;
3 | }
--------------------------------------------------------------------------------
/src/assets/fonts/RobotoMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xieziyu/angular2-draggable/HEAD/src/assets/fonts/RobotoMono-Regular.ttf
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import './styles/custom';
3 | @import './styles/resizable';
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | $handle-border-size: 7px;
2 | $handle-border-offset: -5px;
3 |
4 | $handle-corner-size: 12px;
5 | $handle-corner-offset: 1px;
--------------------------------------------------------------------------------
/projects/angular2-draggable/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/angular2-draggable",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "useTabs": false,
4 | "singleQuote": true,
5 | "semi": true,
6 | "arrowParens": "avoid",
7 | "trailingComma": "es5",
8 | "bracketSameLine": true,
9 | "printWidth": 100
10 | }
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { AppModule } from './app/app.module';
4 |
5 | platformBrowserDynamic()
6 | .bootstrapModule(AppModule)
7 | .catch(err => console.error(err));
8 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-basic/draggable-basic.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Drag Me!
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss'],
7 | })
8 | export class AppComponent {
9 | isCollapsed = false;
10 | }
11 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of angular2-draggable
3 | */
4 |
5 | export * from './lib/angular-draggable.directive';
6 | export * from './lib/angular-resizable.directive';
7 | export * from './lib/angular-draggable.module';
8 | export * from './lib/models/position';
9 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/models/resize-handle-type.ts:
--------------------------------------------------------------------------------
1 | export interface ResizeHandleStyle {
2 | n?: string;
3 | s?: string;
4 | e?: string;
5 | w?: string;
6 | ne?: string;
7 | nw?: string;
8 | se?: string;
9 | sw?: string;
10 | }
11 |
12 | export type ResizeHandleType = string | ResizeHandleStyle;
13 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.lib.json",
4 | "compilerOptions": {
5 | "declarationMap": false
6 | },
7 | "angularCompilerOptions": {
8 | "compilationMode": "partial"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-layout/draggable-layout.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-draggable-layout',
5 | templateUrl: './draggable-layout.component.html',
6 | styleUrls: ['./draggable-layout.component.scss'],
7 | })
8 | export class DraggableLayoutComponent {}
9 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-layout/resizable-layout.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-resizable-layout',
5 | templateUrl: './resizable-layout.component.html',
6 | styleUrls: ['./resizable-layout.component.scss'],
7 | })
8 | export class ResizableLayoutComponent {}
9 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts"
10 | ],
11 | "include": [
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-basic/resizable-basic.component.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/models/resize-event.ts:
--------------------------------------------------------------------------------
1 | import { ISize } from './size';
2 |
3 | export interface IResizeEvent {
4 | host: any;
5 | handle: any;
6 | size: ISize;
7 | position: {
8 | top: number;
9 | left: number;
10 | };
11 | direction: {
12 | n: boolean;
13 | s: boolean;
14 | w: boolean;
15 | e: boolean;
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "**/*.spec.ts",
12 | "**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular2DraggableDemo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/lib",
6 | "declaration": true,
7 | "declarationMap": true,
8 | "inlineSources": true,
9 | "types": []
10 | },
11 | "exclude": [
12 | "**/*.spec.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-min-max/resizable-min-max.component.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/app/pages/welcome/welcome-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { WelcomeComponent } from './welcome.component';
4 |
5 | const routes: Routes = [{ path: '', component: WelcomeComponent }];
6 |
7 | @NgModule({
8 | imports: [RouterModule.forChild(routes)],
9 | exports: [RouterModule],
10 | })
11 | export class WelcomeRoutingModule {}
12 |
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/pages/welcome/welcome.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { SharedModule } from '../../shared/shared.module';
3 | import { WelcomeRoutingModule } from './welcome-routing.module';
4 | import { WelcomeComponent } from './welcome.component';
5 |
6 | @NgModule({
7 | imports: [WelcomeRoutingModule, SharedModule],
8 | declarations: [WelcomeComponent],
9 | exports: [WelcomeComponent],
10 | })
11 | export class WelcomeModule {}
12 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-swap/draggable-swap.component.scss:
--------------------------------------------------------------------------------
1 | .box-container {
2 | position: relative;
3 | height: 150px;
4 | padding: 45px 0;
5 | }
6 |
7 | .box-swap {
8 | position: absolute;
9 | text-align: center;
10 | width: 150px;
11 | height: 60px;
12 | padding: 15px 35px;
13 | line-height: 30px;
14 | color: white;
15 |
16 | &.static-block {
17 | transition: all 0.3s ease-out;
18 | background-color: #ff4d4f;
19 | }
20 | }
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/angular-draggable.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { AngularDraggableDirective } from './angular-draggable.directive';
3 | import { AngularResizableDirective } from './angular-resizable.directive';
4 |
5 | @NgModule({
6 | imports: [],
7 | declarations: [AngularDraggableDirective, AngularResizableDirective],
8 | exports: [AngularDraggableDirective, AngularResizableDirective],
9 | })
10 | export class AngularDraggableModule {}
11 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-methods/draggable-methods.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | .resetPosition()
5 |
6 |
7 |
8 | Drag Me!
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/app/layout/layout.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { RouterModule } from '@angular/router';
4 | import { SharedModule } from '../shared/shared.module';
5 | import { AppMenuComponent } from './app-menu/app-menu.component';
6 |
7 | @NgModule({
8 | declarations: [AppMenuComponent],
9 | imports: [CommonModule, SharedModule, RouterModule],
10 | exports: [AppMenuComponent],
11 | })
12 | export class LayoutModule {}
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[html]": {
3 | "editor.defaultFormatter": "esbenp.prettier-vscode",
4 | "editor.codeActionsOnSave": {
5 | "source.fixAll.eslint": "explicit"
6 | },
7 | "editor.formatOnSave": false
8 | },
9 | "[typescript]": {
10 | "editor.defaultFormatter": "dbaeumer.vscode-eslint",
11 | "editor.codeActionsOnSave": {
12 | "source.fixAll.eslint": "explicit"
13 | },
14 | "editor.formatOnSave": false
15 | },
16 | "eslint.format.enable": true
17 | }
--------------------------------------------------------------------------------
/src/app/shared/icons-provider.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { NzIconModule } from 'ng-zorro-antd/icon';
3 |
4 | import {
5 | MenuFoldOutline,
6 | MenuUnfoldOutline,
7 | FormOutline,
8 | DashboardOutline,
9 | } from '@ant-design/icons-angular/icons';
10 |
11 | const icons = [MenuFoldOutline, MenuUnfoldOutline, DashboardOutline, FormOutline];
12 |
13 | @NgModule({
14 | imports: [NzIconModule.forRoot(icons)],
15 | exports: [NzIconModule],
16 | })
17 | export class IconsProviderModule {}
18 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/demo-draggable-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { DraggableLayoutComponent } from './draggable-layout/draggable-layout.component';
4 |
5 | const routes: Routes = [
6 | { path: '', redirectTo: 'demo', pathMatch: 'full' },
7 | { path: 'demo', component: DraggableLayoutComponent },
8 | ];
9 |
10 | @NgModule({
11 | imports: [RouterModule.forChild(routes)],
12 | exports: [RouterModule],
13 | })
14 | export class DemoDraggableRoutingModule {}
15 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/demo-resizable-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 | import { ResizableLayoutComponent } from './resizable-layout/resizable-layout.component';
4 |
5 | const routes: Routes = [
6 | { path: '', redirectTo: 'demo', pathMatch: 'full' },
7 | { path: 'demo', component: ResizableLayoutComponent },
8 | ];
9 |
10 | @NgModule({
11 | imports: [RouterModule.forChild(routes)],
12 | exports: [RouterModule],
13 | })
14 | export class DemoResizableRoutingModule {}
15 |
--------------------------------------------------------------------------------
/src/styles/_resizable.scss:
--------------------------------------------------------------------------------
1 | .resizable-widget {
2 | width: 200px;
3 | height: 200px;
4 | background-color: $white;
5 | border: solid 1px $gray-400;
6 | padding: 0.5em;
7 | box-sizing: content-box;
8 | }
9 |
10 | .widget-header {
11 | text-align: center;
12 | border: 1px solid $gray-400;
13 | background: #e9e9e9;
14 | color: #333333;
15 | font-weight: bold;
16 | padding: 0.25em;
17 | }
18 |
19 | .widget-container {
20 | width: 300px;
21 | height: 300px;
22 | padding: 0.5em;
23 | background-color: $blue;
24 | border: solid 1px $gray-400;
25 | }
--------------------------------------------------------------------------------
/src/app/menus.ts:
--------------------------------------------------------------------------------
1 | import type { ThemeType } from '@ant-design/icons-angular';
2 |
3 | export interface AppMenu {
4 | path: string;
5 | text: string;
6 | icon?: string;
7 | iconTheme?: ThemeType;
8 | submenus?: AppMenu[];
9 | }
10 |
11 | export const APP_MENUS: AppMenu[] = [
12 | {
13 | path: '/welcome',
14 | icon: 'home',
15 | text: 'Home',
16 | },
17 | {
18 | path: '/draggable',
19 | icon: 'drag',
20 | text: 'Draggable',
21 | },
22 | {
23 | path: '/resizable',
24 | icon: 'expand',
25 | text: 'Resizable',
26 | },
27 | ];
28 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-grid/draggable-grid.component.scss:
--------------------------------------------------------------------------------
1 | .grid-container {
2 | position: relative;
3 | height: 250px;
4 | width: 250px;
5 | }
6 |
7 | .drag-grid {
8 | position: absolute;
9 | width: 50px;
10 | height: 50px;
11 | background-color: #f8cb00;
12 | color: white;
13 | line-height: 50px;
14 | text-align: center;
15 | }
16 |
17 | .grid {
18 | position: absolute;
19 | width: 50px;
20 | height: 50px;
21 | background-color: #20c997;
22 | color: white;
23 | text-align: center;
24 | line-height: 50px;
25 | border: 1px dashed #067002;
26 | }
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-iframe/resizable-iframe.component.scss:
--------------------------------------------------------------------------------
1 | .iframe-drag-bounds {
2 | width: 100%;
3 | height: 400px;
4 | border: 1px dashed #A4B7C1;
5 | }
6 |
7 | .iframe-resize-container {
8 | position: relative;
9 | width: 300px;
10 | height: 300px;
11 | }
12 |
13 | .iframe-drag-handle {
14 | position: absolute;
15 | top: 0;
16 | right: 35px;
17 | height: 2em;
18 | background: #cccccc;
19 | z-index: 8;
20 | padding: 0.25em;
21 | text-align: right;
22 | }
23 |
24 | .iframe-content {
25 | position: absolute;
26 | height: 100%;
27 | top: 0;
28 | left: 0;
29 | }
--------------------------------------------------------------------------------
/projects/angular2-draggable/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular2-draggable",
3 | "version": "16.0.0",
4 | "author": "Xie, Ziyu",
5 | "license": "MIT",
6 | "keywords": [
7 | "angular",
8 | "ng",
9 | "draggable",
10 | "resizable"
11 | ],
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/xieziyu/angular2-draggable.git"
15 | },
16 | "homepage": "https://xieziyu.github.io/angular2-draggable",
17 | "bugs": "https://github.com/xieziyu/angular2-draggable/issues",
18 | "dependencies": {
19 | "tslib": "^2.3.0"
20 | },
21 | "sideEffects": false
22 | }
23 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Compiled output
2 | /dist
3 | /tmp
4 | /out-tsc
5 | /bazel-out
6 |
7 | # Node
8 | /node_modules
9 | npm-debug.log
10 | yarn-error.log
11 |
12 | # IDEs and editors
13 | .idea/
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # Visual Studio Code
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 | .history/*
28 |
29 | # Miscellaneous
30 | /.angular/cache
31 | .sass-cache/
32 | /connect.lock
33 | /coverage
34 | /libpeerconnection.log
35 | testem.log
36 | /typings
37 |
38 | # System files
39 | .DS_Store
40 | Thumbs.db
41 |
--------------------------------------------------------------------------------
/scripts/build-scss.js:
--------------------------------------------------------------------------------
1 | const sass = require('node-sass');
2 | const fs = require('fs');
3 |
4 | const SCSS_FILE = './projects/angular2-draggable/src/scss/resizable.scss';
5 | const OUTPUT_PATH = './dist/angular2-draggable/css';
6 |
7 | const result = sass.renderSync({
8 | file: SCSS_FILE,
9 | outputStyle: 'expanded'
10 | });
11 |
12 | const minResult = sass.renderSync({
13 | file: SCSS_FILE,
14 | outputStyle: 'compressed'
15 | });
16 |
17 | try {
18 | fs.mkdirSync(OUTPUT_PATH);
19 | } catch(e) {
20 | // no need
21 | }
22 | fs.writeFileSync(OUTPUT_PATH + '/resizable.css', result.css);
23 | fs.writeFileSync(OUTPUT_PATH + '/resizable.min.css', minResult.css);
24 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-swap/draggable-swap.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 | A (Drag)
12 |
13 |
18 | B
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-grid/resizable-grid.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-resizable-grid',
8 | templateUrl: './resizable-grid.component.html',
9 | styleUrls: ['./resizable-grid.component.scss'],
10 | })
11 | export class ResizableGridComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./resizable-grid.component.html')
15 | .default;
16 | component = require('!!raw-loader!./resizable-grid.component.ts').default;
17 | // IGNORE END
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-basic/draggable-basic.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-draggable-basic',
8 | templateUrl: './draggable-basic.component.html',
9 | styleUrls: ['./draggable-basic.component.scss'],
10 | })
11 | export class DraggableBasicComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./draggable-basic.component.html')
15 | .default;
16 | component = require('!!raw-loader!./draggable-basic.component.ts').default;
17 | // IGNORE END
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-basic/resizable-basic.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-resizable-basic',
8 | templateUrl: './resizable-basic.component.html',
9 | styleUrls: ['./resizable-basic.component.scss'],
10 | })
11 | export class ResizableBasicComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./resizable-basic.component.html')
15 | .default;
16 | component = require('!!raw-loader!./resizable-basic.component.ts').default;
17 | // IGNORE END
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-grid/resizable-grid.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-methods/draggable-methods.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-draggable-methods',
8 | templateUrl: './draggable-methods.component.html',
9 | styleUrls: ['./draggable-methods.component.scss'],
10 | })
11 | export class DraggableMethodsComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./draggable-methods.component.html')
15 | .default;
16 | component = require('!!raw-loader!./draggable-methods.component.ts').default;
17 | // IGNORE END
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-min-max/resizable-min-max.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-resizable-min-max',
8 | templateUrl: './resizable-min-max.component.html',
9 | styleUrls: ['./resizable-min-max.component.scss'],
10 | })
11 | export class ResizableMinMaxComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./resizable-min-max.component.html')
15 | .default;
16 | component = require('!!raw-loader!./resizable-min-max.component.ts').default;
17 | // IGNORE END
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/shared/code-block/code-block.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-containment/resizable-containment.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-containment/resizable-containment.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-resizable-containment',
8 | templateUrl: './resizable-containment.component.html',
9 | styleUrls: ['./resizable-containment.component.scss'],
10 | })
11 | export class ResizableContainmentComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./resizable-containment.component.html')
15 | .default;
16 | component = require('!!raw-loader!./resizable-containment.component.ts').default;
17 | // IGNORE END
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-grid/draggable-grid.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-draggable-grid',
8 | templateUrl: './draggable-grid.component.html',
9 | styleUrls: ['./draggable-grid.component.scss'],
10 | })
11 | export class DraggableGridComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./draggable-grid.component.html')
15 | .default;
16 | component = require('!!raw-loader!./draggable-grid.component.ts').default;
17 | // IGNORE END
18 | gridSize = 50;
19 | grids = [0, 50, 100, 150, 200];
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-iframe/resizable-iframe.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
Draggable & Resizable
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-iframe/resizable-iframe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-resizable-iframe',
8 | templateUrl: './resizable-iframe.component.html',
9 | styleUrls: ['./resizable-iframe.component.scss'],
10 | })
11 | export class ResizableIframeComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./resizable-iframe.component.html')
15 | .default;
16 | component = require('!!raw-loader!./resizable-iframe.component.ts').default;
17 | scss = require('!!raw-loader!./resizable-iframe.component.scss').default;
18 | // IGNORE END
19 | }
20 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - fix/action
7 | jobs:
8 | build:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 | - name: Setup Node
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: 18
19 | cache: 'yarn'
20 | - name: Install
21 | run: yarn
22 | - name: Build
23 | run: yarn release
24 | - name: Deploy to GitHub Pages
25 | if: success()
26 | uses: crazy-max/ghaction-github-pages@v3
27 | with:
28 | target_branch: gh-pages
29 | build_dir: dist/docs
30 | env:
31 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-grid/draggable-grid.component.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | const routes: Routes = [
5 | { path: '', pathMatch: 'full', redirectTo: '/welcome' },
6 | {
7 | path: 'welcome',
8 | loadChildren: () => import('./pages/welcome/welcome.module').then(m => m.WelcomeModule),
9 | },
10 | {
11 | path: 'draggable',
12 | loadChildren: () =>
13 | import('./pages/demo-draggable/demo-draggable.module').then(m => m.DemoDraggableModule),
14 | },
15 | {
16 | path: 'resizable',
17 | loadChildren: () =>
18 | import('./pages/demo-resizable/demo-resizable.module').then(m => m.DemoResizableModule),
19 | },
20 | ];
21 |
22 | @NgModule({
23 | imports: [RouterModule.forRoot(routes)],
24 | exports: [RouterModule],
25 | })
26 | export class AppRoutingModule {}
27 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-options/draggable-options.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-draggable-options',
8 | templateUrl: './draggable-options.component.html',
9 | styleUrls: ['./draggable-options.component.scss'],
10 | })
11 | export class DraggableOptionsComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./draggable-options.component.html')
15 | .default;
16 | component = require('!!raw-loader!./draggable-options.component.ts').default;
17 | // IGNORE END
18 | draggable = true;
19 | useHandle = false;
20 | zIndex;
21 | zIndexMoving;
22 | preventDefaultEvent = false;
23 | trackPosition = true;
24 | position;
25 | lockAxis;
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/shared/shared.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { NgZorroCustomModule } from './ng-zorro-custom.module';
4 | import { MarkdownModule } from 'ngx-markdown';
5 | import { AngularDraggableModule } from 'angular2-draggable';
6 |
7 | import { IconsProviderModule } from './icons-provider.module';
8 | import { CodeBlockComponent } from './code-block/code-block.component';
9 |
10 | @NgModule({
11 | declarations: [CodeBlockComponent],
12 | imports: [
13 | CommonModule,
14 | IconsProviderModule,
15 | NgZorroCustomModule,
16 | MarkdownModule.forChild(),
17 | AngularDraggableModule,
18 | ],
19 | exports: [
20 | IconsProviderModule,
21 | NgZorroCustomModule,
22 | MarkdownModule,
23 | CodeBlockComponent,
24 | AngularDraggableModule,
25 | ],
26 | })
27 | export class SharedModule {}
28 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/models/size.ts:
--------------------------------------------------------------------------------
1 | export interface ISize {
2 | width: number;
3 | height: number;
4 | }
5 |
6 | export class Size implements ISize {
7 | constructor(public width: number, public height: number) {}
8 |
9 | static getCurrent(el: Element) {
10 | let size = new Size(0, 0);
11 |
12 | if (window) {
13 | const computed = window.getComputedStyle(el);
14 | if (computed) {
15 | size.width = parseInt(computed.getPropertyValue('width'), 10);
16 | size.height = parseInt(computed.getPropertyValue('height'), 10);
17 | }
18 | return size;
19 | } else {
20 | console.error('Not Supported!');
21 | return null;
22 | }
23 | }
24 |
25 | static copy(s: Size) {
26 | return new Size(0, 0).set(s);
27 | }
28 |
29 | set(s: ISize) {
30 | this.width = s.width;
31 | this.height = s.height;
32 | return this;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-events/draggable-events.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
12 |
13 |
14 |
22 |
Drag me
23 |
check your console
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/app/layout/app-menu/app-menu.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 | import { Router, NavigationEnd } from '@angular/router';
3 | import { filter } from 'rxjs/operators';
4 | import { AppMenu, APP_MENUS } from '../../menus';
5 |
6 | interface AppMenuEx extends AppMenu {
7 | pathRegex: RegExp;
8 | }
9 |
10 | @Component({
11 | selector: 'app-menu',
12 | templateUrl: './app-menu.component.html',
13 | styleUrls: ['./app-menu.component.scss'],
14 | })
15 | export class AppMenuComponent {
16 | @Input() isCollapsed: boolean;
17 | menus: AppMenuEx[];
18 | currentUrl: string;
19 |
20 | constructor(private router: Router) {
21 | router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe((e: NavigationEnd) => {
22 | this.currentUrl = e.url;
23 | });
24 | this.menus = APP_MENUS.map(m => ({
25 | path: m.path,
26 | pathRegex: new RegExp(m.path),
27 | text: m.text,
28 | icon: m.icon,
29 | iconTheme: m.iconTheme,
30 | submenus: m.submenus ? m.submenus.concat() : undefined,
31 | }));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Xie, Ziyu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-events/resizable-events.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | .resetSize()
4 |
5 | Toggle aspectRatio
6 |
7 |
8 |
9 |
18 |
23 |
24 | State: {{ state }}
25 | Size: {{ size | json }}
26 | Position: {{ position | json }}
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "paths": {
6 | "angular2-draggable": [
7 | "dist/angular2-draggable"
8 | ]
9 | },
10 | "baseUrl": "./",
11 | "outDir": "./dist/out-tsc",
12 | "forceConsistentCasingInFileNames": true,
13 | "strict": false,
14 | "noImplicitOverride": true,
15 | "noPropertyAccessFromIndexSignature": true,
16 | "noImplicitReturns": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "sourceMap": true,
19 | "declaration": false,
20 | "downlevelIteration": true,
21 | "experimentalDecorators": true,
22 | "moduleResolution": "node",
23 | "importHelpers": true,
24 | "target": "ES2022",
25 | "module": "ES2022",
26 | "useDefineForClassFields": false,
27 | "lib": [
28 | "ES2022",
29 | "dom"
30 | ]
31 | },
32 | "angularCompilerOptions": {
33 | "enableI18nLegacyMessageIdFormat": false,
34 | "strictInjectionParameters": true,
35 | "strictInputAccessModifiers": true,
36 | "strictTemplates": true
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Xie, Ziyu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-events/draggable-events.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-draggable-events',
8 | templateUrl: './draggable-events.component.html',
9 | styleUrls: ['./draggable-events.component.scss'],
10 | })
11 | export class DraggableEventsComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./draggable-events.component.html')
15 | .default;
16 | component = require('!!raw-loader!./draggable-events.component.ts').default;
17 | // IGNORE END
18 | movingOffset = { x: 0, y: 0 };
19 | endOffset = { x: 0, y: 0 };
20 |
21 | onStart(event) {
22 | console.log('started output:', event);
23 | }
24 |
25 | onStop(event) {
26 | console.log('stopped output:', event);
27 | }
28 |
29 | onMoving(event) {
30 | this.movingOffset.x = event.x;
31 | this.movingOffset.y = event.y;
32 | }
33 |
34 | onMoveEnd(event) {
35 | this.endOffset.x = event.x;
36 | this.endOffset.y = event.y;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "overrides": [
4 | {
5 | "files": ["*.ts"],
6 | "extends": [
7 | "plugin:@angular-eslint/recommended",
8 | "plugin:@angular-eslint/template/process-inline-templates",
9 | "plugin:prettier/recommended"
10 | ],
11 | "rules": {
12 | "@angular-eslint/directive-selector": "off",
13 | "@angular-eslint/component-selector": "off"
14 | }
15 | },
16 | // NOTE: WE ARE NOT APPLYING PRETTIER IN THIS OVERRIDE, ONLY @ANGULAR-ESLINT/TEMPLATE
17 | {
18 | "files": ["*.html"],
19 | "extends": ["plugin:@angular-eslint/template/recommended"],
20 | "rules": {}
21 | },
22 | // NOTE: WE ARE NOT APPLYING @ANGULAR-ESLINT/TEMPLATE IN THIS OVERRIDE, ONLY PRETTIER
23 | {
24 | "files": ["*.html"],
25 | "excludedFiles": ["*inline-template-*.component.html"],
26 | "extends": ["plugin:prettier/recommended"],
27 | "rules": {
28 | // NOTE: WE ARE OVERRIDING THE DEFAULT CONFIG TO ALWAYS SET THE PARSER TO ANGULAR (SEE BELOW)
29 | "prettier/prettier": ["error", { "parser": "angular" }]
30 | }
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/shared/code-block/code-block.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-code-block',
5 | templateUrl: './code-block.component.html',
6 | styleUrls: ['./code-block.component.scss'],
7 | })
8 | export class CodeBlockComponent implements OnInit {
9 | @Input() html: string;
10 | @Input() component: string;
11 | @Input() scss: string;
12 | @Input() data: string;
13 | htmlCode: string;
14 | componentCode: string;
15 | scssCode: string;
16 | dataCode: string;
17 |
18 | constructor() {}
19 |
20 | ngOnInit() {
21 | if (this.html) {
22 | this.htmlCode = this.html.match(/DEMO START -->\n((.*\n)*)
32 | Drag Me!
33 |
34 |
35 | Disabled
36 | \`\`\``;
37 | resizableHTML = `## Resizable HTML
38 | \`\`\`html
39 |
40 | I'm resizable
41 |
42 |
43 | Disabled
44 | \`\`\``;
45 | resizableCSS = `## Resizable CSS
46 | \`\`\`diff
47 | // angular.json
48 | "styles": [
49 | ...
50 | + "node_modules/angular2-draggable/css/resizable.min.css"
51 | ]
52 | \`\`\``;
53 | }
54 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-layout/resizable-layout.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home
5 |
6 | Resizable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
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 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/demo-draggable.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { SharedModule } from '../../shared/shared.module';
4 | import { DemoDraggableRoutingModule } from './demo-draggable-routing.module';
5 | import { DraggableBasicComponent } from './draggable-basic/draggable-basic.component';
6 | import { DraggableLayoutComponent } from './draggable-layout/draggable-layout.component';
7 | import { DraggableOptionsComponent } from './draggable-options/draggable-options.component';
8 | import { DraggableEventsComponent } from './draggable-events/draggable-events.component';
9 | import { DraggableBoundaryComponent } from './draggable-boundary/draggable-boundary.component';
10 | import { DraggableMethodsComponent } from './draggable-methods/draggable-methods.component';
11 | import { DraggableGridComponent } from './draggable-grid/draggable-grid.component';
12 | import { DraggableSwapComponent } from './draggable-swap/draggable-swap.component';
13 |
14 | @NgModule({
15 | declarations: [
16 | DraggableBasicComponent,
17 | DraggableLayoutComponent,
18 | DraggableOptionsComponent,
19 | DraggableEventsComponent,
20 | DraggableBoundaryComponent,
21 | DraggableMethodsComponent,
22 | DraggableGridComponent,
23 | DraggableSwapComponent,
24 | ],
25 | imports: [CommonModule, SharedModule, DemoDraggableRoutingModule],
26 | })
27 | export class DemoDraggableModule {}
28 |
--------------------------------------------------------------------------------
/src/app/layout/app-menu/app-menu.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | {{ menu.text }}
11 |
12 |
13 |
14 |
15 |
20 |
21 |
26 |
27 |
32 | {{ submenu.text }}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/app/pages/demo-resizable/resizable-events/resizable-events.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { AngularResizableDirective } from 'angular2-draggable';
3 | // IGNORE START
4 | declare const require: any;
5 | // IGNORE END
6 |
7 | @Component({
8 | selector: 'app-resizable-events',
9 | templateUrl: './resizable-events.component.html',
10 | styleUrls: ['./resizable-events.component.scss'],
11 | })
12 | export class ResizableEventsComponent {
13 | // IGNORE START
14 | html =
15 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./resizable-events.component.html')
16 | .default;
17 | component = require('!!raw-loader!./resizable-events.component.ts').default;
18 | // IGNORE END
19 | state = '';
20 | size: any = null;
21 | position: any = null;
22 | aspectRatio = false;
23 |
24 | onResizeStart(event) {
25 | this.state = 'Resize Started';
26 | this.size = event.size;
27 | this.position = event.position;
28 | }
29 |
30 | onResizing(event) {
31 | this.state = 'Resizing';
32 | this.size = event.size;
33 | this.position = event.position;
34 | }
35 |
36 | onResizeStop(event) {
37 | this.state = 'Resize Stopped';
38 | this.size = event.size;
39 | this.position = event.position;
40 | }
41 |
42 | onReset(block: AngularResizableDirective) {
43 | block.resetSize();
44 | let { size, position } = block.getStatus();
45 | this.size = size;
46 | this.position = position;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/shared/ng-zorro-custom.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { NZ_I18N, en_US } from 'ng-zorro-antd/i18n';
3 | import { NzIconModule } from 'ng-zorro-antd/icon';
4 | import { NzMenuModule } from 'ng-zorro-antd/menu';
5 | import { NzGridModule } from 'ng-zorro-antd/grid';
6 | import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb';
7 | import { NzLayoutModule } from 'ng-zorro-antd/layout';
8 | import { NzButtonModule } from 'ng-zorro-antd/button';
9 | import { NzMessageModule } from 'ng-zorro-antd/message';
10 | import { NzTabsModule } from 'ng-zorro-antd/tabs';
11 | import { NzCardModule } from 'ng-zorro-antd/card';
12 | import { NzDividerModule } from 'ng-zorro-antd/divider';
13 | import { NzPageHeaderModule } from 'ng-zorro-antd/page-header';
14 | import { NzAlertModule } from 'ng-zorro-antd/alert';
15 | import { NzTypographyModule } from 'ng-zorro-antd/typography';
16 | import { NzSpaceModule } from 'ng-zorro-antd/space';
17 |
18 | const NG_ZORRO_MODULES = [
19 | NzIconModule,
20 | NzMenuModule,
21 | NzGridModule,
22 | NzBreadCrumbModule,
23 | NzLayoutModule,
24 | NzButtonModule,
25 | NzMessageModule,
26 | NzTabsModule,
27 | NzCardModule,
28 | NzDividerModule,
29 | NzPageHeaderModule,
30 | NzAlertModule,
31 | NzTypographyModule,
32 | NzSpaceModule,
33 | ];
34 |
35 | @NgModule({
36 | imports: [...NG_ZORRO_MODULES],
37 | exports: [...NG_ZORRO_MODULES],
38 | providers: [{ provide: NZ_I18N, useValue: en_US }],
39 | })
40 | export class NgZorroCustomModule {}
41 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-layout/draggable-layout.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home
5 |
6 | Draggable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
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 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/widgets/resize-handle.ts:
--------------------------------------------------------------------------------
1 | import { Renderer2 } from '@angular/core';
2 |
3 | export class ResizeHandle {
4 | protected _handle: Element;
5 | private _onResize;
6 |
7 | constructor(
8 | protected parent: Element,
9 | protected renderer: Renderer2,
10 | public type: string,
11 | public css: string,
12 | private onMouseDown: any,
13 | private existHandle?: Element
14 | ) {
15 | // generate handle div or using exist handle
16 | let handle = this.existHandle || renderer.createElement('div');
17 | renderer.addClass(handle, 'ng-resizable-handle');
18 | renderer.addClass(handle, css);
19 |
20 | // add default diagonal for se handle
21 | if (type === 'se') {
22 | renderer.addClass(handle, 'ng-resizable-diagonal');
23 | }
24 |
25 | // append div to parent
26 | if (this.parent && !this.existHandle) {
27 | parent.appendChild(handle);
28 | }
29 |
30 | // create and register event listener
31 | this._onResize = event => {
32 | onMouseDown(event, this);
33 | };
34 | handle.addEventListener('mousedown', this._onResize, { passive: false });
35 | handle.addEventListener('touchstart', this._onResize, { passive: false });
36 |
37 | // done
38 | this._handle = handle;
39 | }
40 |
41 | dispose() {
42 | this._handle.removeEventListener('mousedown', this._onResize);
43 | this._handle.removeEventListener('touchstart', this._onResize);
44 |
45 | if (this.parent && !this.existHandle) {
46 | this.parent.removeChild(this._handle);
47 | }
48 | this._handle = null;
49 | this._onResize = null;
50 | }
51 |
52 | get el() {
53 | return this._handle;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-swap/draggable-swap.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | // IGNORE START
3 | declare const require: any;
4 | // IGNORE END
5 |
6 | @Component({
7 | selector: 'app-draggable-swap',
8 | templateUrl: './draggable-swap.component.html',
9 | styleUrls: ['./draggable-swap.component.scss'],
10 | })
11 | export class DraggableSwapComponent {
12 | // IGNORE START
13 | html =
14 | require('!!html-loader?{"minimize": {"removeComments":false,"caseSensitive":true}}!./draggable-swap.component.html')
15 | .default;
16 | component = require('!!raw-loader!./draggable-swap.component.ts').default;
17 | scss = require('!!raw-loader!./draggable-swap.component.scss').default;
18 | // IGNORE END
19 | positionA = { x: 0, y: 0 };
20 | positionB = { x: 160, y: 0 };
21 |
22 | onMoving(event) {
23 | const boxWidth = 150;
24 | const boxHeight = 60;
25 |
26 | if (
27 | this.positionA.x < this.positionB.x &&
28 | event.x + boxWidth >= this.positionB.x + boxWidth / 2 &&
29 | event.x <= this.positionB.x + boxWidth &&
30 | event.y + boxHeight >= this.positionA.y &&
31 | event.y <= this.positionA.y + boxHeight
32 | ) {
33 | let tmp = this.positionB;
34 | this.positionB = this.positionA;
35 | this.positionA = tmp;
36 | } else if (
37 | this.positionA.x >= this.positionB.x &&
38 | event.x <= this.positionB.x + boxWidth / 2 &&
39 | event.x + boxWidth >= this.positionB.x &&
40 | event.y + boxHeight >= this.positionA.y &&
41 | event.y <= this.positionA.y + boxHeight
42 | ) {
43 | let tmp = this.positionB;
44 | this.positionB = this.positionA;
45 | this.positionA = tmp;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | text-rendering: optimizeLegibility;
4 | -webkit-font-smoothing: antialiased;
5 | -moz-osx-font-smoothing: grayscale;
6 | }
7 |
8 | .app-layout {
9 | height: 100vh;
10 | }
11 |
12 | .menu-sidebar {
13 | position: relative;
14 | z-index: 10;
15 | min-height: 100vh;
16 | box-shadow: 2px 0 6px rgba(0,21,41,.35);
17 | }
18 |
19 | .header-trigger {
20 | padding: 20px 24px;
21 | font-size: 20px;
22 | cursor: pointer;
23 | transition: all .3s,padding 0s;
24 | }
25 |
26 | .trigger:hover {
27 | color: #1890ff;
28 | }
29 |
30 | .sidebar-logo {
31 | position: relative;
32 | height: 64px;
33 | padding-left: 24px;
34 | overflow: hidden;
35 | line-height: 64px;
36 | background: #001529;
37 | transition: all .3s;
38 | }
39 |
40 | .sidebar-logo img {
41 | display: inline-block;
42 | height: 32px;
43 | width: 32px;
44 | vertical-align: middle;
45 | }
46 |
47 | .sidebar-logo h1 {
48 | display: inline-block;
49 | margin: 0 0 0 20px;
50 | color: #fff;
51 | font-weight: 600;
52 | font-size: 25px;
53 | font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
54 | vertical-align: middle;
55 | }
56 |
57 | nz-header {
58 | padding: 0;
59 | width: 100%;
60 | z-index: 2;
61 | }
62 |
63 | .app-header {
64 | position: relative;
65 | height: 64px;
66 | padding: 0;
67 | background: #fff;
68 | box-shadow: 0 1px 4px rgba(0,21,41,.08);
69 | display: flex;
70 | justify-content: space-between;
71 | align-items: center;
72 | }
73 |
74 | .github-logo {
75 | padding: 0px 24px;
76 | font-size: 25px;
77 | cursor: pointer;
78 | transition: all .3s,padding 0s;
79 | }
80 |
81 | a.github-logo {
82 | color: rgba(0,0,0,.65)
83 | }
84 |
85 | nz-content {
86 | margin: 24px;
87 | }
88 |
89 | .inner-content {
90 | padding: 24px;
91 | background: #fff;
92 | // height: 100%;
93 | }
94 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-boundary/draggable-boundary.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | toggle [inBounds]
6 |
7 |
8 |
9 | toggle [outOfBounds] top {{ myOutOfBounds.top ? ' (active)' : '' }}
10 |
11 |
12 |
13 |
14 | toggle [outOfBounds] right {{ myOutOfBounds.right ? ' (active)' : '' }}
15 |
16 |
17 |
18 |
19 | toggle [outOfBounds] bottom {{ myOutOfBounds.bottom ? ' (active)' : '' }}
20 |
21 |
22 |
23 |
24 | toggle [outOfBounds] left {{ myOutOfBounds.left ? ' (active)' : '' }}
25 |
26 |
27 |
28 |
29 |
38 |
#myBounds
39 |
46 |
Drag me {{ inBounds ? 'in #myBounds' : '' }}
47 |
check your console
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/app/pages/welcome/welcome.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Angular2-Draggable
5 |
6 |
7 |
8 |
9 |
10 |
11 | Angular(ver >= 4.x) directives that make the DOM element
12 | draggable and
13 | resizable
14 |
15 |
16 | GitHub
24 | Documents
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/scss/resizable.scss:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .ng-resizable {
4 | position: relative;
5 | }
6 |
7 | .ng-resizable-handle {
8 | position: absolute;
9 | font-size: 0.1px;
10 | display: block;
11 | -ms-touch-action: none;
12 | touch-action: none;
13 |
14 | &.ng-resizable-e {
15 | cursor: e-resize;
16 | width: $handle-border-size;
17 | right: $handle-border-offset;
18 | height: 100%;
19 | top: 0;
20 | }
21 |
22 | &.ng-resizable-w {
23 | cursor: w-resize;
24 | width: $handle-border-size;
25 | left: $handle-border-offset;
26 | height: 100%;
27 | top: 0;
28 | }
29 |
30 | &.ng-resizable-s {
31 | cursor: s-resize;
32 | height: $handle-border-size;
33 | bottom: $handle-border-offset;
34 | width: 100%;
35 | left: 0;
36 | }
37 |
38 | &.ng-resizable-n {
39 | cursor: n-resize;
40 | height: $handle-border-size;
41 | top: $handle-border-offset;
42 | width: 100%;
43 | left: 0;
44 | }
45 |
46 | &.ng-resizable-se {
47 | cursor: se-resize;
48 | width: $handle-corner-size;
49 | height: $handle-corner-size;
50 | right: $handle-corner-offset;
51 | bottom: $handle-corner-offset;
52 | }
53 |
54 | &.ng-resizable-sw {
55 | cursor: sw-resize;
56 | width: $handle-corner-size;
57 | height: $handle-corner-size;
58 | left: $handle-corner-offset;
59 | bottom: $handle-corner-offset;
60 | }
61 |
62 | &.ng-resizable-ne {
63 | cursor: ne-resize;
64 | width: $handle-corner-size;
65 | height: $handle-corner-size;
66 | right: $handle-corner-offset;
67 | top: $handle-corner-offset;
68 | }
69 |
70 | &.ng-resizable-nw {
71 | cursor: nw-resize;
72 | width: $handle-corner-size;
73 | height: $handle-corner-size;
74 | left: $handle-corner-offset;
75 | top: $handle-corner-offset;
76 | }
77 | }
78 |
79 | .ng-resizable-diagonal {
80 | box-sizing: border-box;
81 | width: 0;
82 | height: 0;
83 | border-bottom: $handle-corner-size solid #aaa;
84 | border-left: $handle-corner-size solid transparent;
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/src/styles/_custom.scss:
--------------------------------------------------------------------------------
1 | $white: #fff;
2 | $gray-100: #f0f3f5;
3 | $gray-200: #c2cfd6;
4 | $gray-300: #a4b7c1;
5 | $gray-400: #869fac;
6 | $gray-500: #678898;
7 | $gray-600: #536c79;
8 | $gray-700: #3e515b;
9 | $gray-800: #29363d;
10 | $gray-900: #151b1e;
11 | $black: #000 !default;
12 |
13 | $blue: #1890ff;
14 | $red: #ff4d4f;
15 | $green: #49aa19;
16 |
17 | @font-face {
18 | font-family: 'Roboto Mono';
19 | src: url('../assets/fonts/RobotoMono-Regular.ttf');
20 | }
21 |
22 | markdown table {
23 | margin: auto !important;
24 | }
25 |
26 | .text-center {
27 | text-align: center !important;
28 | }
29 |
30 | .mr-2 {
31 | margin-right: 0.5em;
32 | }
33 |
34 | .ant-page-header {
35 | padding: 0;
36 | }
37 |
38 | .code-block {
39 | pre {
40 | max-height: 400px;
41 | }
42 | }
43 |
44 | .code-block-sm {
45 | pre {
46 | height: 200px;
47 | overflow-y: auto;
48 | }
49 | }
50 |
51 | .button-groups {
52 | button {
53 | margin-right: 16px;
54 | }
55 | padding: 16px 0;
56 | }
57 |
58 | .drag-block-sm {
59 | width: 100px;
60 | height: 100px;
61 | color: white;
62 | line-height: 100px;
63 | text-align: center;
64 | }
65 |
66 | .drag-block {
67 | width: 200px;
68 | height: 200px;
69 | background-color: $blue;
70 | color: white;
71 | padding: 75px 0px;
72 | line-height: 50px;
73 | text-align: center;
74 | }
75 |
76 | .drag-block-lg {
77 | width: 300px;
78 | height: 300px;
79 | background-color: $red;
80 | padding-top: 20px;
81 | color: white;
82 | text-align: center;
83 | overflow-y: auto;
84 | }
85 |
86 | .drag-block-handle {
87 | width: 100px;
88 | height: 43px;
89 | margin-top: -20px;
90 | padding-top: 10px;
91 | margin-bottom: 10px;
92 | }
93 |
94 | .ng-draggable {
95 | cursor: grab;
96 | background-color: $blue;
97 | }
98 |
99 | .ng-dragging {
100 | cursor: grabbing;
101 | }
102 |
103 | .drag-boundary {
104 | width: 400px;
105 | height: 400px;
106 | overflow: hidden;
107 | border: solid 1px $green;
108 | position: relative;
109 |
110 | .label {
111 | color: white;
112 | background-color: $green;
113 | width: 100px;
114 | text-align: center;
115 | right: 0px;
116 | position: absolute;
117 | }
118 | }
119 |
120 | .top-b {
121 | border-top-color: $red !important;
122 | }
123 |
124 | .bottom-b {
125 | border-bottom-color: $red !important;
126 | }
127 |
128 | .left-b {
129 | border-left-color: $red !important;
130 | }
131 |
132 | .right-b {
133 | border-right-color: $red !important;
134 | }
135 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/models/position.ts:
--------------------------------------------------------------------------------
1 | export interface IPosition {
2 | x: number;
3 | y: number;
4 | }
5 |
6 | export class Position implements IPosition {
7 | constructor(public x: number, public y: number) {}
8 |
9 | static fromEvent(e: MouseEvent | TouchEvent, el: any = null) {
10 | /**
11 | * Fix issue: Resize doesn't work on Windows10 IE11 (and on some windows 7 IE11)
12 | * https://github.com/xieziyu/angular2-draggable/issues/164
13 | * e instanceof MouseEvent check returns false on IE11
14 | */
15 | if (this.isMouseEvent(e)) {
16 | return new Position(e.clientX, e.clientY);
17 | } else {
18 | if (el === null || e.changedTouches.length === 1) {
19 | return new Position(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
20 | }
21 |
22 | /**
23 | * Fix issue: Multiple phone draggables at the same time
24 | * https://github.com/xieziyu/angular2-draggable/issues/128
25 | */
26 | for (let i = 0; i < e.changedTouches.length; i++) {
27 | if (e.changedTouches[i].target === el) {
28 | return new Position(e.changedTouches[i].clientX, e.changedTouches[i].clientY);
29 | }
30 | }
31 | }
32 | return null;
33 | }
34 |
35 | static isMouseEvent(e: MouseEvent | TouchEvent): e is MouseEvent {
36 | return Object.prototype.toString.apply(e).indexOf('MouseEvent') === 8;
37 | }
38 |
39 | static isIPosition(obj): obj is IPosition {
40 | return !!obj && 'x' in obj && 'y' in obj;
41 | }
42 |
43 | static getCurrent(el: Element) {
44 | let pos = new Position(0, 0);
45 |
46 | if (window) {
47 | const computed = window.getComputedStyle(el);
48 | if (computed) {
49 | let x = parseInt(computed.getPropertyValue('left'), 10);
50 | let y = parseInt(computed.getPropertyValue('top'), 10);
51 | pos.x = isNaN(x) ? 0 : x;
52 | pos.y = isNaN(y) ? 0 : y;
53 | }
54 | return pos;
55 | } else {
56 | console.error('Not Supported!');
57 | return null;
58 | }
59 | }
60 |
61 | static copy(p: IPosition) {
62 | return new Position(0, 0).set(p);
63 | }
64 |
65 | get value(): IPosition {
66 | return { x: this.x, y: this.y };
67 | }
68 |
69 | add(p: IPosition) {
70 | this.x += p.x;
71 | this.y += p.y;
72 | return this;
73 | }
74 |
75 | subtract(p: IPosition) {
76 | this.x -= p.x;
77 | this.y -= p.y;
78 | return this;
79 | }
80 |
81 | multiply(n: number) {
82 | this.x *= n;
83 | this.y *= n;
84 | }
85 |
86 | divide(n: number) {
87 | this.x /= n;
88 | this.y /= n;
89 | }
90 |
91 | reset() {
92 | this.x = 0;
93 | this.y = 0;
94 | return this;
95 | }
96 |
97 | set(p: IPosition) {
98 | this.x = p.x;
99 | this.y = p.y;
100 | return this;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular2-draggable-demo",
3 | "version": "16.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve --port=4202",
8 | "build": "ng build",
9 | "build:prod": "ng build --configuration production --base-href ./",
10 | "build:lib": "ng build angular2-draggable",
11 | "build:lib:prod": "ng build angular2-draggable --configuration production",
12 | "build:css": "node scripts/build-scss.js",
13 | "test": "ng test",
14 | "test:lib": "ng test angular2-draggable",
15 | "lint": "ng lint",
16 | "fix": "ng lint --fix",
17 | "e2e": "ng e2e",
18 | "clean": "rimraf -rf dist",
19 | "compodoc": "compodoc -p projects/angular2-draggable/tsconfig.lib.json -d dist/docs/api-doc --theme stripe",
20 | "serve:doc": "compodoc -p projects/angular2-draggable/tsconfig.lib.json -s",
21 | "build:doc": "run-s build:prod compodoc",
22 | "update:file": "cpr README.md dist/angular2-draggable/README.md -o && cpr CHANGELOG.md dist/angular2-draggable/CHANGELOG.md -o",
23 | "release": "run-s clean dist build:doc",
24 | "demo": "run-s build:lib build:css start",
25 | "dist": "run-s clean build:lib:prod build:css update:file"
26 | },
27 | "private": true,
28 | "dependencies": {
29 | "@angular/animations": "^16.0.0",
30 | "@angular/common": "^16.0.0",
31 | "@angular/compiler": "^16.0.0",
32 | "@angular/core": "^16.0.0",
33 | "@angular/forms": "^16.0.0",
34 | "@angular/platform-browser": "^16.0.0",
35 | "@angular/platform-browser-dynamic": "^16.0.0",
36 | "@angular/router": "^16.0.0",
37 | "@ant-design/icons-angular": "^15.0.0",
38 | "ng-zorro-antd": "15.1.0",
39 | "ngx-markdown": "^16.0.0",
40 | "prismjs": "^1.29.0",
41 | "rxjs": "~7.8.0",
42 | "tslib": "^2.3.0",
43 | "zone.js": "~0.13.0"
44 | },
45 | "devDependencies": {
46 | "@angular-devkit/build-angular": "^16.0.2",
47 | "@angular-eslint/builder": "^16.0.2",
48 | "@angular-eslint/eslint-plugin": "^16.0.2",
49 | "@angular-eslint/eslint-plugin-template": "^16.0.2",
50 | "@angular-eslint/schematics": "^16.0.2",
51 | "@angular-eslint/template-parser": "^16.0.2",
52 | "@angular/cli": "~16.0.2",
53 | "@angular/compiler-cli": "^16.0.0",
54 | "@compodoc/compodoc": "^1.1.19",
55 | "@types/jasmine": "~4.3.0",
56 | "@typescript-eslint/eslint-plugin": "^5.59.6",
57 | "@typescript-eslint/parser": "^5.59.6",
58 | "cpr": "^3.0.1",
59 | "eslint": "^8.40.0",
60 | "eslint-config-prettier": "^8.8.0",
61 | "eslint-plugin-prettier": "^4.2.1",
62 | "html-loader": "^4.2.0",
63 | "jasmine-core": "~4.6.0",
64 | "karma": "~6.4.0",
65 | "karma-chrome-launcher": "~3.2.0",
66 | "karma-coverage": "~2.2.0",
67 | "karma-jasmine": "~5.1.0",
68 | "karma-jasmine-html-reporter": "~2.0.0",
69 | "ng-packagr": "^16.0.0",
70 | "node-sass": "^8.0.0",
71 | "npm-run-all": "^4.1.5",
72 | "prettier": "^2.8.8",
73 | "prettier-eslint": "^15.0.1",
74 | "raw-loader": "^4.0.2",
75 | "rimraf": "^5.0.1",
76 | "ts-node": "^10.9.1",
77 | "typescript": "~5.0.2"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/prism-material-dark.css:
--------------------------------------------------------------------------------
1 | code[class*="language-"],
2 | pre[class*="language-"] {
3 | text-align: left;
4 | white-space: pre;
5 | word-spacing: normal;
6 | word-break: normal;
7 | word-wrap: normal;
8 | color: #eee;
9 | background: #2f2f2f;
10 | font-family: Roboto Mono, monospace;
11 | font-size: 1em;
12 | line-height: 1.5em;
13 |
14 | -moz-tab-size: 4;
15 | -o-tab-size: 4;
16 | tab-size: 4;
17 |
18 | -webkit-hyphens: none;
19 | -moz-hyphens: none;
20 | -ms-hyphens: none;
21 | hyphens: none;
22 | }
23 |
24 | code[class*="language-"]::-moz-selection,
25 | pre[class*="language-"]::-moz-selection,
26 | code[class*="language-"] ::-moz-selection,
27 | pre[class*="language-"] ::-moz-selection {
28 | background: #363636;
29 | }
30 |
31 | code[class*="language-"]::selection,
32 | pre[class*="language-"]::selection,
33 | code[class*="language-"] ::selection,
34 | pre[class*="language-"] ::selection {
35 | background: #363636;
36 | }
37 |
38 | :not(pre) > code[class*="language-"] {
39 | white-space: normal;
40 | border-radius: 0.2em;
41 | padding: 0.1em;
42 | }
43 |
44 | pre[class*="language-"] {
45 | overflow: auto;
46 | position: relative;
47 | margin: 0.5em 0;
48 | padding: 1.25em 1em;
49 | }
50 |
51 | .language-css > code,
52 | .language-sass > code,
53 | .language-scss > code {
54 | color: #fd9170;
55 | }
56 |
57 | [class*="language-"] .namespace {
58 | opacity: 0.7;
59 | }
60 |
61 | .token.atrule {
62 | color: #c792ea;
63 | }
64 |
65 | .token.attr-name {
66 | color: #ffcb6b;
67 | }
68 |
69 | .token.attr-value {
70 | color: #a5e844;
71 | }
72 |
73 | .token.attribute {
74 | color: #a5e844;
75 | }
76 |
77 | .token.boolean {
78 | color: #c792ea;
79 | }
80 |
81 | .token.builtin {
82 | color: #ffcb6b;
83 | }
84 |
85 | .token.cdata {
86 | color: #80cbc4;
87 | }
88 |
89 | .token.char {
90 | color: #80cbc4;
91 | }
92 |
93 | .token.class {
94 | color: #ffcb6b;
95 | }
96 |
97 | .token.class-name {
98 | color: #f2ff00;
99 | }
100 |
101 | .token.comment {
102 | color: #616161;
103 | }
104 |
105 | .token.constant {
106 | color: #c792ea;
107 | }
108 |
109 | .token.deleted {
110 | color: #ff6666;
111 | }
112 |
113 | .token.doctype {
114 | color: #616161;
115 | }
116 |
117 | .token.entity {
118 | color: #ff6666;
119 | }
120 |
121 | .token.function {
122 | color: #c792ea;
123 | }
124 |
125 | .token.hexcode {
126 | color: #f2ff00;
127 | }
128 |
129 | .token.id {
130 | color: #c792ea;
131 | font-weight: bold;
132 | }
133 |
134 | .token.important {
135 | color: #c792ea;
136 | font-weight: bold;
137 | }
138 |
139 | .token.inserted {
140 | color: #80cbc4;
141 | }
142 |
143 | .token.keyword {
144 | color: #c792ea;
145 | }
146 |
147 | .token.number {
148 | color: #fd9170;
149 | }
150 |
151 | .token.operator {
152 | color: #89ddff;
153 | }
154 |
155 | .token.prolog {
156 | color: #616161;
157 | }
158 |
159 | .token.property {
160 | color: #80cbc4;
161 | }
162 |
163 | .token.pseudo-class {
164 | color: #a5e844;
165 | }
166 |
167 | .token.pseudo-element {
168 | color: #a5e844;
169 | }
170 |
171 | .token.punctuation {
172 | color: #89ddff;
173 | }
174 |
175 | .token.regex {
176 | color: #f2ff00;
177 | }
178 |
179 | .token.selector {
180 | color: #ff6666;
181 | }
182 |
183 | .token.string {
184 | color: #a5e844;
185 | }
186 |
187 | .token.symbol {
188 | color: #c792ea;
189 | }
190 |
191 | .token.tag {
192 | color: #ff6666;
193 | }
194 |
195 | .token.unit {
196 | color: #fd9170;
197 | }
198 |
199 | .token.url {
200 | color: #ff6666;
201 | }
202 |
203 | .token.variable {
204 | color: #ff6666;
205 | }
206 |
--------------------------------------------------------------------------------
/src/app/pages/demo-draggable/draggable-options/draggable-options.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | toggle [ngDraggable]
7 |
8 |
9 |
10 |
14 | set [position]
15 |
16 |
17 |
18 | toggle [handle]
19 |
20 |
21 | set [zIndex] to 1000
22 |
23 |
24 |
25 | set [zIndexMoving] to 99999
26 |
27 |
28 |
29 |
30 | toggle [preventDefaultEvent]
31 |
32 |
33 |
34 |
35 | toggle [trackPosition]
36 |
37 |
38 |
39 |
40 | toggle [lockAxis]='y'
41 |
42 |
43 |
44 |
45 |
46 |
57 |
#myHandle
58 |
[handle]="myHandle"
59 |
[ngDraggable] = {{ draggable }}
60 |
[position] = {{ position === undefined ? 'undefined' : (position | json) }}
61 |
[zIndex] = {{ zIndex === undefined ? 'undefined' : zIndex }}
62 |
[zIndexMoving] = {{ zIndexMoving === undefined ? 'undefined' : zIndexMoving }}
63 |
[preventDefaultEvent] = {{ preventDefaultEvent }}
64 |
[trackPosition] = {{ trackPosition }}
65 |
[lockAxis] = {{ lockAxis === undefined ? 'undefined' : lockAxis }}
66 | Drag Me!
67 |
68 |
78 |
[ngDraggable] = {{ draggable }}
79 |
[position] = {{ position === undefined ? 'undefined' : (position | json) }}
80 |
[zIndex] = {{ zIndex === undefined ? 'undefined' : zIndex }}
81 |
[zIndexMoving] = {{ zIndexMoving === undefined ? 'undefined' : zIndexMoving }}
82 |
[preventDefaultEvent] = {{ preventDefaultEvent }}
83 |
[trackPosition] = {{ trackPosition }}
84 |
[lockAxis] = {{ lockAxis === undefined ? 'undefined' : lockAxis }}
85 | Drag Me!
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "cli": {
6 | "packageManager": "yarn",
7 | "schematicCollections": [
8 | "@angular-eslint/schematics"
9 | ]
10 | },
11 | "projects": {
12 | "angular2-draggable-demo": {
13 | "projectType": "application",
14 | "schematics": {
15 | "@schematics/angular:component": {
16 | "style": "scss",
17 | "skipTests": true
18 | },
19 | "@schematics/angular:service": {
20 | "skipTests": true
21 | }
22 | },
23 | "root": "",
24 | "sourceRoot": "src",
25 | "prefix": "app",
26 | "architect": {
27 | "build": {
28 | "builder": "@angular-devkit/build-angular:browser",
29 | "options": {
30 | "outputPath": "dist/docs",
31 | "index": "src/index.html",
32 | "main": "src/main.ts",
33 | "polyfills": [
34 | "zone.js"
35 | ],
36 | "tsConfig": "tsconfig.app.json",
37 | "inlineStyleLanguage": "scss",
38 | "assets": [
39 | "src/favicon.ico",
40 | "src/assets",
41 | {
42 | "glob": "**/*",
43 | "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
44 | "output": "/assets/"
45 | }
46 | ],
47 | "styles": [
48 | "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
49 | "src/styles.scss",
50 | "src/prism-material-dark.css",
51 | "dist/angular2-draggable/css/resizable.min.css"
52 | ],
53 | "scripts": [
54 | "node_modules/marked/marked.min.js",
55 | "node_modules/prismjs/prism.js",
56 | "node_modules/prismjs/components/prism-markup.min.js",
57 | "node_modules/prismjs/components/prism-css.min.js",
58 | "node_modules/prismjs/components/prism-scss.min.js",
59 | "node_modules/prismjs/components/prism-typescript.min.js",
60 | "node_modules/prismjs/components/prism-javascript.min.js",
61 | "node_modules/prismjs/components/prism-bash.min.js",
62 | "node_modules/prismjs/components/prism-diff.min.js"
63 | ]
64 | },
65 | "configurations": {
66 | "production": {
67 | "budgets": [
68 | {
69 | "type": "initial",
70 | "maximumWarning": "2mb",
71 | "maximumError": "5mb"
72 | },
73 | {
74 | "type": "anyComponentStyle",
75 | "maximumWarning": "6kb",
76 | "maximumError": "10kb"
77 | }
78 | ],
79 | "outputHashing": "all"
80 | },
81 | "development": {
82 | "buildOptimizer": false,
83 | "optimization": false,
84 | "vendorChunk": true,
85 | "extractLicenses": false,
86 | "sourceMap": true,
87 | "namedChunks": true
88 | }
89 | },
90 | "defaultConfiguration": "production"
91 | },
92 | "serve": {
93 | "builder": "@angular-devkit/build-angular:dev-server",
94 | "configurations": {
95 | "production": {
96 | "browserTarget": "angular2-draggable-demo:build:production"
97 | },
98 | "development": {
99 | "browserTarget": "angular2-draggable-demo:build:development"
100 | }
101 | },
102 | "defaultConfiguration": "development"
103 | },
104 | "extract-i18n": {
105 | "builder": "@angular-devkit/build-angular:extract-i18n",
106 | "options": {
107 | "browserTarget": "angular2-draggable-demo:build"
108 | }
109 | },
110 | "test": {
111 | "builder": "@angular-devkit/build-angular:karma",
112 | "options": {
113 | "polyfills": [
114 | "zone.js",
115 | "zone.js/testing"
116 | ],
117 | "tsConfig": "tsconfig.spec.json",
118 | "inlineStyleLanguage": "scss",
119 | "assets": [
120 | "src/favicon.ico",
121 | "src/assets"
122 | ],
123 | "styles": [
124 | "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
125 | "src/styles.scss"
126 | ],
127 | "scripts": []
128 | }
129 | },
130 | "lint": {
131 | "builder": "@angular-eslint/builder:lint",
132 | "options": {
133 | "lintFilePatterns": [
134 | "src/**/*.ts",
135 | "src/**/*.html"
136 | ]
137 | }
138 | }
139 | }
140 | },
141 | "angular2-draggable": {
142 | "projectType": "library",
143 | "schematics": {
144 | "@schematics/angular:component": {
145 | "style": "scss",
146 | "skipTests": true
147 | },
148 | "@schematics/angular:service": {
149 | "skipTests": true
150 | },
151 | "@schematics/angular:directive": {
152 | "skipTests": true
153 | }
154 | },
155 | "root": "projects/angular2-draggable",
156 | "sourceRoot": "projects/angular2-draggable/src",
157 | "prefix": "lib",
158 | "architect": {
159 | "build": {
160 | "builder": "@angular-devkit/build-angular:ng-packagr",
161 | "options": {
162 | "project": "projects/angular2-draggable/ng-package.json"
163 | },
164 | "configurations": {
165 | "production": {
166 | "tsConfig": "projects/angular2-draggable/tsconfig.lib.prod.json"
167 | },
168 | "development": {
169 | "tsConfig": "projects/angular2-draggable/tsconfig.lib.json"
170 | }
171 | },
172 | "defaultConfiguration": "production"
173 | },
174 | "test": {
175 | "builder": "@angular-devkit/build-angular:karma",
176 | "options": {
177 | "tsConfig": "projects/angular2-draggable/tsconfig.spec.json",
178 | "polyfills": [
179 | "zone.js",
180 | "zone.js/testing"
181 | ]
182 | }
183 | },
184 | "lint": {
185 | "builder": "@angular-eslint/builder:lint",
186 | "options": {
187 | "lintFilePatterns": [
188 | "projects/angular2-draggable/src/**/*.ts",
189 | "projects/angular2-draggable/src/**/*.html"
190 | ]
191 | }
192 | }
193 | }
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular2-draggable
2 |
3 |
4 | [][npm-badge-url]
5 | [][npm-badge-url]
6 | [][ci-url]
7 |
8 | + [Online Demo](https://xieziyu.github.io/angular2-draggable)
9 | + [Online Docs](https://xieziyu.github.io/angular2-draggable/api-doc)
10 |
11 | ## Table of contents
12 | - [angular2-draggable](#angular2-draggable)
13 | - [Table of contents](#table-of-contents)
14 | - [Getting Started](#getting-started)
15 | - [Installation](#installation)
16 | - [Draggable](#draggable)
17 | - [Resizable](#resizable)
18 | - [API](#api)
19 | - [Directive:](#directive)
20 | - [CSS:](#css)
21 | - [Events](#events)
22 | - [Demo](#demo)
23 |
24 | # Getting Started
25 | angular2-draggable has angular directives that make the DOM element draggable and resizable.
26 | + `ngDraggable`
27 | + v16.x requires Angular >= 16
28 | + v2.x requires Angular >= 6
29 | + v1.5.0 requires Angular >= 4 && < 6
30 |
31 | + `ngResizable`
32 | + provided since v2.0, requires Angular >= 6
33 |
34 | [CHANGELOG](./CHANGELOG.md)
35 |
36 | # Installation
37 | ```
38 | npm install angular2-draggable --save
39 | ```
40 |
41 | # Draggable
42 | Please refer to the [demo](https://xieziyu.github.io/angular2-draggable) page.
43 |
44 | 1. Firstly, import `AngularDraggableModule` in your app module (or any other proper angular module):
45 | ```typescript
46 | import { AngularDraggableModule } from 'angular2-draggable';
47 |
48 | @NgModule({
49 | imports: [
50 | ...,
51 | AngularDraggableModule
52 | ],
53 | ...
54 | })
55 | export class AppModule { }
56 | ```
57 |
58 | 2. Then: use `ngDraggable` directive to make the DOM element draggable.
59 | + Simple example:
60 |
61 | + html:
62 | ```html
63 | Drag me!
64 | ```
65 |
66 | + Use `[handle]` to move parent element:
67 |
68 | + html:
69 | ```html
70 |
71 |
72 |
You can't drag this block now!
73 |
74 | ```
75 |
76 | # Resizable
77 | Please refer to the [demo](https://xieziyu.github.io/angular2-draggable/#/resizable/default) page.
78 |
79 | Besides of importing `AngularDraggableModule`, you need to import `resizable.min.css` in your project. If you use `angular-cli`, you can add this in `angular.json`:
80 |
81 | ```diff
82 | "styles": [
83 | ...
84 | + "node_modules/angular2-draggable/css/resizable.min.css"
85 | ]
86 | ```
87 |
88 | Then you can use `ngResizable` directive to make the element resizable:
89 | ```html
90 | I'm now resizable
91 |
92 | Resizable is disabled now
93 |
94 | Each side is resizable
95 | ```
96 |
97 | Well you can use both directives concurrently if you wish:
98 | ```html
99 | I'm now draggable and resizable
100 | ```
101 |
102 | # API
103 |
104 | ## Directive:
105 | + `ngDraggable` directive support following input porperties:
106 |
107 | | Input | Type | Default | Description |
108 | | ----- | ---- | ------- | ----------- |
109 | | ngDraggable | boolean | `true` | You can toggle the draggable capability by setting `true` or `false` |
110 | | handle | HTMLElement | null | Use template variable to refer to the handle element. Then only the handle element is draggable |
111 | | zIndex | string | null | Use it to set z-index property when element is not moving |
112 | | zIndexMoving | string | null | Use it to set z-index property when element is moving |
113 | | bounds | HTMLElemnt | null | Use it to set the boundary |
114 | | inBounds | boolean | `false` | Use it make element stay in the bounds |
115 | | outOfBounds | `{ top: boolean; bottom: boolean; right: boolean; left: boolean }` | `false` | Set it to allow element get out of bounds from the direction. Refer to [demo](https://xieziyu.github.io/angular2-draggable/#/usage/boundary) |
116 | | position | `{ x: number, y: number }` | `{ x:0, y:0 }` | Use it to set position offset |
117 | | gridSize | number | 1 | Use it for snapping to grid. Refer to [demo](https://xieziyu.github.io/angular2-draggable/#/advance/snap-grid) |
118 | | preventDefaultEvent | boolean | `false` | Whether to prevent default mouse event |
119 | | scale | number | 1 | Set it when parent element has CSS transform scale |
120 | | lockAxis | `'x' \| 'y'` | null | Restrict dragging to a specific axis by locking another one |
121 |
122 | + `ngResizable` directive support following input porperties:
123 |
124 | | Input | Type | Default | Description |
125 | | ----- | ---- | ------- | ----------- |
126 | | ngResizable | boolean | `true` | You can toggle the resizable capability by setting `true` or `false` |
127 | | 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"` |
128 | | 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) |
129 | | 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' |
130 | | rzGrid | number \| number[] | 1 | Snaps the resizing element to a grid, every x and y pixels. Array values: `[x, y]`|
131 | | rzMinWidth | number | 1 | The minimum width the resizable should be allowed to resize to. |
132 | | rzMaxWidth | number | 1 | The maximum width the resizable should be allowed to resize to. |
133 | | rzMinHeight | number | 1 | The minimum height the resizable should be allowed to resize to. |
134 | | rzMaxHeight | number | 1 | The maximum height the resizable should be allowed to resize to. |
135 | | preventDefaultEvent | boolean | `false` | Whether to prevent default mouse event. |
136 | | rzScale | number | 1 | Set it when parent element has CSS transform scale |
137 |
138 | ## CSS:
139 | + When `ngDraggable` is enabled on some element, `ng-draggable` and `ng-dragging` class is automatically toggled on it. You can use it to customize the pointer style. For example:
140 |
141 | ```css
142 | .ng-draggable {
143 | cursor: grab;
144 | }
145 |
146 | .ng-dragging {
147 | cursor: grabbing;
148 | }
149 | ```
150 |
151 | + When `ngResizable` is enabled on some element, `ng-resizable` class is automatically assigned to it. And handle elements will be created with `ng-resizable-handle`. You can customize the handle style.
152 |
153 | # Events
154 | + `ngDraggable` directive:
155 |
156 | | Output | $event | Description |
157 | | ------ | ------ | ----------- |
158 | | started | `nativeElement` of host | emitted when start dragging |
159 | | stopped | `nativeElement` of host | emitted when stop dragging |
160 | | edge | { top: boolean, right: boolean, bottom: boolean, left: boolean } | emitted after `[bounds]` is set |
161 | | movingOffset | { x: number, y: number } | emit position offset when moving |
162 | | endOffset | { x: number, y: number } | emit position offset when stop moving |
163 |
164 | Simple example:
165 | ```html
166 |
171 | Drag me!
172 |
173 | ```
174 |
175 | + `ngResizable` directive:
176 |
177 | | Output | $event | description |
178 | | ------ | ------ | ----------- |
179 | | rzStart | `IResizeEvent` | emitted when start resizing |
180 | | rzResizing | `IResizeEvent` | emitted when resizing |
181 | | rzStop | `IResizeEvent` | emitted when stop resizing |
182 |
183 | ```typescript
184 | export interface IResizeEvent {
185 | host: any;
186 | handle: any;
187 | size: {
188 | width: number;
189 | height: number;
190 | };
191 | position: {
192 | top: number;
193 | left: number;
194 | };
195 | direction: {
196 | n: boolean;
197 | s: boolean;
198 | w: boolean;
199 | e: boolean;
200 | };
201 | }
202 | ```
203 |
204 | Simple example:
205 | ```html
206 |
210 | Resizable
211 |
212 | ```
213 |
214 | # Demo
215 | You can clone this repo to your working copy and then launch the demo page in your local machine:
216 | ```bash
217 | npm install
218 | npm run demo
219 |
220 | # or
221 | yarn install
222 | yarn demo
223 | ```
224 | The demo page server is listening to: http://localhost:4203
225 |
226 |
227 | [npm-badge-url]: https://www.npmjs.com/package/angular2-draggable
228 | [ci-url]: https://github.com/xieziyu/angular2-draggable/actions/workflows/ci.yaml
229 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 16.0.0 (2023-05-20)
2 |
3 | #### New
4 |
5 | + Support Angular 16
6 |
7 | ## 2.3.2 (2019-06-10)
8 | + **ngResizable**: Fix [issue #164](https://github.com/xieziyu/angular2-draggable/issues/164): Resize doesn't work on Windows10 IE11 ([PR #171](https://github.com/xieziyu/angular2-draggable/pull/171) by [shumih](https://github.com/shumih]), [PR #174](https://github.com/xieziyu/angular2-draggable/pull/174) by [LiorSaadon](https://github.com/LiorSaadon]))
9 |
10 | ## 2.3.0 (2019-05-14)
11 |
12 | #### New
13 | + **ngDraggable**: Add CSS class `ng-dragging` when dragging.
14 | + **ngResizable**: Add `direction` property in `IResizeEvent`.
15 |
16 | #### Bugfix
17 | + **ngResizable**: Fix [issue #157](https://github.com/xieziyu/angular2-draggable/issues/159): Problem resizing with containment
18 |
19 | ## 2.2.4 (2019-04-19)
20 |
21 | #### Bugfix
22 | + **ngResizable**: Fix [issue #157](https://github.com/xieziyu/angular2-draggable/issues/157): calling resetSize() method cause exception
23 |
24 | ## 2.2.3 (2019-04-18)
25 |
26 | #### Bugfix
27 | + **ngDraggable**:
28 | + Fix draggable position bouncing when draggable is scaled and position is set ([by agnitos](https://github.com/agnitos)) - [PR #150](https://github.com/xieziyu/angular2-draggable/pull/150)
29 | + Fix translate in draggable.directive ([by Volker505](https://github.com/Volker505)) - [PR #151](https://github.com/xieziyu/angular2-draggable/pull/151)
30 | + Fix issue with dragging window inside iframe for IE ([by fdabrowski](https://github.com/fdabrowski)) - [PR #154](https://github.com/xieziyu/angular2-draggable/pull/154)
31 | + Fix Element move when resizing using the NW and NE handles and aspect ratio is enabled ([by dioseltorre](https://github.com/dioseltorre)) - [PR #156](https://github.com/xieziyu/angular2-draggable/pull/156)
32 |
33 | ## 2.2.2 (2019-03-01)
34 |
35 | #### Bugfix
36 | + **ngDraggable**: Fixed ngDraggable toggle bug. ([by agnitos](https://github.com/agnitos)) - [PR #145](https://github.com/xieziyu/angular2-draggable/pull/145)
37 |
38 | ## 2.2.1 (2018-12-25)
39 |
40 | #### Bugfix
41 | + **ngDraggable**: Fixed flickering of the component on initial drag while scale is applied to the parent. ([by rathodsanjay](https://github.com/rathodsanjay) - [PR #134](https://github.com/xieziyu/angular2-draggable/pull/123))
42 |
43 | ---
44 |
45 | ## 2.2.0 (2018-12-22)
46 |
47 | #### New
48 | + **ngDraggable**: add [lockAxis] input to restrict dragging to a specific axis by locking another one.
49 |
50 | #### Bugfix
51 | + **ngDraggable**:
52 | + fix [issue #112](https://github.com/xieziyu/angular2-draggable/issues/112): Control change detection with HostListener events. Performance updated.
53 | + fix [issue #128](https://github.com/xieziyu/angular2-draggable/issues/128): Multiple phone draggables at the same time.
54 | + **ngResizable**:
55 | + fix [issue #132](https://github.com/xieziyu/angular2-draggable/issues/132): Aspect ratio feature exits Y-Axis boundary on resize.
56 | + Performance updated.
57 |
58 | ---
59 |
60 | ## 2.1.9 (2018-11-29)
61 |
62 | #### Bugfix
63 | + **ngDraggable**: [#31](https://github.com/xieziyu/angular2-draggable/issues/31) Problems when scale transform applied to parent. ([by rathodsanjay](https://github.com/rathodsanjay) - [PR #123](https://github.com/xieziyu/angular2-draggable/pull/123))
64 |
65 | ---
66 |
67 | ## 2.1.8 (2018-11-11)
68 |
69 | #### New
70 | + **ngResizable**: add [preventDefaultEvent] flag to ngResizable mousedown ([by mecp](https://github.com/mecp) - [PR #119](https://github.com/xieziyu/angular2-draggable/pull/119))
71 |
72 | ---
73 |
74 | ## 2.1.7 (2018-10-31)
75 |
76 | #### Bugfix
77 | + **ngResizable**: [#116](https://github.com/xieziyu/angular2-draggable/issues/116) ngResizable Locks Height When rzHandles Includes Only e, w. (Thanks to [Yamazaki93](https://github.com/Yamazaki93))
78 |
79 | ---
80 |
81 | ## 2.1.6 (2018-10-26)
82 |
83 | #### Bugfix
84 | + **ngResizable**: rzResizing IE event issue. [#115](https://github.com/xieziyu/angular2-draggable/issues/115)
85 |
86 | ---
87 |
88 | ## 2.1.5 (2018-10-15)
89 |
90 | #### Bugfix
91 | + **ngDraggable**: EndOffset event not working properly with SnapToGrid. [#114](https://github.com/xieziyu/angular2-draggable/issues/114)
92 |
93 | ---
94 |
95 | ## 2.1.4 (2018-09-17)
96 |
97 | #### Bugfix
98 | + Fix a build issue
99 | + **ngResizable**: Resize bounds on a draggable element inside a containment is off. [#100](https://github.com/xieziyu/angular2-draggable/issues/100)
100 |
101 | ---
102 |
103 | ## 2.1.2 (2018-08-20)
104 |
105 | #### Bugfix
106 | + **ngDraggable**: Item is produced with div partially out of bounds. [#97](https://github.com/xieziyu/angular2-draggable/issues/97)
107 |
108 | ---
109 |
110 | ## 2.1.1 (2018-08-14)
111 |
112 | #### New
113 | + **ngResizable**: Provide `[rzGrid]`. Snaps the resizing element to a grid.
114 | + **ngResizable**: Provide `[rzMinWidth]`, `[rzMaxWidth]`, `[rzMinHeight]`, `[rzMaxHeight]`. The minimum/maximum width/height the resizable should be allowed to resize to.
115 |
116 | #### Bugfix
117 | + **ngResizable**: resizing from w, nw or n with a min/max size moves the window if it goes below/above the min/max size. [#94](https://github.com/xieziyu/angular2-draggable/issues/94)
118 |
119 | ---
120 |
121 | ## 2.0.1 (2018-08-08)
122 |
123 | #### Bugfix
124 | + click events are blocked. [#87](https://github.com/xieziyu/angular2-draggable/issues/87), [#84](https://github.com/xieziyu/angular2-draggable/issues/84)
125 |
126 | ---
127 |
128 | ## 2.0.0 (2018-08-03)
129 |
130 | #### Bugfix
131 | + Fix [issue #84](https://github.com/xieziyu/angular2-draggable/issues/84): iFrames, and context unrelated elements block all events, and are unusable
132 |
133 | ---
134 |
135 | ## 2.0.0-beta.2 (2018-07-02)
136 |
137 | #### New
138 | + **ngResizable**: Provide `[rzAspectRatio]`, whether the element should be constrained to a specific aspect ratio.
139 | + **ngResizable**: Provide `[rzContainment]`, constrains resizing to within the bounds of the specified element or region.
140 |
141 | ---
142 |
143 | ## 2.0.0-beta.1 (2018-06-26)
144 |
145 | #### New
146 | + **ngResizable**: Provide `(rzStart)`, `(rzResizing)`, `(rzStop)` event emitters
147 | + **ngResizable**: Provide `resetSize()`, `getStatus()` methods
148 |
149 | ---
150 |
151 | ## 2.0.0-beta.0 (2018-06-25)
152 |
153 | #### New
154 | + `ngResizable` directive which you can use to make the element resizable.
155 | + Provide `[rzHandles]` option for which handles can be used for resizing.
156 |
157 | ## 1.4.2 (2018-05-23)
158 |
159 | #### Changes
160 | + Expose boundsCheck() method.
161 |
162 | ---
163 |
164 | ## 1.4.1 (2018-05-11)
165 |
166 | #### Bugfix
167 | + Handle Drag is not working in Firefox [#68](https://github.com/xieziyu/angular2-draggable/issues/68).
168 |
169 | ---
170 |
171 | ## 1.4.0 (2018-05-04)
172 |
173 | #### New
174 | + Provide `[gridSize]` option for snapping to grid. Refer to [demo](https://xieziyu.github.io/angular2-draggable/#/advance/snap-grid). (PR [#64](https://github.com/xieziyu/angular2-draggable/pull/64) by [PAHADIx](https://github.com/PAHADIx))
175 |
176 | #### Changes
177 | + Code optimized. (PR [#60](https://github.com/xieziyu/angular2-draggable/pull/60) by [korn3l](https://github.com/korn3l))
178 |
179 | ---
180 |
181 | ## 1.3.2 (2018-04-10)
182 |
183 | #### New
184 | + Provide `[outOfBounds]` option. Set it to allow element get out of bounds from the direction. (PR [#57](https://github.com/xieziyu/angular2-draggable/issues/58) by [waldo2188](https://github.com/waldo2188))
185 |
186 | ---
187 |
188 | ## 1.3.1 (2018-03-15)
189 |
190 | #### New
191 | + Provide `(movingOffset)` event emitter: emit position offset when moving
192 | + Provide `(endOffset)` event emitter: emit position offset when stop moving
193 |
194 | ---
195 |
196 | ## 1.3.0 (2018-03-09)
197 |
198 | #### New
199 | + Provide `[position]` option: to set initial position offset.
200 |
201 | ---
202 |
203 | ## 1.2.1 (2018-02-08)
204 |
205 | #### Bugfix
206 | + `[preventDefaultEvent]` should not prevent events of elements outside the handle.
207 |
208 | ---
209 |
210 | ## 1.2.0 (2018-02-07)
211 |
212 | #### New
213 | + Provide `resetPosition()` method to reset position.
214 |
215 | #### Breaking Changes
216 | + Use `Renderer2` of angular-core. So we don't support angular version < 4.0.
217 |
218 | #### Bugfix
219 | + `[trackPosition]` was not working as expected.
220 |
221 | #### Changes
222 | + The directive now `exportAs: 'ngDraggable'`.
223 | + `[preventDefaultEvent]` set default to false.
224 |
225 | ---
226 |
227 | ## 1.1.0 (2018-02-01)
228 |
229 | #### New
230 | + Provide `[trackPosition]` option: whether to track the element's movement. (PR by [Blackbaud-MikitaYankouski](https://github.com/Blackbaud-MikitaYankouski))
231 | + Provide `[scale]` option: to fix scaling issue [#31](https://github.com/xieziyu/angular2-draggable/issues/31)
232 | + Provide `[preventDefaultEvent]` option: whether to prevent default mouse or touch event. (default: true)
233 |
234 | ---
235 |
236 | ## 1.1.0-beta.0 (2017-12-20)
237 |
238 | #### New
239 | + Provide `[zIndex]` and `[zIndexMoving]` to control z-index property.
240 | + Provide `[bounds]`, `(edge)` and `[inBounds]` to do boundary check and limit element staying in the bounds.
241 |
242 | ---
243 |
244 |
245 | ## [1.0.7](https://github.com/xieziyu/angular2-draggable/compare/v1.0.6...v1.0.7) (2017-09-19)
246 |
247 | ### Bugfix
248 | + Fix an issue when dragging with touch.
249 |
250 | ---
251 |
252 |
253 | ## [1.0.6](https://github.com/xieziyu/angular2-draggable/compare/v1.0.5...v1.0.6) (2017-08-26)
254 |
255 | ### Bugfix
256 | + Fix an issue: clicking before dragging leading to unexpected offset ([PR #12](https://github.com/xieziyu/angular2-draggable/pull/12) by [bmartinson13](https://github.com/bmartinson13))
257 |
258 | ---
259 |
260 |
261 | ## [1.0.5](https://github.com/xieziyu/angular2-draggable/compare/v1.0.4...v1.0.5) (2017-07-24)
262 |
263 | ### New
264 | + Fix cross-browser compatibility issues.
265 |
266 | ---
267 |
268 |
269 | ## [1.0.4](https://github.com/xieziyu/angular2-draggable/compare/v1.0.3...v1.0.4) (2017-07-05)
270 |
271 | ### New
272 | + Publish `UMD` bundle
273 |
274 | ---
275 |
276 |
277 | ## [1.0.3](https://github.com/xieziyu/angular2-draggable/compare/v1.0.2...v1.0.3) (2017-06-13)
278 |
279 | ### New
280 | + Support `started` and `stopped` dragging event.
281 |
282 | ---
283 |
284 |
285 | ## [1.0.2](https://github.com/xieziyu/angular2-draggable/compare/v1.0.1...v1.0.2) (2017-05-05)
286 |
287 | ### BugFix
288 | + It now saves and restores the `position` and `z-index` properties.
289 | + It now calculates the correct `left` and `top` properties from CSS value.
290 |
291 | ---
292 |
293 |
294 | ## [1.0.1](https://github.com/xieziyu/angular2-draggable/compare/v1.0.0...v1.0.1) (2017-05-05)
295 |
296 | ### BugFix
297 | + Giving all draggable elements the position relative (PR: [#1](https://github.com/xieziyu/angular2-draggable/pull/1), thanks to tylerlindell)
298 |
299 | ### Changes
300 | + Bring moving element to the top by setting z-index (PR: [#1](https://github.com/xieziyu/angular2-draggable/pull/1), thanks to tylerlindell)
301 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/angular-draggable.directive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef,
4 | Renderer2,
5 | Input,
6 | Output,
7 | OnInit,
8 | HostListener,
9 | EventEmitter,
10 | OnChanges,
11 | SimpleChanges,
12 | OnDestroy,
13 | AfterViewInit,
14 | } from '@angular/core';
15 |
16 | import { Subscription, fromEvent } from 'rxjs';
17 | import { IPosition, Position } from './models/position';
18 | import { HelperBlock } from './widgets/helper-block';
19 |
20 | @Directive({
21 | selector: '[ngDraggable]',
22 | exportAs: 'ngDraggable',
23 | })
24 | export class AngularDraggableDirective implements OnInit, OnDestroy, OnChanges, AfterViewInit {
25 | private allowDrag = true;
26 | private moving = false;
27 | private orignal: Position = null;
28 | private oldTrans = new Position(0, 0);
29 | private tempTrans = new Position(0, 0);
30 | private currTrans = new Position(0, 0);
31 | private oldZIndex = '';
32 | private _zIndex = '';
33 | private needTransform = false;
34 |
35 | private draggingSub: Subscription = null;
36 |
37 | /**
38 | * Bugfix: iFrames, and context unrelated elements block all events, and are unusable
39 | * https://github.com/xieziyu/angular2-draggable/issues/84
40 | */
41 | private _helperBlock: HelperBlock = null;
42 |
43 | @Output() started = new EventEmitter();
44 | @Output() stopped = new EventEmitter();
45 | @Output() edge = new EventEmitter();
46 |
47 | /** Make the handle HTMLElement draggable */
48 | @Input() handle: HTMLElement;
49 |
50 | /** Set the bounds HTMLElement */
51 | @Input() bounds: HTMLElement;
52 |
53 | /** List of allowed out of bounds edges **/
54 | @Input() outOfBounds = {
55 | top: false,
56 | right: false,
57 | bottom: false,
58 | left: false,
59 | };
60 |
61 | /** Round the position to nearest grid */
62 | @Input() gridSize = 1;
63 |
64 | /** Set z-index when dragging */
65 | @Input() zIndexMoving: string;
66 |
67 | /** Set z-index when not dragging */
68 | @Input() set zIndex(setting: string) {
69 | this.renderer.setStyle(this.el.nativeElement, 'z-index', setting);
70 | this._zIndex = setting;
71 | }
72 | /** Whether to limit the element stay in the bounds */
73 | @Input() inBounds = false;
74 |
75 | /** Whether the element should use it's previous drag position on a new drag event. */
76 | @Input() trackPosition = true;
77 |
78 | /** Input css scale transform of element so translations are correct */
79 | @Input() scale = 1;
80 |
81 | /** Whether to prevent default event */
82 | @Input() preventDefaultEvent = false;
83 |
84 | /** Set initial position by offsets */
85 | @Input() position: IPosition = { x: 0, y: 0 };
86 |
87 | /** Lock axis: 'x' or 'y' */
88 | @Input() lockAxis: string = null;
89 |
90 | /** Emit position offsets when moving */
91 | @Output() movingOffset = new EventEmitter();
92 |
93 | /** Emit position offsets when put back */
94 | @Output() endOffset = new EventEmitter();
95 |
96 | @Input()
97 | set ngDraggable(setting: any) {
98 | if (setting !== undefined && setting !== null && setting !== '') {
99 | this.allowDrag = !!setting;
100 |
101 | let element = this.getDragEl();
102 |
103 | if (this.allowDrag) {
104 | this.renderer.addClass(element, 'ng-draggable');
105 | } else {
106 | this.putBack();
107 | this.renderer.removeClass(element, 'ng-draggable');
108 | }
109 | }
110 | }
111 |
112 | constructor(private el: ElementRef, private renderer: Renderer2) {
113 | this._helperBlock = new HelperBlock(el.nativeElement, renderer);
114 | }
115 |
116 | ngOnInit() {
117 | if (this.allowDrag) {
118 | let element = this.getDragEl();
119 | this.renderer.addClass(element, 'ng-draggable');
120 | }
121 | this.resetPosition();
122 | }
123 |
124 | ngOnDestroy() {
125 | this.bounds = null;
126 | this.handle = null;
127 | this.orignal = null;
128 | this.oldTrans = null;
129 | this.tempTrans = null;
130 | this.currTrans = null;
131 | this._helperBlock.dispose();
132 | this._helperBlock = null;
133 |
134 | if (this.draggingSub) {
135 | this.draggingSub.unsubscribe();
136 | }
137 | }
138 |
139 | ngOnChanges(changes: SimpleChanges) {
140 | if (changes['position'] && !changes['position'].isFirstChange()) {
141 | let p = changes['position'].currentValue;
142 |
143 | if (!this.moving) {
144 | if (Position.isIPosition(p)) {
145 | this.oldTrans.set(p);
146 | } else {
147 | this.oldTrans.reset();
148 | }
149 |
150 | this.transform();
151 | } else {
152 | this.needTransform = true;
153 | }
154 | }
155 | }
156 |
157 | ngAfterViewInit() {
158 | if (this.inBounds) {
159 | this.boundsCheck();
160 | this.oldTrans.add(this.tempTrans);
161 | this.tempTrans.reset();
162 | }
163 | }
164 |
165 | private getDragEl() {
166 | return this.handle ? this.handle : this.el.nativeElement;
167 | }
168 |
169 | resetPosition() {
170 | if (Position.isIPosition(this.position)) {
171 | this.oldTrans.set(this.position);
172 | } else {
173 | this.oldTrans.reset();
174 | }
175 | this.tempTrans.reset();
176 | this.transform();
177 | }
178 |
179 | private moveTo(p: Position) {
180 | if (this.orignal) {
181 | p.subtract(this.orignal);
182 | this.tempTrans.set(p);
183 | this.tempTrans.divide(this.scale);
184 | this.transform();
185 |
186 | if (this.bounds) {
187 | let edgeEv = this.boundsCheck();
188 | if (edgeEv) {
189 | this.edge.emit(edgeEv);
190 | }
191 | }
192 |
193 | this.movingOffset.emit(this.currTrans.value);
194 | }
195 | }
196 |
197 | private transform() {
198 | let translateX = this.tempTrans.x + this.oldTrans.x;
199 | let translateY = this.tempTrans.y + this.oldTrans.y;
200 |
201 | if (this.lockAxis === 'x') {
202 | translateX = this.oldTrans.x;
203 | this.tempTrans.x = 0;
204 | } else if (this.lockAxis === 'y') {
205 | translateY = this.oldTrans.y;
206 | this.tempTrans.y = 0;
207 | }
208 |
209 | // Snap to grid: by grid size
210 | if (this.gridSize > 1) {
211 | translateX = Math.round(translateX / this.gridSize) * this.gridSize;
212 | translateY = Math.round(translateY / this.gridSize) * this.gridSize;
213 | }
214 |
215 | let value = `translate(${Math.round(translateX)}px, ${Math.round(translateY)}px)`;
216 |
217 | this.renderer.setStyle(this.el.nativeElement, 'transform', value);
218 | this.renderer.setStyle(this.el.nativeElement, '-webkit-transform', value);
219 | this.renderer.setStyle(this.el.nativeElement, '-ms-transform', value);
220 | this.renderer.setStyle(this.el.nativeElement, '-moz-transform', value);
221 | this.renderer.setStyle(this.el.nativeElement, '-o-transform', value);
222 |
223 | // save current position
224 | this.currTrans.x = translateX;
225 | this.currTrans.y = translateY;
226 | }
227 |
228 | private pickUp() {
229 | // get old z-index:
230 | this.oldZIndex = this.el.nativeElement.style.zIndex ? this.el.nativeElement.style.zIndex : '';
231 |
232 | if (window) {
233 | this.oldZIndex = window
234 | .getComputedStyle(this.el.nativeElement, null)
235 | .getPropertyValue('z-index');
236 | }
237 |
238 | if (this.zIndexMoving) {
239 | this.renderer.setStyle(this.el.nativeElement, 'z-index', this.zIndexMoving);
240 | }
241 |
242 | if (!this.moving) {
243 | this.started.emit(this.el.nativeElement);
244 | this.moving = true;
245 |
246 | const element = this.getDragEl();
247 | this.renderer.addClass(element, 'ng-dragging');
248 |
249 | /**
250 | * Fix performance issue:
251 | * https://github.com/xieziyu/angular2-draggable/issues/112
252 | */
253 | this.subscribeEvents();
254 | }
255 | }
256 |
257 | private subscribeEvents() {
258 | this.draggingSub = fromEvent(document, 'mousemove', { passive: false }).subscribe(event =>
259 | this.onMouseMove(event as MouseEvent)
260 | );
261 | this.draggingSub.add(
262 | fromEvent(document, 'touchmove', { passive: false }).subscribe(event =>
263 | this.onMouseMove(event as TouchEvent)
264 | )
265 | );
266 | this.draggingSub.add(
267 | fromEvent(document, 'mouseup', { passive: false }).subscribe(() => this.putBack())
268 | );
269 | // checking if browser is IE or Edge - https://github.com/xieziyu/angular2-draggable/issues/153
270 | let isIEOrEdge = /msie\s|trident\//i.test(window.navigator.userAgent);
271 | if (!isIEOrEdge) {
272 | this.draggingSub.add(
273 | fromEvent(document, 'mouseleave', { passive: false }).subscribe(() => this.putBack())
274 | );
275 | }
276 | this.draggingSub.add(
277 | fromEvent(document, 'touchend', { passive: false }).subscribe(() => this.putBack())
278 | );
279 | this.draggingSub.add(
280 | fromEvent(document, 'touchcancel', { passive: false }).subscribe(() => this.putBack())
281 | );
282 | }
283 |
284 | private unsubscribeEvents() {
285 | this.draggingSub.unsubscribe();
286 | this.draggingSub = null;
287 | }
288 |
289 | boundsCheck() {
290 | if (this.bounds) {
291 | let boundary = this.bounds.getBoundingClientRect();
292 | let elem = this.el.nativeElement.getBoundingClientRect();
293 | let result = {
294 | top: this.outOfBounds.top ? true : boundary.top < elem.top,
295 | right: this.outOfBounds.right ? true : boundary.right > elem.right,
296 | bottom: this.outOfBounds.bottom ? true : boundary.bottom > elem.bottom,
297 | left: this.outOfBounds.left ? true : boundary.left < elem.left,
298 | };
299 |
300 | if (this.inBounds) {
301 | if (!result.top) {
302 | this.tempTrans.y -= (elem.top - boundary.top) / this.scale;
303 | }
304 |
305 | if (!result.bottom) {
306 | this.tempTrans.y -= (elem.bottom - boundary.bottom) / this.scale;
307 | }
308 |
309 | if (!result.right) {
310 | this.tempTrans.x -= (elem.right - boundary.right) / this.scale;
311 | }
312 |
313 | if (!result.left) {
314 | this.tempTrans.x -= (elem.left - boundary.left) / this.scale;
315 | }
316 |
317 | this.transform();
318 | }
319 |
320 | return result;
321 | }
322 | return null;
323 | }
324 |
325 | /** Get current offset */
326 | getCurrentOffset() {
327 | return this.currTrans.value;
328 | }
329 |
330 | private putBack() {
331 | if (this._zIndex) {
332 | this.renderer.setStyle(this.el.nativeElement, 'z-index', this._zIndex);
333 | } else if (this.zIndexMoving) {
334 | if (this.oldZIndex) {
335 | this.renderer.setStyle(this.el.nativeElement, 'z-index', this.oldZIndex);
336 | } else {
337 | this.el.nativeElement.style.removeProperty('z-index');
338 | }
339 | }
340 |
341 | if (this.moving) {
342 | this.stopped.emit(this.el.nativeElement);
343 |
344 | // Remove the helper div:
345 | this._helperBlock.remove();
346 |
347 | if (this.needTransform) {
348 | if (Position.isIPosition(this.position)) {
349 | this.oldTrans.set(this.position);
350 | } else {
351 | this.oldTrans.reset();
352 | }
353 |
354 | this.transform();
355 | this.needTransform = false;
356 | }
357 |
358 | if (this.bounds) {
359 | let edgeEv = this.boundsCheck();
360 | if (edgeEv) {
361 | this.edge.emit(edgeEv);
362 | }
363 | }
364 |
365 | this.moving = false;
366 | this.endOffset.emit(this.currTrans.value);
367 |
368 | if (this.trackPosition) {
369 | this.oldTrans.add(this.tempTrans);
370 | }
371 |
372 | this.tempTrans.reset();
373 |
374 | if (!this.trackPosition) {
375 | this.transform();
376 | }
377 |
378 | const element = this.getDragEl();
379 | this.renderer.removeClass(element, 'ng-dragging');
380 |
381 | /**
382 | * Fix performance issue:
383 | * https://github.com/xieziyu/angular2-draggable/issues/112
384 | */
385 | this.unsubscribeEvents();
386 | }
387 | }
388 |
389 | checkHandleTarget(target: EventTarget, element: Element) {
390 | // Checks if the target is the element clicked, then checks each child element of element as well
391 | // Ignores button clicks
392 |
393 | // Ignore elements of type button
394 | if (element.tagName === 'BUTTON') {
395 | return false;
396 | }
397 |
398 | // If the target was found, return true (handle was found)
399 | if (element === target) {
400 | return true;
401 | }
402 |
403 | // Recursively iterate this elements children
404 | for (let child in element.children) {
405 | if (element.children.hasOwnProperty(child)) {
406 | if (this.checkHandleTarget(target, element.children[child])) {
407 | return true;
408 | }
409 | }
410 | }
411 |
412 | // Handle was not found in this lineage
413 | // Note: return false is ignore unless it is the parent element
414 | return false;
415 | }
416 |
417 | @HostListener('mousedown', ['$event'])
418 | @HostListener('touchstart', ['$event'])
419 | onMouseDown(event: MouseEvent | TouchEvent) {
420 | // 1. skip right click;
421 | if (event instanceof MouseEvent && event.button === 2) {
422 | return;
423 | }
424 | // 2. if handle is set, the element can only be moved by handle
425 | let target = event.target || event.srcElement;
426 | if (this.handle !== undefined && !this.checkHandleTarget(target, this.handle)) {
427 | return;
428 | }
429 |
430 | // 3. if allow drag is set to false, ignore the mousedown
431 | if (this.allowDrag === false) {
432 | return;
433 | }
434 |
435 | if (this.preventDefaultEvent) {
436 | event.stopPropagation();
437 | event.preventDefault();
438 | }
439 |
440 | this.orignal = Position.fromEvent(event, this.getDragEl());
441 | this.pickUp();
442 | }
443 |
444 | onMouseMove(event: MouseEvent | TouchEvent) {
445 | if (this.moving && this.allowDrag) {
446 | if (this.preventDefaultEvent) {
447 | event.stopPropagation();
448 | event.preventDefault();
449 | }
450 |
451 | // Add a transparent helper div:
452 | this._helperBlock.add();
453 | this.moveTo(Position.fromEvent(event, this.getDragEl()));
454 | }
455 | }
456 | }
457 |
--------------------------------------------------------------------------------
/projects/angular2-draggable/src/lib/angular-resizable.directive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ElementRef,
4 | Renderer2,
5 | Input,
6 | Output,
7 | OnInit,
8 | EventEmitter,
9 | OnChanges,
10 | SimpleChanges,
11 | OnDestroy,
12 | AfterViewInit,
13 | } from '@angular/core';
14 |
15 | import { Subscription, fromEvent } from 'rxjs';
16 | import { HelperBlock } from './widgets/helper-block';
17 | import { ResizeHandle } from './widgets/resize-handle';
18 | import { ResizeHandleType } from './models/resize-handle-type';
19 | import { Position, IPosition } from './models/position';
20 | import { Size } from './models/size';
21 | import { IResizeEvent } from './models/resize-event';
22 |
23 | @Directive({
24 | selector: '[ngResizable]',
25 | exportAs: 'ngResizable',
26 | })
27 | export class AngularResizableDirective implements OnInit, OnChanges, OnDestroy, AfterViewInit {
28 | private _resizable = true;
29 | private _handles: { [key: string]: ResizeHandle } = {};
30 | private _handleType: string[] = [];
31 | private _handleResizing: ResizeHandle = null;
32 | private _direction: { n: boolean; s: boolean; w: boolean; e: boolean } = null;
33 | private _directionChanged: { n: boolean; s: boolean; w: boolean; e: boolean } = null;
34 | private _aspectRatio = 0;
35 | private _containment: HTMLElement = null;
36 | private _origMousePos: Position = null;
37 |
38 | /** Original Size and Position */
39 | private _origSize: Size = null;
40 | private _origPos: Position = null;
41 |
42 | /** Current Size and Position */
43 | private _currSize: Size = null;
44 | private _currPos: Position = null;
45 |
46 | /** Initial Size and Position */
47 | private _initSize: Size = null;
48 | private _initPos: Position = null;
49 |
50 | /** Snap to gird */
51 | private _gridSize: IPosition = null;
52 |
53 | private _bounding: any = null;
54 |
55 | /**
56 | * Bugfix: iFrames, and context unrelated elements block all events, and are unusable
57 | * https://github.com/xieziyu/angular2-draggable/issues/84
58 | */
59 | private _helperBlock: HelperBlock = null;
60 |
61 | private draggingSub: Subscription = null;
62 | private _adjusted = false;
63 |
64 | /** Disables the resizable if set to false. */
65 | @Input() set ngResizable(v: any) {
66 | if (v !== undefined && v !== null && v !== '') {
67 | this._resizable = !!v;
68 | this.updateResizable();
69 | }
70 | }
71 |
72 | /**
73 | * Which handles can be used for resizing.
74 | * @example
75 | * [rzHandles] = "'n,e,s,w,se,ne,sw,nw'"
76 | * equals to: [rzHandles] = "'all'"
77 | *
78 | * */
79 | @Input() rzHandles: ResizeHandleType = 'e,s,se';
80 |
81 | /**
82 | * Using exist handles for resizing instead of generate them.
83 | * @example
84 | * [rzHandleDoms] = {
85 | * e: handelE,
86 | * s: handelS,
87 | * se: handelSE
88 | * };
89 | * */
90 | @Input() rzHandleDoms: {
91 | se?: ElementRef;
92 | sw?: ElementRef;
93 | ne?: ElementRef;
94 | nw?: ElementRef;
95 | n?: ElementRef;
96 | e?: ElementRef;
97 | s?: ElementRef;
98 | w?: ElementRef;
99 | } = {};
100 |
101 | /**
102 | * Whether the element should be constrained to a specific aspect ratio.
103 | * Multiple types supported:
104 | * boolean: When set to true, the element will maintain its original aspect ratio.
105 | * number: Force the element to maintain a specific aspect ratio during resizing.
106 | */
107 | @Input() rzAspectRatio: boolean | number = false;
108 |
109 | /**
110 | * Constrains resizing to within the bounds of the specified element or region.
111 | * Multiple types supported:
112 | * Selector: The resizable element will be contained to the bounding box of the first element found by the selector.
113 | * If no element is found, no containment will be set.
114 | * Element: The resizable element will be contained to the bounding box of this element.
115 | * String: Possible values: "parent".
116 | */
117 | @Input() rzContainment: string | HTMLElement = null;
118 |
119 | /**
120 | * Snaps the resizing element to a grid, every x and y pixels.
121 | * A number for both width and height or an array values like [ x, y ]
122 | */
123 | @Input() rzGrid: number | number[] = null;
124 |
125 | /** The minimum width the resizable should be allowed to resize to. */
126 | @Input() rzMinWidth: number = null;
127 |
128 | /** The minimum height the resizable should be allowed to resize to. */
129 | @Input() rzMinHeight: number = null;
130 |
131 | /** The maximum width the resizable should be allowed to resize to. */
132 | @Input() rzMaxWidth: number = null;
133 |
134 | /** The maximum height the resizable should be allowed to resize to. */
135 | @Input() rzMaxHeight: number = null;
136 |
137 | /** Input css scale transform of element so translations are correct */
138 | @Input() rzScale = 1;
139 |
140 | /** Whether to prevent default event */
141 | @Input() preventDefaultEvent = true;
142 |
143 | /** emitted when start resizing */
144 | @Output() rzStart = new EventEmitter();
145 |
146 | /** emitted when start resizing */
147 | @Output() rzResizing = new EventEmitter();
148 |
149 | /** emitted when stop resizing */
150 | @Output() rzStop = new EventEmitter();
151 |
152 | constructor(private el: ElementRef, private renderer: Renderer2) {
153 | this._helperBlock = new HelperBlock(el.nativeElement, renderer);
154 | }
155 |
156 | ngOnChanges(changes: SimpleChanges) {
157 | if (changes['rzHandles'] && !changes['rzHandles'].isFirstChange()) {
158 | this.updateResizable();
159 | }
160 |
161 | if (changes['rzAspectRatio'] && !changes['rzAspectRatio'].isFirstChange()) {
162 | this.updateAspectRatio();
163 | }
164 |
165 | if (changes['rzContainment'] && !changes['rzContainment'].isFirstChange()) {
166 | this.updateContainment();
167 | }
168 | }
169 |
170 | ngOnInit() {
171 | this.updateResizable();
172 | }
173 |
174 | ngOnDestroy() {
175 | this.removeHandles();
176 | this._containment = null;
177 | this._helperBlock.dispose();
178 | this._helperBlock = null;
179 | }
180 |
181 | ngAfterViewInit() {
182 | const elm = this.el.nativeElement;
183 | this._initSize = Size.getCurrent(elm);
184 | this._initPos = Position.getCurrent(elm);
185 | this._currSize = Size.copy(this._initSize);
186 | this._currPos = Position.copy(this._initPos);
187 | this.updateAspectRatio();
188 | this.updateContainment();
189 | }
190 |
191 | /** A method to reset size */
192 | public resetSize() {
193 | this._currSize = Size.copy(this._initSize);
194 | this._currPos = Position.copy(this._initPos);
195 | this.doResize();
196 | }
197 |
198 | /** A method to get current status */
199 | public getStatus() {
200 | if (!this._currPos || !this._currSize) {
201 | return null;
202 | }
203 |
204 | return {
205 | size: {
206 | width: this._currSize.width,
207 | height: this._currSize.height,
208 | },
209 | position: {
210 | top: this._currPos.y,
211 | left: this._currPos.x,
212 | },
213 | };
214 | }
215 |
216 | private updateResizable() {
217 | const element = this.el.nativeElement;
218 |
219 | // clear handles:
220 | this.renderer.removeClass(element, 'ng-resizable');
221 | this.removeHandles();
222 |
223 | // create new ones:
224 | if (this._resizable) {
225 | this.renderer.addClass(element, 'ng-resizable');
226 | this.createHandles();
227 | }
228 | }
229 |
230 | /** Use it to update aspect */
231 | private updateAspectRatio() {
232 | if (typeof this.rzAspectRatio === 'boolean') {
233 | if (this.rzAspectRatio && this._currSize.height) {
234 | this._aspectRatio = this._currSize.width / this._currSize.height;
235 | } else {
236 | this._aspectRatio = 0;
237 | }
238 | } else {
239 | let r = Number(this.rzAspectRatio);
240 | this._aspectRatio = isNaN(r) ? 0 : r;
241 | }
242 | }
243 |
244 | /** Use it to update containment */
245 | private updateContainment() {
246 | if (!this.rzContainment) {
247 | this._containment = null;
248 | return;
249 | }
250 |
251 | if (typeof this.rzContainment === 'string') {
252 | if (this.rzContainment === 'parent') {
253 | this._containment = this.el.nativeElement.parentElement;
254 | } else {
255 | this._containment = document.querySelector(this.rzContainment);
256 | }
257 | } else {
258 | this._containment = this.rzContainment;
259 | }
260 | }
261 |
262 | /** Use it to create handle divs */
263 | private createHandles() {
264 | if (!this.rzHandles) {
265 | return;
266 | }
267 |
268 | let tmpHandleTypes: string[];
269 | if (typeof this.rzHandles === 'string') {
270 | if (this.rzHandles === 'all') {
271 | tmpHandleTypes = ['n', 'e', 's', 'w', 'ne', 'se', 'nw', 'sw'];
272 | } else {
273 | tmpHandleTypes = this.rzHandles.replace(/ /g, '').toLowerCase().split(',');
274 | }
275 |
276 | for (let type of tmpHandleTypes) {
277 | // default handle theme: ng-resizable-$type.
278 | let handle = this.createHandleByType(type, `ng-resizable-${type}`);
279 | if (handle) {
280 | this._handleType.push(type);
281 | this._handles[type] = handle;
282 | }
283 | }
284 | } else {
285 | tmpHandleTypes = Object.keys(this.rzHandles);
286 | for (let type of tmpHandleTypes) {
287 | // custom handle theme.
288 | let handle = this.createHandleByType(type, this.rzHandles[type]);
289 | if (handle) {
290 | this._handleType.push(type);
291 | this._handles[type] = handle;
292 | }
293 | }
294 | }
295 | }
296 |
297 | /** Use it to create a handle */
298 | private createHandleByType(type: string, css: string): ResizeHandle {
299 | const _el = this.el.nativeElement;
300 | const _h = this.rzHandleDoms[type] ? this.rzHandleDoms[type].nativeElement : null;
301 |
302 | if (!type.match(/^(se|sw|ne|nw|n|e|s|w)$/)) {
303 | console.error('Invalid handle type:', type);
304 | return null;
305 | }
306 |
307 | return new ResizeHandle(_el, this.renderer, type, css, this.onMouseDown.bind(this), _h);
308 | }
309 |
310 | private removeHandles() {
311 | for (let type of this._handleType) {
312 | this._handles[type].dispose();
313 | }
314 |
315 | this._handleType = [];
316 | this._handles = {};
317 | }
318 |
319 | onMouseDown(event: MouseEvent | TouchEvent, handle: ResizeHandle) {
320 | // skip right click;
321 | if (event instanceof MouseEvent && event.button === 2) {
322 | return;
323 | }
324 |
325 | if (this.preventDefaultEvent) {
326 | // prevent default events
327 | event.stopPropagation();
328 | event.preventDefault();
329 | }
330 |
331 | if (!this._handleResizing) {
332 | this._origMousePos = Position.fromEvent(event);
333 | this.startResize(handle);
334 |
335 | this.subscribeEvents();
336 | }
337 | }
338 |
339 | private subscribeEvents() {
340 | this.draggingSub = fromEvent(document, 'mousemove', { passive: false }).subscribe(event =>
341 | this.onMouseMove(event as MouseEvent)
342 | );
343 | this.draggingSub.add(
344 | fromEvent(document, 'touchmove', { passive: false }).subscribe(event =>
345 | this.onMouseMove(event as TouchEvent)
346 | )
347 | );
348 | this.draggingSub.add(
349 | fromEvent(document, 'mouseup', { passive: false }).subscribe(() => this.onMouseLeave())
350 | );
351 | // fix for issue #164
352 | let isIEOrEdge = /msie\s|trident\//i.test(window.navigator.userAgent);
353 | if (!isIEOrEdge) {
354 | this.draggingSub.add(
355 | fromEvent(document, 'mouseleave', { passive: false }).subscribe(() => this.onMouseLeave())
356 | );
357 | }
358 | this.draggingSub.add(
359 | fromEvent(document, 'touchend', { passive: false }).subscribe(() => this.onMouseLeave())
360 | );
361 | this.draggingSub.add(
362 | fromEvent(document, 'touchcancel', { passive: false }).subscribe(() => this.onMouseLeave())
363 | );
364 | }
365 |
366 | private unsubscribeEvents() {
367 | this.draggingSub.unsubscribe();
368 | this.draggingSub = null;
369 | }
370 |
371 | onMouseLeave() {
372 | if (this._handleResizing) {
373 | this.stopResize();
374 | this._origMousePos = null;
375 | this.unsubscribeEvents();
376 | }
377 | }
378 |
379 | onMouseMove(event: MouseEvent | TouchEvent) {
380 | if (
381 | this._handleResizing &&
382 | this._resizable &&
383 | this._origMousePos &&
384 | this._origPos &&
385 | this._origSize
386 | ) {
387 | this.resizeTo(Position.fromEvent(event));
388 | this.onResizing();
389 | }
390 | }
391 |
392 | private startResize(handle: ResizeHandle) {
393 | const elm = this.el.nativeElement;
394 | this._origSize = Size.getCurrent(elm);
395 | this._origPos = Position.getCurrent(elm); // x: left, y: top
396 | this._currSize = Size.copy(this._origSize);
397 | this._currPos = Position.copy(this._origPos);
398 | if (this._containment) {
399 | this.getBounding();
400 | }
401 | this.getGridSize();
402 |
403 | // Add a transparent helper div:
404 | this._helperBlock.add();
405 | this._handleResizing = handle;
406 | this.updateDirection();
407 | this.rzStart.emit(this.getResizingEvent());
408 | }
409 |
410 | private stopResize() {
411 | // Remove the helper div:
412 | this._helperBlock.remove();
413 | this.rzStop.emit(this.getResizingEvent());
414 | this._handleResizing = null;
415 | this._direction = null;
416 | this._origSize = null;
417 | this._origPos = null;
418 | if (this._containment) {
419 | this.resetBounding();
420 | }
421 | }
422 |
423 | private onResizing() {
424 | this.rzResizing.emit(this.getResizingEvent());
425 | }
426 |
427 | private getResizingEvent(): IResizeEvent {
428 | return {
429 | host: this.el.nativeElement,
430 | handle: this._handleResizing ? this._handleResizing.el : null,
431 | size: {
432 | width: this._currSize.width,
433 | height: this._currSize.height,
434 | },
435 | position: {
436 | top: this._currPos.y,
437 | left: this._currPos.x,
438 | },
439 | direction: { ...this._directionChanged },
440 | };
441 | }
442 |
443 | private updateDirection() {
444 | this._direction = {
445 | n: !!this._handleResizing.type.match(/n/),
446 | s: !!this._handleResizing.type.match(/s/),
447 | w: !!this._handleResizing.type.match(/w/),
448 | e: !!this._handleResizing.type.match(/e/),
449 | };
450 |
451 | this._directionChanged = { ...this._direction };
452 |
453 | // if aspect ration should be preserved:
454 | if (this.rzAspectRatio) {
455 | // if north then west (unless ne)
456 | if (this._directionChanged.n && !this._directionChanged.e) {
457 | this._directionChanged.w = true;
458 | }
459 |
460 | // if south then east (unless sw)
461 | if (this._directionChanged.s && !this._directionChanged.w) {
462 | this._directionChanged.e = true;
463 | }
464 |
465 | // if east then south (unless ne)
466 | if (this._directionChanged.e && !this._directionChanged.n) {
467 | this._directionChanged.s = true;
468 | }
469 |
470 | // if west then south (unless nw)
471 | if (this._directionChanged.w && !this._directionChanged.n) {
472 | this._directionChanged.s = true;
473 | }
474 | }
475 | }
476 |
477 | private resizeTo(p: Position) {
478 | p.subtract(this._origMousePos).divide(this.rzScale);
479 |
480 | const tmpX = Math.round(p.x / this._gridSize.x) * this._gridSize.x;
481 | const tmpY = Math.round(p.y / this._gridSize.y) * this._gridSize.y;
482 |
483 | if (this._direction.n) {
484 | // n, ne, nw
485 | this._currPos.y = this._origPos.y + tmpY;
486 | this._currSize.height = this._origSize.height - tmpY;
487 | } else if (this._direction.s) {
488 | // s, se, sw
489 | this._currSize.height = this._origSize.height + tmpY;
490 | }
491 |
492 | if (this._direction.e) {
493 | // e, ne, se
494 | this._currSize.width = this._origSize.width + tmpX;
495 | } else if (this._direction.w) {
496 | // w, nw, sw
497 | this._currSize.width = this._origSize.width - tmpX;
498 | this._currPos.x = this._origPos.x + tmpX;
499 | }
500 |
501 | this.checkBounds();
502 | this.checkSize();
503 | this.adjustByRatio();
504 | this.doResize();
505 | }
506 |
507 | private doResize() {
508 | const container = this.el.nativeElement;
509 | if (!this._direction || this._direction.n || this._direction.s || this._aspectRatio) {
510 | this.renderer.setStyle(container, 'height', this._currSize.height + 'px');
511 | }
512 | if (!this._direction || this._direction.w || this._direction.e || this._aspectRatio) {
513 | this.renderer.setStyle(container, 'width', this._currSize.width + 'px');
514 | }
515 | this.renderer.setStyle(container, 'left', this._currPos.x + 'px');
516 | this.renderer.setStyle(container, 'top', this._currPos.y + 'px');
517 | }
518 |
519 | private adjustByRatio() {
520 | if (this._aspectRatio && !this._adjusted) {
521 | if (this._direction.e || this._direction.w) {
522 | const newHeight = Math.floor(this._currSize.width / this._aspectRatio);
523 |
524 | if (this._direction.n) {
525 | this._currPos.y += this._currSize.height - newHeight;
526 | }
527 |
528 | this._currSize.height = newHeight;
529 | } else {
530 | const newWidth = Math.floor(this._aspectRatio * this._currSize.height);
531 |
532 | if (this._direction.n) {
533 | this._currPos.x += this._currSize.width - newWidth;
534 | }
535 |
536 | this._currSize.width = newWidth;
537 | }
538 | }
539 | }
540 |
541 | private checkBounds() {
542 | if (this._containment) {
543 | const maxWidth =
544 | this._bounding.width -
545 | this._bounding.pr -
546 | this._bounding.deltaL -
547 | this._bounding.translateX -
548 | this._currPos.x;
549 | const maxHeight =
550 | this._bounding.height -
551 | this._bounding.pb -
552 | this._bounding.deltaT -
553 | this._bounding.translateY -
554 | this._currPos.y;
555 |
556 | if (this._direction.n && this._currPos.y + this._bounding.translateY < 0) {
557 | this._currPos.y = -this._bounding.translateY;
558 | this._currSize.height = this._origSize.height + this._origPos.y + this._bounding.translateY;
559 | }
560 |
561 | if (this._direction.w && this._currPos.x + this._bounding.translateX < 0) {
562 | this._currPos.x = -this._bounding.translateX;
563 | this._currSize.width = this._origSize.width + this._origPos.x + this._bounding.translateX;
564 | }
565 |
566 | if (this._currSize.width > maxWidth) {
567 | this._currSize.width = maxWidth;
568 | }
569 |
570 | if (this._currSize.height > maxHeight) {
571 | this._currSize.height = maxHeight;
572 | }
573 |
574 | /**
575 | * Fix Issue: Additional check for aspect ratio
576 | * https://github.com/xieziyu/angular2-draggable/issues/132
577 | */
578 | if (this._aspectRatio) {
579 | this._adjusted = false;
580 |
581 | if (
582 | (this._direction.w || this._direction.e) &&
583 | this._currSize.width / this._aspectRatio >= maxHeight
584 | ) {
585 | const newWidth = Math.floor(maxHeight * this._aspectRatio);
586 |
587 | if (this._direction.w) {
588 | this._currPos.x += this._currSize.width - newWidth;
589 | }
590 |
591 | this._currSize.width = newWidth;
592 | this._currSize.height = maxHeight;
593 | this._adjusted = true;
594 | }
595 |
596 | if (
597 | (this._direction.n || this._direction.s) &&
598 | this._currSize.height * this._aspectRatio >= maxWidth
599 | ) {
600 | const newHeight = Math.floor(maxWidth / this._aspectRatio);
601 |
602 | if (this._direction.n) {
603 | this._currPos.y += this._currSize.height - newHeight;
604 | }
605 |
606 | this._currSize.width = maxWidth;
607 | this._currSize.height = newHeight;
608 | this._adjusted = true;
609 | }
610 | }
611 | }
612 | }
613 |
614 | private checkSize() {
615 | const minHeight = !this.rzMinHeight ? 1 : this.rzMinHeight;
616 | const minWidth = !this.rzMinWidth ? 1 : this.rzMinWidth;
617 |
618 | if (this._currSize.height < minHeight) {
619 | this._currSize.height = minHeight;
620 |
621 | if (this._direction.n) {
622 | this._currPos.y = this._origPos.y + (this._origSize.height - minHeight);
623 | }
624 | }
625 |
626 | if (this._currSize.width < minWidth) {
627 | this._currSize.width = minWidth;
628 |
629 | if (this._direction.w) {
630 | this._currPos.x = this._origPos.x + (this._origSize.width - minWidth);
631 | }
632 | }
633 |
634 | if (this.rzMaxHeight && this._currSize.height > this.rzMaxHeight) {
635 | this._currSize.height = this.rzMaxHeight;
636 |
637 | if (this._direction.n) {
638 | this._currPos.y = this._origPos.y + (this._origSize.height - this.rzMaxHeight);
639 | }
640 | }
641 |
642 | if (this.rzMaxWidth && this._currSize.width > this.rzMaxWidth) {
643 | this._currSize.width = this.rzMaxWidth;
644 |
645 | if (this._direction.w) {
646 | this._currPos.x = this._origPos.x + (this._origSize.width - this.rzMaxWidth);
647 | }
648 | }
649 | }
650 |
651 | private getBounding() {
652 | const el = this._containment;
653 | const computed = window.getComputedStyle(el);
654 | if (computed) {
655 | let p = computed.getPropertyValue('position');
656 |
657 | const nativeEl = window.getComputedStyle(this.el.nativeElement);
658 | let transforms = nativeEl
659 | .getPropertyValue('transform')
660 | .replace(/[^-\d,]/g, '')
661 | .split(',');
662 |
663 | this._bounding = {};
664 | this._bounding.width = el.clientWidth;
665 | this._bounding.height = el.clientHeight;
666 | this._bounding.pr = parseInt(computed.getPropertyValue('padding-right'), 10);
667 | this._bounding.pb = parseInt(computed.getPropertyValue('padding-bottom'), 10);
668 | this._bounding.deltaL = this.el.nativeElement.offsetLeft - this._currPos.x;
669 | this._bounding.deltaT = this.el.nativeElement.offsetTop - this._currPos.y;
670 |
671 | if (transforms.length >= 6) {
672 | this._bounding.translateX = parseInt(transforms[4], 10);
673 | this._bounding.translateY = parseInt(transforms[5], 10);
674 | } else {
675 | this._bounding.translateX = 0;
676 | this._bounding.translateY = 0;
677 | }
678 |
679 | this._bounding.position = computed.getPropertyValue('position');
680 |
681 | if (p === 'static') {
682 | this.renderer.setStyle(el, 'position', 'relative');
683 | }
684 | }
685 | }
686 |
687 | private resetBounding() {
688 | if (this._bounding && this._bounding.position === 'static') {
689 | this.renderer.setStyle(this._containment, 'position', 'relative');
690 | }
691 | this._bounding = null;
692 | }
693 |
694 | private getGridSize() {
695 | // set default value:
696 | this._gridSize = { x: 1, y: 1 };
697 |
698 | if (this.rzGrid) {
699 | if (typeof this.rzGrid === 'number') {
700 | this._gridSize = { x: this.rzGrid, y: this.rzGrid };
701 | } else if (Array.isArray(this.rzGrid)) {
702 | this._gridSize = { x: this.rzGrid[0], y: this.rzGrid[1] };
703 | }
704 | }
705 | }
706 | }
707 |
--------------------------------------------------------------------------------