diff --git a/dev/docs/components.md b/dev/docs/components.md index 2f59ae344..ac0e929cd 100644 --- a/dev/docs/components.md +++ b/dev/docs/components.md @@ -1,16 +1,22 @@ # JavaScript Components -This document details the format for JavaScript components in BookStack. +This document details the format for JavaScript components in BookStack. This is a really simple class-based setup with a few helpers provided. #### Defining a Component in JS ```js class Dropdown { setup() { + this.toggle = this.$refs.toggle; + this.menu = this.$refs.menu; + + this.speed = parseInt(this.$opts.speed); } } ``` +All usage of $refs, $manyRefs and $opts should be done at the top of the `setup` function so any requirements can be easily seen. + #### Using a Component in HTML A component is used like so: diff --git a/resources/js/components/entity-search.js b/resources/js/components/entity-search.js new file mode 100644 index 000000000..8b1861ecf --- /dev/null +++ b/resources/js/components/entity-search.js @@ -0,0 +1,59 @@ +import {onSelect} from "../services/dom"; + +/** + * Class EntitySearch + * @extends {Component} + */ +class EntitySearch { + setup() { + this.entityId = this.$opts.entityId; + this.entityType = this.$opts.entityType; + + this.contentView = this.$refs.contentView; + this.searchView = this.$refs.searchView; + this.searchResults = this.$refs.searchResults; + this.searchInput = this.$refs.searchInput; + this.searchForm = this.$refs.searchForm; + this.clearButton = this.$refs.clearButton; + this.loadingBlock = this.$refs.loadingBlock; + + this.setupListeners(); + } + + setupListeners() { + this.searchInput.addEventListener('change', this.runSearch.bind(this)); + this.searchForm.addEventListener('submit', e => { + e.preventDefault(); + this.runSearch(); + }); + + onSelect(this.clearButton, this.clearSearch.bind(this)); + } + + runSearch() { + const term = this.searchInput.value.trim(); + if (term.length === 0) { + return this.clearSearch(); + } + + this.searchView.classList.remove('hidden'); + this.contentView.classList.add('hidden'); + this.loadingBlock.classList.remove('hidden'); + + const url = window.baseUrl(`/search/${this.entityType}/${this.entityId}`); + window.$http.get(url, {term}).then(resp => { + this.searchResults.innerHTML = resp.data; + }).catch(console.error).then(() => { + this.loadingBlock.classList.add('hidden'); + }); + } + + clearSearch() { + this.searchView.classList.add('hidden'); + this.contentView.classList.remove('hidden'); + this.loadingBlock.classList.add('hidden'); + this.searchInput.value = ''; + } +} + +export default EntitySearch; \ No newline at end of file diff --git a/resources/js/vues/entity-dashboard.js b/resources/js/vues/entity-dashboard.js deleted file mode 100644 index d10da7000..000000000 --- a/resources/js/vues/entity-dashboard.js +++ /dev/null @@ -1,44 +0,0 @@ -let data = { - id: null, - type: '', - searching: false, - searchTerm: '', - searchResults: '', -}; - -let computed = { - -}; - -let methods = { - - searchBook() { - if (this.searchTerm.trim().length === 0) return; - this.searching = true; - this.searchResults = ''; - let url = window.baseUrl(`/search/${this.type}/${this.id}`); - url += `?term=${encodeURIComponent(this.searchTerm)}`; - this.$http.get(url).then(resp => { - this.searchResults = resp.data; - }); - }, - - checkSearchForm() { - this.searching = this.searchTerm > 0; - }, - - clearSearch() { - this.searching = false; - this.searchTerm = ''; - } - -}; - -function mounted() { - this.id = Number(this.$el.getAttribute('entity-id')); - this.type = this.$el.getAttribute('entity-type'); -} - -export default { - data, computed, methods, mounted -}; \ No newline at end of file diff --git a/resources/js/vues/vues.js b/resources/js/vues/vues.js index a53adf53f..f18724929 100644 --- a/resources/js/vues/vues.js +++ b/resources/js/vues/vues.js @@ -4,14 +4,12 @@ function exists(id) { return document.getElementById(id) !== null; } -import entityDashboard from "./entity-dashboard"; import imageManager from "./image-manager"; import tagManager from "./tag-manager"; import attachmentManager from "./attachment-manager"; import pageEditor from "./page-editor"; let vueMapping = { - 'entity-dashboard': entityDashboard, 'image-manager': imageManager, 'tag-manager': tagManager, 'attachment-manager': attachmentManager, diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index e3a536fc9..def198bdd 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -1,9 +1,9 @@ @extends('tri-layout') @section('container-attrs') - id="entity-dashboard" - entity-id="{{ $book->id }}" - entity-type="book" + component="entity-search" + option:entity-search:entity-id="{{ $book->id }}" + option:entity-search:entity-type="book" @stop @section('body') @@ -15,11 +15,11 @@
-

{{$book->name}}

-
-

{!! nl2br(e($book->description)) !!}

+

{{$book->name}}

+
+

{!! nl2br(e($book->description)) !!}

@if(count($bookChildren) > 0) -
+
@foreach($bookChildren as $childElement) @if($childElement->isA('chapter')) @include('chapters.list-item', ['chapter' => $childElement]) @@ -29,7 +29,7 @@ @endforeach
@else -
+

{{ trans('entities.books_empty_contents') }}

@@ -52,7 +52,7 @@ @endif
- @include('partials.entity-dashboard-search-results') + @include('partials.entity-search-results')
@stop @@ -126,7 +126,7 @@ @section('left') - @include('partials.entity-dashboard-search-box') + @include('partials.entity-search-form', ['label' => trans('entities.books_search_this')]) @if($book->tags->count() > 0)
diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php index 105cda760..db02ebcc4 100644 --- a/resources/views/chapters/show.blade.php +++ b/resources/views/chapters/show.blade.php @@ -1,9 +1,9 @@ @extends('tri-layout') @section('container-attrs') - id="entity-dashboard" - entity-id="{{ $chapter->id }}" - entity-type="chapter" + component="entity-search" + option:entity-search:entity-id="{{ $chapter->id }}" + option:entity-search:entity-type="chapter" @stop @section('body') @@ -16,17 +16,17 @@
-

{{ $chapter->name }}

-
-

{!! nl2br(e($chapter->description)) !!}

+

{{ $chapter->name }}

+
+

{!! nl2br(e($chapter->description)) !!}

@if(count($pages) > 0) -
+
@foreach($pages as $page) @include('pages.list-item', ['page' => $page]) @endforeach
@else -
+

{{ trans('entities.chapters_empty') }}

@@ -49,7 +49,7 @@ @endif
- @include('partials.entity-dashboard-search-results') + @include('partials.entity-search-results')
@stop @@ -130,7 +130,7 @@ @section('left') - @include('partials.entity-dashboard-search-box') + @include('partials.entity-search-form', ['label' => trans('entities.chapters_search_this')]) @if($chapter->tags->count() > 0)
diff --git a/resources/views/partials/entity-dashboard-search-box.blade.php b/resources/views/partials/entity-dashboard-search-box.blade.php deleted file mode 100644 index 2e0395253..000000000 --- a/resources/views/partials/entity-dashboard-search-box.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -
- -
\ No newline at end of file diff --git a/resources/views/partials/entity-dashboard-search-results.blade.php b/resources/views/partials/entity-dashboard-search-results.blade.php deleted file mode 100644 index 68c6f53ad..000000000 --- a/resources/views/partials/entity-dashboard-search-results.blade.php +++ /dev/null @@ -1,15 +0,0 @@ -
-
-

- {{ trans('entities.search_results') }} -

- -
- -
- @include('partials.loading-icon') -
-
-
\ No newline at end of file diff --git a/resources/views/partials/entity-search-form.blade.php b/resources/views/partials/entity-search-form.blade.php new file mode 100644 index 000000000..b65d5df94 --- /dev/null +++ b/resources/views/partials/entity-search-form.blade.php @@ -0,0 +1,10 @@ +{{-- +@label - Placeholder/aria-label text +--}} +
+ +
\ No newline at end of file diff --git a/resources/views/partials/entity-search-results.blade.php b/resources/views/partials/entity-search-results.blade.php new file mode 100644 index 000000000..74619831a --- /dev/null +++ b/resources/views/partials/entity-search-results.blade.php @@ -0,0 +1,15 @@ + \ No newline at end of file