49 Commits

Author SHA1 Message Date
jakob.scheid 7bf44ca720 feat(language-switch-in-footer): show the dropdown menu above instead of below the button 2026-06-01 18:50:14 +02:00
jakob.scheid 53c7cb1c79 feat(language-switch-in-footer): use CSS variable for the language switch button vertical padding 2026-06-01 18:50:14 +02:00
jakob.scheid 368747f323 feat(language-switch-in-footer): add footer segment flexbox gap 2026-06-01 18:50:14 +02:00
jakob.scheid a46a4da005 feat(language-switch-in-footer): make footer segment class flexbox 2026-06-01 18:50:14 +02:00
jakob.scheid 5e15c01ed6 feat(language-switch-in-footer): add language switch button component to the footer 2026-06-01 18:50:14 +02:00
jakob.scheid c64539539d feat(language-switch-in-footer): remove language switch button component from the navbar 2026-06-01 18:50:14 +02:00
jakob.scheid fea440edaa Merge pull request 'Show search bar below the navbar on the search results view on small screens' (#99) from feature/place-search-bar-below-navbar into main
Deploy on dev / Deploy on dev (push) Successful in 35s
Reviewed-on: #99
Reviewed-by: Johannes D. Vos
2026-06-01 18:49:58 +02:00
jakob.scheid 47744e7be6 feat(place-search-bar-below-navbar): add horizontal padding to the search bar wrapper in the navbar (on large screens) 2026-06-01 17:49:27 +02:00
jakob.scheid 6a31373b7d feat(place-search-bar-below-navbar): set width of the search bar wrapper in the navbar (on large screens) 2026-06-01 17:48:50 +02:00
jakob.scheid a824ee57df feat(place-search-bar-below-navbar): add padding to the search bar wrapper below the navbar (on small screens) 2026-06-01 17:45:20 +02:00
jakob.scheid 9ed8790649 feat(place-search-bar-below-navbar): show search bar wrapper component below the navbar on small screens 2026-06-01 17:44:06 +02:00
jakob.scheid dd678fb393 set search form width to 100% 2026-06-01 17:39:11 +02:00
jakob.scheid 17570d400d feat(place-search-bar-below-navbar): hide navbar search bar wrapper component on small screens 2026-06-01 15:26:47 +02:00
jakob.scheid cabebfb9fe feat(place-search-bar-below-navbar): use navbar search bar wrapper component in the navbar 2026-06-01 15:23:57 +02:00
jakob.scheid 9b175e3923 feat(place-search-bar-below-navbar): add search bar model to the navbar search bar wrapper component 2026-06-01 15:23:35 +02:00
jakob.scheid 873bd74804 feat(place-search-bar-below-navbar): show search bar in the navbar search bar wrapper component only when the search results view is active 2026-06-01 15:20:30 +02:00
jakob.scheid 0df77ef6ee feat(place-search-bar-below-navbar): integrate search bar in the navbar search bar wrapper component 2026-06-01 15:17:32 +02:00
jakob.scheid 4daf550568 feat(place-search-bar-below-navbar): add empty navbar search bar wrapper component 2026-06-01 15:16:09 +02:00
jakob.scheid f7a76d5d20 Merge pull request 'Make the search bar on the start page fully wide on small screens' (#94) from feature/fully-wide-search-bar-on-startpage-on-small-screens into main
Deploy on dev / Deploy on dev (push) Successful in 35s
Reviewed-on: #94
Reviewed-by: Johannes D. Vos
2026-06-01 15:00:41 +02:00
jakob.scheid 34af4c874f add ARIA label to the search submit button 2026-06-01 14:34:11 +02:00
jakob.scheid c98e8d1f96 add title to the search submit button 2026-06-01 14:33:55 +02:00
jakob.scheid a2268ea275 adapt the color of the magnifying glass search icon to the color scheme 2026-06-01 14:23:05 +02:00
jakob.scheid 720fbd5697 support old browsers 2026-06-01 14:15:30 +02:00
jakob.scheid ba754771f4 Center magnifying glass icon on the search submit button 2026-06-01 13:45:53 +02:00
jakob.scheid b0bf852e26 replace 'search' text on the search submit button with a magnifying glass icon 2026-06-01 13:44:03 +02:00
jakob.scheid 83351110b3 add magnifying glass icon 2026-06-01 13:35:48 +02:00
jakob.scheid a30703dc3e feat(fully-wide-search-bar): Reduce main content horizontal padding on small screens 2026-06-01 13:29:36 +02:00
jakob.scheid f480794817 Merge pull request 'Use icons instead of Unicode characters' (#91) from feature/icons into main
Deploy on dev / Deploy on dev (push) Successful in 33s
Reviewed-on: #91
Reviewed-by: Johannes D. Vos
2026-06-01 12:28:35 +02:00
jakob.scheid 0e9e0ecec7 feat(icons): use icon at the language switching button 2026-06-01 12:28:20 +02:00
jakob.scheid 54090751a5 feat(icons): use icons at the color scheme button 2026-06-01 12:28:20 +02:00
jakob.scheid 274d25d654 feat(icons): Invert icons according to the color scheme 2026-06-01 12:28:20 +02:00
jakob.scheid 35db1d65f2 Add --invert CSS variable for the invertion (according to the color scheme) 2026-06-01 12:28:20 +02:00
jakob.scheid 20e350d88b Add icon component <img> flexbox wrapper 2026-06-01 12:28:20 +02:00
jakob.scheid 534734ce9a Ensure unit of the size prop in the icon component 2026-06-01 12:28:20 +02:00
jakob.scheid 1679800272 Add utility function to ensure that a CSS dimension has a unit 2026-06-01 12:28:20 +02:00
jakob.scheid bf3786249b Add icon component prop to specify the size of the icon 2026-06-01 12:28:20 +02:00
jakob.scheid f24749423b Add icon component 2026-06-01 12:28:20 +02:00
jakob.scheid b617078e58 Add black and white circle icon 2026-06-01 12:28:20 +02:00
jakob.scheid c5467620c5 Add sun icon 2026-06-01 12:28:20 +02:00
jakob.scheid fc4584f932 Add crescent moon icon 2026-06-01 12:28:20 +02:00
jakob.scheid da319c28d5 Add chevron down icon 2026-06-01 12:28:20 +02:00
jakob.scheid 6f2aaa62f0 Merge pull request 'Add changelog' (#92) from docs/changelog into main
Deploy on dev / Deploy on dev (push) Successful in 35s
Reviewed-on: #92
Reviewed-by: Johannes D. Vos
2026-06-01 12:28:05 +02:00
jakob.scheid 8f65d3ae60 docs(changelog): add 'Added' section to the unreleased release 2026-06-01 12:27:51 +02:00
jakob.scheid f6d848a714 docs(changelog): add changelog boilerplate 2026-06-01 12:27:51 +02:00
jakob.scheid 2340a6a193 Merge pull request 'Set up Vitest' (#93) from testing/set-up-vitest into main
Deploy on dev / Deploy on dev (push) Successful in 35s
Reviewed-on: #93
Reviewed-by: Jakob Gregory
2026-06-01 12:26:52 +02:00
jakob.scheid 0dcb6f0821 chore(set-up-vitest): Add testing configuration 2026-06-01 01:01:38 +02:00
jakob.scheid f0dc5d4bdc chore(set-up-vitest): add jsdom dependency 2026-06-01 00:58:14 +02:00
jakob.scheid 59e19c7666 chore(set-up-vitest): add test scripts 2026-06-01 00:55:02 +02:00
jakob.scheid 0373ea20f7 chore(set-up-vitest): add Vitest dependency 2026-06-01 00:53:31 +02:00
20 changed files with 2339 additions and 51 deletions
+1071 -2
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
# Changelog
## [Unreleased]
### Added
- Start page
- Settings page
- Footer
- Navbar
- Searchbar
- Icons
- Internationalization and some major languages
- Color scheme
- Search results view with error message which indicates that the search is not available
+836 -1
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -6,7 +6,9 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview",
"test": "vitest",
"test:run": "vitest run"
}, },
"dependencies": { "dependencies": {
"terser": "^5.47.1", "terser": "^5.47.1",
@@ -16,7 +18,9 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^6.0.6", "@vitejs/plugin-vue": "^6.0.6",
"jsdom": "^29.1.1",
"vite": "^8.0.10", "vite": "^8.0.10",
"vite-plugin-html": "^3.2.2" "vite-plugin-html": "^3.2.2",
"vitest": "^4.1.7"
} }
} }
+6
View File
@@ -49,6 +49,12 @@ watch(colorScheme, val => updateColorScheme(val))
flex-grow: 1; flex-grow: 1;
} }
@media (max-width: 48rem) {
.main-content {
--main-content-padding-x: 15px;
}
}
#app-wrapper { #app-wrapper {
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
+26
View File
@@ -0,0 +1,26 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path
stroke="currentColor"
stroke-width="3"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
d="M4 8 l8 8 l8 -8"
/>
</svg>

After

Width:  |  Height:  |  Size: 830 B

+38
View File
@@ -0,0 +1,38 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path
fill="currentColor"
d="
M12 2
a10 10 0 0 0 -10 10
a10 10 0 0 0 10 10
a10 10 0 0 0 10 -10
a10 10 0 0 0 -10 -10
"
/>
<path
style="filter: invert(1)"
fill="currentColor"
d="
M12 3
v18
a9 9 0 0 0 9 -9
a9 9 0 0 0 -9 -9
"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+36
View File
@@ -0,0 +1,36 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path
fill="currentColor"
stroke="none"
stroke-width="0"
d="
M12.621094 23.988281
c-6.960938 0 -12.621094 -5.664062000000001 -12.621094 -12.621093
c0 -4.8203130000000005 2.683594 -9.148438 7.003906 -11.300781800000001
c0.19140600000000063 -0.09765619999999942 0.4257819999999999 -0.058593699999999416 0.578125 0.09374980000000058
c0.15234400000000026 0.152344 0.19531300000000051 0.38671900000000003 0.09765699999999988 0.582032
c-0.7890629999999996 1.601562 -1.1875 3.320312 -1.1875 5.113281000000001
c0.0 6.402342999999999 5.207031 11.613281 11.609374000000003 11.613281
c1.8125 0.0 3.550781999999998 -0.41015600000000063 5.167968999999999 -1.2148439999999994
c0.19531299999999874 -0.09765600000000063 0.42968799999999874 -0.05859399999999937 0.5820310000000006 0.09375
c0.15234399999999937 0.15234399999999937 0.1914069999999981 0.38671899999999937 0.0976569999999981 0.5820319999999981
c-2.140625 4.351562000000001 -6.480468999999999 7.058593000000002 -11.328125 7.058593000000002
"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+36
View File
@@ -0,0 +1,36 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path
stroke="currentColor"
stroke-width="3"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
d="M2 22 l8 -8"
/>
<circle
stroke="currentColor"
stroke-width="3"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
cx="15.5"
cy="8.5"
r="6.5"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+45
View File
@@ -0,0 +1,45 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<circle fill="currentColor" r="6" cx="12" cy="12" />
<!-- sunbeams -->
<!-- left -->
<circle fill="currentColor" r="2" cx="3" cy="12" />
<!-- right -->
<circle fill="currentColor" r="2" cx="21" cy="12" />
<!-- top -->
<circle fill="currentColor" r="2" cx="12" cy="3" />
<!-- top -->
<circle fill="currentColor" r="2" cx="12" cy="21" />
<!-- top left -->
<circle fill="currentColor" r="2" cx="5.636038969321072" cy="5.636038969321072" />
<!-- top right -->
<circle fill="currentColor" r="2" cx="18.36396103067893" cy="5.636038969321072" />
<!-- bottom left -->
<circle fill="currentColor" r="2" cx="5.636038969321072" cy="18.36396103067893" />
<!-- bottom right -->
<circle fill="currentColor" r="2" cx="18.36396103067893" cy="18.36396103067893" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -15,6 +15,8 @@ limitations under the License.
--> -->
<script setup> <script setup>
import Icon from '@/features/icons/components/Icon.vue';
import { inject } from 'vue'; import { inject } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -29,9 +31,9 @@ const colorSchemeNextMapper = {
}; };
const colorSchemeIconMapper = { const colorSchemeIconMapper = {
'dark': '', 'dark': 'crescent-moon',
'light': '', 'light': 'sun',
'auto': '' 'auto': 'circle-black-white'
}; };
const getTooltipTranslation = function (colorScheme) { const getTooltipTranslation = function (colorScheme) {
@@ -45,7 +47,10 @@ const getTooltipTranslation = function (colorScheme) {
:aria-label="getTooltipTranslation(colorScheme)" :aria-label="getTooltipTranslation(colorScheme)"
:title="getTooltipTranslation(colorScheme)" :title="getTooltipTranslation(colorScheme)"
> >
{{ colorSchemeIconMapper[colorSchemeNextMapper[colorScheme]] }} <Icon
:name="colorSchemeIconMapper[colorSchemeNextMapper[colorScheme]]"
size="16"
/>
</button> </button>
</template> </template>
+7 -8
View File
@@ -15,6 +15,8 @@ limitations under the License.
--> -->
<script setup> <script setup>
import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
const { t } = useI18n(); const { t } = useI18n();
@@ -36,11 +38,10 @@ const copyrightPeriod =
<RouterLink to="settings" class="link"> <RouterLink to="settings" class="link">
{{ t('preferences.settings') }} {{ t('preferences.settings') }}
</RouterLink> </RouterLink>
<LanguageSwitchButton />
</div> </div>
<div class="footer-segment"> <div class="footer-segment">
<div class="copyright-note"> &copy; {{ copyrightPeriod }} Seekra
&copy; {{ copyrightPeriod }} Seekra
</div>
</div> </div>
</footer> </footer>
</template> </template>
@@ -51,6 +52,9 @@ const copyrightPeriod =
} }
.footer-segment { .footer-segment {
display: flex;
justify-content: center;
gap: 32px;
padding: var(--padding-y); padding: var(--padding-y);
background-color: var(--light-bg); background-color: var(--light-bg);
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
@@ -59,9 +63,4 @@ const copyrightPeriod =
.global-footer a { .global-footer a {
color: var(--dark); color: var(--dark);
} }
.copyright-note {
display: flex;
justify-content: center;
}
</style> </style>
@@ -15,6 +15,8 @@ limitations under the License.
--> -->
<script setup> <script setup>
import Icon from '@/features/icons/components/Icon.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { loadLanguage, LANGUAGES_RTL, SUPPORTED_LANGUAGES } from '@/i18n'; import { loadLanguage, LANGUAGES_RTL, SUPPORTED_LANGUAGES } from '@/i18n';
@@ -64,10 +66,20 @@ const open = function () {
aria-haspopup="listbox" aria-haspopup="listbox"
> >
<span class="lang-code">{{ t(`preferences.locale.languages.${locale}`) }}</span> <span class="lang-code">{{ t(`preferences.locale.languages.${locale}`) }}</span>
<span class="chevron" :class="{ open: isOpen }"></span> <span class="chevron" :class="{ open: isOpen }">
<Icon name="chevron-down" size="1em" />
</span>
</button> </button>
<ul v-if="isOpen" ref="languageDropdown" class="language-dropdown" role="listbox"> <ul
v-if="isOpen"
ref="languageDropdown"
class="language-dropdown"
role="listbox"
:style="{
bottom: 'calc(var(--offset) + 2 * var(--trigger-padding-y) + 1em)' // easier to add auto adaption to the available space
}"
>
<li <li
v-for="lang in SUPPORTED_LANGUAGES" v-for="lang in SUPPORTED_LANGUAGES"
:key="lang" :key="lang"
@@ -84,6 +96,7 @@ const open = function () {
<style scoped> <style scoped>
.language-switch { .language-switch {
--trigger-padding-y: 4px;
position: relative; position: relative;
} }
@@ -94,7 +107,7 @@ const open = function () {
background: none; background: none;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 6px;
padding: 4px 10px; padding: var(--trigger-padding-y) 10px;
cursor: pointer; cursor: pointer;
color: var(--dark); color: var(--dark);
} }
@@ -114,9 +127,9 @@ const open = function () {
} }
.language-dropdown { .language-dropdown {
--offset: 6px;
position: absolute; position: absolute;
right: 0; right: 0;
top: calc(100% + 6px);
background-color: var(--light-bg); background-color: var(--light-bg);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
+54
View File
@@ -0,0 +1,54 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { computed } from 'vue';
import { ensureUnit } from '@/utils/cssDimensions';
const props = defineProps({
name: {
required: true,
type: String
},
size: {
type: [Number, String],
default: 24
}
})
const icons = import.meta.glob('@/assets/icons/*.svg', {
eager: true,
import: 'default'
})
const Icon = computed(() => icons[`/src/assets/icons/${props.name}.svg`])
const size = computed(() => ensureUnit(props.size))
</script>
<template>
<div class="icon-container">
<img :src="Icon" :style="{ height: `${size}`, width: `${size}` }" />
</div>
</template>
<style scoped>
.icon-container {
display: flex;
align-items: center;
filter: var(--invert);
}
</style>
+27 -28
View File
@@ -15,25 +15,9 @@ limitations under the License.
--> -->
<script setup> <script setup>
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import ColorSchemeButton from '@/features/colorScheme/components/ColorSchemeButton.vue'; import ColorSchemeButton from '@/features/colorScheme/components/ColorSchemeButton.vue';
import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue';
import logo from '@/assets/images/logo.svg'; import logo from '@/assets/images/logo.svg';
import Searchbar from '@/features/search/components/Searchbar.vue'; import NavbarSearchBarWrapper from './NavbarSearchBarWrapper.vue';
const route = useRoute();
const searchQueryModel = defineModel();
watch(() => route.name, name => {
searchQueryModel.value = name === 'searchResults' ? route.query.q || '' : '';
});
watch(() => route.query.q, q => {
if (route.name === 'searchResults') {
searchQueryModel.value = q || '';
}
});
</script> </script>
<template> <template>
@@ -41,21 +25,16 @@ watch(() => route.query.q, q => {
<RouterLink to="/" class="link button link"> <RouterLink to="/" class="link button link">
<img :src="logo" alt="Seekra" class="nav-logo" /> <img :src="logo" alt="Seekra" class="nav-logo" />
</RouterLink> </RouterLink>
<Searchbar <NavbarSearchBarWrapper class="navbar-search-bar-wrapper" />
v-if="route.name === 'searchResults'"
class="search-bar"
v-model="searchQueryModel"
auto-submit
/>
<ul class="right-links"> <ul class="right-links">
<li>
<LanguageSwitchButton />
</li>
<li> <li>
<ColorSchemeButton /> <ColorSchemeButton />
</li> </li>
</ul> </ul>
</nav> </nav>
<div class="navbar-search-bar-wrapper-small-screens-wrapper">
<NavbarSearchBarWrapper class="navbar-search-bar-wrapper-small-screens" />
</div>
</template> </template>
<style scoped> <style scoped>
@@ -89,7 +68,27 @@ watch(() => route.query.q, q => {
height: 24px; height: 24px;
width: auto; width: auto;
} }
.search-bar {
width: 70%; .navbar-search-bar-wrapper {
width: 100%;
padding: 0 30px;
}
.navbar-search-bar-wrapper-small-screens-wrapper {
padding: 0 15px 15px;
}
.navbar-search-bar-wrapper-small-screens {
display: none;
}
@media (max-width: 48rem) {
.navbar-search-bar-wrapper {
display: none;
}
.navbar-search-bar-wrapper-small-screens {
display: block;
}
} }
</style> </style>
@@ -0,0 +1,43 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import Searchbar from '@/features/search/components/Searchbar.vue';
import { watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const searchQueryModel = defineModel();
watch(() => route.name, name => {
searchQueryModel.value = name === 'searchResults' ? route.query.q || '' : '';
});
watch(() => route.query.q, q => {
if (route.name === 'searchResults') {
searchQueryModel.value = q || '';
}
});
</script>
<template>
<Searchbar
v-if="route.name === 'searchResults'"
v-model="searchQueryModel"
auto-submit
/>
</template>
+23 -2
View File
@@ -15,6 +15,8 @@ limitations under the License.
--> -->
<script setup> <script setup>
import Icon from '@/features/icons/components/Icon.vue';
const searchQuery = defineModel(); const searchQuery = defineModel();
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
@@ -37,7 +39,7 @@ const submitSearch = function () {
<template> <template>
<div> <div>
<form @submit.prevent="submitSearch"> <form @submit.prevent="submitSearch" class="search-form">
<div class="search-wrapper"> <div class="search-wrapper">
<input <input
v-model="searchQuery" v-model="searchQuery"
@@ -45,7 +47,14 @@ const submitSearch = function () {
:placeholder="t('search.searchBar.placeholder')" :placeholder="t('search.searchBar.placeholder')"
required required
/> />
<button type="submit" class="search-button">{{ t('search.searchBar.submit') }}</button> <button
type="submit"
class="search-button"
:title="t('search.searchBar.submit')"
:aria-label="t('search.searchBar.submit')"
>
<Icon class="search-icon" name="magnifying-glass" size="1.1em" />
</button>
</div> </div>
</form> </form>
</div> </div>
@@ -79,6 +88,7 @@ const submitSearch = function () {
.search-button { .search-button {
font-size: 1rem; font-size: 1rem;
height: calc(var(--content-height) + 2 * var(--submit-button-padding-y)); height: calc(var(--content-height) + 2 * var(--submit-button-padding-y));
width: calc(var(--content-height) + 2 * var(--submit-button-padding-y));
border-radius: calc(var(--content-height) * 0.5 + var(--submit-button-padding-y)); border-radius: calc(var(--content-height) * 0.5 + var(--submit-button-padding-y));
border: none; border: none;
padding: var(--submit-button-padding-y) 20px; padding: var(--submit-button-padding-y) 20px;
@@ -86,9 +96,20 @@ const submitSearch = function () {
color: var(--white); color: var(--white);
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
display: flex;
justify-content: center;
align-items: center;
}
.search-button .search-icon {
filter: invert(1);
} }
.search-button:hover { .search-button:hover {
background: var(--primary-color-l-1); background: var(--primary-color-l-1);
} }
.search-form {
width: 100%;
}
</style> </style>
+6
View File
@@ -82,6 +82,8 @@ limitations under the License.
--blue-box-shadow: oklch(0.52 0.15 268 / 0.25); --blue-box-shadow: oklch(0.52 0.15 268 / 0.25);
--light-hover: var(--light-d-2); --light-hover: var(--light-d-2);
--invert: invert(0);
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@@ -114,6 +116,8 @@ limitations under the License.
--gray-box-shadow: oklch(0.25 0.0001 271 / 0.7); --gray-box-shadow: oklch(0.25 0.0001 271 / 0.7);
--light-hover: var(--light-d-5); --light-hover: var(--light-d-5);
--invert: invert(1);
} }
} }
@@ -146,4 +150,6 @@ limitations under the License.
--gray-box-shadow: oklch(0.25 0.0001 271 / 0.7); --gray-box-shadow: oklch(0.25 0.0001 271 / 0.7);
--light-hover: var(--light-d-5); --light-hover: var(--light-d-5);
--invert: invert(1);
} }
+31
View File
@@ -0,0 +1,31 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export const ensureUnit = function (d) {
if (!d) d = 0;
// ensure that it is a string
if (typeof d === 'number') d = d.toString();
if (d === '') d = '0';
// if it ends with a number
if (/\d$/.test(d)) {
d += 'px';
};
return d;
};
+7
View File
@@ -21,6 +21,9 @@ import { createHtmlPlugin } from 'vite-plugin-html';
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
build: {
target: 'es2020'
},
plugins: [ plugins: [
vue(), vue(),
createHtmlPlugin({ createHtmlPlugin({
@@ -31,5 +34,9 @@ export default defineConfig({
alias: { alias: {
'@': path.resolve(__dirname, './src') '@': path.resolve(__dirname, './src')
} }
},
test: {
globals: false,
environment: 'jsdom'
} }
}) })