Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,38 @@
composer require rapidez/fullscreen-search
```

To use the views from this package instead of the default ones, you'll need to publish the "core overwrite views" with the following command:
```
php artisan vendor:publish --provider="Rapidez\FullscreenSearch\FullscreenSearchServiceProvider" --tag=core-overwrites
```

## Configuration

You can publish the config with:
Don't forget to add this to your config:

tailwind.config.js
```
'header-autocomplete-popup': '40',
```

config/rapidez/frontend.php
```
php artisan vendor:publish --tag=rapidez-fullscreen-search-config
'autocomplete' => [
'additionals' => [
'history' => [],
'search-suggestions' => [],
'categories' => [
'defaultValues' => fn () => config('rapidez.models.category')::where('parent_id', config('rapidez.root_category_id'))
->limit(config('rapidez.frontend.autocomplete.additionals.categories.size', config('rapidez.frontend.autocomplete.size', 4)))
->get(),
],
'popular-products' => ['size' => 4],
'products' => [
'size' => 4,
],
],
'size' => 4,
],
```

## Views
Expand Down
3 changes: 0 additions & 3 deletions config/rapidez/fullscreen-search.php

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.all-results')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.categories')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.history')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.no-results')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.popular-products')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.products')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.results')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.search-suggestions')
88 changes: 88 additions & 0 deletions resources/views/layouts/partials/header/autocomplete.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<autocomplete v-slot="autocompleteSlotProps" :hits-per-page="{{ config('rapidez.frontend.autocomplete.additionals.products.size', config('rapidez.frontend.autocomplete.size', 3)) }}">
<toggler v-slot="autoCompleteToggler">
<div class="w-full">
<template v-if="autocompleteSlotProps.searchClient">
<ais-instant-search
:search-client="autocompleteSlotProps.searchClient"
:middlewares="autocompleteSlotProps.middlewares"
:index-name="config.index.product"
class="contents"
v-cloak
>
<div>
<ais-autocomplete v-slot="{ currentRefinement, refine }">
<div>
<x-rapidez::autocomplete.input
v-bind:value="currentRefinement"
v-on:focus="() => {
refine(autocompleteFacadeQuery || currentRefinement);
autocompleteFacadeQuery = null;
autoCompleteToggler.toggle(true);
window.setTimeout(() => window.requestAnimationFrame(() => window.document.getElementById('autocomplete-input-fullscreen').focus()));
}"
v-on:input="refine($event.currentTarget.value)"
list="search-history"
id="autocomplete-input"
/>
</div>
</ais-autocomplete>
<div class="fixed inset-0 bg-white group z-header-autocomplete-popup flex flex-col h-full" v-if="autoCompleteToggler.isOpen">
<div class="py-3 bg">
<div class="container">
<input checked type="checkbox" class="prevent-scroll hidden">
<ais-autocomplete v-slot="{ currentRefinement, refine }">
<div class="flex gap-x-3">
<div class="h-12 shrink-0 xl:flex-1 max-sm:hidden">
<div class="max-h-full h-full *:h-full *:w-auto *:block cursor-pointer inline-block" v-on:click="autoCompleteToggler.close(); refine('')">
@include('rapidez-fullscreen-search::layouts.partials.header.autocomplete.logo')
</div>
</div>

<div class="max-w-2xl w-full mx-auto">
<x-rapidez::autocomplete.input
v-bind:value="currentRefinement"
v-on:focus="() => {
refine(autocompleteFacadeQuery || currentRefinement);
autocompleteFacadeQuery = null;
}"
@keydown.escape="autoCompleteToggler.close()"
v-on:input="refine($event.currentTarget.value)"
id="autocomplete-input-fullscreen"
list="search-history"
/>
</div>

<div class="flex xl:flex-1 justify-end">
<button v-on:click="autoCompleteToggler.close(); refine('')" class="flex items-center justify-center shrink-0 text-muted hover:text">
<x-heroicon-o-x-mark class="size-8" />
</button>
</div>
</div>
</ais-autocomplete>
</div>
</div>

<div class="h-full bg-white overflow-hidden relative z-10">
<div class="size-full overflow-y-auto">
<div class="container pt-5 pb-8 overflow-y-auto gap-y-3 flex flex-col">
@include('rapidez::layouts.partials.header.autocomplete.results')
</div>
</div>
</div>
</div>

<ais-stats-analytics></ais-stats-analytics>
</div>
</ais-instant-search>
</template>

<div class="relative w-full" v-else>
<x-rapidez::autocomplete.input
v-model="autocompleteFacadeQuery"
v-on:focus="autoCompleteToggler.toggle(true), window.document.dispatchEvent(new window.Event('loadAutoComplete'))"
id="autocomplete-input"
/>
</div>
</div>
</toggler>
</autocomplete>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<ais-hits>
<template v-slot="{ items }">
<div v-if="items && items.length">
<x-rapidez::button.outline
type="submit"
form="autocomplete-form"
>
<span>@lang('View all products')</span>
</x-rapidez::button.outline>
</div>
</template>
</ais-hits>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<ais-index v-bind:index-name="config.index.category" v-bind:index-id="'autocomplete_' + config.index.category" class="max-w-2xl w-full mx-auto">
<div>
@if ($size = Arr::get($fields, 'size'))
<ais-configure :hits-per-page.camel="{{ $size }}" />
@endif
<ais-hits v-slot="{ items }">
<div>
<div v-if="items && items.length">
<x-rapidez::autocomplete.title class="pb-1 px-0 sm:px-5 font-normal">
@lang('Categories')
</x-rapidez::autocomplete.title>
<ul class="flex flex-col font-sans">
<li v-for="(item, count) in items" class="flex flex-1 items-center w-full hover:bg rounded">
<a v-bind:href="item.url" class="relative flex items-center group w-full sm:px-5 py-1.5 gap-x-2.5 text-sm">
<x-heroicon-o-magnifying-glass class="size-5 text-muted" />

<span>
<x-rapidez::highlight attribute="name" class="line-clamp-2 inline" />

<span class="text-muted inline" v-if="item.parents && item.parents.length">
@lang('in')
<template v-for="parent in item.parents">
<span class="lowercase"> @{{ parent }}</span>
</template>
</span>
</span>
</a>
</li>
</ul>
</div>
@if ($defaultValues = Arr::get($fields, 'defaultValues'))
<div v-else>
<x-rapidez::autocomplete.title class="pb-1 px-0 sm:px-5 font-normal">
@lang('Categories')
</x-rapidez::autocomplete.title>
<ul class="flex flex-col font-sans">
@foreach (collect(value($defaultValues))->take(Arr::get($fields, 'size', config('rapidez.frontend.autocomplete.size', 3))) as $category)
<li class="flex flex-1 items-center w-full hover:bg rounded">
<a href="{{ $category['url'] }}" class="relative flex items-center group w-full sm:px-5 py-1.5 gap-x-2.5 text-sm">
<x-heroicon-o-magnifying-glass class="size-5 text-muted" />

{{ $category['name'] }}
</a>
</li>
@endforeach
</ul>
</div>
@endif
</div>
</ais-hits>
</div>
</ais-index>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<ais-state-results v-slot="{ state: { query: searchQuery } }" class="max-w-2xl w-full mx-auto">
<div>
<div v-if="autocompleteSlotProps.searchHistory && autocompleteSlotProps.searchHistory?.filter(([query, metadata]) => query.includes(searchQuery.toLowerCase())).length">
<x-rapidez::autocomplete.title class="pb-1 px-0 sm:px-5 font-normal">
@lang('Previous Searches')
</x-rapidez::autocomplete.title>
<ul class="flex flex-col font-sans">
<li
v-for="[query, metadata] in autocompleteSlotProps.searchHistory
.filter(([query, metadata]) => query.includes(searchQuery.toLowerCase()))
.slice(0, {{ Arr::get($fields, 'size', config('rapidez.frontend.autocomplete.size', 3)) }})"
class="flex flex-1 items-center w-full hover:bg rounded"
>
<a
v-bind:href="'{{ route('search', ['q' => 'searchPlaceholder']) }}'.replace('searchPlaceholder', encodeURIComponent(query))"
class="relative flex items-center group w-full sm:px-5 py-1.5 text-sm gap-x-2.5"
data-turbo="false"
>
<x-heroicon-o-clock class="size-5 text-muted shrink-0" />
@{{ query }}
</a>
</li>
</ul>
{{-- Add search suggestions to the phone's keyboard --}}
<datalist id="search-history" v-if="window.matchMedia('(pointer:coarse)').matches">
<option v-bind:value="query" v-for="[query, metadata] in autocompleteSlotProps.searchHistory"></option>
</datalist>
</div>
</div>
</ais-state-results>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<img src="https://raw.githubusercontent.com/rapidez/art/master/r.svg" alt="Rapidez logo" height="50" width="50">
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

<ais-hits v-slot="{ items }" class="empty:hidden">
<listing
v-bind:query="() => [{
function_score: {
script_score: {
script: {
source: `(doc['reviews_score'].empty ? 0 : doc['reviews_score'].value) * (1 - 1 / Math.max(1, doc['reviews_count'].empty ? 0 : doc['reviews_count'].value))`,
},
},
},
}]"
v-if="!items.length"
v-slot="popularProducts"
v-cloak
:key="'autocomplete-popular-products'"
>
<div>
<ais-instant-search
v-if="popularProducts.searchClient"
:search-client="popularProducts.searchClient"
:index-name="popularProducts.index"
:middlewares="popularProducts.middlewares"
>
<h2 class="font-medium text-2xl mb-5">
@lang('Popular products')
</h2>
{{-- __NO_QUERY__ is a temporary band-aid for https://github.com/searchkit/searchkit/pull/1407 --}}
<ais-configure :page="0" query="__NO_QUERY__"/>

@if ($size = Arr::get($fields, 'size'))
<ais-configure :hits-per-page.camel="{{ $size }}" />
@endif

<ais-hits v-slot="{ items, sendEvent }">
<div v-if="items && items.length" class="overflow-hidden">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 -mx-5 -mb-px *:border-b">
<template v-for="(item, count) in items">
@include('rapidez::listing.partials.item')
</template>
</div>
</div>
</ais-hits>
</ais-instant-search>
</div>
</listing>
</ais-hits>

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<ais-infinite-hits v-bind:index-name="config.index.product" v-bind:index-id="'autocomplete_' + config.index.product" v-slot="{ items, sendEvent, isLastPage, refineNext }">
<div>
<div v-if="items && items.length">
<div class="mb-5 flex max-md:flex-col-reverse gap-y-3 md:items-center justify-between">
<p class="font-medium text-2xl inline-block">
<ais-state-results v-slot="{ query }">
<template v-if="query && query !== '__NO_QUERY__'">
@lang('Search for'): @{{ query }}
</template>
<template v-else>
@lang('Search')
</template>
</ais-state-results>
</p>

@include('rapidez::layouts.partials.header.autocomplete.all-results')
</div>

<div class="overflow-hidden">
<div class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 -mx-5 -mb-px *:border-b">
<template v-for="(item, count) in items">
@include('rapidez::listing.partials.item')
</template>
<div v-if="!isLastPage" v-intersection-observer="([entry]) => entry?.isIntersecting ? refineNext() : ''"></div>
</div>
<ais-state-results v-slot="{ status }" v-if="!isLastPage">
<div class="flex pt-4">
<x-rapidez::button class="mx-auto" v-bind:disabed="status !== 'idle'" v-on:click="() => status === 'idle' && refineNext()">
<span v-if="status === 'idle'">@lang('Show more')</span>
<span v-else>@lang('Loading')...</span>
</x-rapidez::button>
</div>
</ais-state-results>
</div>
</div>
</div>
</ais-infinite-hits>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="max-w-2xl w-full mx-auto sm:columns-2 *:break-inside-avoid space-y-3">
@foreach (config('rapidez.frontend.autocomplete.additionals') as $key => $fields)
@continue(in_array($key, ['popular-products', 'products']))
@includeIf('rapidez::layouts.partials.header.autocomplete.' . $key)
@endforeach
</div>

@include('rapidez::layouts.partials.header.autocomplete.popular-products')
@include('rapidez::layouts.partials.header.autocomplete.products')
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<search-suggestions v-slot="searchSuggestions" :force-results="false">
<ais-state-results v-slot="{ query }">
<ais-instant-search
v-if="searchSuggestions.searchClient"
:future="{ preserveSharedStateOnUnmount: true }"
:index-name="config.index.search_query"
:search-client="searchSuggestions.searchClient"
>
<ais-configure
:query="query || ' '"
:hits-per-page.camel="{{ Arr::get($fields, 'size', config('rapidez.frontend.autocomplete.size', 3)) }}"
filters="display_in_terms:1"
/>
<ais-hits v-slot="{ items }" class="max-w-2xl w-full mx-auto">
<div v-if="items && items.length">
<x-rapidez::autocomplete.title class="pb-1 px-0 sm:px-5 font-normal">
@lang('Suggestions')
</x-rapidez::autocomplete.title>
<ul class="flex flex-col font-sans">
<li v-for="(item, count) in items" class="flex flex-1 items-center w-full rounded hover:bg">
<a
v-bind:href="window.url(item.redirect || '{{ route('search', ['q' => 'searchPlaceholder']) }}'.replace('searchPlaceholder', encodeURIComponent(item.query_text)))"
class="relative flex items-center group w-full sm:px-5 py-1.5 text-sm gap-x-2.5"
data-turbo="false"
>
<x-heroicon-o-arrow-trending-up class="size-5 text-muted" />
<x-rapidez::highlight attribute="query_text" class="line-clamp-2 first-letter:uppercase"/>
</a>
</li>
</ul>
</div>
</ais-hits>
</ais-instant-search>
</ais-state-results>
</search-suggestions>
Loading