added Back To Top functionality and Pankuzu list, Q and A Box, and Hamburger menu components

This commit is contained in:
2024-10-12 20:52:13 +09:00
parent 545ac73e0e
commit e99db9f8bd
80 changed files with 1413 additions and 723 deletions

67
components/BackToTop.vue Normal file
View 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>

View File

@@ -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(() => ({

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;