generated from Seekra/repository-template
Add search form submit #32 #45
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-5
@@ -31,11 +31,10 @@ import Footer from './features/footer/components/Footer.vue';
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.main-content {
|
.main-content {
|
||||||
display: flex;
|
--main-content-padding-x: 30px;
|
||||||
flex-direction: column;
|
--main-content-padding-y: 40px;
|
||||||
align-items: center;
|
padding: var(--main-content-padding-y) var(--main-content-padding-x);
|
||||||
width: 100%;
|
width: calc(100% - var(--main-content-padding-x) * 2);
|
||||||
gap: 60px;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -16,7 +16,9 @@ limitations under the License.
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav class="global-nav">
|
<nav class="global-nav">
|
||||||
<span id="logo">Seekra</span>
|
<RouterLink to="/" class="link button link">
|
||||||
|
<span id="logo">Seekra</span>
|
||||||
|
</RouterLink>
|
||||||
<ul class="right-links">
|
<ul class="right-links">
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -14,12 +14,37 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const searchQuery = defineModel();
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const props = defineProps(['autoSubmit'])
|
||||||
|
|
||||||
|
const submitSearch = function () {
|
||||||
|
if (props.autoSubmit !== undefined) {
|
||||||
|
router.push({
|
||||||
|
name: 'searchResults',
|
||||||
|
query: { q: searchQuery.value }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="search-wrapper">
|
<form @submit.prevent="submitSearch">
|
||||||
<input type="search" name="search" placeholder="Search..." required />
|
<div class="search-wrapper">
|
||||||
<button type="submit" class="search-button">Search</button>
|
<input
|
||||||
</div>
|
v-model="searchQuery"
|
||||||
</template>
|
type="search"
|
||||||
|
placeholder="Search..."
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<button type="submit" class="search-button">Search</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.search-wrapper {
|
.search-wrapper {
|
||||||
@@ -53,7 +78,7 @@ limitations under the License.
|
|||||||
border: none;
|
border: none;
|
||||||
padding: var(--submit-button-padding-y) 20px;
|
padding: var(--submit-button-padding-y) 20px;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
color: white;
|
color: var(--white);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<script setup>
|
||||||
|
import Searchbar from '@/features/search/components/Searchbar.vue';
|
||||||
|
|
||||||
|
const props = defineProps(['searchQuery']);
|
||||||
|
|
||||||
|
const searchQueryModel = defineModel();
|
||||||
|
searchQueryModel.value = props.searchQuery;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Searchbar class="search-bar" v-model="searchQueryModel" auto-submit />
|
||||||
|
|
||||||
|
<div class="search-results-error-message-container">
|
||||||
|
<div class="search-results-error-message">
|
||||||
|
<p>Search is not available right now.</p>
|
||||||
|
<p>Please try again to another time.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="search-results-container">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-bar {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 67.5rem) {
|
||||||
|
.search-bar {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-container {
|
||||||
|
margin-top: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-error-message-container {
|
||||||
|
--error-message-height: 100px;
|
||||||
|
--error-message-padding: 2em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: calc(100% - 2 * var(--main-content-padding-x));
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50vh - 0.5 * var(--error-message-height) - var(--error-message-padding));
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-error-message {
|
||||||
|
padding: var(--error-message-padding);
|
||||||
|
font-size: 18px;
|
||||||
|
box-shadow: 0px 0px 42px #cdcdcdb3;
|
||||||
|
border-radius: 28px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
animation: fade-in 0.8s ease-out;
|
||||||
|
height: var(--error-message-height);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
outline: 1px solid var(--light-d-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 48rem) {
|
||||||
|
.search-results-error-message-container {
|
||||||
|
--error-message-height: 150px;
|
||||||
|
}
|
||||||
|
.search-results-error-message {
|
||||||
|
padding: 1em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 100;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+32
-6
@@ -14,21 +14,47 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
import SearchView from '../views/SearchView.vue'
|
import SearchView from '../views/SearchView.vue';
|
||||||
|
import SearchResultsView from '@/features/search/views/SearchResultsView.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'search',
|
name: 'startPage',
|
||||||
component: SearchView
|
component: SearchView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/search',
|
||||||
|
name: 'searchResults',
|
||||||
|
component: SearchResultsView,
|
||||||
|
props: route => ({
|
||||||
|
searchQuery: route.query.q
|
||||||
|
}),
|
||||||
|
meta: {
|
||||||
|
title: (route) => route.query.q
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
routes
|
routes
|
||||||
})
|
});
|
||||||
|
|
||||||
export default router
|
// set page title
|
||||||
|
router.afterEach(to => {
|
||||||
|
const title =
|
||||||
|
typeof to.meta.title === 'function'
|
||||||
|
? to.meta.title(to)
|
||||||
|
: to.meta.title;
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
document.title = `${title} - Seekra`;
|
||||||
|
} else {
|
||||||
|
document.title = 'Seekra';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -24,3 +24,11 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover:not(.button-link) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
+37
-11
@@ -15,23 +15,43 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Searchbar from '../features/search/components/Searchbar.vue'
|
import Searchbar from '../features/search/components/Searchbar.vue';
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const searchQuery = ref('');
|
||||||
|
|
||||||
|
const submitSearch = function () {
|
||||||
|
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header class="global-header">
|
<div class="search-content">
|
||||||
<h1>Seekra</h1>
|
<header class="global-header">
|
||||||
<span class="slogan">
|
<h1>Seekra</h1>
|
||||||
Built to search.
|
<span class="slogan">
|
||||||
</span>
|
Built to search.
|
||||||
</header>
|
</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
<form id="search-form">
|
<div class="search-container">
|
||||||
<Searchbar />
|
<Searchbar v-model="searchQuery" ref="searchbar" class="search-bar" auto-submit />
|
||||||
</form>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.search-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
.global-header {
|
.global-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -61,8 +81,14 @@ import Searchbar from '../features/search/components/Searchbar.vue'
|
|||||||
font-size: small;
|
font-size: small;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-form {
|
.search-container {
|
||||||
width: 70%;
|
width: 70%;
|
||||||
max-width: 624px;
|
max-width: 624px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 67.5rem) {
|
||||||
|
.search-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user