From c70ed272711880348b0054acb34ffd1414ba18b2 Mon Sep 17 00:00:00 2001 From: Kovah Date: Thu, 28 Mar 2019 04:52:15 +0100 Subject: [PATCH] Drag n drop sorting for custom fields (#237) * Implement sorting for custom fields Adds angular/cdk to sources and makes DragDropModule globally available. Updates the add-edit component with necessary tags. Restructures and updates the box styling for drag 'n drop handling. * Set the correct Angular CDK version * Remove unused class for drag handle * Add missing locale entry for drag handle * Fix styling of the drag handle * Move drag handle to right side of custom fields * Revert changes in package-lock.json * Update reference for jslib --- jslib | 2 +- package-lock.json | 15 + package.json | 1 + src/app/app.module.ts | 2 + src/app/vault/add-edit.component.html | 62 +-- src/locales/en/messages.json | 3 + src/scss/box.scss | 610 +++++++++++++------------- 7 files changed, 372 insertions(+), 323 deletions(-) diff --git a/jslib b/jslib index 593870e9365..f39bdc4269c 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 593870e9365d68e924955f1ec192a70216b63621 +Subproject commit f39bdc4269c1105211ac901b7d2d51978375b222 diff --git a/package-lock.json b/package-lock.json index 0de21c284be..b0805a2b66b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -413,6 +413,15 @@ "tslib": "^1.9.0" } }, + "@angular/cdk": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.2.1.tgz", + "integrity": "sha512-oU1Pjq3JkDtkXquLxWK84A2jOCeYRf352dVGbQCxWoSOQ5KBtMAd42huGidPiOSHN6/f7xZwL3n4fq3fVIut8A==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^1.7.1" + } + }, "@angular/common": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.1.tgz", @@ -9278,6 +9287,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "optional": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", diff --git a/package.json b/package.json index 6891e513645..6c21dbb4cf7 100644 --- a/package.json +++ b/package.json @@ -232,6 +232,7 @@ }, "dependencies": { "@angular/animations": "7.2.1", + "@angular/cdk": "7.2.1", "@angular/common": "7.2.1", "@angular/compiler": "7.2.1", "@angular/core": "7.2.1", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e6c21cc6779..4f1ba7dbdba 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,6 +9,7 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { AppRoutingModule } from './app-routing.module'; import { ServicesModule } from './services.module'; +import { DragDropModule } from '@angular/cdk/drag-drop'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; @@ -138,6 +139,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); }), ToasterModule.forRoot(), InfiniteScrollModule, + DragDropModule, ], declarations: [ AddEditComponent, diff --git a/src/app/vault/add-edit.component.html b/src/app/vault/add-edit.component.html index ecb6cac1e4c..92acab02f94 100644 --- a/src/app/vault/add-edit.component.html +++ b/src/app/vault/add-edit.component.html @@ -261,36 +261,42 @@ {{'customFields' | i18n}}
- -
- - - - - -
- - - -
- -
- - +
+ +
+ + + + +
+ + + +
+ +
+ + + +
+ + +
-
- + +
{{'newCustomField' | i18n}} diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 515401b6f62..074c202d7d2 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -297,6 +297,9 @@ "value": { "message": "Value" }, + "dragToSort": { + "message": "Drag to sort" + }, "cfTypeText": { "message": "Text" }, diff --git a/src/scss/box.scss b/src/scss/box.scss index a1648af622c..dfddb09695f 100644 --- a/src/scss/box.scss +++ b/src/scss/box.scss @@ -1,6 +1,7 @@ @import "variables.scss"; .box { + position: relative; width: 100%; .box-header { @@ -34,300 +35,6 @@ padding: 10px 15px; } - .box-content-row { - display: block; - padding: 10px 15px; - position: relative; - z-index: 1; - - &:before { - content: ""; - position: absolute; - right: 0; - bottom: 0; - height: 1px; - width: calc(100% - 10px); - border-bottom: 1px solid #000000; - - @include themify($themes) { - border-bottom-color: themed('boxBorderColor'); - } - } - - &:first-child, &:last-child { - border-radius: $border-radius; - - .progress { - border-bottom-left-radius: $border-radius; - border-bottom-right-radius: $border-radius; - } - } - - &:last-child { - &:before { - border: none; - height: 0; - } - } - - &:after { - content: ""; - display: table; - clear: both; - } - - &:hover, &:focus, &.active { - @include themify($themes) { - background-color: themed('boxBackgroundHoverColor'); - } - } - - &.pre { - white-space: pre; - overflow-x: auto; - } - - &.pre-line { - white-space: pre-line; - overflow-x: auto; - } - - .row-label, label { - font-size: $font-size-small; - display: block; - width: 100%; - margin-bottom: 5px; - - @include themify($themes) { - color: themed('mutedColor'); - } - - .sub-label { - margin-left: 10px; - } - } - - .text, .detail { - display: block; - - @include themify($themes) { - color: themed('textColor'); - } - } - - .detail { - font-size: $font-size-small; - - @include themify($themes) { - color: themed('mutedColor'); - } - } - - .img-right { - float: right; - margin-left: 10px; - } - - .row-main { - flex-grow: 1; - } - - &.box-content-row-flex, .box-content-row-flex, &.box-content-row-checkbox, &.box-content-row-input, - &.box-content-row-slider, &.box-content-row-multi { - display: flex; - align-items: center; - word-break: break-word; - } - - &.box-content-row-multi { - width: 100%; - - input:not([type="checkbox"]) { - width: 100%; - } - - input + label.sr-only + select { - margin-top: 5px; - } - - > a { - padding: 8px 8px 8px 4px; - margin: 0; - - @include themify($themes) { - color: themed('dangerColor'); - } - } - } - - &.box-content-row-checkbox, &.box-content-row-input, &.box-content-row-slider { - label, .row-label { - font-size: $font-size-base; - display: inline; - width: initial; - margin-bottom: 0; - - @include themify($themes) { - color: themed('textColor'); - } - } - - > span { - @include themify($themes) { - color: themed('mutedColor'); - } - } - - > input { - margin: 0 0 0 auto; - padding: 0; - } - - > * { - margin-right: 15px; - - &:last-child { - margin-right: 0; - } - } - } - - &.box-content-row-input { - label { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - input { - text-align: right; - width: 45px !important; - } - } - - &.box-content-row-slider { - input[type="range"] { - height: 10px; - width: 110px !important; - } - - input[type="number"] { - text-align: right; - width: 45px !important; - } - - label { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - - input:not([type="checkbox"]), textarea { - border: none; - width: 100%; - background-color: transparent; - - &::-webkit-input-placeholder { - @include themify($themes) { - color: themed('inputPlaceholderColor'); - } - } - - &:focus { - outline: none; - } - } - - select { - width: 100%; - border: 1px solid #000000; - border-radius: $border-radius; - - @include themify($themes) { - border-color: themed('inputBorderColor'); - } - } - - .action-buttons { - display: flex; - margin-left: 5px; - - .row-btn { - cursor: pointer; - padding: 10px 8px; - background: none; - border: none; - - @include themify($themes) { - color: themed('boxRowButtonColor'); - } - - &:hover, &:focus { - @include themify($themes) { - color: themed('boxRowButtonHoverColor'); - } - } - - &.disabled { - @include themify($themes) { - color: themed('disabledIconColor'); - } - - &:hover { - @include themify($themes) { - color: themed('disabledIconColor'); - } - } - } - - &:last-child { - padding-right: 2px !important; - } - } - - &.no-pad .row-btn { - padding-top: 0; - padding-bottom: 0; - } - } - - select.field-type { - margin: 5px 0 0 25px; - width: calc(100% - 25px); - } - - .row-sub-icon { - @include themify($themes) { - color: themed('disabledIconColor'); - } - } - - .row-sub-label { - margin: 0 15px; - white-space: nowrap; - - @include themify($themes) { - color: themed('mutedColor'); - } - } - - .progress { - display: flex; - height: 5px; - overflow: hidden; - margin: 5px -15px -10px; - - .progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - white-space: nowrap; - background-color: $brand-primary; - } - } - } - &.condensed .box-content-row, .box-content-row.condensed { padding-top: 5px; padding-bottom: 5px; @@ -349,3 +56,318 @@ } } } + +.box-content-row { + display: block; + padding: 10px 15px; + position: relative; + z-index: 1; + + &:before { + content: ""; + position: absolute; + right: 0; + bottom: 0; + height: 1px; + width: calc(100% - 10px); + border-bottom: 1px solid #000000; + + @include themify($themes) { + border-bottom-color: themed('boxBorderColor'); + } + } + + &:first-child, &:last-child { + border-radius: $border-radius; + + .progress { + border-bottom-left-radius: $border-radius; + border-bottom-right-radius: $border-radius; + } + } + + &:last-child { + &:before { + border: none; + height: 0; + } + } + + &:after { + content: ""; + display: table; + clear: both; + } + + &:hover, &:focus, &.active { + @include themify($themes) { + background-color: themed('boxBackgroundHoverColor'); + } + } + + &.pre { + white-space: pre; + overflow-x: auto; + } + + &.pre-line { + white-space: pre-line; + overflow-x: auto; + } + + .row-label, label { + font-size: $font-size-small; + display: block; + width: 100%; + margin-bottom: 5px; + + @include themify($themes) { + color: themed('mutedColor'); + } + + .sub-label { + margin-left: 10px; + } + } + + .text, .detail { + display: block; + + @include themify($themes) { + color: themed('textColor'); + } + } + + .detail { + font-size: $font-size-small; + + @include themify($themes) { + color: themed('mutedColor'); + } + } + + .img-right { + float: right; + margin-left: 10px; + } + + .row-main { + flex-grow: 1; + } + + &.box-content-row-flex, .box-content-row-flex, &.box-content-row-checkbox, &.box-content-row-input, + &.box-content-row-slider, &.box-content-row-multi { + display: flex; + align-items: center; + word-break: break-word; + } + + &.box-content-row-multi { + width: 100%; + + input:not([type="checkbox"]) { + width: 100%; + } + + input + label.sr-only + select { + margin-top: 5px; + } + + > a { + padding: 8px 8px 8px 4px; + margin: 0; + + @include themify($themes) { + color: themed('dangerColor'); + } + } + } + + &.box-content-row-checkbox, &.box-content-row-input, &.box-content-row-slider { + label, .row-label { + font-size: $font-size-base; + display: inline; + width: initial; + margin-bottom: 0; + + @include themify($themes) { + color: themed('textColor'); + } + } + + > span { + @include themify($themes) { + color: themed('mutedColor'); + } + } + + > input { + margin: 0 0 0 auto; + padding: 0; + } + + > * { + margin-right: 15px; + + &:last-child { + margin-right: 0; + } + } + } + + &.box-content-row-input { + label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + input { + text-align: right; + width: 45px !important; + } + } + + &.box-content-row-slider { + input[type="range"] { + height: 10px; + width: 110px !important; + } + + input[type="number"] { + text-align: right; + width: 45px !important; + } + + label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + input:not([type="checkbox"]), textarea { + border: none; + width: 100%; + background-color: transparent; + + &::-webkit-input-placeholder { + @include themify($themes) { + color: themed('inputPlaceholderColor'); + } + } + + &:focus { + outline: none; + } + } + + select { + width: 100%; + border: 1px solid #000000; + border-radius: $border-radius; + + @include themify($themes) { + border-color: themed('inputBorderColor'); + } + } + + .action-buttons { + display: flex; + margin-left: 5px; + + .row-btn { + cursor: pointer; + padding: 10px 8px; + background: none; + border: none; + + @include themify($themes) { + color: themed('boxRowButtonColor'); + } + + &:hover, &:focus { + @include themify($themes) { + color: themed('boxRowButtonHoverColor'); + } + } + + &.disabled { + @include themify($themes) { + color: themed('disabledIconColor'); + } + + &:hover { + @include themify($themes) { + color: themed('disabledIconColor'); + } + } + } + + &:last-child { + padding-right: 2px !important; + } + } + + &.no-pad .row-btn { + padding-top: 0; + padding-bottom: 0; + } + } + + select.field-type { + margin: 5px 0 0 25px; + width: calc(100% - 25px); + } + + .row-sub-icon { + @include themify($themes) { + color: themed('disabledIconColor'); + } + } + + .row-sub-label { + margin: 0 15px; + white-space: nowrap; + + @include themify($themes) { + color: themed('mutedColor'); + } + } + + .progress { + display: flex; + height: 5px; + overflow: hidden; + margin: 5px -15px -10px; + + .progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + white-space: nowrap; + background-color: $brand-primary; + } + } +} + +.box-drag-handle { + cursor: move; + margin-left: 5px; + user-select: none; + + @include themify($themes) { + color: themed('mutedColor'); + } +} + +.cdk-drag-preview { + position: relative; + display: flex; + align-items: center; + opacity: .8; + + @include themify($themes) { + background-color: themed('boxBackgroundColor'); + } +}