mirror of
https://github.com/uetchy/namae.git
synced 2025-03-16 20:20:38 +09:00
feat: refine footer
This commit is contained in:
parent
04725d0a65
commit
0d632a8eb2
195
src/App.tsx
195
src/App.tsx
@ -1,206 +1,35 @@
|
||||
import Tooltip from 'rc-tooltip';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IoIosFlash, IoIosRocket } from 'react-icons/io';
|
||||
import { Redirect, Route, Switch, useParams } from 'react-router-dom';
|
||||
import styled, { createGlobalStyle } from 'styled-components';
|
||||
import Cards from './components/cards';
|
||||
import {
|
||||
AvailableIcon,
|
||||
COLORS as ResultColor,
|
||||
ResultIcon,
|
||||
ResultItem,
|
||||
ResultName,
|
||||
} from './components/cards/core';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import Footer from './components/Footer';
|
||||
import Form from './components/Form';
|
||||
import Welcome from './components/Welcome';
|
||||
import { useStoreState } from './store';
|
||||
import { mobile } from './util/css';
|
||||
import Home from './pages/Home';
|
||||
import Search from './pages/Search';
|
||||
import { GlobalStyle } from './theme';
|
||||
import { useOpenSearch } from './util/hooks';
|
||||
import { isStandalone } from './util/pwa';
|
||||
import { sanitize } from './util/text';
|
||||
|
||||
export default function App() {
|
||||
const OpenSearch = useOpenSearch('/opensearch.xml');
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<Helmet>
|
||||
<link
|
||||
rel="search"
|
||||
type="application/opensearchdescription+xml"
|
||||
title="namae"
|
||||
href="/opensearch.xml"
|
||||
/>
|
||||
</Helmet>
|
||||
<OpenSearch />
|
||||
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<Home />
|
||||
</Route>
|
||||
|
||||
<Route path="/s/:query">
|
||||
<Search />
|
||||
</Route>
|
||||
|
||||
<Route path="*">
|
||||
<Redirect to="/" />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
{!isStandalone() && <Footer />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Home() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>namae — {t('title')}</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<Form />
|
||||
</Header>
|
||||
<Content>
|
||||
<Welcome />
|
||||
</Content>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Search() {
|
||||
const { query } = useParams<{ query: string }>();
|
||||
const currentQuery = sanitize(query);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Search for "{currentQuery}" — namae</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<Form initialValue={currentQuery} />
|
||||
</Header>
|
||||
<Content>
|
||||
<Legend>
|
||||
<Stat />
|
||||
<ResultItem color={ResultColor.available}>
|
||||
<ResultIcon>
|
||||
<IoIosRocket />
|
||||
</ResultIcon>
|
||||
<ResultName>{t('available')}</ResultName>
|
||||
<AvailableIcon>
|
||||
<IoIosFlash />
|
||||
</AvailableIcon>
|
||||
</ResultItem>
|
||||
<ResultItem color={ResultColor.unavailable}>
|
||||
<ResultIcon>
|
||||
<IoIosRocket />
|
||||
</ResultIcon>
|
||||
<ResultName>{t('unavailable')}</ResultName>
|
||||
</ResultItem>
|
||||
</Legend>
|
||||
<Cards query={currentQuery} />
|
||||
</Content>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Stat() {
|
||||
const totalCount = useStoreState((state) => state.stats.totalCount);
|
||||
const availableCount = useStoreState((state) => state.stats.availableCount);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const uniqueness = availableCount !== 0 ? availableCount / totalCount : 0.0;
|
||||
const uniquenessText = ((n) => {
|
||||
if (n > 0.7 && n <= 1.0) {
|
||||
return t('uniqueness.high');
|
||||
} else if (n > 0.4 && n <= 0.7) {
|
||||
return t('uniqueness.moderate');
|
||||
} else {
|
||||
return t('uniqueness.low');
|
||||
}
|
||||
})(uniqueness);
|
||||
|
||||
return (
|
||||
<UniquenessIndicator>
|
||||
<Tooltip
|
||||
overlay={t('uniqueness.description')}
|
||||
placement="top"
|
||||
trigger={['hover']}>
|
||||
<span>
|
||||
{uniquenessText} ({(uniqueness * 100).toFixed(1)} UNIQ)
|
||||
</span>
|
||||
</Tooltip>
|
||||
</UniquenessIndicator>
|
||||
);
|
||||
}
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
line-height: 1.625em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: #ffffff;
|
||||
|
||||
${mobile} {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
padding-top: 100px;
|
||||
|
||||
${mobile} {
|
||||
padding-top: 60px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Header = styled.header`
|
||||
padding: 0 40px;
|
||||
background-image: linear-gradient(180deg, #bda2ff 0%, #1b24cc 99%);
|
||||
|
||||
${mobile} {
|
||||
padding: 0 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Legend = styled.div`
|
||||
margin-top: -100px;
|
||||
padding: 100px 0 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
background-color: #f6f6fa;
|
||||
|
||||
${mobile} {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: -80px;
|
||||
padding: 70px 0 30px;
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
> * {
|
||||
margin: 0 10px 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const UniquenessIndicator = styled.div`
|
||||
color: #7b7b7b;
|
||||
`;
|
||||
|
@ -2,27 +2,72 @@ import React from 'react';
|
||||
import { OutboundLink } from 'react-ga';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaGithub, FaProductHunt, FaTwitter } from 'react-icons/fa';
|
||||
import { GoHeart } from 'react-icons/go';
|
||||
import styled from 'styled-components';
|
||||
import { Section } from '../theme';
|
||||
import { mobile } from '../util/css';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<LangBox>
|
||||
<a href="/?lng=en">
|
||||
<span role="img" aria-label="English">
|
||||
🇬🇧
|
||||
</span>
|
||||
</a>
|
||||
<a href="/?lng=ja">
|
||||
<span role="img" aria-label="Japanese">
|
||||
🇯🇵
|
||||
</span>
|
||||
</a>
|
||||
</LangBox>
|
||||
<Pane>
|
||||
<Title>Language</Title>
|
||||
<LangBox>
|
||||
<Links>
|
||||
<a href="/?lng=en">
|
||||
<span role="img" aria-label="English">
|
||||
🇬🇧
|
||||
</span>
|
||||
</a>
|
||||
<a href="/?lng=ja">
|
||||
<span role="img" aria-label="Japanese">
|
||||
🇯🇵
|
||||
</span>
|
||||
</a>
|
||||
</Links>
|
||||
</LangBox>
|
||||
</Pane>
|
||||
|
||||
<Box>
|
||||
<Pane>
|
||||
<Title>Community</Title>
|
||||
<ul>
|
||||
<li>
|
||||
<OutboundLink
|
||||
to="https://github.com/uetchy/namae"
|
||||
eventLabel="GitHub Repo"
|
||||
aria-label="Go to GitHub repository"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub
|
||||
</OutboundLink>
|
||||
</li>
|
||||
<li>
|
||||
<OutboundLink
|
||||
to="https://github.com/uetchy/namae/issues"
|
||||
eventLabel="GitHub Issues"
|
||||
aria-label="Go to GitHub Issues"
|
||||
target="_blank"
|
||||
>
|
||||
Issues
|
||||
</OutboundLink>
|
||||
</li>
|
||||
<li>
|
||||
<OutboundLink
|
||||
to="https://dev.to/uetchy/give-your-app-slick-name-with-namae-dev-5c4h"
|
||||
eventLabel="Blog article"
|
||||
aria-label="Go to blog"
|
||||
target="_blank"
|
||||
>
|
||||
Blog Article
|
||||
</OutboundLink>
|
||||
</li>
|
||||
</ul>
|
||||
</Pane>
|
||||
|
||||
<Pane>
|
||||
<Title>About</Title>
|
||||
<p>
|
||||
Made with{' '}
|
||||
<span role="img" aria-label="coffee">
|
||||
@ -33,74 +78,109 @@ const Footer: React.FC = () => {
|
||||
to="https://twitter.com/uechz"
|
||||
eventLabel="Author Page"
|
||||
aria-label="Author page"
|
||||
target="_blank">
|
||||
target="_blank"
|
||||
>
|
||||
<Bold>Yasuaki Uechi</Bold>
|
||||
</OutboundLink>
|
||||
</p>
|
||||
</Box>
|
||||
|
||||
<ShareBox>
|
||||
<Links>
|
||||
<OutboundLink
|
||||
to={`https://twitter.com/intent/tweet?text=${encodeURIComponent(
|
||||
`namae — ${t('title')}`,
|
||||
)}&url=${encodeURIComponent('https://namae.dev')}`}
|
||||
eventLabel="Tweet"
|
||||
aria-label="Tweet this page"
|
||||
target="_blank">
|
||||
<FaTwitter />
|
||||
</OutboundLink>
|
||||
<OutboundLink
|
||||
to="https://www.producthunt.com/posts/namae"
|
||||
eventLabel="ProductHunt"
|
||||
aria-label="Go to ProductHunt page"
|
||||
target="_blank">
|
||||
<FaProductHunt />
|
||||
</OutboundLink>
|
||||
<OutboundLink
|
||||
to="https://github.com/uetchy/namae"
|
||||
eventLabel="GitHub Repo"
|
||||
aria-label="Go to GitHub repository"
|
||||
target="_blank">
|
||||
<FaGithub />
|
||||
</OutboundLink>
|
||||
</Links>
|
||||
</ShareBox>
|
||||
<ShareBox>
|
||||
<Links>
|
||||
<OutboundLink
|
||||
to={`https://twitter.com/intent/tweet?text=${encodeURIComponent(
|
||||
`namae — ${t('title')}`,
|
||||
)}&url=${encodeURIComponent('https://namae.dev')}`}
|
||||
eventLabel="Tweet"
|
||||
aria-label="Tweet this page"
|
||||
target="_blank"
|
||||
>
|
||||
<FaTwitter />
|
||||
</OutboundLink>
|
||||
<OutboundLink
|
||||
to="https://www.producthunt.com/posts/namae"
|
||||
eventLabel="ProductHunt"
|
||||
aria-label="Go to ProductHunt page"
|
||||
target="_blank"
|
||||
>
|
||||
<FaProductHunt />
|
||||
</OutboundLink>
|
||||
<OutboundLink
|
||||
to="https://github.com/uetchy/namae"
|
||||
eventLabel="GitHub Repo"
|
||||
aria-label="Go to GitHub repository"
|
||||
target="_blank"
|
||||
>
|
||||
<FaGithub />
|
||||
</OutboundLink>
|
||||
<OutboundLink
|
||||
to="https://github.com/sponsors/uetchy"
|
||||
eventLabel="GitHub Sponsors"
|
||||
aria-label="Go to GitHub Sponsors"
|
||||
target="_blank"
|
||||
>
|
||||
<SponsorBadge>
|
||||
<GoHeart size="1.3rem" />
|
||||
<span>Sponsor</span>
|
||||
</SponsorBadge>
|
||||
</OutboundLink>
|
||||
</Links>
|
||||
</ShareBox>
|
||||
</Pane>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
export default Footer;
|
||||
|
||||
const Container = styled.div`
|
||||
const Container = styled(Section)`
|
||||
--text: #bdbdbd;
|
||||
--background: #404040;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 40px 0;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
|
||||
a {
|
||||
color: black;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
|
||||
${mobile} {
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
const Box = styled.footer`
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
const Pane = styled.div`
|
||||
font-size: 1rem;
|
||||
|
||||
${mobile} {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled.h3`
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
const LangBox = styled.div`
|
||||
line-height: 1em;
|
||||
font-size: 0.8rem;
|
||||
`;
|
||||
|
||||
const LangBox = styled(Box)`
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const ShareBox = styled(Box)`
|
||||
const ShareBox = styled.div`
|
||||
margin-top: 15px;
|
||||
line-height: 1em;
|
||||
font-size: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const Links = styled.div`
|
||||
@ -108,10 +188,35 @@ const Links = styled.div`
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
margin: 0 3px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Bold = styled.span`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const SponsorBadge = styled.div`
|
||||
padding: 5px 13px 5px 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
line-height: 1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
border-radius: 5px;
|
||||
background-color: #f8f9fa;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
|
||||
transition: opacity 200ms ease-out;
|
||||
|
||||
:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: rgb(236, 69, 171);
|
||||
margin-right: 5px;
|
||||
}
|
||||
`;
|
||||
|
26
src/pages/Home.tsx
Normal file
26
src/pages/Home.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Content, Header } from '../theme';
|
||||
import Form from '../components/Form';
|
||||
import Welcome from '../components/Welcome';
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>namae — {t('title')}</title>
|
||||
</Helmet>
|
||||
|
||||
<Header>
|
||||
<Form />
|
||||
</Header>
|
||||
|
||||
<Content>
|
||||
<Welcome />
|
||||
</Content>
|
||||
</>
|
||||
);
|
||||
}
|
116
src/pages/Search.tsx
Normal file
116
src/pages/Search.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import Tooltip from 'rc-tooltip';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IoIosFlash, IoIosRocket } from 'react-icons/io';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import Cards from '../components/cards';
|
||||
import {
|
||||
AvailableIcon,
|
||||
COLORS as ResultColor,
|
||||
ResultIcon,
|
||||
ResultItem,
|
||||
ResultName,
|
||||
} from '../components/cards/core';
|
||||
import Form from '../components/Form';
|
||||
import { useStoreState } from '../store';
|
||||
import { Content, Header } from '../theme';
|
||||
import { mobile } from '../util/css';
|
||||
import { sanitize } from '../util/text';
|
||||
|
||||
export default function Search() {
|
||||
const { query } = useParams<{ query: string }>();
|
||||
const currentQuery = sanitize(query);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Search for "{currentQuery}" — namae</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<Form initialValue={currentQuery} />
|
||||
</Header>
|
||||
<Content>
|
||||
<Legend>
|
||||
<Stat />
|
||||
<ResultItem color={ResultColor.available}>
|
||||
<ResultIcon>
|
||||
<IoIosRocket />
|
||||
</ResultIcon>
|
||||
<ResultName>{t('available')}</ResultName>
|
||||
<AvailableIcon>
|
||||
<IoIosFlash />
|
||||
</AvailableIcon>
|
||||
</ResultItem>
|
||||
<ResultItem color={ResultColor.unavailable}>
|
||||
<ResultIcon>
|
||||
<IoIosRocket />
|
||||
</ResultIcon>
|
||||
<ResultName>{t('unavailable')}</ResultName>
|
||||
</ResultItem>
|
||||
</Legend>
|
||||
<Cards query={currentQuery} />
|
||||
</Content>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Stat() {
|
||||
const totalCount = useStoreState((state) => state.stats.totalCount);
|
||||
const availableCount = useStoreState((state) => state.stats.availableCount);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const uniqueness = availableCount !== 0 ? availableCount / totalCount : 0.0;
|
||||
const uniquenessText = ((n) => {
|
||||
if (n > 0.7 && n <= 1.0) {
|
||||
return t('uniqueness.high');
|
||||
} else if (n > 0.4 && n <= 0.7) {
|
||||
return t('uniqueness.moderate');
|
||||
} else {
|
||||
return t('uniqueness.low');
|
||||
}
|
||||
})(uniqueness);
|
||||
|
||||
return (
|
||||
<UniquenessIndicator>
|
||||
<Tooltip
|
||||
overlay={t('uniqueness.description')}
|
||||
placement="top"
|
||||
trigger={['hover']}
|
||||
>
|
||||
<span>
|
||||
{uniquenessText} ({(uniqueness * 100).toFixed(1)} UNIQ)
|
||||
</span>
|
||||
</Tooltip>
|
||||
</UniquenessIndicator>
|
||||
);
|
||||
}
|
||||
|
||||
export const Legend = styled.div`
|
||||
margin-top: -100px;
|
||||
padding: 100px 0 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
background-color: #f6f6fa;
|
||||
|
||||
${mobile} {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: -80px;
|
||||
padding: 70px 0 30px;
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
> * {
|
||||
margin: 0 10px 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const UniquenessIndicator = styled.div`
|
||||
color: #7b7b7b;
|
||||
`;
|
53
src/theme/index.tsx
Normal file
53
src/theme/index.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import styled, { createGlobalStyle } from 'styled-components';
|
||||
import { mobile } from '../util/css';
|
||||
|
||||
export const GlobalStyle = createGlobalStyle`
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
line-height: 1.625em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: #ffffff;
|
||||
|
||||
${mobile} {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const Content = styled.div`
|
||||
padding-top: 100px;
|
||||
|
||||
${mobile} {
|
||||
padding-top: 60px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Header = styled.header`
|
||||
padding: 0 40px;
|
||||
background-image: linear-gradient(180deg, #bda2ff 0%, #1b24cc 99%);
|
||||
|
||||
${mobile} {
|
||||
padding: 0 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Section = styled.div`
|
||||
padding: 100px 20vw;
|
||||
|
||||
${mobile} {
|
||||
padding: 60px 40px;
|
||||
}
|
||||
`;
|
@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
export function useDeferredState<T>(
|
||||
duration = 1000,
|
||||
@ -19,3 +20,16 @@ export function useDeferredState<T>(
|
||||
|
||||
return [response, setInnerValue];
|
||||
}
|
||||
|
||||
export function useOpenSearch(xmlPath: string) {
|
||||
return () => (
|
||||
<Helmet>
|
||||
<link
|
||||
rel="search"
|
||||
type="application/opensearchdescription+xml"
|
||||
title="namae"
|
||||
href={xmlPath}
|
||||
/>
|
||||
</Helmet>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user