]> BookStack Code Mirror - bookstack/commitdiff
Updated tags list to new responsive format
authorDan Brown <redacted>
Mon, 31 Oct 2022 11:40:28 +0000 (11:40 +0000)
committerDan Brown <redacted>
Mon, 31 Oct 2022 11:40:28 +0000 (11:40 +0000)
app/Actions/TagRepo.php
app/Http/Controllers/TagController.php
app/Http/Controllers/UserPreferencesController.php
resources/lang/en/entities.php
resources/sass/_blocks.scss
resources/sass/_layout.scss
resources/sass/_opacity.scss [new file with mode: 0644]
resources/sass/styles.scss
resources/views/tags/index.blade.php
resources/views/tags/parts/table-row.blade.php [deleted file]
resources/views/tags/parts/tags-list-item.blade.php [new file with mode: 0644]

index 2618ed2e902329512defd05545e355bc22c9c286..cece30de003b3a48b0d72f2d0e30b28e7dfa828e 100644 (file)
@@ -4,6 +4,7 @@ namespace BookStack\Actions;
 
 use BookStack\Auth\Permissions\PermissionApplicator;
 use BookStack\Entities\Models\Entity;
+use BookStack\Util\SimpleListOptions;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\DB;
@@ -20,8 +21,14 @@ class TagRepo
     /**
      * Start a query against all tags in the system.
      */
-    public function queryWithTotals(string $searchTerm, string $nameFilter): Builder
+    public function queryWithTotals(SimpleListOptions $listOptions, string $nameFilter): Builder
     {
+        $searchTerm = $listOptions->getSearch();
+        $sort = $listOptions->getSort();
+        if ($sort === 'name' && $nameFilter) {
+            $sort = 'value';
+        }
+
         $query = Tag::query()
             ->select([
                 'name',
@@ -32,7 +39,7 @@ class TagRepo
                 DB::raw('SUM(IF(entity_type = \'book\', 1, 0)) as book_count'),
                 DB::raw('SUM(IF(entity_type = \'bookshelf\', 1, 0)) as shelf_count'),
             ])
-            ->orderBy($nameFilter ? 'value' : 'name');
+            ->orderBy($sort, $listOptions->getOrder());
 
         if ($nameFilter) {
             $query->where('name', '=', $nameFilter);
index 056cc9902d564f4204899a08e0b4b7af3bd8725c..a221437ddd5333b24e5692c621d721e284582e18 100644 (file)
@@ -3,6 +3,7 @@
 namespace BookStack\Http\Controllers;
 
 use BookStack\Actions\TagRepo;
+use BookStack\Util\SimpleListOptions;
 use Illuminate\Http\Request;
 
 class TagController extends Controller
@@ -19,22 +20,26 @@ class TagController extends Controller
      */
     public function index(Request $request)
     {
-        $search = $request->get('search', '');
+        $listOptions = SimpleListOptions::fromRequest($request, 'tags')->withSortOptions([
+            'name' => trans('common.sort_name'),
+            'usages' => trans('entities.tags_usages'),
+        ]);
+
         $nameFilter = $request->get('name', '');
         $tags = $this->tagRepo
-            ->queryWithTotals($search, $nameFilter)
+            ->queryWithTotals($listOptions, $nameFilter)
             ->paginate(50)
             ->appends(array_filter([
-                'search' => $search,
+                ...$listOptions->getPaginationAppends(),
                 'name'   => $nameFilter,
             ]));
 
         $this->setPageTitle(trans('entities.tags'));
 
         return view('tags.index', [
-            'tags'       => $tags,
-            'search'     => $search,
-            'nameFilter' => $nameFilter,
+            'tags'        => $tags,
+            'nameFilter'  => $nameFilter,
+            'listOptions' => $listOptions,
         ]);
     }
 
index 8e9160810eedfddbe245b944a3be57160d60867f..ca77dcd0b49c44cb6a99d61e33eaedb2f719ca3c 100644 (file)
@@ -62,7 +62,7 @@ class UserPreferencesController extends Controller
      */
     public function changeSort(Request $request, string $id, string $type)
     {
-        $validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles', 'webhooks'];
+        $validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles', 'webhooks', 'tags'];
         if (!in_array($type, $validSortTypes)) {
             return redirect()->back(500);
         }
index bf6201900fc3653724e85d715411695325b6fd0a..b3dfb0bf7620f56efe9282ff7c0fe240c5919cd3 100644 (file)
@@ -275,6 +275,7 @@ return [
     'shelf_tags' => 'Shelf Tags',
     'tag' => 'Tag',
     'tags' =>  'Tags',
+    'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.',
     'tag_name' =>  'Tag Name',
     'tag_value' => 'Tag Value (Optional)',
     'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
index 0398224ca52a406ee29d7ae160fa63a00caae4b4..6058add828e2eccef4d46ca8465439a78277c5db 100644 (file)
   margin-bottom: 0;
 }
 
-td .tag-item {
+.item-list-row .tag-item {
   margin-bottom: 0;
 }
 
-/**
- * Pill boxes
- */
-
-.pill {
-  display: inline-block;
-  border: 1px solid currentColor;
-  padding: .2em .8em;
-  font-size: 0.8em;
-  border-radius: 1rem;
-  position: relative;
-  overflow: hidden;
-  line-height: 1.4;
-  &:before {
-    content: '';
-    background-color: currentColor;
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    opacity: 0.1;
-  }
-}
-
 /**
  * API Docs
  */
index 51389dc691acf679e3892244a8c8cef2705c7c6a..105b6a16ffc20fb6c4c05c2a990d5e94ed1aff1f 100644 (file)
@@ -160,6 +160,11 @@ body.flexbox {
     flex-basis: auto;
     flex-grow: 0;
   }
+  &.fill-area {
+    flex-grow: 1;
+    flex-shrink: 0;
+    min-width: fit-content;
+  }
 }
 
 .flex-2 {
diff --git a/resources/sass/_opacity.scss b/resources/sass/_opacity.scss
new file mode 100644 (file)
index 0000000..235aed4
--- /dev/null
@@ -0,0 +1,28 @@
+
+.opacity-10 {
+  opacity: 0.1;
+}
+.opacity-20 {
+  opacity: 0.2;
+}
+.opacity-30 {
+  opacity: 0.3;
+}
+.opacity-40 {
+  opacity: 0.4;
+}
+.opacity-50 {
+  opacity: 0.5;
+}
+.opacity-60 {
+  opacity: 0.6;
+}
+.opacity-70 {
+  opacity: 0.7;
+}
+.opacity-80 {
+  opacity: 0.8;
+}
+.opacity-90 {
+  opacity: 0.9;
+}
\ No newline at end of file
index 44d0055b581d55ea2a8be119ab6aa11ec88b31be..5e31dbdfb577016667bdb15c901f3d11c33a8325 100644 (file)
@@ -4,6 +4,7 @@
 @import "variables";
 @import "mixins";
 @import "spacing";
+@import "opacity";
 @import "html";
 @import "text";
 @import "colors";
index c88449ce7ad1e9492bb4b303cc683bfbe76f0d75..b6b3325e0516a15d3c9ab6aabd4ebad5afcdd739 100644 (file)
@@ -5,25 +5,28 @@
 
         <main class="card content-wrap mt-xxl">
 
-            <div class="flex-container-row wrap justify-space-between items-center mb-s">
-                <h1 class="list-heading">{{ trans('entities.tags') }}</h1>
-
-                <div>
-                    <div class="block inline mr-xs">
-                        <form method="get" action="{{ url("/tags") }}">
-                            @include('form.request-query-inputs', ['params' => ['name']])
-                            <input type="text"
-                                   name="search"
-                                   placeholder="{{ trans('common.search') }}"
-                                   value="{{ $search }}">
-                        </form>
-                    </div>
+            <h1 class="list-heading">{{ trans('entities.tags') }}</h1>
+
+            <p class="text-muted">{{ trans('entities.tags_index_desc') }}</p>
+
+            <div class="flex-container-row wrap justify-space-between items-center mb-s gap-m">
+                <div class="block inline mr-xs">
+                    <form method="get" action="{{ url("/tags") }}">
+                        @include('form.request-query-inputs', ['params' => ['name']])
+                        <input type="text"
+                               name="search"
+                               placeholder="{{ trans('common.search') }}"
+                               value="{{ $listOptions->getSearch() }}">
+                    </form>
+                </div>
+                <div class="block inline">
+                    @include('common.sort', $listOptions->getSortControlData())
                 </div>
             </div>
 
             @if($nameFilter)
-                <div class="mb-m">
-                    <span class="mr-xs">{{ trans('common.filter_active') }}</span>
+                <div class="my-m">
+                    <strong class="mr-xs">{{ trans('common.filter_active') }}</strong>
                     @include('entities.tag', ['tag' => new \BookStack\Actions\Tag(['name' => $nameFilter])])
                     <form method="get" action="{{ url("/tags") }}" class="inline block">
                         @include('form.request-query-inputs', ['params' => ['search']])
             @endif
 
             @if(count($tags) > 0)
-                <table class="table expand-to-padding mt-m">
+                <div class="item-list mt-m">
                     @foreach($tags as $tag)
-                        @include('tags.parts.table-row', ['tag' => $tag, 'nameFilter' => $nameFilter])
+                        @include('tags.parts.tags-list-item', ['tag' => $tag, 'nameFilter' => $nameFilter])
                     @endforeach
-                </table>
+                </div>
 
-                <div>
+                <div class="my-m">
                     {{ $tags->links() }}
                 </div>
             @else
diff --git a/resources/views/tags/parts/table-row.blade.php b/resources/views/tags/parts/table-row.blade.php
deleted file mode 100644 (file)
index aa04959..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<tr>
-    <td>
-        <span class="text-bigger mr-xl">@include('entities.tag', ['tag' => $tag])</span>
-    </td>
-    <td width="70" class="px-xs">
-        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() }}"
-           title="{{ trans('entities.tags_usages') }}"
-           class="pill text-muted">@icon('leaderboard'){{ $tag->usages }}</a>
-    </td>
-    <td width="70" class="px-xs">
-        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:page}' }}"
-           title="{{ trans('entities.tags_assigned_pages') }}"
-           class="pill text-page">@icon('page'){{ $tag->page_count }}</a>
-    </td>
-    <td width="70" class="px-xs">
-        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:chapter}' }}"
-           title="{{ trans('entities.tags_assigned_chapters') }}"
-           class="pill text-chapter">@icon('chapter'){{ $tag->chapter_count }}</a>
-    </td>
-    <td width="70" class="px-xs">
-        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:book}' }}"
-           title="{{ trans('entities.tags_assigned_books') }}"
-           class="pill text-book">@icon('book'){{ $tag->book_count }}</a>
-    </td>
-    <td width="70" class="px-xs">
-        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:bookshelf}' }}"
-           title="{{ trans('entities.tags_assigned_shelves') }}"
-           class="pill text-bookshelf">@icon('bookshelf'){{ $tag->shelf_count }}</a>
-    </td>
-    <td class="text-right text-muted">
-        @if($tag->values ?? false)
-            <a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_x_unique_values', ['count' => $tag->values]) }}</a>
-        @elseif(empty($nameFilter))
-            <a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_all_values') }}</a>
-        @endif
-    </td>
-</tr>
\ No newline at end of file
diff --git a/resources/views/tags/parts/tags-list-item.blade.php b/resources/views/tags/parts/tags-list-item.blade.php
new file mode 100644 (file)
index 0000000..3962db7
--- /dev/null
@@ -0,0 +1,31 @@
+<div class="item-list-row flex-container-row items-center wrap">
+    <div class="{{ isset($nameFilter) && $tag->value ? 'flex-2' : 'flex' }} py-s px-m min-width-m">
+        <span class="text-bigger mr-xl">@include('entities.tag', ['tag' => $tag])</span>
+    </div>
+    <div class="flex-2 flex-container-row justify-center items-center gap-m py-s px-m min-width-l wrap">
+        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() }}"
+           title="{{ trans('entities.tags_usages') }}"
+           class="flex fill-area min-width-xxs bold text-right text-muted"><span class="opacity-60">@icon('leaderboard')</span>{{ $tag->usages }}</a>
+        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:page}' }}"
+           title="{{ trans('entities.tags_assigned_pages') }}"
+           class="flex fill-area min-width-xxs bold text-right text-page"><span class="opacity-60">@icon('page')</span>{{ $tag->page_count }}</a>
+        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:chapter}' }}"
+           title="{{ trans('entities.tags_assigned_chapters') }}"
+           class="flex fill-area min-width-xxs bold text-right text-chapter"><span class="opacity-60">@icon('chapter')</span>{{ $tag->chapter_count }}</a>
+        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:book}' }}"
+           title="{{ trans('entities.tags_assigned_books') }}"
+           class="flex fill-area min-width-xxs bold text-right text-book"><span class="opacity-60">@icon('book')</span>{{ $tag->book_count }}</a>
+        <a href="{{ isset($tag->value) ? $tag->valueUrl() : $tag->nameUrl() . '+{type:bookshelf}' }}"
+           title="{{ trans('entities.tags_assigned_shelves') }}"
+           class="flex fill-area min-width-xxs bold text-right text-bookshelf"><span class="opacity-60">@icon('bookshelf')</span>{{ $tag->shelf_count }}</a>
+    </div>
+    @if($tag->values ?? false)
+        <div class="flex text-s-right text-muted py-s px-m min-width-s">
+            <a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_x_unique_values', ['count' => $tag->values]) }}</a>
+        </div>
+    @elseif(empty($nameFilter))
+        <div class="flex text-s-right text-muted py-s px-m min-width-s">
+            <a href="{{ url('/tags?name=' . urlencode($tag->name)) }}">{{ trans('entities.tags_all_values') }}</a>
+        </div>
+    @endif
+</div>
\ No newline at end of file