nhd

ngx-hm-dnd

[See ngx-hm-dnd in action!](https://github.com/SHANG-TING/ngx-hm-dnd/tree/bookCategory)

Showing:

Popularity

Downloads/wk

2

Maintenance

No Maintenance Data Available

Package

Dependencies

16

Size (min+gzip)

67.4KB

License

MIT

Type Definitions

Built-In

Tree-Shakeable

Yes?

Categories

Readme

NgxHmDnd/BookCateogry

See ngx-hm-dnd in action!

Installation


install package

npm install --save ngx-hm-dnd

Usage


import module:

import { NgxHmDndModule } from 'ngx-hm-dnd';

@NgModule({
  imports: [...something..., NgxHmDndModule],
  ...
})
export class YourModule {}

Examples


app.component.html

<nav id="nav">
  <ul [ngxHmDnd]="'group-1'" [sourceData]="bookCategory" [enable]="false">
    <li *ngFor="let category of bookCategory; let i = index;"
      [ngClass]="{'selected': selectedIndex === i}"
      (click)="selectedIndex = i">
      <a>{{ category.name }}</a>
    </li>
  </ul>
</nav>

<div *ngIf="bookCategory[selectedIndex].books.length > 0" style="padding-top: 90px;">
    <div *ngIf="display" class="image-wrapper"
      [ngxHmDnd]="'group-1'" [sourceData]="bookCategory[selectedIndex].books" (complete)="complete($event)">
      <div class="image-item insta-page"
        *ngFor="let book of bookCategory[selectedIndex].books; let i = index;"
        [attr.sortEnable]="!book.checked"
        [ngClass]="{'selected': book.checked}"
        (click)="book.checked = !book.checked" >
         <a class="image insta-image">
           <img alt="image" [src]="book.path" />
          </a>
       </div>
    </div>
  {{ bookCategory[selectedIndex].books | json }}
</div>

app.component.ts

import { Component } from '@angular/core';
import { IComplete } from 'ngx-hm-dnd';

export class AppComponent {
  private _selectedIndex = 0;
  get selectedIndex(): number {
    return this._selectedIndex;
  }
  set selectedIndex(val: number) {
    if (this._selectedIndex !== val) {
      this._selectedIndex = val;
      this.display = false;
      setTimeout(() => {
        this.display = true;
      }, 0);
    }
  }
  display = true;
  bookCategory = [
    {
      name: 'Book Category 1',
      books: [
        { name: 'Book 1', checked: false, path: 'https://cdn.scotch.io/scotchy-uploads/2015/12/ng-forward-logo.png' },
        { name: 'Book 2', checked: false, path: 'https://www.primefaces.org/wp-content/uploads/2016/10/primeng.png' },
        { name: 'Book 3', checked: false, path: 'https://angularair.com/logo.png' },
        {
          name: 'Book 4', checked: false,
          path: 'https://mdbootstrap.com/wp-content/themes/mdbootstrap4/content/angular/tutorials/main/img/3.png'
        },
        { name: 'Book 5', checked: false, path: 'https://www.infopulse.com/files/images/Angular-20170712-300x300.png' },
        {
          name: 'Book 6', checked: false,
          path: 'https://resources.cloud.genuitec.com/wp-content/uploads/2018/01/authenticationangular-blog.png'
        },
      ]
    },
    {
      name: 'Book Category 2',
      books: [
        { name: 'Book 7', checked: false, path: 'https://cdn-images-1.medium.com/max/1600/0*1rMayMgSu9dxFXuU.png' },
        { name: 'Book 8', checked: false, path: 'https://frontendmasters.com/assets/angular-app-dev-300x300.png' },
      ]
    },
    {
      name: 'Book Category 3',
      books: [
        { name: 'Book 9', checked: false, path: 'https://www.infopulse.com/files/images/angular-2-sharepoint-integration-round.png' },
        { name: 'Book 10', checked: false, path: 'https://www.mobileacademy.ro/wp-content/uploads/2018/01/ionic-angular-300.jpg' }
      ]
    }
  ];

  complete($event: IComplete) {
    const temp = $event.from.data[$event.from.selectedIndex];
    if (temp.checked) {
      const checkedBooks = $event.from.data.filter(x => x.checked);
      checkedBooks.forEach(checkedBook => {
        $event.from.data.splice($event.from.data.indexOf(checkedBook), 1);
        checkedBook.checked = false;
        $event.to.data[$event.to.selectedIndex].books.push(checkedBook);
      });
    } else {
      $event.from.data.splice($event.from.selectedIndex, 1);
      $event.to.data[$event.to.selectedIndex].books.push(temp);
    }
    this.selectedIndex = $event.to.selectedIndex;
  }
}

app.component.scss

$insta-wrapper-max-width: 965px;
$insta-wrapper-min-height: 100px;
$height: 100%;
$width: 100%;
$transition: all .17s cubic-bezier(.4, 0, 1, 1);

.image-wrapper {
  box-sizing: border-box;
  display: flex;
  flex-flow: row wrap;
  flex-grow: 1;
  justify-content: space-around;
  margin: 0 auto 30px;
  max-width: $insta-wrapper-max-width;
  min-height: $insta-wrapper-min-height;
  position: relative;
  width: $width;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

@for $i from 1 through 10 {
  .image-item {
    // 動畫效果
    animation: slide-item .3s ease forwards;
    // transform: translateY(250px);
    // visibility: hidden;

    box-sizing: border-box;
    flex: 1 0 33%;
    opacity: 1;
    transform: translateY(0);
    visibility: visible;
    width: $width;

    // 改善畫面選取效能
    touch-action: none;
    user-select: none;
    -webkit-user-drag: none;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);

    &:nth-child(#{$i}) {
      animation-delay: (#{$i * .1s});
    }
  }

  .insta-page {
    padding: 10px;
  }
  .selected {
    background-color: yellow;
  }
}

.image {
  display: block;
  position: relative;
  transition: $transition;
  width: $width;
  // 防止圖片被抓起來
  pointer-events: none;

  img {
    display: block;
    max-width: $width;
  }
}

@keyframes slide-item {
  60% {
    transform: translateY(-10px);
  }

  100% {
    // opacity: 1;
    transform: translateY(0);
    visibility: visible;
  }
}

@keyframes fadeIn {

  0% {
    transform: translateX(-150px);
  }

  100% {
    transform: translateX(0);
  }
}


nav {
  position: fixed;
  width: 100%;
  top: 0;
  background: #dd1d8d;
  -webkit-transition: all 650ms cubic-bezier(0.22, 1, 0.25, 1);
  transition: all 650ms cubic-bezier(0.22, 1, 0.25, 1);
  z-index: 3;
  &before {
    content: "";
    display: block;
    position: absolute;
    background: rgba(0, 0, 0, 0.27);
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
  }
  ul {
    position: relative;
    margin: 0;
    z-index: 2;
    text-transform: uppercase;
    text-align: center;
    li {
      display: inline-block;
      padding: 1.35em 0;
      margin-right: 3em;
      font-size: 0.9em;

      // 改善畫面選取效能
      touch-action: none;
      user-select: none;
      -webkit-user-drag: none;
      -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
      a {
        margin: 0 25px;
        text-decoration: none;
        color: #fff;
        font-size: 0.9em;
        // 防止文字被選取
        pointer-events: none;
      }
    }
    li.selected {
      background-color: #b61c1c;
      opacity: 0.7;
    }
  }
}

View more examples

Configuration (Input)


ArgumentRequiredDefault valueTypeLocationDescription
ngx-hm-dndnononestringcontainer可以定義群組名稱來限制可排序的區域
source-datano[]any[]container需要雙向綁定的資料
enablenotruebooleancontainer此區域是否啟動排序功能
attr.sortEnablenotruebooleancontainer-items此項目是否啟動排序功能

Methods (Output)


MethodLocationDescription
completecontainer取得目前被移動的element、雙向綁定的資料,以及移動至目標的element、雙向綁定的資料

Collaborators

@SHANG-TING

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100