added Back To Top functionality and Pankuzu list, Q and A Box, and Hamburger menu components
This commit is contained in:
67
components/BackToTop.vue
Normal file
67
components/BackToTop.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
const scrollDistance = useScrollDistance();
|
||||
const triggerDistance = 100;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="back-to-top-transition">
|
||||
<div v-if="scrollDistance > triggerDistance" class="back-to-top">
|
||||
<NuxtLink to="#App">
|
||||
<Icon name="hugeicons:rocket-02" />
|
||||
<p>TOP</p>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.back-to-top {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
display: flex;
|
||||
justify-self: end;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
border-radius: 5rem;
|
||||
background-color: var(--neptune1);
|
||||
color: var(--starlight1);
|
||||
}
|
||||
|
||||
.back-to-top a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
place-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
color: var(--starlight1);
|
||||
& > span {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin: 0;
|
||||
margin-inline: auto;
|
||||
}
|
||||
& > p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.back-to-top {
|
||||
scale: 75%;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.back-to-top-transition-enter-active,
|
||||
.back-to-top-transition-leave-active {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.back-to-top-transition-enter-from,
|
||||
.back-to-top-transition-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropDownProperty } from "#imports";
|
||||
import { DropDownAlignment, type DropDownProperty } from "#imports";
|
||||
|
||||
const property = defineProps<DropDownProperty>();
|
||||
|
||||
const isOpen = ref<boolean>(false);
|
||||
const showInMobile = ref<boolean>(property.showInMobile);
|
||||
const { viewPortType } = useWindowDimensions();
|
||||
|
||||
const listAlignment = ref<typeof DropDownAlignment | number>(
|
||||
property.alignment | DropDownAlignment.Left
|
||||
property.alignment as DropDownAlignment | DropDownAlignment.Left
|
||||
);
|
||||
|
||||
const alignmentClass = computed(() => ({
|
||||
|
||||
130
components/HamburgerMenu.vue
Normal file
130
components/HamburgerMenu.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import type { HamburgerMenuProperty } from "#imports";
|
||||
|
||||
const property = defineProps<HamburgerMenuProperty>();
|
||||
|
||||
const iconList = ["ic:sharp-density-medium", "ic:outline-close"];
|
||||
const isOpen = ref<boolean>(false);
|
||||
|
||||
const handleClickEvent = () => {
|
||||
isOpen.value = !isOpen.value;
|
||||
return;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hamburger-menu-menu">
|
||||
<button class="hamburger-menu-icon" @click="handleClickEvent">
|
||||
<Icon :name="iconList[0]" />
|
||||
</button>
|
||||
<Transition name="hamberger-menu">
|
||||
<div class="hamburger-menu-item-list" v-if="isOpen">
|
||||
<button @click="handleClickEvent">
|
||||
<Icon :name="iconList[1]" />
|
||||
</button>
|
||||
<ul>
|
||||
<li
|
||||
v-for="entry in property.entries"
|
||||
:key="property.entries.indexOf(entry)"
|
||||
>
|
||||
<NuxtLink :to="entry.link" @click="handleClickEvent">
|
||||
{{ entry.text }}
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.hamberger-menu-enter-active,
|
||||
.hamberger-menu-leave-active {
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
|
||||
.hamberger-menu-enter-from,
|
||||
.hamberger-menu-leave-to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.hamburger-menu-menu {
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hamburger-menu-icon {
|
||||
background: none;
|
||||
width: fit-content;
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
color: var(--neptune1);
|
||||
z-index: 150;
|
||||
& > span {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hamburger-menu-item-list {
|
||||
--menu-width: 45vw;
|
||||
display: block;
|
||||
background: var(--neptune1);
|
||||
color: var(--starlight);
|
||||
font-weight: bold;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: calc(100vw - var(--menu-width));
|
||||
z-index: 100;
|
||||
width: var(--menu-width);
|
||||
height: 100vh;
|
||||
text-wrap: nowrap;
|
||||
& button {
|
||||
--button-size: 2.5rem;
|
||||
display: flex;
|
||||
background-color: var(--neptune2);
|
||||
border: none;
|
||||
border-radius: 0.75rem;
|
||||
width: var(--button-size);
|
||||
height: var(--button-size);
|
||||
transform: translateX(
|
||||
calc(var(--menu-width) - var(--button-size) - 1rem)
|
||||
);
|
||||
margin-top: 1rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
& button span {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: var(--neptune1);
|
||||
}
|
||||
& ul {
|
||||
list-style: none;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
& ul li {
|
||||
margin: 1rem 0;
|
||||
transition: 0.2s linear;
|
||||
}
|
||||
& ul li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
& ul li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
& ul li a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
color: currentColor;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
& ul li:hover a {
|
||||
text-decoration: currentColor underline dashed;
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -29,8 +29,10 @@ div {
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
div {
|
||||
font-size: 28pt;
|
||||
font-size: 24pt;
|
||||
height: 6rem;
|
||||
width: calc(100vw - 2rem);
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
import PankuzuEntries from "~/assets/pankuzuEntries.json";
|
||||
import type { PankuzuListProperty } from "~/utils/pankuzuList";
|
||||
|
||||
console.log(route.path);
|
||||
console.log(router.getRoutes());
|
||||
console.log(router.resolve({ name: "news" }));
|
||||
const property = defineProps<PankuzuListProperty>();
|
||||
const route = useRoute();
|
||||
|
||||
const getLinkArray = (): Array<string> => {
|
||||
let links = route.fullPath.split("/");
|
||||
@@ -17,26 +16,83 @@ const linkArray = getLinkArray();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>Pankuzu List</div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li v-for="link in linkArray">
|
||||
<NuxtLink
|
||||
:to="link"
|
||||
v-if="linkArray.indexOf(link) !== linkArray.length - 1"
|
||||
>
|
||||
{{ link }}
|
||||
</NuxtLink>
|
||||
<p v-else>{{ link }}</p>
|
||||
<li v-for="link in linkArray" :key="linkArray.indexOf(link)">
|
||||
<p v-if="link in PankuzuEntries">
|
||||
<NuxtLink :to="link">
|
||||
{{ PankuzuEntries[link] }}
|
||||
</NuxtLink>
|
||||
</p>
|
||||
<p v-else>{{ property.currentPageName as string }}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
list-style-type: none;
|
||||
nav {
|
||||
background-color: var(--starlight1);
|
||||
height: 5rem;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
ul {
|
||||
--list-width: 30rem;
|
||||
list-style-type: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: var(--list-width);
|
||||
padding: 0;
|
||||
transform: translateX(50vw) translateX(calc(-1 * var(--list-width)));
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: inline-flex;
|
||||
padding: 0;
|
||||
margin-inline: 0.25rem;
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
& > p:first-of-type {
|
||||
position: relative;
|
||||
}
|
||||
& > p > a {
|
||||
display: grid;
|
||||
text-decoration: none;
|
||||
color: var(--venus2);
|
||||
height: 100%;
|
||||
align-content: center;
|
||||
}
|
||||
& > p > a:visited {
|
||||
color: var(--venus2);
|
||||
}
|
||||
& > p > a:hover {
|
||||
color: var(--neptune2);
|
||||
}
|
||||
& > p:has(a) {
|
||||
align-content: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
ul li:not(:first-of-type) p:first-of-type {
|
||||
& {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: "ー";
|
||||
top: 0;
|
||||
left: -1.25rem;
|
||||
width: 1rem;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,16 +19,41 @@ const property = defineProps<QAndABoxProperty>();
|
||||
}
|
||||
|
||||
.q-and-a > h2 {
|
||||
position: relative;
|
||||
color: var(--deep-space);
|
||||
background-color: var(--starlight1);
|
||||
border-radius: 1rem;
|
||||
font-weight: 600;
|
||||
line-height: 2;
|
||||
padding-inline: 1.5rem;
|
||||
padding: 0.5rem 1.5rem;
|
||||
}
|
||||
|
||||
.q-and-a > h2::before {
|
||||
content: 'Q.';
|
||||
position: absolute;
|
||||
display: flex;
|
||||
top: -1.5rem;
|
||||
left: -1.75rem;
|
||||
width: 3.2rem;
|
||||
height: 3.2rem;
|
||||
border-radius: 3.2rem;
|
||||
place-content: center;
|
||||
background-color: var(--venus2);
|
||||
color: var(--starlight1);
|
||||
}
|
||||
|
||||
.q-and-a > div {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
margin-inline: 3rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.q-and-a > h2 {
|
||||
line-height: 1.25;
|
||||
}
|
||||
.q-and-a > div {
|
||||
margin-inline: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -45,10 +45,14 @@ const showThePast = (event: Event) => {
|
||||
<div class="links">
|
||||
<ul>
|
||||
<li>
|
||||
<NuxtLink to="/"> Home </NuxtLink>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink to="/news"> News </NuxtLink>
|
||||
<ul style="padding-left: 0">
|
||||
<li>
|
||||
<NuxtLink to="/"> Home </NuxtLink>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink to="/news"> News </NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink to="/projects"> Projects </NuxtLink>
|
||||
@@ -76,9 +80,32 @@ const showThePast = (event: Event) => {
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink to="/about/sera">
|
||||
About {{ SiteInfo.clubNameAbbreviation }}
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/about">About</NuxtLink>
|
||||
<ul>
|
||||
<li>
|
||||
<NuxtLink to="/about/sera">
|
||||
About
|
||||
{{ SiteInfo.clubNameAbbreviation }}
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink to="/about/achievements">
|
||||
活動実績
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink to="/about/gallery">
|
||||
写真集
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li>
|
||||
<NuxtLink
|
||||
to="/about/for-middle-schoolers-and-new-comers"
|
||||
>
|
||||
中学生・新入生向け
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -157,6 +184,10 @@ const showThePast = (event: Event) => {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--ocean-blue);
|
||||
color: var(--sunlight);
|
||||
@@ -209,9 +240,11 @@ footer {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.links ul {
|
||||
list-style: none;
|
||||
.links > ul {
|
||||
font-weight: 600;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
& li {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
@@ -223,13 +256,12 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
.sns-list h3 {
|
||||
.sns-list > h3 {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.sns-list ul {
|
||||
list-style: none;
|
||||
.sns-list > ul {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
& > li {
|
||||
@@ -267,11 +299,21 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet Style */
|
||||
@media screen and (max-width: 1024px) {
|
||||
.links > ul {
|
||||
grid: repeat(2, 1fr) / repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Phone Style */
|
||||
@media screen and (max-width: 640px) {
|
||||
.top-column {
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-rows: repeat(3, auto);
|
||||
}
|
||||
.links > ul {
|
||||
grid: repeat(3, auto) / 1fr;
|
||||
}
|
||||
.sns-list {
|
||||
display: block;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import LogoSVG from "public/sera-logo-no-text.svg";
|
||||
import SiteInfo from "~/assets/siteinfo.json";
|
||||
import type { DropDownEntry } from "~/utils/dropDown";
|
||||
|
||||
const { viewPortType } = useWindowDimensions();
|
||||
|
||||
const exploreDropDownEntries: Array<DropDownEntry> = [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "Projects", link: "/projects" },
|
||||
{ text: "CanSat", link: "/projects/cansat" },
|
||||
{ text: "Rocket", link: "/projects/rocket" },
|
||||
{ text: "Edu-Robot", link: "/projects/education" },
|
||||
{ text: "Education", link: "/projects/education" },
|
||||
{ text: "CubeSat KOSEN-X", link: "/projects/kosen-x" },
|
||||
{ text: "About", link: "/about" },
|
||||
];
|
||||
@@ -16,6 +19,23 @@ const mediaDropDownEntries: Array<DropDownEntry> = [
|
||||
{ text: "News", link: "/news" },
|
||||
{ text: "Gallery", link: "/about/gallery" },
|
||||
];
|
||||
|
||||
const hamburgerMenuEntries: Array<DropDownEntry> = [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "News", link: "/news" },
|
||||
{ text: "Projects", link: "/projects" },
|
||||
{ text: "Rocket", link: "/projects/rocket" },
|
||||
{ text: "CanSat", link: "/projects/cansat" },
|
||||
{ text: "CubeSat KOSEN-X", link: "/projects/kosen-x" },
|
||||
{ text: "教育プロジェクト", link: "/projects/education" },
|
||||
{ text: `About ${SiteInfo.clubNameAbbreviation}`, link: "/about/sera" },
|
||||
{ text: "活動実績", link: "/about/achievements" },
|
||||
{ text: "写真集", link: "/about/gallery" },
|
||||
{
|
||||
text: "中学生・新入生向け",
|
||||
link: "/about/for-middle-schoolers-and-new-comers"
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -43,6 +63,10 @@ const mediaDropDownEntries: Array<DropDownEntry> = [
|
||||
:entries="mediaDropDownEntries"
|
||||
class="right-dropdown"
|
||||
/>
|
||||
<HamburgerMenu
|
||||
:entries="hamburgerMenuEntries"
|
||||
v-if="viewPortType === ViewPortType.MOBILE"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -109,6 +133,10 @@ header {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.navigation-menu {
|
||||
padding-inline: 1rem;
|
||||
width: calc(100% - 2rem);
|
||||
}
|
||||
.left-dropdown,
|
||||
.right-dropdown {
|
||||
display: none;
|
||||
|
||||
Reference in New Issue
Block a user