Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The search box would be useful even on a small screen. #5376

Open
1 task done
toras9000 opened this issue Dec 30, 2024 · 4 comments
Open
1 task done

The search box would be useful even on a small screen. #5376

toras9000 opened this issue Dec 30, 2024 · 4 comments

Comments

@toras9000
Copy link

Describe the feature you'd like

The search box in the header bar of the BookStack screen is useful and I use it frequently.
However, as the browser window gets smaller, this search box disappears relatively early and is stored in a drop-down menu.
To refer to a more specific situation, if the display resolution is 1920x1080, the search box disappears from the header bar when the width of the browser window is set to 960, half of the screen width.
For example, Windows has the ability to snap a window to half of the screen area, and I believe that there are a reasonable number of uses for placing a window on half of the screen area in other environments as well.

We think it would be more convenient to have the search box displayed even when the window is a little narrower.

Describe the benefits this would bring to existing BookStack users

The immediate availability of a search box facilitates access to articles.

Can the goal of this request already be achieved via other means?

Perhaps it is possible to customize it through a visual theme system.
However, it was not so easy as far as I myself tried.
Simply rewriting the CSS values will result in a broken layout and multiple lines.
It seems to me that if you use that method, you would probably need to customize the entire header bar.

Have you searched for an existing open/closed issue?

  • I have searched for existing issues and none cover my fundamental request

How long have you been using BookStack?

1 to 5 years

Additional context

No response

@DiscordDigital
Copy link

I can confirm that I've noticed this before while using BookStack, there's definitely enough space to fit a search bar into the "mobile view", which could enhance the accessibility, I'm the type of user that jumps across the platform using the search

@DiscordDigital
Copy link

DiscordDigital commented Dec 30, 2024

I wrote a codeblock to achieve something like this just now, this goes into the "Custom HTML Head Content" in the Customization settings:

<style>
    @media screen and (max-width: 1000px) {
        .mobileNavSearch {
            position: absolute;
            left: 50%;
            transform: translate(-50%, 14px);
        }

        .mobileNavSearchWithLogo {
            transform: translate(-50%, 17px);
        }

        .logo-text {
            display: none;
        }
    }

    @media screen and (max-width: 470px) {
        .mobileNavSearch #header-search-box-input {
            width: 240px;
        }
    }

    @media screen and (max-width: 420px) {
        .mobileNavSearch #header-search-box-input {
            width: 200px;
        }
    }
</style>
<script>
    const waitFor = (search, callback, timeout = 2000, options = { childList: true, subtree: true }) => {
        if (typeof search !== 'string' || typeof callback !== 'function') {
            throw new Error('Invalid parameters. Expected string for search and function for callback.');
        }

        const observerAction = (observer, addedNode, observerTimeout) => {
            callback(addedNode);
            observer.disconnect();
            clearTimeout(observerTimeout);
        };

        const observer = new MutationObserver(function (mutationsList, observer) {
            mutationsList.forEach(function (mutation) {
                if (mutation.type === 'childList') {
                    mutation.addedNodes.forEach(function (addedNode) {
                        if (search.startsWith(".")) {
                            if (addedNode.classList && addedNode.classList.contains(search.substring(1))) {
                                observerAction(observer, addedNode, observerTimeout);
                            }
                        } else if (search.startsWith("#")) {
                            if (addedNode.id && addedNode.id === search.substring(1)) {
                                observerAction(observer, addedNode, observerTimeout);
                            }
                        } else {
                            if (addedNode.innerText && addedNode.innerText == search) {
                                observerAction(observer, addedNode, observerTimeout);
                            }
                        }
                    });
                }
            });
        });

        const observerTimeout = setTimeout(() => {
            observer.disconnect();
        }, timeout);

        observer.observe(document.documentElement, options);
    }

    function mobileSearchBar() {
        const normalNav = document.getElementsByClassName("search-box")[0].parentElement;
        normalNav.classList.add("mobileNavSearch");
        normalNav.classList.remove("hide-under-l");

        if (document.getElementsByClassName("logo-image")[0]) {
            if (!document.getElementsByClassName("logo-image")[0].classList.contains("none")) {
                normalNav.classList.add("mobileNavSearchWithLogo");
            }
        }
    }

    waitFor(".mobile-menu-toggle", mobileSearchBar);
</script>

Please keep in mind that this will also remove the logo text on the mobile header when enabled, so there's enough space for the search bar.

@ssddanbrown
Copy link
Member

I wouldn't be opposed to making the responsive breakdown a bit more nuanced, so instead of going from:
[logo]--[search]--[buttons] >> [logo]----[mobile-menu]

we have another stage in the middle:

[logo]--[search]--[buttons] >> [logo]--[search]--[mobile-menu] >> [logo]----[mobile-menu]

@whoamiafterall
Copy link

whoamiafterall commented Jan 6, 2025

Preview on wiki.aktivismus.org

We solved the same problem in all-blade.php (through the theme system)

Code

 @extends('layouts.simple')

 @section('body')
    <div class="container mt-xl" id="search-system">

        <div class="grid right-focus reverse-collapse gap-xl">
            <div>
                <div>
                    <h5>{{ trans('entities.search_advanced') }}</h5>

                    <form method="get" action="{{ url('/search') }}">
                        <h6>{{ trans('entities.search_terms') }}</h6>
                        <input type="text" name="search" value="{{ implode(' ', $options->searches) }}">

                        <h6>{{ trans('entities.search_content_type') }}</h6>
                        <div class="form-group">

                            <?php
                            $types = explode('|', $options->filters['type'] ?? '');
                            $hasTypes = $types[0] !== '';
                            ?>
                            @include('search.parts.type-filter', ['checked' => !$hasTypes || in_array('page', $types), 'entity' => 'page', 'transKey' => 'page'])
                            @include('search.parts.type-filter', ['checked' => !$hasTypes || in_array('chapter', $types), 'entity' => 'chapter', 'transKey' => 'chapter'])
                            <br>
                                @include('search.parts.type-filter', ['checked' => !$hasTypes || in_array('book', $types), 'entity' => 'book', 'transKey' => 'book'])
                                @include('search.parts.type-filter', ['checked' => !$hasTypes || in_array('bookshelf', $types), 'entity' => 'bookshelf', 'transKey' => 'shelf'])
                        </div>

                        <h6>{{ trans('entities.search_exact_matches') }}</h6>
                        @include('search.parts.term-list', ['type' => 'exact', 'currentList' => $options->exacts])

                        <h6>{{ trans('entities.search_tags') }}</h6>
                        @include('search.parts.term-list', ['type' => 'tags', 'currentList' => $options->tags])

                        @if(!user()->isGuest())
                            <h6>{{ trans('entities.search_options') }}</h6>

                            @component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'viewed_by_me', 'value' => null])
                                {{ trans('entities.search_viewed_by_me') }}
                            @endcomponent
                            @component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'not_viewed_by_me', 'value' => null])
                                {{ trans('entities.search_not_viewed_by_me') }}
                            @endcomponent
                            @component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'is_restricted', 'value' => null])
                                {{ trans('entities.search_permissions_set') }}
                            @endcomponent
                            @component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'created_by', 'value' => 'me'])
                                {{ trans('entities.search_created_by_me') }}
                            @endcomponent
                            @component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'updated_by', 'value' => 'me'])
                                {{ trans('entities.search_updated_by_me') }}
                            @endcomponent
                            @component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'owned_by', 'value' => 'me'])
                                {{ trans('entities.search_owned_by_me') }}
                            @endcomponent
                        @endif

                        <h6>{{ trans('entities.search_date_options') }}</h6>
                        @include('search.parts.date-filter', ['name' => 'updated_after', 'filters' => $options->filters])
                        @include('search.parts.date-filter', ['name' => 'updated_before', 'filters' => $options->filters])
                        @include('search.parts.date-filter', ['name' => 'created_after', 'filters' => $options->filters])
                        @include('search.parts.date-filter', ['name' => 'created_before', 'filters' => $options->filters])

                        @if(isset($options->filters['created_by']) && $options->filters['created_by'] !== "me")
                            <input type="hidden" name="filters[created_by]" value="{{ $options->filters['created_by'] }}">
                        @endif
                        @if(isset($options->filters['updated_by']) && $options->filters['updated_by'] !== "me")
                            <input type="hidden" name="filters[updated_by]" value="{{ $options->filters['updated_by'] }}">
                        @endif

                        <button type="submit" class="button">{{ trans('entities.search_update') }}</button>
                    </form>

                </div>
            </div>
            <div>
                <div class="card content-wrap">
                    <h1 class="list-heading">{{ trans('entities.search_results') }}</h1>

                    <form action="{{ url('/search') }}" method="GET"  class="search-box flexible hide-over-l">
                        <input value="{{$searchTerm}}" type="text" name="term" placeholder="{{ trans('common.search') }}">
                        <button tabindex="-1" type="submit">@icon('search')</button>
                    </form>

                    <h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
                    <div class="book-contents">
                        @include('entities.list', ['entities' => $entities, 'showPath' => true, 'showTags' => true])
                    </div>

                    @if($hasNextPage)
                        <div class="text-right mt-m">
                            <a href="{{ $nextPageLink }}" class="button outline">{{ trans('entities.search_more') }}</a>
                        </div>
                    @endif
                </div>
            </div>
        </div>

    </div>
 @stop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants