Showing
11 changed files
with
147 additions
and
27 deletions
| ... | @@ -60,7 +60,7 @@ class Entity extends Ownable | ... | @@ -60,7 +60,7 @@ class Entity extends Ownable |
| 60 | */ | 60 | */ |
| 61 | public function tags() | 61 | public function tags() |
| 62 | { | 62 | { |
| 63 | - return $this->morphMany(Tag::class, 'entity'); | 63 | + return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc'); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | /** | 66 | /** | ... | ... |
| ... | @@ -4,10 +4,11 @@ | ... | @@ -4,10 +4,11 @@ |
| 4 | "gulp": "^3.9.0" | 4 | "gulp": "^3.9.0" |
| 5 | }, | 5 | }, |
| 6 | "dependencies": { | 6 | "dependencies": { |
| 7 | - "angular": "^1.5.0-rc.0", | 7 | + "angular": "^1.5.5", |
| 8 | - "angular-animate": "^1.5.0-rc.0", | 8 | + "angular-animate": "^1.5.5", |
| 9 | - "angular-resource": "^1.5.0-rc.0", | 9 | + "angular-resource": "^1.5.5", |
| 10 | - "angular-sanitize": "^1.5.0-rc.0", | 10 | + "angular-sanitize": "^1.5.5", |
| 11 | + "angular-ui-sortable": "^0.14.0", | ||
| 11 | "babel-runtime": "^5.8.29", | 12 | "babel-runtime": "^5.8.29", |
| 12 | "bootstrap-sass": "^3.0.0", | 13 | "bootstrap-sass": "^3.0.0", |
| 13 | "dropzone": "^4.0.1", | 14 | "dropzone": "^4.0.1", | ... | ... |
public/libs/jquery/jquery-ui.min.js
0 → 100644
This diff is collapsed.
Click to expand it.
| ... | @@ -406,6 +406,13 @@ module.exports = function (ngApp, events) { | ... | @@ -406,6 +406,13 @@ module.exports = function (ngApp, events) { |
| 406 | const pageId = Number($attrs.pageId); | 406 | const pageId = Number($attrs.pageId); |
| 407 | $scope.tags = []; | 407 | $scope.tags = []; |
| 408 | 408 | ||
| 409 | + $scope.sortOptions = { | ||
| 410 | + handle: '.handle', | ||
| 411 | + items: '> tr', | ||
| 412 | + containment: "parent", | ||
| 413 | + axis: "y" | ||
| 414 | + }; | ||
| 415 | + | ||
| 409 | /** | 416 | /** |
| 410 | * Push an empty tag to the end of the scope tags. | 417 | * Push an empty tag to the end of the scope tags. |
| 411 | */ | 418 | */ |
| ... | @@ -415,6 +422,7 @@ module.exports = function (ngApp, events) { | ... | @@ -415,6 +422,7 @@ module.exports = function (ngApp, events) { |
| 415 | value: '' | 422 | value: '' |
| 416 | }); | 423 | }); |
| 417 | } | 424 | } |
| 425 | + $scope.addEmptyTag = addEmptyTag; | ||
| 418 | 426 | ||
| 419 | /** | 427 | /** |
| 420 | * Get all tags for the current book and add into scope. | 428 | * Get all tags for the current book and add into scope. |
| ... | @@ -463,6 +471,9 @@ module.exports = function (ngApp, events) { | ... | @@ -463,6 +471,9 @@ module.exports = function (ngApp, events) { |
| 463 | } | 471 | } |
| 464 | }; | 472 | }; |
| 465 | 473 | ||
| 474 | + /** | ||
| 475 | + * Save the tags to the current page. | ||
| 476 | + */ | ||
| 466 | $scope.saveTags = function() { | 477 | $scope.saveTags = function() { |
| 467 | setTagOrder(); | 478 | setTagOrder(); |
| 468 | let postData = {tags: $scope.tags}; | 479 | let postData = {tags: $scope.tags}; |
| ... | @@ -473,6 +484,15 @@ module.exports = function (ngApp, events) { | ... | @@ -473,6 +484,15 @@ module.exports = function (ngApp, events) { |
| 473 | }) | 484 | }) |
| 474 | }; | 485 | }; |
| 475 | 486 | ||
| 487 | + /** | ||
| 488 | + * Remove a tag from the current list. | ||
| 489 | + * @param tag | ||
| 490 | + */ | ||
| 491 | + $scope.removeTag = function(tag) { | ||
| 492 | + let cIndex = $scope.tags.indexOf(tag); | ||
| 493 | + $scope.tags.splice(cIndex, 1); | ||
| 494 | + }; | ||
| 495 | + | ||
| 476 | }]); | 496 | }]); |
| 477 | 497 | ||
| 478 | }; | 498 | }; | ... | ... |
| ... | @@ -301,6 +301,42 @@ module.exports = function (ngApp, events) { | ... | @@ -301,6 +301,42 @@ module.exports = function (ngApp, events) { |
| 301 | 301 | ||
| 302 | } | 302 | } |
| 303 | } | 303 | } |
| 304 | - }]) | 304 | + }]); |
| 305 | + | ||
| 306 | + ngApp.directive('toolbox', [function() { | ||
| 307 | + return { | ||
| 308 | + restrict: 'A', | ||
| 309 | + link: function(scope, elem, attrs) { | ||
| 310 | + | ||
| 311 | + // Get common elements | ||
| 312 | + const $buttons = elem.find('[tab-button]'); | ||
| 313 | + const $content = elem.find('[tab-content]'); | ||
| 314 | + const $toggle = elem.find('[toolbox-toggle]'); | ||
| 315 | + | ||
| 316 | + // Handle toolbox toggle click | ||
| 317 | + $toggle.click((e) => { | ||
| 318 | + elem.toggleClass('open'); | ||
| 319 | + }); | ||
| 320 | + | ||
| 321 | + // Set an active tab/content by name | ||
| 322 | + function setActive(tabName, openToolbox) { | ||
| 323 | + $buttons.removeClass('active'); | ||
| 324 | + $content.hide(); | ||
| 325 | + $buttons.filter(`[tab-button="${tabName}"]`).addClass('active'); | ||
| 326 | + $content.filter(`[tab-content="${tabName}"]`).show(); | ||
| 327 | + if (openToolbox) elem.addClass('open'); | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + // Set the first tab content active on load | ||
| 331 | + setActive($content.first().attr('tab-content'), false); | ||
| 332 | + | ||
| 333 | + // Handle tab button click | ||
| 334 | + $buttons.click(function(e) { | ||
| 335 | + let name = $(this).attr('tab-button'); | ||
| 336 | + setActive(name, true); | ||
| 337 | + }); | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | + }]); | ||
| 305 | 341 | ||
| 306 | }; | 342 | }; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -5,9 +5,9 @@ var angular = require('angular'); | ... | @@ -5,9 +5,9 @@ var angular = require('angular'); |
| 5 | var ngResource = require('angular-resource'); | 5 | var ngResource = require('angular-resource'); |
| 6 | var ngAnimate = require('angular-animate'); | 6 | var ngAnimate = require('angular-animate'); |
| 7 | var ngSanitize = require('angular-sanitize'); | 7 | var ngSanitize = require('angular-sanitize'); |
| 8 | +require('angular-ui-sortable'); | ||
| 8 | 9 | ||
| 9 | -var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize']); | 10 | +var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']); |
| 10 | - | ||
| 11 | 11 | ||
| 12 | // Global Event System | 12 | // Global Event System |
| 13 | var Events = { | 13 | var Events = { | ... | ... |
| ... | @@ -65,6 +65,9 @@ $button-border-radius: 2px; | ... | @@ -65,6 +65,9 @@ $button-border-radius: 2px; |
| 65 | &:focus, &:active { | 65 | &:focus, &:active { |
| 66 | outline: 0; | 66 | outline: 0; |
| 67 | } | 67 | } |
| 68 | + &:hover { | ||
| 69 | + text-decoration: none; | ||
| 70 | + } | ||
| 68 | &.neg { | 71 | &.neg { |
| 69 | color: $negative; | 72 | color: $negative; |
| 70 | } | 73 | } | ... | ... |
| ... | @@ -21,6 +21,11 @@ | ... | @@ -21,6 +21,11 @@ |
| 21 | 21 | ||
| 22 | [ng\:cloak], [ng-cloak], .ng-cloak { | 22 | [ng\:cloak], [ng-cloak], .ng-cloak { |
| 23 | display: none !important; | 23 | display: none !important; |
| 24 | + user-select: none; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +[ng-click] { | ||
| 28 | + cursor: pointer; | ||
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | // Jquery Sortable Styles | 31 | // Jquery Sortable Styles |
| ... | @@ -206,18 +211,31 @@ $btt-size: 40px; | ... | @@ -206,18 +211,31 @@ $btt-size: 40px; |
| 206 | // Attribute form | 211 | // Attribute form |
| 207 | .floating-toolbox { | 212 | .floating-toolbox { |
| 208 | background-color: #FFF; | 213 | background-color: #FFF; |
| 209 | - border: 1px solid #BBB; | 214 | + border: 1px solid #DDD; |
| 210 | - border-radius: 3px; | ||
| 211 | - position: fixed; | ||
| 212 | right: $-xl*2; | 215 | right: $-xl*2; |
| 213 | - top: 100px; | ||
| 214 | z-index: 99; | 216 | z-index: 99; |
| 215 | - height: 800px; | 217 | + width: 48px; |
| 216 | - width: 480px; | 218 | + overflow: hidden; |
| 217 | - overflow-y: scroll; | ||
| 218 | - display: flex; | ||
| 219 | align-items: stretch; | 219 | align-items: stretch; |
| 220 | flex-direction: row; | 220 | flex-direction: row; |
| 221 | + display: flex; | ||
| 222 | + transition: width ease-in-out 180ms; | ||
| 223 | + margin-top: -1px; | ||
| 224 | + &.open { | ||
| 225 | + width: 480px; | ||
| 226 | + } | ||
| 227 | + [toolbox-toggle] i { | ||
| 228 | + transition: transform ease-in-out 180ms; | ||
| 229 | + } | ||
| 230 | + [toolbox-toggle] { | ||
| 231 | + transition: background-color ease-in-out 180ms; | ||
| 232 | + } | ||
| 233 | + &.open [toolbox-toggle] { | ||
| 234 | + background-color: rgba(255, 0, 0, 0.29); | ||
| 235 | + } | ||
| 236 | + &.open [toolbox-toggle] i { | ||
| 237 | + transform: rotate(180deg); | ||
| 238 | + } | ||
| 221 | > div { | 239 | > div { |
| 222 | flex: 1; | 240 | flex: 1; |
| 223 | position: relative; | 241 | position: relative; |
| ... | @@ -229,27 +247,35 @@ $btt-size: 40px; | ... | @@ -229,27 +247,35 @@ $btt-size: 40px; |
| 229 | flex: 0; | 247 | flex: 0; |
| 230 | } | 248 | } |
| 231 | .tabs i { | 249 | .tabs i { |
| 250 | + color: rgba(0, 0, 0, 0.5); | ||
| 232 | padding: 0; | 251 | padding: 0; |
| 233 | margin: 0; | 252 | margin: 0; |
| 234 | } | 253 | } |
| 235 | - .tabs [tab-button] { | 254 | + .tabs > span { |
| 236 | display: block; | 255 | display: block; |
| 237 | cursor: pointer; | 256 | cursor: pointer; |
| 238 | - color: #666; | 257 | + padding: $-s $-m; |
| 239 | - padding: $-m; | 258 | + font-size: 13.5px; |
| 259 | + line-height: 1.6; | ||
| 240 | border-bottom: 1px solid rgba(255, 255, 255, 0.3); | 260 | border-bottom: 1px solid rgba(255, 255, 255, 0.3); |
| 241 | - &.active { | 261 | + } |
| 262 | + &.open .tabs > span.active { | ||
| 242 | color: #444; | 263 | color: #444; |
| 243 | background-color: rgba(0, 0, 0, 0.1); | 264 | background-color: rgba(0, 0, 0, 0.1); |
| 244 | } | 265 | } |
| 266 | + div[tab-content] { | ||
| 267 | + padding-bottom: 45px; | ||
| 268 | + display: flex; | ||
| 269 | + flex: 1; | ||
| 245 | } | 270 | } |
| 246 | div[tab-content] .padded { | 271 | div[tab-content] .padded { |
| 247 | - padding: 0 $-m; | 272 | + flex: 1; |
| 273 | + padding-top: 0; | ||
| 248 | } | 274 | } |
| 249 | h4 { | 275 | h4 { |
| 250 | font-size: 24px; | 276 | font-size: 24px; |
| 251 | margin: $-m 0 0 0; | 277 | margin: $-m 0 0 0; |
| 252 | - padding: 0 $-m; | 278 | + padding: 0 $-l $-s $-l; |
| 253 | } | 279 | } |
| 254 | .tags input { | 280 | .tags input { |
| 255 | max-width: 100%; | 281 | max-width: 100%; |
| ... | @@ -266,6 +292,7 @@ $btt-size: 40px; | ... | @@ -266,6 +292,7 @@ $btt-size: 40px; |
| 266 | display: block; | 292 | display: block; |
| 267 | width: 100%; | 293 | width: 100%; |
| 268 | padding: $-s; | 294 | padding: $-s; |
| 295 | + height: 45px; | ||
| 269 | border: 0; | 296 | border: 0; |
| 270 | margin: 0; | 297 | margin: 0; |
| 271 | box-shadow: none; | 298 | box-shadow: none; |
| ... | @@ -274,4 +301,19 @@ $btt-size: 40px; | ... | @@ -274,4 +301,19 @@ $btt-size: 40px; |
| 274 | box-shadow: none; | 301 | box-shadow: none; |
| 275 | } | 302 | } |
| 276 | } | 303 | } |
| 304 | + .handle { | ||
| 305 | + user-select: none; | ||
| 306 | + cursor: move; | ||
| 307 | + color: #999; | ||
| 308 | + } | ||
| 309 | + form { | ||
| 310 | + display: flex; | ||
| 311 | + flex: 1; | ||
| 312 | + flex-direction: column; | ||
| 313 | + overflow-y: scroll; | ||
| 314 | + } | ||
| 315 | +} | ||
| 316 | + | ||
| 317 | +[tab-content] { | ||
| 318 | + display: none; | ||
| 277 | } | 319 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -15,6 +15,7 @@ | ... | @@ -15,6 +15,7 @@ |
| 15 | 15 | ||
| 16 | <!-- Scripts --> | 16 | <!-- Scripts --> |
| 17 | <script src="/libs/jquery/jquery.min.js?version=2.1.4"></script> | 17 | <script src="/libs/jquery/jquery.min.js?version=2.1.4"></script> |
| 18 | + <script src="/libs/jquery/jquery-ui.min.js?version=1.11.4"></script> | ||
| 18 | 19 | ||
| 19 | @yield('head') | 20 | @yield('head') |
| 20 | 21 | ... | ... |
| 1 | -<div class="floating-toolbox"> | 1 | + |
| 2 | +<div toolbox class="floating-toolbox"> | ||
| 2 | <div class="tabs primary-background-light"> | 3 | <div class="tabs primary-background-light"> |
| 3 | - <span tab-button class="active"><i class="zmdi zmdi-tag"></i></span> | 4 | + <span toolbox-toggle><i class="zmdi zmdi-caret-left-circle"></i></span> |
| 4 | - <span tab-button><i class="zmdi zmdi-wrench"></i></span> | 5 | + <span tab-button="tags" title="Page Tags" class="active"><i class="zmdi zmdi-tag"></i></span> |
| 5 | </div> | 6 | </div> |
| 6 | - <div tab-content ng-controller="PageTagController" page-id="{{ $page->id or 0 }}"> | 7 | + <div tab-content="tags" ng-controller="PageTagController" page-id="{{ $page->id or 0 }}"> |
| 7 | <form ng-submit="saveTags()" > | 8 | <form ng-submit="saveTags()" > |
| 8 | <h4>Page Tags</h4> | 9 | <h4>Page Tags</h4> |
| 9 | <div class="padded tags"> | 10 | <div class="padded tags"> |
| 11 | + <p class="muted small">Add some tags to better categorise your content. <br> You can assign a value to a tag for more in-depth organisation.</p> | ||
| 10 | <table class="no-style" style="width: 100%;"> | 12 | <table class="no-style" style="width: 100%;"> |
| 13 | + <tbody ui-sortable="sortOptions" ng-model="tags" > | ||
| 11 | <tr ng-repeat="tag in tags"> | 14 | <tr ng-repeat="tag in tags"> |
| 15 | + <td width="20" ><i class="handle zmdi zmdi-menu"></i></td> | ||
| 12 | <td><input class="outline" type="text" ng-model="tag.name" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag"></td> | 16 | <td><input class="outline" type="text" ng-model="tag.name" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag"></td> |
| 13 | <td><input class="outline" type="text" ng-model="tag.value" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag Value (Optional)"></td> | 17 | <td><input class="outline" type="text" ng-model="tag.value" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag Value (Optional)"></td> |
| 18 | + <td width="10" class="text-center text-neg" style="padding: 0;" ng-click="removeTag(tag)"><i class="zmdi zmdi-close"></i></td> | ||
| 14 | </tr> | 19 | </tr> |
| 20 | + </tbody> | ||
| 21 | + </table> | ||
| 22 | + <table class="no-style" style="width: 100%;"> | ||
| 23 | + <tbody> | ||
| 24 | + <tr class="unsortable"> | ||
| 25 | + <td width="34"></td> | ||
| 26 | + <td ng-click="addEmptyTag()"> | ||
| 27 | + <button type="button" class="text-button">Add another tag</button> | ||
| 28 | + </td> | ||
| 29 | + <td></td> | ||
| 30 | + </tr> | ||
| 31 | + </tbody> | ||
| 15 | </table> | 32 | </table> |
| 16 | </div> | 33 | </div> |
| 17 | <button class="button pos" type="submit">Save Tags</button> | 34 | <button class="button pos" type="submit">Save Tags</button> | ... | ... |
| ... | @@ -15,7 +15,7 @@ | ... | @@ -15,7 +15,7 @@ |
| 15 | .nav-tabs a.selected, .nav-tabs .tab-item.selected { | 15 | .nav-tabs a.selected, .nav-tabs .tab-item.selected { |
| 16 | border-bottom-color: {{ Setting::get('app-color') }}; | 16 | border-bottom-color: {{ Setting::get('app-color') }}; |
| 17 | } | 17 | } |
| 18 | - p.primary:hover, p .primary:hover, span.primary:hover, .text-primary:hover, a, a:hover, a:focus { | 18 | + p.primary:hover, p .primary:hover, span.primary:hover, .text-primary:hover, a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus { |
| 19 | color: {{ Setting::get('app-color') }}; | 19 | color: {{ Setting::get('app-color') }}; |
| 20 | } | 20 | } |
| 21 | </style> | 21 | </style> | ... | ... |
-
Please register or sign in to post a comment