feat: migrate from Create React App to Vite
Rebuild the site on the stack the other converted sites share: Vite 8, React 19, TypeScript 7, react-router 8, Biome and sanitize.css, with the site data moved out of the bundle into static-contents/. - base the database and pico code on the other converted sites, keeping this site's layout, pages and item fields - read file timestamps as Unix seconds in the file and license views - serve all module data from modules/, redirecting the old download and /database/file URLs - keep form controls on white - track page views with GA4
This commit is contained in:
+21
-29
@@ -1,33 +1,25 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.eslintcache
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# database contents
|
||||
/dl-limit-items.csv
|
||||
/public/database/items.json
|
||||
/public/database/file
|
||||
/public/credits/images
|
||||
/public/credits/*.json
|
||||
/src/database/assets/*.json
|
||||
/src/pico/assets/*.json
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Keep @types/node on the 24.x line to match the deployment target. Its
|
||||
// "latest" dist-tag points at the 22.x line, so the default target would
|
||||
// report no updates at all instead of offering 24.x patches.
|
||||
export default {
|
||||
target: (name) => (name === '@types/node' ? 'minor' : 'latest'),
|
||||
};
|
||||
@@ -1,44 +1,108 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
# OpenNeuro Repository
|
||||
|
||||
## Available Scripts
|
||||
Static front-end for the archived [INCF J-Node OpenNeuro Repository](https://open.neuroinf.jp/),
|
||||
built with [Vite](https://vite.dev/), React and TypeScript.
|
||||
|
||||
In the project directory, you can run:
|
||||
## Requirements
|
||||
|
||||
### `yarn start`
|
||||
- Node.js 20.19+ / 22.12+ (Vite 8)
|
||||
- npm
|
||||
|
||||
Runs the app in the development mode.<br />
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
## Setup
|
||||
|
||||
The page will reload if you make edits.<br />
|
||||
You will also see any lint errors in the console.
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### `yarn test`
|
||||
## Site data
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br />
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
The pages are rendered from JSON dumped out of the original XooNIps/XOOPS
|
||||
installation by [xoops-static-exporter](https://git.ni.riken.jp/niu/xoops-static-exporter)
|
||||
(`sites/open.neuroinf.jp/`). All of it lives in `static-contents/`, which sits
|
||||
next to the repository rather than inside it:
|
||||
|
||||
### `yarn build`
|
||||
```
|
||||
open.neuroinf.jp/
|
||||
├── open.neuroinf.jp/ this repository
|
||||
└── static-contents/
|
||||
└── modules/ one directory per source XOOPS module
|
||||
├── credits/ pico: config.json, <content id>.json, images/
|
||||
└── xoonips/ tree.json, items.json, file/<id>/
|
||||
```
|
||||
|
||||
Builds the app for production to the `build` folder.<br />
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
The `RewriteBase` in every `modules/xoonips/file/<id>/.htaccess` carries the
|
||||
absolute path of that directory and has to move with the layout.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br />
|
||||
Your app is ready to be deployed!
|
||||
The dev and preview servers mount that directory at the site root. It is kept
|
||||
out of `dist/`, and it is not tracked in git.
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
The site information pages (`/credits/`) come from a pico module named
|
||||
`credits`. The XooNIps item types carry fields specific to this site (research
|
||||
area, species, scale, method, referee, ...); they are marked
|
||||
`OpenNeuro extensions` in `src/database/`, and the fields that render them are
|
||||
the `OpenNeuro*` components in `src/database/item-type/lib/field/`. The top,
|
||||
categories, overview and news pages and the page layout are in `src/custom/`.
|
||||
|
||||
### `yarn eject`
|
||||
## Scripts
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
| Command | Description |
|
||||
| --- | --- |
|
||||
| `npm run dev` | Start the dev server on http://localhost:5173 |
|
||||
| `npm run build` | Type-check and build into `dist/` |
|
||||
| `npm run preview` | Serve the production build on http://localhost:4173 |
|
||||
| `npm run lint` | Lint with [Biome](https://biomejs.dev/) |
|
||||
| `npm run format` | Format with Biome |
|
||||
| `npm run check` | Lint, format and organize imports, applying the safe fixes |
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
`@types/node` is pinned to the major that matches the Node runtime in use (24);
|
||||
`.ncurc.js` keeps `npm-check-updates` from bumping it past that.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
There is currently no test suite.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
## Styles
|
||||
|
||||
## Learn More
|
||||
The base stylesheet is `sanitize.css` with its `forms` and `typography` sheets,
|
||||
with its `box-sizing: border-box` kept. The site theme is
|
||||
`src/custom/assets/style.css`. Where a sanitize.css default does not suit the
|
||||
design, the rule for that place sets its own value rather than overriding the
|
||||
element everywhere.
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
## Deployment
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
Build locally and copy the result up; the server does not need a toolchain.
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npm run check
|
||||
npm run build
|
||||
../deploy.sh
|
||||
```
|
||||
|
||||
`deploy.sh`, next to `static-contents/`, sends the data to
|
||||
`archive:/data/www/open.neuroinf.jp/static-contents/`, then `dist/` to
|
||||
`.../html/`, and finally links every top-level entry of `static-contents/` into
|
||||
the document root over ssh (`ln -sfn ../static-contents/* .`). The data has to
|
||||
go first: the document root's `rsync --delete` removes those links, and they
|
||||
are only put back at the end.
|
||||
|
||||
Apache needs `Options FollowSymLinks` and `AllowOverride All` on both
|
||||
directories. Use symlinks rather than `Alias`: an `Alias` maps the URL outside
|
||||
the document root, so `.htaccess` no longer applies and the SPA fallback and
|
||||
the redirects stop working.
|
||||
|
||||
### Redirects
|
||||
|
||||
`.htaccess` redirects these addresses with a 301:
|
||||
|
||||
| From | To |
|
||||
| --- | --- |
|
||||
| `/modules/xoonips/download.php?file_id=N` | `/modules/xoonips/file/N` |
|
||||
| `/database/file/...` | `/modules/xoonips/file/...` |
|
||||
|
||||
XOOPS-style addresses (`/modules/xoonips/detail.php?item_id=N`,
|
||||
`/modules/credits/...`, `/index.php`) are redirected inside the app.
|
||||
|
||||
## Google Analytics
|
||||
|
||||
`GOOGLE_ANALYTICS_TRACKING_ID` in `src/config.ts` carries the GA4 measurement
|
||||
ID. Emptying it disables tracking. Only production builds send page views.
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.5.13/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"includes": ["**", "!**/dist", "!src/common/assets", "!src/custom/assets"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 4,
|
||||
"lineWidth": 120
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"jsxQuoteStyle": "double",
|
||||
"semicolons": "always",
|
||||
"trailingCommas": "es5",
|
||||
"arrowParentheses": "always"
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"preset": "recommended"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
@@ -13,11 +13,12 @@
|
||||
<meta name="keywords" content="neuroinformatics, xoonips, database, platform, openneuro repository, neuroscience, data sharing" />
|
||||
<meta name="author" content="Neuroinformatics Unit, RIKEN Center for Brain Science" />
|
||||
<meta name="copyright" content="Copyright © 2018" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>OpenNeuro Repository - Welcome</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+2153
File diff suppressed because it is too large
Load Diff
+44
-61
@@ -1,63 +1,46 @@
|
||||
{
|
||||
"name": "open",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@types/async-lock": "^1.1.2",
|
||||
"@types/jest": "26.0.23",
|
||||
"@types/lokijs": "^1.5.4",
|
||||
"@types/node": "15.6.1",
|
||||
"@types/react": "17.0.8",
|
||||
"@types/react-dom": "17.0.5",
|
||||
"@types/react-helmet": "^6.1.1",
|
||||
"@types/react-html-parser": "^2.0.1",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/react-router-hash-link": "^2.4.0",
|
||||
"async-lock": "^1.3.0",
|
||||
"axios": "^0.21.1",
|
||||
"lokijs": "^1.5.12",
|
||||
"moment": "^2.29.1",
|
||||
"rc-tree": "^4.1.5",
|
||||
"react": "^17.0.2",
|
||||
"react-app-polyfill": "^2.0.0",
|
||||
"react-cookie": "^4.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-ga": "^3.3.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-html-parser": "^2.0.2",
|
||||
"react-image-lightbox": "^5.1.1",
|
||||
"react-overlays": "^5.0.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-hash-link": "^2.4.3",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-spinner-material": "^1.3.1",
|
||||
"typescript": "4.3.2",
|
||||
"xregexp": "^5.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all",
|
||||
"ie 11"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version",
|
||||
"ie 11"
|
||||
]
|
||||
}
|
||||
"name": "open",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "biome lint",
|
||||
"format": "biome format --write",
|
||||
"check": "biome check --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dr.pogodin/react-helmet": "^3.2.3",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.3.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.3.1",
|
||||
"@fortawesome/react-fontawesome": "^3.5.0",
|
||||
"@orrisroot/react-html-parser": "^3.0.1",
|
||||
"async-lock": "^1.4.1",
|
||||
"date-fns": "^4.4.0",
|
||||
"ky": "^2.1.0",
|
||||
"lokijs": "^1.5.12",
|
||||
"rc-tree": "^5.13.1",
|
||||
"react": "^19.3.0",
|
||||
"react-cookie": "^8.1.2",
|
||||
"react-dom": "^19.3.0",
|
||||
"react-ga4": "^3.0.1",
|
||||
"react-overlays": "^5.2.1",
|
||||
"react-router": "^8.3.1",
|
||||
"sanitize.css": "^13.0.0",
|
||||
"yet-another-react-lightbox": "^3.32.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.5.13",
|
||||
"@types/async-lock": "^1.4.2",
|
||||
"@types/lokijs": "^1.5.14",
|
||||
"@types/node": "^24.13.4",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@vitejs/plugin-react": "^6.1.1",
|
||||
"sirv": "^3.0.2",
|
||||
"typescript": "~7.0.2",
|
||||
"vite": "^8.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -2,7 +2,10 @@ RewriteEngine on
|
||||
RewriteBase /
|
||||
|
||||
RewriteCond %{QUERY_STRING} (^|&)file_id=([0-9]+)($|&)
|
||||
RewriteRule ^modules/xoonips/download.php /database/file/%2? [R=301,L]
|
||||
RewriteRule ^modules/xoonips/download.php /modules/xoonips/file/%2? [R=301,L]
|
||||
|
||||
# File downloads moved under /modules/xoonips/ ; keep the v1.0.0 links alive.
|
||||
RewriteRule ^database/file/(.*)$ /modules/xoonips/file/$1 [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d [OR]
|
||||
|
||||
+13
-13
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"short_name": "OpenNeuro Repository",
|
||||
"name": "OpenNeuro Repository",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "32x32",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
"short_name": "OpenNeuro Repository",
|
||||
"name": "OpenNeuro Repository",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "32x32",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
@import url("./common/assets/xoops.css");
|
||||
@import url("./custom/assets/style.css");
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
+13
-16
@@ -1,20 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
import { HelmetProvider } from '@dr.pogodin/react-helmet';
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import './App.css';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import AppRoot from './common/AppRoot';
|
||||
import './App.css';
|
||||
|
||||
class App extends Component {
|
||||
const App = () => (
|
||||
<HelmetProvider>
|
||||
<CookiesProvider>
|
||||
<BrowserRouter>
|
||||
<AppRoot />
|
||||
</BrowserRouter>
|
||||
</CookiesProvider>
|
||||
</HelmetProvider>
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<CookiesProvider>
|
||||
<BrowserRouter>
|
||||
<AppRoot />
|
||||
</BrowserRouter >
|
||||
</CookiesProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
||||
+33
-53
@@ -1,67 +1,47 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ReactCookieProps, withCookies } from 'react-cookie';
|
||||
import ReactGA from 'react-ga';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useCookies } from 'react-cookie';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useLocation } from 'react-router';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Page from '../custom/Page';
|
||||
|
||||
interface Props extends RouteComponentProps, ReactCookieProps { }
|
||||
interface State {
|
||||
lang: MultiLang;
|
||||
const isAnalyticsEnabled = import.meta.env.PROD && Config.GOOGLE_ANALYTICS_TRACKING_ID !== '';
|
||||
|
||||
if (isAnalyticsEnabled) {
|
||||
// Page views are sent from the location effect below, the first one included;
|
||||
// gtag's own page_view on config would count the landing page twice.
|
||||
ReactGA.initialize(Config.GOOGLE_ANALYTICS_TRACKING_ID, { gtagOptions: { send_page_view: false } });
|
||||
}
|
||||
|
||||
class AppRoot extends Component<Props, State> {
|
||||
const toMultiLang = (value: unknown): MultiLang | null => (value === 'en' || value === 'ja' ? value : null);
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { lang: 'en' };
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.initialize(Config.GOOGLE_ANALYTICS_TRACKING_ID);
|
||||
}
|
||||
}
|
||||
const AppRoot = () => {
|
||||
const location = useLocation();
|
||||
const [cookies, setCookie] = useCookies(['ml_lang']);
|
||||
const previousLocation = useRef<string | null>(null);
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const params = new URLSearchParams(nextProps.location.search);
|
||||
const param_lang = params.get('ml_lang');
|
||||
const cookie_lang = typeof nextProps.cookies !== 'undefined' ? nextProps.cookies.get('ml_lang') : null;
|
||||
let lang = param_lang || (typeof cookie_lang === 'string' ? cookie_lang : null) || prevState.lang;
|
||||
if (lang !== 'en' && lang !== 'ja') {
|
||||
lang = 'en';
|
||||
}
|
||||
if (cookie_lang !== lang) {
|
||||
if (typeof nextProps.cookies !== 'undefined') {
|
||||
nextProps.cookies.set('ml_lang', lang, { path: '/' });
|
||||
}
|
||||
}
|
||||
if (prevState.lang !== lang) {
|
||||
return { lang };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const paramLang = toMultiLang(new URLSearchParams(location.search).get('ml_lang'));
|
||||
const cookieLang = toMultiLang(cookies.ml_lang);
|
||||
const lang: MultiLang = paramLang ?? cookieLang ?? 'en';
|
||||
|
||||
componentDidMount() {
|
||||
const { pathname } = this.props.location;
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.set({ page: pathname });
|
||||
ReactGA.pageview(pathname);
|
||||
useEffect(() => {
|
||||
if (cookies.ml_lang !== lang) {
|
||||
setCookie('ml_lang', lang, { path: '/' });
|
||||
}
|
||||
}
|
||||
}, [cookies.ml_lang, lang, setCookie]);
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { pathname, search } = this.props.location;
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.set({ page: pathname });
|
||||
ReactGA.pageview(pathname);
|
||||
useEffect(() => {
|
||||
if (isAnalyticsEnabled) {
|
||||
ReactGA.send({ hitType: 'pageview', page: location.pathname + location.search });
|
||||
}
|
||||
if (pathname !== prevProps.location.pathname || search !== prevProps.location.search) {
|
||||
const current = location.pathname + location.search;
|
||||
if (previousLocation.current !== null && previousLocation.current !== current) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
previousLocation.current = current;
|
||||
}, [location.pathname, location.search]);
|
||||
|
||||
render() {
|
||||
const { lang } = this.state;
|
||||
return <Page lang={lang} />;
|
||||
}
|
||||
}
|
||||
return <Page lang={lang} />;
|
||||
};
|
||||
|
||||
export default withCookies(withRouter(AppRoot));
|
||||
export default AppRoot;
|
||||
|
||||
@@ -1,40 +1,30 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import { Navigate, useLocation } from 'react-router';
|
||||
import type { MultiLang } from '../config';
|
||||
import PageNotFound from './lib/PageNotFound';
|
||||
|
||||
interface Params {
|
||||
module: string;
|
||||
pathname: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class XoopsPathRedirect extends Component<Props> {
|
||||
|
||||
getRedirectUrl() {
|
||||
const { pathname } = this.props.location;
|
||||
switch (pathname || '') {
|
||||
case '/index.php': {
|
||||
return '/';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
const getRedirectUrl = (pathname: string): string => {
|
||||
switch (pathname || '') {
|
||||
case '/index.php':
|
||||
return '/';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (this.props.location.pathname === '/') {
|
||||
return null;
|
||||
}
|
||||
const url = this.getRedirectUrl();
|
||||
if (url === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <Redirect to={url} />;
|
||||
const XoopsPathRedirect = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
if (location.pathname === '/') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const url = getRedirectUrl(location.pathname);
|
||||
if (url === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <Navigate to={url} replace />;
|
||||
};
|
||||
|
||||
export default XoopsPathRedirect;
|
||||
|
||||
+62
-17
@@ -1,17 +1,62 @@
|
||||
img {border: 0;}
|
||||
|
||||
#xoopsHiddenText {visibility: hidden; color: #000000; font-weight: normal; font-style: normal; text-decoration: none;}
|
||||
|
||||
.pagneutral {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/pagneutral.gif);}
|
||||
.pagact {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/pagact.gif);}
|
||||
.paginact {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/paginact.gif);}
|
||||
|
||||
|
||||
#mainmenu a {text-align:left; display: block; margin: 0; padding: 4px;}
|
||||
#mainmenu a.menuTop {padding-left: 3px;}
|
||||
#mainmenu a.menuMain {padding-left: 3px;}
|
||||
#mainmenu a.menuSub {padding-left: 9px;}
|
||||
|
||||
#usermenu a {text-align:left; display: block; margin: 0; padding: 4px;}
|
||||
#usermenu a.menuTop {}
|
||||
#usermenu a.highlight {color: #0000ff; background-color: #fcc;}
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#xoopsHiddenText {
|
||||
visibility: hidden;
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.pagneutral {
|
||||
font-size: 10px;
|
||||
width: 16px;
|
||||
height: 19px;
|
||||
text-align: center;
|
||||
background-image: url(./images/pagneutral.gif);
|
||||
}
|
||||
.pagact {
|
||||
font-size: 10px;
|
||||
width: 16px;
|
||||
height: 19px;
|
||||
text-align: center;
|
||||
background-image: url(./images/pagact.gif);
|
||||
}
|
||||
.paginact {
|
||||
font-size: 10px;
|
||||
width: 16px;
|
||||
height: 19px;
|
||||
text-align: center;
|
||||
background-image: url(./images/paginact.gif);
|
||||
}
|
||||
|
||||
#mainmenu a {
|
||||
text-align: left;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
}
|
||||
#mainmenu a.menuTop {
|
||||
padding-left: 3px;
|
||||
}
|
||||
#mainmenu a.menuMain {
|
||||
padding-left: 3px;
|
||||
}
|
||||
#mainmenu a.menuSub {
|
||||
padding-left: 9px;
|
||||
}
|
||||
|
||||
#usermenu a {
|
||||
text-align: left;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
}
|
||||
#usermenu a.menuTop {
|
||||
}
|
||||
#usermenu a.highlight {
|
||||
color: #0000ff;
|
||||
background-color: #fcc;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { MultiLang } from '../../config';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const SelectLang = (props: Props) => {
|
||||
const { lang, history } = props;
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const langs: MultiLang[] = ['en', 'ja'];
|
||||
const titles = {
|
||||
en: 'English',
|
||||
@@ -15,20 +16,26 @@ const SelectLang = (props: Props) => {
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<select value={lang} onChange={(e) => {
|
||||
const xlang = e.target.value;
|
||||
const params = new URLSearchParams(history.location.search);
|
||||
params.set('ml_lang', xlang);
|
||||
const url = history.location.pathname + '?' + params.toString();
|
||||
history.push(url);
|
||||
}}>
|
||||
<select
|
||||
value={lang}
|
||||
onChange={(e) => {
|
||||
const xlang = e.target.value;
|
||||
const params = new URLSearchParams(location.search);
|
||||
params.set('ml_lang', xlang);
|
||||
const url = `${location.pathname}?${params.toString()}`;
|
||||
navigate(url);
|
||||
}}
|
||||
>
|
||||
{langs.map((xlang) => {
|
||||
return <option key={xlang} value={xlang}>{titles[xlang]}</option>;
|
||||
return (
|
||||
<option key={xlang} value={xlang}>
|
||||
{titles[xlang]}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default withRouter(SelectLang);
|
||||
export default SelectLang;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { ComponentProps, MouseEvent } from 'react';
|
||||
import { Link, type To } from 'react-router';
|
||||
|
||||
type LinkProps = ComponentProps<typeof Link>;
|
||||
|
||||
const RETRY_INTERVAL_MS = 16;
|
||||
const RETRY_COUNT = 20;
|
||||
|
||||
const hashOf = (to: To): string => {
|
||||
if (typeof to === 'string') {
|
||||
const idx = to.indexOf('#');
|
||||
return idx === -1 ? '' : to.slice(idx + 1);
|
||||
}
|
||||
return (to.hash ?? '').replace(/^#/, '');
|
||||
};
|
||||
|
||||
const scrollToHash = (hash: string, remaining = RETRY_COUNT) => {
|
||||
if (hash === '') {
|
||||
return;
|
||||
}
|
||||
const id = decodeURIComponent(hash);
|
||||
const target = document.getElementById(id) ?? document.querySelector(`[name="${CSS.escape(id)}"]`);
|
||||
if (target !== null) {
|
||||
target.scrollIntoView();
|
||||
return;
|
||||
}
|
||||
// The target may not be in the DOM yet (pico pages fetch their content).
|
||||
if (remaining > 0) {
|
||||
setTimeout(() => {
|
||||
scrollToHash(hash, remaining - 1);
|
||||
}, RETRY_INTERVAL_MS);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A `<Link>` that also scrolls to the element named by the URL fragment.
|
||||
* Replaces `react-router-hash-link`, which is unmaintained and still targets react-router v5.
|
||||
*/
|
||||
const HashLink = ({ onClick, to, ...rest }: LinkProps) => {
|
||||
const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {
|
||||
onClick?.(e);
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
// Let react-router commit the navigation before looking for the target.
|
||||
setTimeout(() => {
|
||||
scrollToHash(hashOf(to));
|
||||
}, 0);
|
||||
};
|
||||
return <Link {...rest} to={to} onClick={handleClick} />;
|
||||
};
|
||||
|
||||
export default HashLink;
|
||||
@@ -0,0 +1,27 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
margin: 100px 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 8px solid #eeeeee;
|
||||
border-top-color: #cccccc;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.spinner {
|
||||
animation-duration: 4s;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
import React from 'react';
|
||||
import Spinner from 'react-spinner-material';
|
||||
import styles from './Loading.module.css';
|
||||
|
||||
const Loading = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignContent: 'center', margin: '100px 0' }}>
|
||||
<Spinner radius={60} color={'#cccccc'} stroke={8} visible={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const Loading = () => (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.spinner} role="status" aria-label="Loading" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Loading;
|
||||
export default Loading;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../config';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props {
|
||||
@@ -8,8 +7,9 @@ interface Props {
|
||||
|
||||
const NoticeSiteHasBeenArchived = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const notice = '[en]This site has been archived since FY2019 and is no longer updated.[/en][ja]このサイトは、2019年度よりアーカイブサイトとして運用されています。[/ja]';
|
||||
const notice =
|
||||
'[en]This site has been archived since FY2019 and is no longer updated.[/en][ja]このサイトは、2019年度よりアーカイブサイトとして運用されています。[/ja]';
|
||||
return <p style={{ color: 'red' }}>{Functions.mlang(notice, lang)}</p>;
|
||||
}
|
||||
};
|
||||
|
||||
export default NoticeSiteHasBeenArchived;
|
||||
export default NoticeSiteHasBeenArchived;
|
||||
|
||||
@@ -1,54 +1,44 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class PageNotFound extends Component<Props> {
|
||||
const TOP_URL = '/';
|
||||
|
||||
private url = '/';
|
||||
private timer: NodeJS.Timer | null = null;
|
||||
const PageNotFound = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
goToTopPage() {
|
||||
if (this.props.location.pathname !== '/') {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
this.timer = setTimeout(() => {
|
||||
this.timer = null;
|
||||
this.props.history.push(this.url);
|
||||
}, 5000);
|
||||
useEffect(() => {
|
||||
if (location.pathname === TOP_URL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const timer = setTimeout(() => {
|
||||
void navigate(TOP_URL);
|
||||
}, 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [location.pathname, navigate]);
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Page Not Found - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h1>Page Not Found</h1>
|
||||
<section>
|
||||
<p>The page you were trying to access doesn't exist.</p>
|
||||
<p>
|
||||
If the page does not automatically reload, please go to the <a href={TOP_URL}>top page</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
this.goToTopPage();
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Page Not Found - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h1>Page Not Found</h1>
|
||||
<section>
|
||||
<p>The page you were trying to access doesn't exist.</p>
|
||||
<p>If the page does not automatically reload, please click <a href={this.url}>here</a></p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(PageNotFound);
|
||||
export default PageNotFound;
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import ReactHtmlParser, { convertNodeToElement } from 'react-html-parser';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
import { MultiLang } from '../../config';
|
||||
import ReactHtmlParser, {
|
||||
convertNodeToElement,
|
||||
type DomElement,
|
||||
type DomNode,
|
||||
type Transform,
|
||||
} from '@orrisroot/react-html-parser';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import HashLink from './HashLink';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -16,90 +22,121 @@ interface Props {
|
||||
|
||||
const preConvertXCode = (text: string, doxcode: boolean): string => {
|
||||
if (doxcode) {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/sg, (m0, m1) => {
|
||||
return '[code]' + Functions.base64Encode(m1) + '[/code]';
|
||||
return text.replace(/\[code\](.*)\[\/code\]/gs, (_m0, m1) => {
|
||||
return `[code]${Functions.base64Encode(m1)}[/code]`;
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const postConvertXCode = (text: string, doxcode: boolean, doimage: boolean): string => {
|
||||
if (doxcode) {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/sg, (m0, m1) => {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/gs, (_m0, m1) => {
|
||||
const text = convertXCode(Functions.htmlspecialchars(Functions.base64Decode(m1)), doimage);
|
||||
return '<div class="xoopsCode"><pre><code>' + text + '</code></pre></div>';
|
||||
return `<div class="xoopsCode"><pre><code>${text}</code></pre></div>`;
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const convertClickable = (text: string) => {
|
||||
text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/]+)((?:https?|ftp)(?::\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[a-zA-Z0-9=]))/g, (...matches) => {
|
||||
return matches[1] + '<a href="' + matches[2] + '" target="_blank" rel="external noopener noreferrer">' + matches[2] + '</a>';
|
||||
});
|
||||
text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g, (...matches) => {
|
||||
return matches[1] + '<a href="mailto:' + matches[2] + '">' + matches[2] + '</a>';
|
||||
});
|
||||
text = text.replace(
|
||||
/(^|[^\]_a-zA-Z0-9-="'/]+)((?:https?|ftp)(?::\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[a-zA-Z0-9=]))/g,
|
||||
(...matches) => {
|
||||
return (
|
||||
matches[1] +
|
||||
'<a href="' +
|
||||
matches[2] +
|
||||
'" target="_blank" rel="external noopener noreferrer">' +
|
||||
matches[2] +
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
);
|
||||
text = text.replace(
|
||||
/(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g,
|
||||
(...matches) => {
|
||||
return `${matches[1]}<a href="mailto:${matches[2]}">${matches[2]}</a>`;
|
||||
}
|
||||
);
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const convertXCode = (text: string, doimage: boolean): string => {
|
||||
const convertXCode = (text: string, _doimage: boolean): string => {
|
||||
// TODO: implement
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const convertSmiley = (text: string) => {
|
||||
// TODO: implement
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const convertBr = (text: string): string => {
|
||||
return text.replace(/(\r?\n|\r)/g, '<br />');
|
||||
}
|
||||
};
|
||||
|
||||
interface TransformParsedNode {
|
||||
type: string;
|
||||
next: object | null;
|
||||
prev: object | null;
|
||||
parent: object | null;
|
||||
name: string;
|
||||
attribs: any;
|
||||
children: object[];
|
||||
data: string;
|
||||
}
|
||||
|
||||
const cssConvert = (text: string): object => {
|
||||
const ret: any = {};
|
||||
const cssConvert = (text: string): CSSProperties => {
|
||||
const ret: Record<string, string> = {};
|
||||
text.split(';').forEach((line) => {
|
||||
const line_ = line.trim();
|
||||
if (line.length === 0) {
|
||||
if (line_.length === 0) {
|
||||
return;
|
||||
}
|
||||
const kv = line_.split(':');
|
||||
const key = Functions.camelCase(kv[0].trim());
|
||||
const value = kv[1].trim();
|
||||
ret[key] = value;
|
||||
})
|
||||
return ret;
|
||||
}
|
||||
|
||||
const transform = (node: object, idx: number): ReactElement | null | void => {
|
||||
const node_ = node as TransformParsedNode;
|
||||
if (node_.type === 'tag' && node_.name === 'a') {
|
||||
const url = node_.attribs && node_.attribs['href'];
|
||||
const isExternal = !url || /^(mailto|https?:?\/\/)/.test(url);
|
||||
if (!isExternal) {
|
||||
const style = (node_.attribs && node_.attribs['style']) || '';
|
||||
const title = (node_.attribs && node_.attribs['title']) || '';
|
||||
return <HashLink key={idx} to={url} style={cssConvert(style)} title={title}>{node_.children.map((value: object, index: number) => {
|
||||
return convertNodeToElement(value, index, transform);
|
||||
})}</HashLink>;
|
||||
if (kv.length < 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret[Functions.camelCase(kv[0].trim())] = kv.slice(1).join(':').trim();
|
||||
});
|
||||
return ret as CSSProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stored content links are written relative to the page that contains them
|
||||
* (`node4.html`, `manual.html#SECTION1`, ...), so they have to be resolved the
|
||||
* way a browser resolves an `href`. react-router would instead resolve them
|
||||
* against the whole current pathname, turning `/manual_ja/node3.html` +
|
||||
* `node4.html` into `/manual_ja/node3.html/node4.html`.
|
||||
*/
|
||||
const resolveUrl = (href: string, basePath: string): string => {
|
||||
const url = new URL(href, window.location.origin + basePath);
|
||||
return url.pathname + url.search + url.hash;
|
||||
};
|
||||
|
||||
const makeTransform = (basePath: string): Transform => {
|
||||
const transform: Transform = (node: DomNode, idx: number | string) => {
|
||||
if (node.type !== 'tag') {
|
||||
return undefined;
|
||||
}
|
||||
const element = node as DomElement;
|
||||
if (element.name !== 'a') {
|
||||
return undefined;
|
||||
}
|
||||
const url = element.attribs?.href;
|
||||
const download = element.attribs?.download || '';
|
||||
const rel = element.attribs?.rel || '';
|
||||
const klass = element.attribs?.class || '';
|
||||
// Downloads, links to files and links marked external leave the app.
|
||||
const isFile = download !== '' || /\.(zip|pdf|png|gif|jpg)$/.test(url || '');
|
||||
const isExternal = !url || /external/.test(rel) || /external/.test(klass) || /^(mailto|https?:?\/\/)/.test(url);
|
||||
if (isFile || isExternal) {
|
||||
return undefined;
|
||||
}
|
||||
const style = element.attribs?.style || '';
|
||||
const title = element.attribs?.title || '';
|
||||
return (
|
||||
<HashLink key={idx} to={resolveUrl(url, basePath)} style={cssConvert(style)} title={title}>
|
||||
{element.children.map((child, index) => convertNodeToElement(child, index, transform))}
|
||||
</HashLink>
|
||||
);
|
||||
};
|
||||
return transform;
|
||||
};
|
||||
|
||||
const XoopsCode = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const { pathname } = useLocation();
|
||||
let text = props.text;
|
||||
const dohtml = !!props.dohtml;
|
||||
const dosmiley = !!props.dosmiley;
|
||||
@@ -122,7 +159,7 @@ const XoopsCode = (props: Props) => {
|
||||
}
|
||||
text = postConvertXCode(text, doxcode, doimage);
|
||||
text = Functions.mlang(text, lang);
|
||||
return <div>{ReactHtmlParser(text, { transform })}</div>;
|
||||
}
|
||||
return <div>{ReactHtmlParser(text, { transform: makeTransform(pathname) })}</div>;
|
||||
};
|
||||
|
||||
export default XoopsCode;
|
||||
export default XoopsCode;
|
||||
|
||||
+11
-3
@@ -1,9 +1,16 @@
|
||||
const SITE_TITLE = 'OpenNeuro Repository';
|
||||
const SITE_SLOGAN = 'Welcome';
|
||||
const GOOGLE_ANALYTICS_TRACKING_ID = 'UA-122146455-1';
|
||||
// Replaces the Universal Analytics property (UA-122146455-1), shut down in 2023.
|
||||
const GOOGLE_ANALYTICS_TRACKING_ID = 'G-4Y9DZ3VCKD';
|
||||
const XOONIPS_ITEMTYPES = ['binder', 'paper', 'data', 'tool', 'presentation', 'model'];
|
||||
// The site information pages are a pico module named "credits".
|
||||
const PICO_MODULES = ['credits'];
|
||||
|
||||
// Vite's BASE_URL always carries a trailing slash ('/' when the app is served
|
||||
// from the document root). Strip it so it can be concatenated the same way
|
||||
// CRA's Config.BASE_URL was.
|
||||
const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, '');
|
||||
|
||||
export type MultiLang = 'en' | 'ja';
|
||||
|
||||
const Config = {
|
||||
@@ -12,6 +19,7 @@ const Config = {
|
||||
GOOGLE_ANALYTICS_TRACKING_ID,
|
||||
XOONIPS_ITEMTYPES,
|
||||
PICO_MODULES,
|
||||
}
|
||||
BASE_URL,
|
||||
};
|
||||
|
||||
export default Config;
|
||||
export default Config;
|
||||
|
||||
+260
-61
@@ -1,22 +1,23 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import HashLink from '../common/lib/HashLink';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
|
||||
const Categories: React.FC<Props> = (props: Props) => {
|
||||
const Categories = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const title = Functions.mlang('[en]Search by Categories[/en][ja]カテゴリから検索[/ja]', lang);
|
||||
return (
|
||||
<div className="categories">
|
||||
<Helmet>
|
||||
<title>{title} - {Config.SITE_TITLE}</title>
|
||||
<title>
|
||||
{title} - {Config.SITE_TITLE}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h2>{title}</h2>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
@@ -25,47 +26,175 @@ const Categories: React.FC<Props> = (props: Props) => {
|
||||
<div className="name">{Functions.mlang('[ja]動物種[/ja][en]Species[/en]', lang)}</div>
|
||||
<div className="content">
|
||||
<ul>
|
||||
<li><Link to="/database/search/advanced?binder.species=_human_">{Functions.mlang('[ja]ヒト[/ja][en]Human[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_nonhuman%20primate_">{Functions.mlang('[ja]非ヒト霊長目[/ja][en]Nonhuman Primate[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_rodents_">{Functions.mlang('[ja]齧歯目[/ja][en]Rodents[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_other%20vertebrates_">{Functions.mlang('[ja]上記以外の脊椎動物[/ja][en]Other Vertebrates[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_arthropod_">{Functions.mlang('[ja]節足動物[/ja][en]Arthropod[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_other%20invertebrates_">{Functions.mlang('[ja]節足動物以外の無脊椎動物[/ja][en]Other Invertebrates[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_plant_">{Functions.mlang('[ja]植物[/ja][en]Plant[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.species=_others_">{Functions.mlang('[ja]その他[/ja][en]Others[/en]', lang)}</Link></li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_human_">
|
||||
{Functions.mlang('[ja]ヒト[/ja][en]Human[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_nonhuman%20primate_">
|
||||
{Functions.mlang('[ja]非ヒト霊長目[/ja][en]Nonhuman Primate[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_rodents_">
|
||||
{Functions.mlang('[ja]齧歯目[/ja][en]Rodents[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_other%20vertebrates_">
|
||||
{Functions.mlang('[ja]上記以外の脊椎動物[/ja][en]Other Vertebrates[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_arthropod_">
|
||||
{Functions.mlang('[ja]節足動物[/ja][en]Arthropod[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_other%20invertebrates_">
|
||||
{Functions.mlang(
|
||||
'[ja]節足動物以外の無脊椎動物[/ja][en]Other Invertebrates[/en]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_plant_">
|
||||
{Functions.mlang('[ja]植物[/ja][en]Plant[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.species=_others_">
|
||||
{Functions.mlang('[ja]その他[/ja][en]Others[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container">
|
||||
<div className="name">{Functions.mlang('[ja]スケール[/ja][en]Scale[/en]', lang)}</div>
|
||||
<div className="content"><ul>
|
||||
<li><Link to="/database/search/advanced?binder.scale=molecular%20level">{Functions.mlang('[ja]分子レベル[/ja][en]Molecular level[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.scale=cellular%20level">{Functions.mlang('[ja]細胞レベル[/ja][en]Cellular level[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.scale=tissue%2Fnetwork%20level">{Functions.mlang('[ja]組織・ネットワークレベル[/ja][en]Tissue/Network level[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.scale=individual%20level">{Functions.mlang('[ja]個体レベル[/ja][en]Individual level[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.scale=population%20level">{Functions.mlang('[ja]個体集団レベル[/ja][en]Population level[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.scale=multiple%20levels">{Functions.mlang('[ja]マルチレベル[/ja][en]Multiple levels[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.scale=others">{Functions.mlang('[ja]その他[/ja][en]Others[/en]', lang)}</Link></li>
|
||||
</ul>
|
||||
<div className="content">
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=molecular%20level">
|
||||
{Functions.mlang('[ja]分子レベル[/ja][en]Molecular level[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=cellular%20level">
|
||||
{Functions.mlang('[ja]細胞レベル[/ja][en]Cellular level[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=tissue%2Fnetwork%20level">
|
||||
{Functions.mlang(
|
||||
'[ja]組織・ネットワークレベル[/ja][en]Tissue/Network level[/en]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=individual%20level">
|
||||
{Functions.mlang('[ja]個体レベル[/ja][en]Individual level[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=population%20level">
|
||||
{Functions.mlang('[ja]個体集団レベル[/ja][en]Population level[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=multiple%20levels">
|
||||
{Functions.mlang('[ja]マルチレベル[/ja][en]Multiple levels[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.scale=others">
|
||||
{Functions.mlang('[ja]その他[/ja][en]Others[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container">
|
||||
<div className="name">{Functions.mlang('[ja]メソッド[/ja][en]Method[/en]', lang)}</div>
|
||||
<div className="content">
|
||||
<ul>
|
||||
<li><Link to="/database/search/advanced?binder.method=electrophysiology">{Functions.mlang('[ja]電気生理[/ja][en]Electrophysiology[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=brain%20imaging">{Functions.mlang('[ja]脳イメージング[/ja][en]Brain Imaging[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=mathematical%20modeling%2Fanalysis">{Functions.mlang('[ja]数理モデル・解析[/ja][en]Mathematical Modeling/Analysis[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=simulation">{Functions.mlang('[ja]シミュレーション[/ja][en]Simulation[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=molecular%20biology">{Functions.mlang('[ja]分子生物学[/ja][en]Molecular Biology[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=cell%20signaling">{Functions.mlang('[ja]細胞シグナル伝達[/ja][en]Cell Signaling[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=psychophysics">{Functions.mlang('[ja]心理物理学[/ja][en]Psychophysics[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=neurogenetics">{Functions.mlang('[ja]神経遺伝学[/ja][en]Neurogenetics[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=computational%20neuroscience">{Functions.mlang('[ja]計算論的神経科学[/ja][en]Computational Neuroscience[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=behavioral%20neuroscience">{Functions.mlang('[ja]行動学的神経科学[/ja][en]Behavioral Neuroscience[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=clinical%20neuroscience">{Functions.mlang('[ja]臨床神経科学[/ja][en]Clinical Neuroscience[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=integrative%20neuroscience">{Functions.mlang('[ja]統合神経科学[/ja][en]Integrative Neuroscience[/en]', lang)}</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.method=brain%20machine%20interface">{Functions.mlang('[ja]ブレインマシンインタフェイス[/ja][en]Brain Machine Interface[/en]', lang)}</Link></li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=electrophysiology">
|
||||
{Functions.mlang('[ja]電気生理[/ja][en]Electrophysiology[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=brain%20imaging">
|
||||
{Functions.mlang('[ja]脳イメージング[/ja][en]Brain Imaging[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=mathematical%20modeling%2Fanalysis">
|
||||
{Functions.mlang(
|
||||
'[ja]数理モデル・解析[/ja][en]Mathematical Modeling/Analysis[/en]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=simulation">
|
||||
{Functions.mlang('[ja]シミュレーション[/ja][en]Simulation[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=molecular%20biology">
|
||||
{Functions.mlang('[ja]分子生物学[/ja][en]Molecular Biology[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=cell%20signaling">
|
||||
{Functions.mlang('[ja]細胞シグナル伝達[/ja][en]Cell Signaling[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=psychophysics">
|
||||
{Functions.mlang('[ja]心理物理学[/ja][en]Psychophysics[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=neurogenetics">
|
||||
{Functions.mlang('[ja]神経遺伝学[/ja][en]Neurogenetics[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=computational%20neuroscience">
|
||||
{Functions.mlang(
|
||||
'[ja]計算論的神経科学[/ja][en]Computational Neuroscience[/en]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=behavioral%20neuroscience">
|
||||
{Functions.mlang('[ja]行動学的神経科学[/ja][en]Behavioral Neuroscience[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=clinical%20neuroscience">
|
||||
{Functions.mlang('[ja]臨床神経科学[/ja][en]Clinical Neuroscience[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=integrative%20neuroscience">
|
||||
{Functions.mlang('[ja]統合神経科学[/ja][en]Integrative Neuroscience[/en]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.method=brain%20machine%20interface">
|
||||
{Functions.mlang(
|
||||
'[ja]ブレインマシンインタフェイス[/ja][en]Brain Machine Interface[/en]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,43 +204,113 @@ const Categories: React.FC<Props> = (props: Props) => {
|
||||
<div className="name">{Functions.mlang('[ja]タイトル[/ja][en]Title[/en]', lang)}</div>
|
||||
<div className="content">
|
||||
<ul>
|
||||
<li><Link to="/database/list/8">A - E</Link></li>
|
||||
<li><Link to="/database/list/9">F - J</Link></li>
|
||||
<li><Link to="/database/list/10">K - N</Link></li>
|
||||
<li><Link to="/database/list/11">O - S</Link></li>
|
||||
<li><Link to="/database/list/12">T - X</Link></li>
|
||||
<li><Link to="/database/list/13">Y - Z</Link></li>
|
||||
<li><Link to="/database/list/14">Others</Link></li>
|
||||
<li>
|
||||
<Link to="/database/list/8">A - E</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/list/9">F - J</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/list/10">K - N</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/list/11">O - S</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/list/12">T - X</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/list/13">Y - Z</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/list/14">Others</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container reseach_areas">
|
||||
<div className="name">{Functions.mlang('[ja]コンテンツ分野[/ja][en]Research Areas[/en]', lang)}</div>
|
||||
<div className="name">
|
||||
{Functions.mlang('[ja]コンテンツ分野[/ja][en]Research Areas[/en]', lang)}
|
||||
</div>
|
||||
<div className="content">
|
||||
<ul>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=visiome">Visiome Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=bmi">Brain Machine Interface Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=invbrain">Invertebrate Brain Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=braintx">Brain Transcriptome Database</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=cerebellum">Cerebellar Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=nimg">NeuroImaging Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=dynamicbrain">Dynamic Brain Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=cbsn">Comprehensive Brain Science Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=mouse-phenotype">Mouse Phenotype Database</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=sim">Simulation Platform</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=bsd">Brain Science Dictionary</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=vibrism">ViBrism Database</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=cbs-ni">CBS Neuroinformatics</Link></li>
|
||||
<li><Link to="/database/search/advanced?binder.research_area=cns">Comparative Neuroscience Platform</Link></li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=visiome">
|
||||
Visiome Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=bmi">
|
||||
Brain Machine Interface Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=invbrain">
|
||||
Invertebrate Brain Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=braintx">
|
||||
Brain Transcriptome Database
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=cerebellum">
|
||||
Cerebellar Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=nimg">
|
||||
NeuroImaging Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=dynamicbrain">
|
||||
Dynamic Brain Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=cbsn">
|
||||
Comprehensive Brain Science Platform
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=mouse-phenotype">
|
||||
Mouse Phenotype Database
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=sim">Simulation Platform</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=bsd">
|
||||
Brain Science Dictionary
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=vibrism">
|
||||
ViBrism Database
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=cbs-ni">
|
||||
CBS Neuroinformatics
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/database/search/advanced?binder.research_area=cns">
|
||||
Comparative Neuroscience Platform
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="jumpToTop">
|
||||
<a href="#main">Jump to the top</a>
|
||||
<HashLink to="#main">Jump to the top</HashLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Categories;
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Footer: React.FC<Props> = (props: Props) => {
|
||||
return (
|
||||
<footer className="footer">
|
||||
<span className="copyright">Copyright (C) 2018 Neuroinformatics Unit, RIKEN Center for Brain Science</span>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
const Footer = (_props: Props) => (
|
||||
<footer className="footer">
|
||||
<span className="copyright">Copyright (C) 2018 Neuroinformatics Unit, RIKEN Center for Brain Science</span>
|
||||
</footer>
|
||||
);
|
||||
|
||||
export default Footer;
|
||||
|
||||
+83
-75
@@ -1,89 +1,97 @@
|
||||
import React, { ChangeEvent, Component, FormEvent } from 'react';
|
||||
import { Link, RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { type FormEvent, useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router';
|
||||
import SelectLang from '../common/blocks/SelectLang';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import ItemUtil from '../database/lib/ItemUtil';
|
||||
import Functions from '../functions';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
keyword: string;
|
||||
showCreditsMenu: boolean;
|
||||
}
|
||||
const Header = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const navigate = useNavigate();
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [showCreditsMenu, setShowCreditsMenu] = useState(false);
|
||||
|
||||
class Header extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleChangeKeyword = this.handleChangeKeyword.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.onMouseEnter = this.onMouseEnter.bind(this);
|
||||
this.onMouseLeave = this.onMouseLeave.bind(this);
|
||||
this.state = { keyword: '', showCreditsMenu: false };
|
||||
}
|
||||
|
||||
handleChangeKeyword(event: ChangeEvent<HTMLInputElement>) {
|
||||
const keyword = event.target.value;
|
||||
this.setState({ keyword });
|
||||
}
|
||||
|
||||
handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const keyword = this.state.keyword.trim();
|
||||
if (keyword !== '') {
|
||||
const url = ItemUtil.getSearchByKeywordUrl('all', this.state.keyword);
|
||||
this.props.history.push(url);
|
||||
const trimmed = keyword.trim();
|
||||
if (trimmed !== '') {
|
||||
void navigate(ItemUtil.getSearchByKeywordUrl('all', trimmed));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMouseEnter() {
|
||||
this.setState({ showCreditsMenu: true });
|
||||
}
|
||||
|
||||
onMouseLeave() {
|
||||
this.setState({ showCreditsMenu: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
return (
|
||||
<header className="header">
|
||||
<div className="clearfix">
|
||||
<Link className="logo" to="/"><span className="hidden">{Config.SITE_TITLE}</span></Link>
|
||||
<div className="utility">
|
||||
<div><SelectLang lang={lang} /></div>
|
||||
<div className="clearfix">
|
||||
<a className="incf-japan-node" href="https://www.neuroinf.jp/" target="_blank" rel="noopener noreferrer"><span className="hidden">INCF Japan Node</span></a>
|
||||
<div className="search-box">
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input type="text" value={this.state.keyword} onChange={this.handleChangeKeyword} placeholder="enter keywords here..." />
|
||||
</form>
|
||||
</div>
|
||||
return (
|
||||
<header className="header">
|
||||
<div className="clearfix">
|
||||
<Link className="logo" to="/">
|
||||
<span className="hidden">{Config.SITE_TITLE}</span>
|
||||
</Link>
|
||||
<div className="utility">
|
||||
<SelectLang lang={lang} />
|
||||
<div className="clearfix">
|
||||
<a
|
||||
className="incf-japan-node"
|
||||
href="https://www.neuroinf.jp/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="hidden">INCF Japan Node</span>
|
||||
</a>
|
||||
<div className="search-box">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
placeholder="enter keywords here..."
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="mainmenu">
|
||||
<li><Link to="/database/search/itemtype/binder">{Functions.mlang('[en]Contents[/en][ja]コンテンツ[/ja]', lang)}</Link></li>
|
||||
<li><Link to="/categories">{Functions.mlang('[en]Categories[/en][ja]カテゴリから検索[/ja]', lang)}</Link></li>
|
||||
<li><Link to="/overview">{Functions.mlang('[en]Overview[/en][ja]概要[/ja]', lang)}</Link></li>
|
||||
<li><Link to="/news">{Functions.mlang('[en]News[/en][ja]ニュース[/ja]', lang)}</Link></li>
|
||||
<li onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>{Functions.mlang('[en]Credits[/en][ja]サイト情報[/ja]', lang)}
|
||||
{this.state.showCreditsMenu &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/credits/1">Instruction for Use</Link>
|
||||
<Link className="item" to="/credits/2">Copyrights</Link>
|
||||
<Link className="item" to="/credits/3">Privacy Policy</Link>
|
||||
<Link className="item" to="/credits/4">About Us</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
);
|
||||
</div>
|
||||
<ul className="mainmenu">
|
||||
<li>
|
||||
<Link to="/database/search/itemtype/binder">
|
||||
{Functions.mlang('[en]Contents[/en][ja]コンテンツ[/ja]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/categories">
|
||||
{Functions.mlang('[en]Categories[/en][ja]カテゴリから検索[/ja]', lang)}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/overview">{Functions.mlang('[en]Overview[/en][ja]概要[/ja]', lang)}</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/news">{Functions.mlang('[en]News[/en][ja]ニュース[/ja]', lang)}</Link>
|
||||
</li>
|
||||
<li onMouseEnter={() => setShowCreditsMenu(true)} onMouseLeave={() => setShowCreditsMenu(false)}>
|
||||
{Functions.mlang('[en]Credits[/en][ja]サイト情報[/ja]', lang)}
|
||||
{showCreditsMenu && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/credits/1">
|
||||
Instruction for Use
|
||||
</Link>
|
||||
<Link className="item" to="/credits/2">
|
||||
Copyrights
|
||||
</Link>
|
||||
<Link className="item" to="/credits/3">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link className="item" to="/credits/4">
|
||||
About Us
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Header);
|
||||
export default Header;
|
||||
|
||||
+28
-12
@@ -1,21 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const News: React.FC<Props> = (props: Props) => {
|
||||
const News = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const ctitle = Functions.mlang('[en]News[/en][ja]ニュース[/ja]', lang);
|
||||
const title = 'What\'s new';
|
||||
const title = "What's new";
|
||||
return (
|
||||
<div className="news">
|
||||
<Helmet>
|
||||
<title>{title} - {ctitle} - {Config.SITE_TITLE}</title>
|
||||
<title>
|
||||
{title} - {ctitle} - {Config.SITE_TITLE}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h2>{ctitle}</h2>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
@@ -23,20 +24,35 @@ const News: React.FC<Props> = (props: Props) => {
|
||||
<span className="itemTitle">{title}</span>
|
||||
</div>
|
||||
<div className="itemInfo">
|
||||
<span className="itemPoster">Poster : <span className="username">nijcadmin</span></span>
|
||||
<span className="itemPoster">
|
||||
Poster : <span className="username">nijcadmin</span>
|
||||
</span>
|
||||
<span className="itemPostDate"> on 2018-05-29 15:51:21</span>
|
||||
</div>
|
||||
<div className="itemBody">
|
||||
<div className="itemText">
|
||||
<ul>
|
||||
<li>We start accepting new datasets and contents from you. Just select "User Menu » Register" for a workflow to register your dataset.</li>
|
||||
<li>INCF J-Node OpenNeuro Repository has been officially public at the begining of July, 2018.</li>
|
||||
<li>Based on a policy of RIKEN, <u><b>OpenNeuro Repository will be closed soon within March, 2019</b></u>, although we had just opened this service.</li>
|
||||
<li>
|
||||
We start accepting new datasets and contents from you. Just select "User
|
||||
Menu » Register" for a workflow to register your dataset.
|
||||
</li>
|
||||
<li>
|
||||
INCF J-Node OpenNeuro Repository has been officially public at the begining of July, 2018.
|
||||
</li>
|
||||
<li>
|
||||
Based on a policy of RIKEN,{' '}
|
||||
<u>
|
||||
<b>OpenNeuro Repository will be closed soon within March, 2019</b>
|
||||
</u>
|
||||
, although we had just opened this service.
|
||||
</li>
|
||||
</ul>
|
||||
<div className="notice">
|
||||
<h4>IMPORTANT NOTICE:</h4>
|
||||
<ul>
|
||||
<li>OpenNeuro Repository has stopped its service based on the new policy of INCF J-node.</li>
|
||||
<li>
|
||||
OpenNeuro Repository has stopped its service based on the new policy of INCF J-node.
|
||||
</li>
|
||||
<li>Please DO NOT reposit new contents in this database any more.</li>
|
||||
<li>Please DO NOT try to create a new account.</li>
|
||||
</ul>
|
||||
@@ -45,6 +61,6 @@ const News: React.FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default News;
|
||||
|
||||
+15
-9
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import HashLink from '../common/lib/HashLink';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import imageOverview1 from './assets/images/overview1.png';
|
||||
import imageOverview2 from './assets/images/overview2.png';
|
||||
@@ -10,13 +10,15 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Overview: React.FC<Props> = (props: Props) => {
|
||||
const Overview = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const title = Functions.mlang('[en]Overview of OpenNeuro Repository[/en][ja]OpenNeuro Repository 概要[/ja]', lang);
|
||||
return (
|
||||
<div className="overview">
|
||||
<Helmet>
|
||||
<title>{title} - {Config.SITE_TITLE}</title>
|
||||
<title>
|
||||
{title} - {Config.SITE_TITLE}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h2>{title}</h2>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
@@ -24,17 +26,21 @@ const Overview: React.FC<Props> = (props: Props) => {
|
||||
<img src={imageOverview1} alt="Overview" />
|
||||
</div>
|
||||
<p>
|
||||
The INCF J-Node <strong>OpenNeuro</strong> Repository archives research contents, such as primary data (the first-order information) used for peer-reviewed published papers and program codes for data analysis or computational models as supplemental materials of the papers.
|
||||
It covers a variety of contents at multiple scales of time and space, ranging from molecular level to organ and individual levels, across species. The registered contents will be reused by the communities of neuroscience to promote reproducibility, transparency and data accessibility of the research.
|
||||
The INCF J-Node <strong>OpenNeuro</strong> Repository archives research contents, such as primary data
|
||||
(the first-order information) used for peer-reviewed published papers and program codes for data
|
||||
analysis or computational models as supplemental materials of the papers. It covers a variety of
|
||||
contents at multiple scales of time and space, ranging from molecular level to organ and individual
|
||||
levels, across species. The registered contents will be reused by the communities of neuroscience to
|
||||
promote reproducibility, transparency and data accessibility of the research.
|
||||
</p>
|
||||
<div className="overview2">
|
||||
<img src={imageOverview2} alt="Sharing data for your published work with OpenNuero Repository!" />
|
||||
</div>
|
||||
<div className="jumpToTop">
|
||||
<a href="#main">Jump to the top</a>
|
||||
<HashLink to="#main">Jump to the top</HashLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Overview;
|
||||
|
||||
+31
-26
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { Route, Routes } from 'react-router';
|
||||
import XoopsPathRedirect from '../common/XoopsPathRedirect';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Database from '../database/Database';
|
||||
import DatabaseXoopsPathRedirect from '../database/DatabaseXoopsPathRedirect';
|
||||
import Pico from '../pico/Pico';
|
||||
@@ -17,31 +16,37 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Page: React.FC<Props> = (props: Props) => {
|
||||
const Page = (props: Props) => {
|
||||
const { lang } = props;
|
||||
return <div id="page">
|
||||
<Header lang={lang} />
|
||||
<div id="main">
|
||||
<div className="mainContent">
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <Welcome lang={lang} />} />
|
||||
<Route exact path="/categories" render={() => <Categories lang={lang} />} />
|
||||
<Route exact path="/overview" render={() => <Overview lang={lang} />} />
|
||||
<Route exact path="/news" render={() => <News lang={lang} />} />
|
||||
<Route path="/database" render={() => <Database {...props} />} />
|
||||
{Config.PICO_MODULES.map((name) =>
|
||||
<Route key={name} path={'/' + name} render={() => <Pico lang={lang} name={name} />} />
|
||||
)}
|
||||
<Route path="/modules/xoonips" render={() => <DatabaseXoopsPathRedirect lang={lang} />} />
|
||||
{Config.PICO_MODULES.map((name) =>
|
||||
<Route key={name} path={'/modules/' + name} render={() => <PicoXoopsPathRedirect lang={lang} name={name} />} />
|
||||
)}
|
||||
<Route component={XoopsPathRedirect} />
|
||||
</Switch>
|
||||
return (
|
||||
<div id="page">
|
||||
<Header lang={lang} />
|
||||
<div id="main">
|
||||
<div className="mainContent">
|
||||
<Routes>
|
||||
<Route path="/" element={<Welcome lang={lang} />} />
|
||||
<Route path="/categories" element={<Categories lang={lang} />} />
|
||||
<Route path="/overview" element={<Overview lang={lang} />} />
|
||||
<Route path="/news" element={<News lang={lang} />} />
|
||||
<Route path="/database/*" element={<Database lang={lang} />} />
|
||||
{Config.PICO_MODULES.map((name) => (
|
||||
<Route key={name} path={`/${name}/*`} element={<Pico lang={lang} name={name} />} />
|
||||
))}
|
||||
<Route path="/modules/xoonips/*" element={<DatabaseXoopsPathRedirect lang={lang} />} />
|
||||
{Config.PICO_MODULES.map((name) => (
|
||||
<Route
|
||||
key={name}
|
||||
path={`/modules/${name}/*`}
|
||||
element={<PicoXoopsPathRedirect lang={lang} name={name} />}
|
||||
/>
|
||||
))}
|
||||
<Route path="*" element={<XoopsPathRedirect lang={lang} />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
<Footer lang={lang} />
|
||||
</div>
|
||||
<Footer lang={lang} />
|
||||
</div>;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
+25
-12
@@ -1,30 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import imageOpenAccess from './assets/images/open-access.png';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Welcome: React.FC<Props> = (props: Props) => {
|
||||
const Welcome = (props: Props) => {
|
||||
const { lang } = props;
|
||||
return (
|
||||
<div className="welcome">
|
||||
<Helmet>
|
||||
<title>{Config.SITE_TITLE} - {Config.SITE_SLOGAN}</title>
|
||||
<title>
|
||||
{Config.SITE_TITLE} - {Config.SITE_SLOGAN}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h1>Welcome</h1>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<div className="open-access">
|
||||
<img src={imageOpenAccess} alt="Open Access !" />
|
||||
</div>
|
||||
<p>Welcome to J-Node <strong>OpenNeuro</strong> repository!</p>
|
||||
<p>
|
||||
The INCF Japan Node platform <strong>OpenNeuro</strong> repository provides a reliable opportunity for researchers in the field of experimental and computational neuroscience to share supplemental materials to their peer-reviewed publications in academic journals and conference proceedings that are available in the public domain.
|
||||
It aims at playing a core role for promoting and accelerating neuroscience.
|
||||
Toward this end, we encourage databasing of
|
||||
Welcome to J-Node <strong>OpenNeuro</strong> repository!
|
||||
</p>
|
||||
<p>
|
||||
The INCF Japan Node platform <strong>OpenNeuro</strong> repository provides a reliable opportunity for
|
||||
researchers in the field of experimental and computational neuroscience to share supplemental materials
|
||||
to their peer-reviewed publications in academic journals and conference proceedings that are available
|
||||
in the public domain. It aims at playing a core role for promoting and accelerating neuroscience. Toward
|
||||
this end, we encourage databasing of
|
||||
</p>
|
||||
<ul>
|
||||
<li>complete sets of experimental image and/or time-series data,</li>
|
||||
@@ -32,11 +37,19 @@ const Welcome: React.FC<Props> = (props: Props) => {
|
||||
<li>computational models,</li>
|
||||
</ul>
|
||||
<p>
|
||||
which have been described and/or analyzed in the publications, in particular if they are too large in size and/or too complicated to make them public as usual supplemental-material-mechanisms provided by academic journals, or if the authors wish to retain unique services of INCF Japan Node and J-Node <strong>OpenNeuro</strong> repository such as real-time cloud computing services for the data registered in J-Node <strong>OpenNeuro</strong> repository.
|
||||
Each of those materials may be provided by standardized human/machine readable formats, such as model description languages, commonly utilized program source codes and/or executable/machine-readable binary codes. J-Node <strong>OpenNeuro</strong> repository will welcome those materials prior to publication to guarantee data accessibility, which will be accessible only for limited number of peer-reviewers during review process and then opened in the public domain immediately after publication.
|
||||
which have been described and/or analyzed in the publications, in particular if they are too large in
|
||||
size and/or too complicated to make them public as usual supplemental-material-mechanisms provided by
|
||||
academic journals, or if the authors wish to retain unique services of INCF Japan Node and J-Node{' '}
|
||||
<strong>OpenNeuro</strong> repository such as real-time cloud computing services for the data registered
|
||||
in J-Node <strong>OpenNeuro</strong> repository. Each of those materials may be provided by standardized
|
||||
human/machine readable formats, such as model description languages, commonly utilized program source
|
||||
codes and/or executable/machine-readable binary codes. J-Node <strong>OpenNeuro</strong> repository will
|
||||
welcome those materials prior to publication to guarantee data accessibility, which will be accessible
|
||||
only for limited number of peer-reviewers during review process and then opened in the public domain
|
||||
immediately after publication.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Welcome;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 483 B |
+187
-185
@@ -1,23 +1,17 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#root,
|
||||
#page {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #888888;
|
||||
background-color: #fffeee;
|
||||
margin: 0;
|
||||
font-size: 90%;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #888888;
|
||||
background-color: #fffeee;
|
||||
margin: 0;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
h1,
|
||||
@@ -26,374 +20,382 @@ h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
color: inherit;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.802em;
|
||||
border-left: solid 15px #f44903;
|
||||
/* background: url(images/indent.png) no-repeat left center; */
|
||||
text-indent: 10px;
|
||||
margin: 0.67em 0;
|
||||
font-size: 2em;
|
||||
border-left: solid 15px #f44903;
|
||||
text-indent: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.602em;
|
||||
border-left: solid 10px #f44903;
|
||||
text-indent: 10px;
|
||||
font-size: 1.602em;
|
||||
border-left: solid 10px #f44903;
|
||||
text-indent: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.424em;
|
||||
border-left: solid 5px #f44903;
|
||||
text-indent: 10px;
|
||||
font-size: 1.424em;
|
||||
border-left: solid 5px #f44903;
|
||||
text-indent: 10px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.266em;
|
||||
font-size: 1.266em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.125em;
|
||||
font-size: 1.125em;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #f44903;
|
||||
text-decoration: none;
|
||||
color: #f44903;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
}
|
||||
th,
|
||||
td.head {
|
||||
vertical-align: top;
|
||||
vertical-align: top;
|
||||
}
|
||||
td {
|
||||
vertical-align: middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ccc;
|
||||
background-color: #e0e0e0;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ccc;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
tr.even {
|
||||
background-color: #f9f9f9;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
tr.odd {
|
||||
background-color: #fdfdfd;
|
||||
background-color: #fdfdfd;
|
||||
}
|
||||
|
||||
.head,
|
||||
.foot {
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
background-color: #e0e0e0;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.even {
|
||||
background-color: #f9f9f9;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.odd {
|
||||
background-color: #fdfdfd;
|
||||
background-color: #fdfdfd;
|
||||
}
|
||||
|
||||
.clearfix::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.font-weight-bold {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.font-italic {
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.block .blockTitle {
|
||||
height: 21px;
|
||||
margin-bottom: 0.4rem;
|
||||
padding: 0 10px 0 32px;
|
||||
color: #369;
|
||||
line-height: 21px;
|
||||
font-weight: bold;
|
||||
height: 21px;
|
||||
margin-bottom: 0.4rem;
|
||||
padding: 0 10px 0 32px;
|
||||
color: #369;
|
||||
line-height: 21px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.block .blockContent {
|
||||
padding: 0 10px;
|
||||
font-size: 13px;
|
||||
padding: 0 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
header {
|
||||
min-width: 960px;
|
||||
padding-top: 26px;
|
||||
min-width: 960px;
|
||||
padding-top: 26px;
|
||||
}
|
||||
|
||||
header .logo {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 260px;
|
||||
height: 100px;
|
||||
background-image: url(./images/logo.png);
|
||||
display: block;
|
||||
float: left;
|
||||
width: 260px;
|
||||
height: 100px;
|
||||
background-image: url(./images/logo.png);
|
||||
}
|
||||
|
||||
header .utility {
|
||||
float: right;
|
||||
text-align: right;
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
header .utility select {
|
||||
font-size: 13.3333px;
|
||||
line-height: normal;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
header .utility .incf-japan-node {
|
||||
margin: 10px 10px 10px 0;
|
||||
display: block;
|
||||
float: left;
|
||||
width: 160px;
|
||||
height: 32px;
|
||||
background-image: url(./images/incf-japan-node.png);
|
||||
margin: 10px 10px 10px 0;
|
||||
display: block;
|
||||
float: left;
|
||||
width: 160px;
|
||||
height: 32px;
|
||||
background-image: url(./images/incf-japan-node.png);
|
||||
}
|
||||
|
||||
header .utility .search-box {
|
||||
margin: 10px 0 10px 10px;
|
||||
float: right;
|
||||
margin: 10px 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
header .utility .search-box input {
|
||||
width: 195px;
|
||||
padding: 9px 10px;
|
||||
border: none;
|
||||
background: none;
|
||||
font: normal 11px Arial, Helvetica, sans-serif;
|
||||
color: #808080;
|
||||
border: solid 3px #e99e0f;
|
||||
border-radius: 10px;
|
||||
width: 195px;
|
||||
padding: 9px 10px;
|
||||
background-color: #ffffff;
|
||||
font:
|
||||
normal 11px Arial,
|
||||
Helvetica,
|
||||
sans-serif;
|
||||
color: #808080;
|
||||
border: solid 3px #e99e0f;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
header ul.mainmenu {
|
||||
margin: 0 40px;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
list-style-type: none;
|
||||
margin: 0 40px;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
header ul.mainmenu li {
|
||||
margin: 0 2px;
|
||||
width: 120px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
font-size: 0.8em;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #888888;
|
||||
background-color: #ffffff;
|
||||
border: 1px #cccccc solid;
|
||||
border-width: 1px 1px 0 1px;
|
||||
margin: 0 2px;
|
||||
width: 120px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
font-size: 0.8em;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #888888;
|
||||
background-color: #ffffff;
|
||||
border: 1px #cccccc solid;
|
||||
border-width: 1px 1px 0 1px;
|
||||
}
|
||||
|
||||
header ul.mainmenu li a {
|
||||
color: #888888;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
header ul.mainmenu li .submenu {
|
||||
width: 120px;
|
||||
position: relative;
|
||||
float: left;
|
||||
top: -1px;
|
||||
background-color: #e0e0e0;
|
||||
z-index: 10;
|
||||
animation: fadeIn 1s ease 0s 1 normal;
|
||||
width: 120px;
|
||||
position: relative;
|
||||
float: left;
|
||||
top: -1px;
|
||||
background-color: #e0e0e0;
|
||||
z-index: 10;
|
||||
animation: fadeIn 1s ease 0s 1 normal;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
header ul.mainmenu li .submenu .item {
|
||||
display: block;
|
||||
margin: 1px;
|
||||
background-color: #ffffff;
|
||||
display: block;
|
||||
margin: 1px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
footer {
|
||||
min-width: 960px;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
min-width: 960px;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#page {
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.5;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#main {
|
||||
padding: 20px 20px;
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
width: 960px;
|
||||
padding: 20px 20px;
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
width: 960px;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
margin: 0 20px;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.welcome,
|
||||
.overview {
|
||||
text-align: justify;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.welcome ul {
|
||||
padding-left: 20px;
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.welcome ul li {
|
||||
list-style-type: square;
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.overview .overview1,
|
||||
.overview .overview2 {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.news .itemHead {
|
||||
padding: 10px;
|
||||
border-bottom: 1px #f0f0f0 solid;
|
||||
padding: 10px;
|
||||
border-bottom: 1px #f0f0f0 solid;
|
||||
}
|
||||
.news .itemTitle {
|
||||
font-weight: bold;
|
||||
font-size: 1.266em;
|
||||
font-weight: bold;
|
||||
font-size: 1.266em;
|
||||
}
|
||||
.news .itemInfo {
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
font-size: 85%;
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
font-size: 85%;
|
||||
}
|
||||
.news .itemInfo .username {
|
||||
color: #f44903;
|
||||
color: #f44903;
|
||||
}
|
||||
.news .itemText ul {
|
||||
padding-left: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.news .itemText .notice {
|
||||
font-weight: bold;
|
||||
margin-left: 20px;
|
||||
font-weight: bold;
|
||||
margin-left: 20px;
|
||||
}
|
||||
.news .itemText .notice ul {
|
||||
padding-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
.news .itemText .notice ul li {
|
||||
list-style-type: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
.categories .container {
|
||||
float: left;
|
||||
width: 270px;
|
||||
margin: 8px;
|
||||
background-color: #e8e8e8;
|
||||
border-radius: 5px;
|
||||
float: left;
|
||||
width: 270px;
|
||||
margin: 8px;
|
||||
background-color: #e8e8e8;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.categories .container.reseach_areas {
|
||||
width: 556px;
|
||||
width: 556px;
|
||||
}
|
||||
.categories .name {
|
||||
padding: 10px 0 10px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
padding: 10px 0 10px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
}
|
||||
.categories .content {
|
||||
padding: 10px;
|
||||
height: 389px;
|
||||
border: 1px solid #e8e8e8;
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
line-height: 1.7;
|
||||
padding: 10px;
|
||||
height: 389px;
|
||||
border: 1px solid #e8e8e8;
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
.categories .content ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#main .bottom_of_content_body,
|
||||
#main .jumpToTop {
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#main .pico_body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#main .openneuroFreeKeyword > div:not(:last-child),
|
||||
#main .openneuroAuthor > div:not(:last-child) {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #333333;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #333333;
|
||||
}
|
||||
#main .openneuroFreeKeyword > div:not(:first-child),
|
||||
#main .openneuroAuthor > div:not(:first-child) {
|
||||
padding-top: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
#main .openneuroRelatedTo > tbody > tr > td {
|
||||
padding: 10px 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
#main .openneuroRelatedTo > tbody > tr:first-child > td {
|
||||
padding-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
#main .openneuroRelatedTo > tbody > tr:last-child > td {
|
||||
padding-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.listIcon {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
.listIcon img {
|
||||
vertical-align: middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.listBody {
|
||||
vertical-align: top;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@@ -68,3 +68,13 @@
|
||||
.database :global(.advancedSearch .fieldDate input) {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
||||
/* sanitize.css forms.css leaves form controls transparent; keep them on white. */
|
||||
.database :global(.formButton),
|
||||
.database :global(.fieldInputText),
|
||||
.database :global(.fieldSelect),
|
||||
.database :global(.fieldDate select),
|
||||
.database :global(.fieldDate input),
|
||||
.database :global(.listTable select) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
+24
-20
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Route, Routes } from 'react-router';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import styles from './Database.module.css';
|
||||
import DatabaseAdvancedSearch from './DatabaseAdvancedSearch';
|
||||
@@ -22,23 +21,28 @@ const Database = (props: Props) => {
|
||||
return (
|
||||
<div className={styles.database}>
|
||||
<Helmet>
|
||||
<title>{Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.siteTitle(lang)}</title>
|
||||
<title>
|
||||
{Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<Switch>
|
||||
<Route exact path="/database" render={(props: RouteComponentProps) => <DatabaseTop lang={lang} {...props} />} />
|
||||
<Route exact path="/database/item/:id" render={(props: RouteComponentProps<{ id: string }>) => <DatabaseDetailItem lang={lang} {...props} />} />
|
||||
<Route exact path="/database/item/id/:doi" render={(props: RouteComponentProps<{ doi: string }>) => <DatabaseDetailItem lang={lang} {...props} />} />
|
||||
<Route exact path="/database/advanced" render={(props: RouteComponentProps) => <DatabaseAdvancedSearch lang={lang} {...props} />} />
|
||||
<Route exact path="/database/list" render={(props: RouteComponentProps) => <DatabaseSearchByIndexId lang={lang} {...props} />} />
|
||||
<Route exact path="/database/list/:id" render={(props: RouteComponentProps<{ id: string }>) => <DatabaseSearchByIndexId lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search" render={(props: RouteComponentProps) => <DatabaseSearchByKeyword lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search/advanced" render={(props: RouteComponentProps) => <DatabaseSearchByAdvancedKeyword lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search/itemtype/:itemType" render={(props: RouteComponentProps<{ itemType: string }>) => <DatabaseSearchByItemType lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search/itemtype/:itemType/:subItemType" render={(props: RouteComponentProps<{ itemType: string; subItemType: string }>) => <DatabaseSearchByItemType lang={lang} {...props} />} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
<Routes>
|
||||
<Route index element={<DatabaseTop lang={lang} />} />
|
||||
<Route path="item/:id" element={<DatabaseDetailItem lang={lang} />} />
|
||||
<Route path="item/id/:doi" element={<DatabaseDetailItem lang={lang} />} />
|
||||
<Route path="advanced" element={<DatabaseAdvancedSearch lang={lang} />} />
|
||||
<Route path="list" element={<DatabaseSearchByIndexId lang={lang} />} />
|
||||
<Route path="list/:id" element={<DatabaseSearchByIndexId lang={lang} />} />
|
||||
<Route path="search" element={<DatabaseSearchByKeyword lang={lang} />} />
|
||||
<Route path="search/advanced" element={<DatabaseSearchByAdvancedKeyword lang={lang} />} />
|
||||
<Route path="search/itemtype/:itemType" element={<DatabaseSearchByItemType lang={lang} />} />
|
||||
<Route
|
||||
path="search/itemtype/:itemType/:subItemType"
|
||||
element={<DatabaseSearchByItemType lang={lang} />}
|
||||
/>
|
||||
<Route path="*" element={<PageNotFound lang={lang} />} />
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Database;
|
||||
export default Database;
|
||||
|
||||
@@ -1,48 +1,45 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import ItemType from './item-type';
|
||||
import AdvancedSearchQuery from './lib/AdvancedSearchQuery';
|
||||
import ItemUtil from './lib/ItemUtil';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class DatabaseAdvancedSearch extends Component<Props> {
|
||||
const DatabaseAdvancedSearch = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const navigate = useNavigate();
|
||||
// The child fields mutate this instance directly, so it must survive re-renders.
|
||||
const [query] = useState(() => new AdvancedSearchQuery());
|
||||
|
||||
private query: AdvancedSearchQuery = new AdvancedSearchQuery();
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleClickSearchButton = this.handleClickSearchButton.bind(this);
|
||||
}
|
||||
|
||||
handleClickSearchButton() {
|
||||
if (!this.query.empty()) {
|
||||
const url = ItemUtil.getSearchByAdvancedKeywordsUrl(this.query);
|
||||
this.props.history.push(url);
|
||||
const handleClickSearchButton = () => {
|
||||
if (!query.empty()) {
|
||||
void navigate(ItemUtil.getSearchByAdvancedKeywordsUrl(query));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
return (
|
||||
<div className="advancedSearch">
|
||||
<h3>{Functions.mlang('[en]Search Items[/en][ja]アイテム検索[/ja]', lang)}</h3>
|
||||
<div className="search">
|
||||
<button className="formButton" onClick={this.handleClickSearchButton}>Search</button>
|
||||
</div>
|
||||
{Config.XOONIPS_ITEMTYPES.map((type) => {
|
||||
return <ItemType.AdvancedSearch key={type} type={'xnp' + type} lang={lang} query={this.query} />;
|
||||
})}
|
||||
<div className="search">
|
||||
<button className="formButton" onClick={this.handleClickSearchButton}>Search</button>
|
||||
</div>
|
||||
return (
|
||||
<div className="advancedSearch">
|
||||
<h3>{Functions.mlang('[en]Search Items[/en][ja]アイテム検索[/ja]', lang)}</h3>
|
||||
<div className="search">
|
||||
<button type="button" className="formButton" onClick={handleClickSearchButton}>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
{Config.XOONIPS_ITEMTYPES.map((type) => {
|
||||
return <ItemType.AdvancedSearch key={type} type={`xnp${type}`} lang={lang} query={query} />;
|
||||
})}
|
||||
<div className="search">
|
||||
<button type="button" className="formButton" onClick={handleClickSearchButton}>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseAdvancedSearch;
|
||||
export default DatabaseAdvancedSearch;
|
||||
|
||||
@@ -1,89 +1,64 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import ItemType from './item-type';
|
||||
import ItemUtil, { Item } from './lib/ItemUtil';
|
||||
import ItemUtil, { type Item } from './lib/ItemUtil';
|
||||
|
||||
interface Params {
|
||||
id?: string;
|
||||
doi?: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
item: Item | null;
|
||||
}
|
||||
const DatabaseDetailItem = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const params = useParams<{ id?: string; doi?: string }>();
|
||||
const id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
|
||||
const doi = params.doi ?? '';
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [item, setItem] = useState<Item | null>(null);
|
||||
|
||||
class DatabaseDetailItem extends Component<Props, State> {
|
||||
|
||||
private id: number;
|
||||
private doi: string;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
item: null,
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
const receive = (found: Item | null) => {
|
||||
if (active) {
|
||||
setItem(found);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const { params } = this.props.match;
|
||||
this.id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
|
||||
this.doi = typeof params.doi !== 'undefined' ? params.doi : '';
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateItem();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { params } = this.props.match;
|
||||
const id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
|
||||
const doi = typeof params.doi !== 'undefined' ? params.doi : '';
|
||||
if (this.id !== id || this.doi !== doi) {
|
||||
this.id = id;
|
||||
this.doi = doi;
|
||||
this.updateItem();
|
||||
if (doi !== '') {
|
||||
ItemUtil.getByDoi(doi, receive);
|
||||
} else if (id !== 0) {
|
||||
ItemUtil.get(id, receive);
|
||||
} else {
|
||||
receive(null);
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [id, doi]);
|
||||
|
||||
updateItem() {
|
||||
if (this.doi !== '') {
|
||||
ItemUtil.getByDoi(this.doi, (item) => {
|
||||
this.setState({ loading: false, item });
|
||||
});
|
||||
} else if (this.id !== 0) {
|
||||
ItemUtil.get(this.id, (item) => {
|
||||
this.setState({ loading: false, item });
|
||||
});
|
||||
}
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (this.state.loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (this.state.item === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(this.state.item.title, lang)} - {Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{Functions.mlang('[en]Item Detail[/en][ja]アイテム詳細[/ja]', lang)}</h3>
|
||||
<br />
|
||||
<ItemType.Detail lang={lang} item={this.state.item} />
|
||||
</>
|
||||
);
|
||||
if (item === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{Functions.mlang(item.title, lang)} -{' '}
|
||||
{Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{Functions.mlang('[en]Item Detail[/en][ja]アイテム詳細[/ja]', lang)}</h3>
|
||||
<br />
|
||||
<ItemType.Detail lang={lang} item={item} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseDetailItem;
|
||||
export default DatabaseDetailItem;
|
||||
|
||||
@@ -1,56 +1,31 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import AdvancedSearchQuery from './lib/AdvancedSearchQuery';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import type { MultiLang } from '../config';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import ItemUtil, { SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
import ItemUtil, { type SearchCallbackFunc, type SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
lang: MultiLang
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
search: string;
|
||||
query: AdvancedSearchQuery;
|
||||
}
|
||||
const DatabaseSearchByAdvancedKeyword = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const query = useMemo(() => ItemUtil.getAdvancedSearchQueryByQuery(location.search), [location.search]);
|
||||
|
||||
class DatabaseSearchByAdvancedKeyword extends Component<Props, State> {
|
||||
const searchFunc = useCallback(
|
||||
(condition: SortCondition, func: SearchCallbackFunc) => {
|
||||
ItemUtil.getListByAdvancedSearchQuery(query, condition, func);
|
||||
},
|
||||
[query]
|
||||
);
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const search = props.location.search;
|
||||
const query = ItemUtil.getAdvancedSearchQueryByQuery(search);
|
||||
this.state = { search, query };
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const search = nextProps.location.search;
|
||||
if (prevState.search !== search) {
|
||||
const query = ItemUtil.getAdvancedSearchQueryByQuery(search);
|
||||
return { search, query };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return ItemUtil.getSearchByAdvancedKeywordsUrl(this.state.query);
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
ItemUtil.getListByAdvancedSearchQuery(this.state.query, condition, func);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const baseUrl = this.getUrl();
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>Listing item</h3>
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>Listing item</h3>
|
||||
<DatabaseListItem lang={lang} url={ItemUtil.getSearchByAdvancedKeywordsUrl(query)} search={searchFunc} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseSearchByAdvancedKeyword;
|
||||
|
||||
@@ -1,89 +1,60 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Fragment, useCallback } from 'react';
|
||||
import { Link, useParams } from 'react-router';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import DatabaseListIndex from './lib/DatabaseListIndex';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import IndexUtil, { Index, INDEX_ID_PUBLIC } from './lib/IndexUtil';
|
||||
import ItemUtil, { SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
import IndexUtil, { INDEX_ID_PUBLIC, type Index } from './lib/IndexUtil';
|
||||
import ItemUtil, { type SearchCallbackFunc, type SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Params {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface Props extends RouteComponentProps<Params> {
|
||||
export interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
indexId: number;
|
||||
}
|
||||
const DatabaseSearchByIndexId = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const { id } = useParams<{ id?: string }>();
|
||||
const indexId = id ? (id.match(/^\d+$/) !== null ? parseInt(id, 10) : 0) : INDEX_ID_PUBLIC;
|
||||
|
||||
class DatabaseSearchByIndexId extends Component<Props, State> {
|
||||
const searchFunc = useCallback(
|
||||
(condition: SortCondition, func: SearchCallbackFunc) => {
|
||||
if (indexId === 0) {
|
||||
func({ total: 0, data: [] });
|
||||
} else {
|
||||
ItemUtil.getListByIndexId(indexId, condition, func);
|
||||
}
|
||||
},
|
||||
[indexId]
|
||||
);
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { params } = props.match;
|
||||
this.state = {
|
||||
indexId: params.id ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : INDEX_ID_PUBLIC,
|
||||
}
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
const index = IndexUtil.get(indexId);
|
||||
if (index === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { params } = nextProps.match;
|
||||
const indexId = params.id ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : INDEX_ID_PUBLIC;
|
||||
if (prevState.indexId !== indexId) {
|
||||
return { indexId };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
if (this.state.indexId === 0) {
|
||||
return '/';
|
||||
}
|
||||
return IndexUtil.getUrl(this.state.indexId);
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
if (this.state.indexId === 0) {
|
||||
const res = { total: 0, data: [] };
|
||||
func(res);
|
||||
} else {
|
||||
ItemUtil.getListByIndexId(this.state.indexId, condition, func);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const index = IndexUtil.get(this.state.indexId);
|
||||
if (index === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const baseUrl = this.getUrl();
|
||||
const pIndexes = IndexUtil.getParents(this.state.indexId);
|
||||
const parents = pIndexes.map((value: Index) => {
|
||||
const url: string = IndexUtil.getUrl(value.id);
|
||||
const title = Functions.mlang(value.title, lang);
|
||||
return <Fragment key={value.id}>/ <Link to={url}>{title}</Link> </Fragment>;
|
||||
});
|
||||
const title = pIndexes.map((value) => { return '/' + Functions.mlang(value.title, lang); }).join('');
|
||||
return (
|
||||
<div className="list">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(title, lang)} - {Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<div>{parents}</div>
|
||||
<DatabaseListIndex lang={lang} index={index} />
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
const baseUrl = indexId === 0 ? '/' : IndexUtil.getUrl(indexId);
|
||||
const pIndexes = IndexUtil.getParents(indexId);
|
||||
const parents = pIndexes.map((value: Index) => (
|
||||
<Fragment key={value.id}>
|
||||
/ <Link to={IndexUtil.getUrl(value.id)}>{Functions.mlang(value.title, lang)}</Link>{' '}
|
||||
</Fragment>
|
||||
));
|
||||
const title = pIndexes.map((value) => `/${Functions.mlang(value.title, lang)}`).join('');
|
||||
return (
|
||||
<div className="list">
|
||||
<Helmet>
|
||||
<title>
|
||||
{Functions.mlang(title, lang)} - {Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} -{' '}
|
||||
{Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<div>{parents}</div>
|
||||
<DatabaseListIndex lang={lang} index={index} />
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={searchFunc} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseSearchByIndexId;
|
||||
|
||||
@@ -1,73 +1,41 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import { useCallback } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import ItemUtil, { SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
import ItemUtil, { type SearchCallbackFunc, type SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Params {
|
||||
itemType: string;
|
||||
subItemType?: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
itemType: string;
|
||||
subItemType: string;
|
||||
}
|
||||
const DatabaseSearchByItemType = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const params = useParams<{ itemType?: string; subItemType?: string }>();
|
||||
const itemType = params.itemType ?? '';
|
||||
const subItemType = params.subItemType ?? '';
|
||||
|
||||
class DatabaseSearchByItemType extends Component<Props, State> {
|
||||
const searchFunc = useCallback(
|
||||
(condition: SortCondition, func: SearchCallbackFunc) => {
|
||||
if (itemType === '') {
|
||||
func({ total: 0, data: [] });
|
||||
} else {
|
||||
ItemUtil.getListByItemType(itemType, subItemType, condition, func);
|
||||
}
|
||||
},
|
||||
[itemType, subItemType]
|
||||
);
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { params } = this.props.match;
|
||||
this.state = {
|
||||
itemType: params.itemType ? params.itemType : '',
|
||||
subItemType: params.subItemType ? params.subItemType : '',
|
||||
};
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
let baseUrl = ItemUtil.getItemTypeSearchUrl(itemType);
|
||||
if (subItemType !== '') {
|
||||
baseUrl += `/${subItemType}`;
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { params } = nextProps.match;
|
||||
const item_type = params.itemType ? params.itemType : '';
|
||||
const sub_item_type = params.subItemType ? params.subItemType : '';
|
||||
if (prevState.itemType !== item_type || prevState.subItemType !== sub_item_type) {
|
||||
return { item_type, sub_item_type };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
if (this.state.itemType === '') {
|
||||
const res = { total: 0, data: [] };
|
||||
func(res);
|
||||
} else {
|
||||
ItemUtil.getListByItemType(this.state.itemType, this.state.subItemType, condition, func);
|
||||
}
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
let url = ItemUtil.getItemTypeSearchUrl(this.state.itemType);
|
||||
if (this.state.subItemType !== '') {
|
||||
url += '/' + this.state.subItemType;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const baseUrl = this.getUrl();
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={searchFunc} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseSearchByItemType;
|
||||
|
||||
@@ -1,60 +1,39 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import { useCallback } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import ItemUtil, { KeywordSearchType, SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
import ItemUtil, { type SearchCallbackFunc, type SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
type: KeywordSearchType;
|
||||
keyword: string;
|
||||
}
|
||||
const DatabaseSearchByKeyword = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(location.search);
|
||||
|
||||
class DatabaseSearchByKeyword extends Component<Props, State> {
|
||||
const searchFunc = useCallback(
|
||||
(condition: SortCondition, func: SearchCallbackFunc) => {
|
||||
if (keyword === '') {
|
||||
func({ total: 0, data: [] });
|
||||
} else {
|
||||
ItemUtil.getListByKeyword(type, keyword, condition, func);
|
||||
}
|
||||
},
|
||||
[type, keyword]
|
||||
);
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(this.props.location.search);
|
||||
this.state = { type, keyword };
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(nextProps.location.search);
|
||||
if (prevState.type !== type || prevState.keyword !== keyword) {
|
||||
return { type, keyword };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return ItemUtil.getSearchByKeywordUrl(this.state.type, this.state.keyword);
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
if (this.state.keyword === '') {
|
||||
const res = { total: 0, data: [] };
|
||||
func(res);
|
||||
} else {
|
||||
ItemUtil.getListByKeyword(this.state.type, this.state.keyword, condition, func);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const baseUrl = this.getUrl();
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<p>{Functions.mlang('[en]Search Keyword[/en][ja]検索キーワード[/ja]', lang)} : {this.state.keyword}</p>
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<p>
|
||||
{Functions.mlang('[en]Search Keyword[/en][ja]検索キーワード[/ja]', lang)} : {keyword}
|
||||
</p>
|
||||
<DatabaseListItem lang={lang} url={ItemUtil.getSearchByKeywordUrl(type, keyword)} search={searchFunc} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseSearchByKeyword;
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
.itemTypes .itemType {
|
||||
width: 50%;
|
||||
padding: 5px;
|
||||
vertical-align: top;
|
||||
width: 50%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.itemTypes .itemType :global(table) {
|
||||
width: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.itemTypes .itemType :global(table .itemTypeName) {
|
||||
vertical-align: middle;
|
||||
font-size: large;
|
||||
vertical-align: middle;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
/* OpenNeuro extensions: the item type boxes sit on the black main area. */
|
||||
.itemTypes .itemType {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.itemTypes .itemType > div {
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
color: black;
|
||||
padding: 10px;
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import styles from './DatabaseTop.module.css';
|
||||
import ItemType from './item-type';
|
||||
|
||||
@@ -19,13 +18,13 @@ const DatabaseTop = (props: Props) => {
|
||||
return (
|
||||
<table className={styles.itemTypes}>
|
||||
<tbody>
|
||||
{types.map((value, idx) => {
|
||||
{types.map((value) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
{value.map((type, idx) => {
|
||||
<tr key={value.join('-')}>
|
||||
{value.map((type) => {
|
||||
return (
|
||||
<td key={idx} className={styles.itemType}>
|
||||
{type !== '' && <ItemType.Top lang={lang} type={'xnp' + type} />}
|
||||
<td key={type} className={styles.itemType}>
|
||||
{type !== '' && <ItemType.Top lang={lang} type={`xnp${type}`} />}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
@@ -35,6 +34,6 @@ const DatabaseTop = (props: Props) => {
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default DatabaseTop;
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, RouteComponentProps, withRouter } from 'react-router';
|
||||
import { Navigate, useLocation } from 'react-router';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
|
||||
getRedirectUrl(): string {
|
||||
const { location } = this.props;
|
||||
const getRedirectUrl = (location: { pathname: string; search: string }): string => {
|
||||
{
|
||||
const pathname = location.pathname || '';
|
||||
const query = new URLSearchParams(location.search);
|
||||
const search = new RegExp('^/modules/xoonips(?:/+(.*))?$');
|
||||
const search = /^\/modules\/xoonips(?:\/+(.*))?$/;
|
||||
const matches = pathname.match(search);
|
||||
if (matches === null) {
|
||||
return '';
|
||||
@@ -28,11 +25,11 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
case 'detail.php': {
|
||||
const id = query.get('id');
|
||||
if (id !== null) {
|
||||
return '/database/item/id/' + Functions.escape(id);
|
||||
return `/database/item/id/${Functions.escape(id)}`;
|
||||
}
|
||||
const itemId = query.get('item_id');
|
||||
if (itemId !== null && itemId.match(/^\d+$/) !== null) {
|
||||
return '/database/item/' + Functions.escape(itemId);
|
||||
return `/database/item/${Functions.escape(itemId)}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
@@ -40,13 +37,13 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
const indexId = query.get('index_id');
|
||||
if (indexId !== null && indexId.match(/^\d+$/) !== null) {
|
||||
const params = new URLSearchParams();
|
||||
const map: any = {
|
||||
const map: Record<string, { key: string; isNumber: boolean }> = {
|
||||
orderby: { key: 'orderby', isNumber: false },
|
||||
order_dir: { key: 'order_dir', isNumber: true },
|
||||
itemcount: { key: 'itemcount', isNumber: true },
|
||||
page: { key: 'page', isNumber: true }
|
||||
page: { key: 'page', isNumber: true },
|
||||
};
|
||||
for (let k in map) {
|
||||
for (const k in map) {
|
||||
const v = query.get(k);
|
||||
if (v === null || v.length === 0) {
|
||||
continue;
|
||||
@@ -57,7 +54,7 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
params.set(map[k].key, v);
|
||||
}
|
||||
const paramStr = params.toString();
|
||||
return '/database/list/' + Functions.escape(indexId) + (paramStr.length > 0 ? '?' + paramStr : '');
|
||||
return `/database/list/${Functions.escape(indexId)}${paramStr.length > 0 ? `?${paramStr}` : ''}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -78,13 +75,13 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
return '';
|
||||
}
|
||||
const params = new URLSearchParams({ type, keyword });
|
||||
const map: any = {
|
||||
const map: Record<string, { key: string; isNumber: boolean }> = {
|
||||
orderby: { key: 'orderby', isNumber: false },
|
||||
orderdir: { key: 'order_dir', isNumber: true },
|
||||
item_per_page: { key: 'itemcount', isNumber: true },
|
||||
page: { key: 'page', isNumber: true }
|
||||
page: { key: 'page', isNumber: true },
|
||||
};
|
||||
for (let k in map) {
|
||||
for (const k in map) {
|
||||
const v = query.get(k);
|
||||
if (v === null || v.length === 0) {
|
||||
continue;
|
||||
@@ -94,7 +91,7 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
}
|
||||
params.set(map[k].key, v);
|
||||
}
|
||||
return '/database/search?' + params.toString();
|
||||
return `/database/search?${params.toString()}`;
|
||||
}
|
||||
case 'itemtypesearch': {
|
||||
const itemType = query.get('search_itemtype');
|
||||
@@ -102,22 +99,22 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
return '';
|
||||
}
|
||||
const type = itemType.replace('xnp', '');
|
||||
return '/database/search/itemtype/' + Functions.escape(type);
|
||||
return `/database/search/itemtype/${Functions.escape(type)}`;
|
||||
}
|
||||
case 'itemsubtypesearch': {
|
||||
let type = '';
|
||||
let subtype = '';
|
||||
query.forEach((v, k) => {
|
||||
if (k.match(/^xnp[a-z]+$/) !== null && !!v) {
|
||||
if (k.match(/^xnp[a-z]+$/) !== null && v) {
|
||||
type = k.replace('xnp', '');
|
||||
return;
|
||||
}
|
||||
})
|
||||
});
|
||||
if (type === '') {
|
||||
return '';
|
||||
}
|
||||
query.forEach((v, k) => {
|
||||
if (k.match(`^xnp${type}_.+$`) !== null && !!v) {
|
||||
if (k.match(`^xnp${type}_.+$`) !== null && v) {
|
||||
subtype = v;
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +122,7 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
if (subtype === '') {
|
||||
return '';
|
||||
}
|
||||
return '/database/search/itemtype/' + Functions.escape(type) + '/' + Functions.escape(subtype);
|
||||
return `/database/search/itemtype/${Functions.escape(type)}/${Functions.escape(subtype)}`;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
@@ -136,15 +133,16 @@ class DatabaseXoopsPathRedirect extends Component<Props> {
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const url = this.getRedirectUrl();
|
||||
if (url === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <Redirect to={url} />;
|
||||
const DatabaseXoopsPathRedirect = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const url = getRedirectUrl(location);
|
||||
if (url === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
}
|
||||
return <Navigate to={url} replace />;
|
||||
};
|
||||
|
||||
export default withRouter(DatabaseXoopsPathRedirect);
|
||||
export default DatabaseXoopsPathRedirect;
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
border-bottom: 1px solid #404040;
|
||||
border-right: 1px solid #404040;
|
||||
background-color: white;
|
||||
height: 400px;
|
||||
width: calc(100% - 6px);
|
||||
/* border-box totals: 400 + 3px padding + 1px border per side, and a width
|
||||
that keeps the 2px the original content-box rule overflowed by. */
|
||||
height: 408px;
|
||||
width: calc(100% + 2px);
|
||||
overflow: auto;
|
||||
margin: 0 auto;
|
||||
padding: 3px;
|
||||
@@ -14,6 +16,8 @@
|
||||
|
||||
.formButton {
|
||||
margin: 3px 3px 10px;
|
||||
/* sanitize.css forms.css leaves buttons transparent */
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.indexTree div span {
|
||||
@@ -44,7 +48,9 @@
|
||||
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:not(:last-child)) {
|
||||
background-position: -9px 0;
|
||||
}
|
||||
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:not(:last-child).rc-tree-indent-unit-end) {
|
||||
.indexTree:global(
|
||||
.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:not(:last-child).rc-tree-indent-unit-end
|
||||
) {
|
||||
background-position: -18px 0;
|
||||
}
|
||||
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:last-child.rc-tree-indent-unit-end) {
|
||||
@@ -88,4 +94,4 @@
|
||||
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-node-content-wrapper .rc-tree-title) {
|
||||
vertical-align: middle;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,95 @@
|
||||
import Tree from 'rc-tree';
|
||||
import { DataNode, EventDataNode } from 'rc-tree/lib/interface';
|
||||
import React, { Component, ReactText } from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { MultiLang } from '../../config';
|
||||
import type { DataNode, EventDataNode } from 'rc-tree/lib/interface';
|
||||
import { type Key, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import IndexUtil, { Index, INDEX_ID_PUBLIC } from '../lib/IndexUtil';
|
||||
import IndexUtil, { INDEX_ID_PUBLIC, type Index } from '../lib/IndexUtil';
|
||||
import styles from './IndexTree.module.css';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
tree: DataNode[];
|
||||
interface TreeData {
|
||||
elements: DataNode[];
|
||||
expandableKeys: string[];
|
||||
expandedKeys: string[];
|
||||
selectedKeys: number[];
|
||||
defaultExpandedKeys: string[];
|
||||
}
|
||||
|
||||
class IndexTree extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleClickOpenAll = this.handleClickOpenAll.bind(this);
|
||||
this.handleClickCloseAll = this.handleClickCloseAll.bind(this);
|
||||
this.handleExpand = this.handleExpand.bind(this);
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
const res = this.load();
|
||||
this.state = {
|
||||
tree: res.elements,
|
||||
expandableKeys: res.keys,
|
||||
expandedKeys: res.expandedKeys,
|
||||
selectedKeys: [],
|
||||
};
|
||||
}
|
||||
|
||||
load() {
|
||||
const { lang } = this.props;
|
||||
let keys: string[] = [];
|
||||
let eKeys: string[] = [];
|
||||
const makeTreeNode = (index: Index, depth: number): DataNode => {
|
||||
const title = Functions.mlang(index.title, lang) + (index.numOfItems > 0 ? ' (' + index.numOfItems + ')' : '');
|
||||
const children = IndexUtil.getChildren(index.id);
|
||||
if (children.length === 0) {
|
||||
return { key: String(index.id), title: title };
|
||||
}
|
||||
const childTreeNodes = children.map((value: Index) => {
|
||||
return makeTreeNode(value, depth + 1);
|
||||
});
|
||||
if (depth < 1) {
|
||||
eKeys.push(String(index.id));
|
||||
}
|
||||
keys.push(String(index.id));
|
||||
return { key: String(index.id), title: title, children: childTreeNodes };
|
||||
};
|
||||
let elements: DataNode[] = [];
|
||||
const index = IndexUtil.get(INDEX_ID_PUBLIC);
|
||||
if (index !== null) {
|
||||
elements.push(makeTreeNode(index, 0));
|
||||
const load = (lang: MultiLang): TreeData => {
|
||||
const expandableKeys: string[] = [];
|
||||
const defaultExpandedKeys: string[] = [];
|
||||
const makeTreeNode = (index: Index, depth: number): DataNode => {
|
||||
const title = Functions.mlang(index.title, lang) + (index.numOfItems > 0 ? ` (${index.numOfItems})` : '');
|
||||
const children = IndexUtil.getChildren(index.id);
|
||||
if (children.length === 0) {
|
||||
return { key: String(index.id), title: title };
|
||||
}
|
||||
return {
|
||||
elements: elements,
|
||||
keys: keys,
|
||||
expandedKeys: eKeys,
|
||||
const childTreeNodes = children.map((value: Index) => makeTreeNode(value, depth + 1));
|
||||
if (depth < 1) {
|
||||
defaultExpandedKeys.push(String(index.id));
|
||||
}
|
||||
expandableKeys.push(String(index.id));
|
||||
return { key: String(index.id), title: title, children: childTreeNodes };
|
||||
};
|
||||
const elements: DataNode[] = [];
|
||||
const index = IndexUtil.get(INDEX_ID_PUBLIC);
|
||||
if (index !== null) {
|
||||
elements.push(makeTreeNode(index, 0));
|
||||
}
|
||||
return { elements, expandableKeys, defaultExpandedKeys };
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang } = this.props;
|
||||
if (prevProps.lang !== lang) {
|
||||
const res = this.load();
|
||||
this.setState({
|
||||
tree: res.elements,
|
||||
selectedKeys: [],
|
||||
});
|
||||
const toStringKeys = (keys: Key[]): string[] => keys.map((key) => (typeof key === 'string' ? key : String(key)));
|
||||
|
||||
const IndexTree = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const data = useMemo(() => load(lang), [lang]);
|
||||
const [expandedKeys, setExpandedKeys] = useState<string[]>(data.defaultExpandedKeys);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleExpand = (
|
||||
keys: Key[],
|
||||
_info: { node: EventDataNode<DataNode>; expanded: boolean; nativeEvent: MouseEvent }
|
||||
) => {
|
||||
setExpandedKeys(toStringKeys(keys));
|
||||
};
|
||||
|
||||
const handleSelect = (
|
||||
selectedKeys: Key[],
|
||||
_info: {
|
||||
event: 'select';
|
||||
selected: boolean;
|
||||
node: EventDataNode<DataNode>;
|
||||
selectedNodes: DataNode[];
|
||||
nativeEvent: MouseEvent;
|
||||
}
|
||||
}
|
||||
) => {
|
||||
const selectedKey = selectedKeys[0] ?? 0;
|
||||
const key = typeof selectedKey === 'string' ? parseInt(selectedKey, 10) : Number(selectedKey);
|
||||
void navigate(IndexUtil.getUrl(key));
|
||||
};
|
||||
|
||||
handleClickOpenAll() {
|
||||
this.setState({ expandedKeys: this.state.expandableKeys })
|
||||
}
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<button type="button" className={styles.formButton} onClick={() => setExpandedKeys(data.expandableKeys)}>
|
||||
open all
|
||||
</button>
|
||||
<button type="button" className={styles.formButton} onClick={() => setExpandedKeys([])}>
|
||||
close all
|
||||
</button>
|
||||
<Tree
|
||||
className={styles.indexTree}
|
||||
expandedKeys={expandedKeys}
|
||||
selectedKeys={[]}
|
||||
onExpand={handleExpand}
|
||||
onSelect={handleSelect}
|
||||
showIcon={false}
|
||||
treeData={data.elements}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
handleClickCloseAll() {
|
||||
this.setState({ expandedKeys: [] })
|
||||
}
|
||||
|
||||
handleExpand(expandedKeys: ReactText[], info: {
|
||||
node: EventDataNode;
|
||||
expanded: boolean;
|
||||
nativeEvent: MouseEvent;
|
||||
}): void {
|
||||
const keys: string[] = expandedKeys.map((key) => {
|
||||
return typeof key === 'string' ? key : String(key);
|
||||
});
|
||||
this.setState({ expandedKeys: keys });
|
||||
}
|
||||
|
||||
handleSelect(selectedKeys: ReactText[], info: {
|
||||
event: 'select';
|
||||
selected: boolean;
|
||||
node: EventDataNode;
|
||||
selectedNodes: DataNode[];
|
||||
nativeEvent: MouseEvent;
|
||||
}): void {
|
||||
const selectedKey = selectedKeys.shift() || 0;
|
||||
const key = typeof selectedKey === 'string' ? parseInt(selectedKey, 10) : selectedKey;
|
||||
const url = IndexUtil.getUrl(key);
|
||||
this.props.history.push(url);
|
||||
this.setState({ selectedKeys: [] });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<button className={styles.formButton} onClick={this.handleClickOpenAll}>open all</button>
|
||||
<button className={styles.formButton} onClick={this.handleClickCloseAll}>close all</button>
|
||||
<Tree className={styles.indexTree} expandedKeys={this.state.expandedKeys} selectedKeys={this.state.selectedKeys} onExpand={this.handleExpand} onSelect={this.handleSelect} showIcon={false} treeData={this.state.tree} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(IndexTree);
|
||||
export default IndexTree;
|
||||
|
||||
@@ -1,90 +1,69 @@
|
||||
import React, { ChangeEvent, Component, FormEvent } from 'react';
|
||||
import { Link, RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import Config, { MultiLang } from '../../config';
|
||||
import { type ChangeEvent, type FormEvent, useEffect, useState } from 'react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router';
|
||||
import Config, { type MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import ItemUtil, { KeywordSearchType } from '../lib/ItemUtil';
|
||||
import ItemUtil, { type KeywordSearchType } from '../lib/ItemUtil';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
type: KeywordSearchType;
|
||||
keyword: string;
|
||||
pathname: string;
|
||||
}
|
||||
const SEARCH_PATH = '/database/search';
|
||||
|
||||
class Search extends Component<Props, State> {
|
||||
const Search = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [type, setType] = useState<KeywordSearchType>('all');
|
||||
const [keyword, setKeyword] = useState('');
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
type: 'all',
|
||||
keyword: '',
|
||||
pathname: '',
|
||||
};
|
||||
this.handleChangeType = this.handleChangeType.bind(this);
|
||||
this.handleChangeKeyword = this.handleChangeKeyword.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const pathname = nextProps.location.pathname;
|
||||
if (pathname !== prevState.pathname) {
|
||||
if (pathname === '/database/search') {
|
||||
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(nextProps.location.search);
|
||||
return { type, keyword, pathname };
|
||||
}
|
||||
return {
|
||||
type: prevState.type,
|
||||
keyword: prevState.keyword,
|
||||
pathname
|
||||
};
|
||||
// Landing on the results page adopts the query it was called with; moving
|
||||
// anywhere else leaves whatever the visitor has typed alone.
|
||||
useEffect(() => {
|
||||
if (location.pathname === SEARCH_PATH) {
|
||||
const parsed = ItemUtil.getSearchKeywordByQuery(location.search);
|
||||
setType(parsed.type);
|
||||
setKeyword(parsed.keyword);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, [location.pathname, location.search]);
|
||||
|
||||
handleChangeKeyword(event: ChangeEvent<HTMLInputElement>) {
|
||||
const keyword = event.target.value.trim();
|
||||
this.setState({ keyword });
|
||||
}
|
||||
|
||||
handleChangeType(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const type = event.target.value as KeywordSearchType;
|
||||
this.setState({ type });
|
||||
}
|
||||
|
||||
handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const url = ItemUtil.getSearchByKeywordUrl(this.state.type, this.state.keyword);
|
||||
this.props.history.push(url);
|
||||
}
|
||||
void navigate(ItemUtil.getSearchByKeywordUrl(type, keyword));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const options = [
|
||||
{ value: 'all', label: '[en]All[/en][ja]全て[/ja]' },
|
||||
{ value: 'basic', label: '[en]Title & Keyword[/en][ja]タイトル&キーワード[/ja]' },
|
||||
];
|
||||
Config.XOONIPS_ITEMTYPES.forEach((type) => {
|
||||
options.push({value: type, label: Functions.pascalCase(type)})
|
||||
});
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input style={{width: '170px'}} type="text" value={this.state.keyword} onChange={this.handleChangeKeyword} />
|
||||
|
||||
<select value={this.state.type} onChange={this.handleChangeType}>
|
||||
{options.map((option) => {
|
||||
return <option key={option.value} value={option.value}>{Functions.mlang(option.label, lang)}</option>;
|
||||
})}
|
||||
</select>
|
||||
<br />
|
||||
<input className="formButton" type="submit" value="Search" />
|
||||
|
||||
<Link to="/database/advanced">{Functions.mlang('[en]Advanced[/en][ja]詳細検索[/ja]', lang)}</Link>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
const options = [
|
||||
{ value: 'all', label: '[en]All[/en][ja]全て[/ja]' },
|
||||
{ value: 'basic', label: '[en]Title & Keyword[/en][ja]タイトル&キーワード[/ja]' },
|
||||
];
|
||||
Config.XOONIPS_ITEMTYPES.forEach((itemType) => {
|
||||
options.push({ value: itemType, label: Functions.pascalCase(itemType) });
|
||||
});
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
style={{ width: '170px' }}
|
||||
type="text"
|
||||
value={keyword}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => setKeyword(event.target.value.trim())}
|
||||
/>
|
||||
|
||||
<select
|
||||
value={type}
|
||||
onChange={(event: ChangeEvent<HTMLSelectElement>) => setType(event.target.value as KeywordSearchType)}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{Functions.mlang(option.label, lang)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<br />
|
||||
<input className="formButton" type="submit" value="Search" />
|
||||
|
||||
<Link to="/database/advanced">{Functions.mlang('[en]Advanced[/en][ja]詳細検索[/ja]', lang)}</Link>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(Search);
|
||||
export default Search;
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import AdvancedSearchBase, { AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
import AdvancedSearchBase, { type AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
|
||||
class BinderAdvancedSearch extends AdvancedSearchBase {
|
||||
|
||||
constructor(props: AdvancedSearchBaseProps) {
|
||||
super(props);
|
||||
this.type = 'binder';
|
||||
this.title = 'Binder';
|
||||
this.state.values['title'] = '';
|
||||
this.state.values['keyword'] = '';
|
||||
this.state.values['description'] = '';
|
||||
this.state.values['doi'] = '';
|
||||
this.state.values.title = '';
|
||||
this.state.values.keyword = '';
|
||||
this.state.values.description = '';
|
||||
this.state.values.doi = '';
|
||||
}
|
||||
|
||||
getRows() {
|
||||
const rows = [
|
||||
{ label: '[en]Title[/en][ja]タイトル[/ja]', value: this.renderFieldInputText('title', 50) },
|
||||
{ label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]', value: this.renderFieldInputText('keyword', 50) },
|
||||
{
|
||||
label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]',
|
||||
value: this.renderFieldInputText('keyword', 50),
|
||||
},
|
||||
{ label: '[en]Description[/en][ja]概要[/ja]', value: this.renderFieldInputText('description', 50) },
|
||||
{ label: 'ID', value: this.renderFieldInputText('doi', 50) },
|
||||
];
|
||||
|
||||
@@ -1,105 +1,114 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../config';
|
||||
import Functions from '../../../functions';
|
||||
import { ItemBinder } from '../../lib/ItemUtil';
|
||||
import type { ItemBinder } from '../../lib/ItemUtil';
|
||||
import DetailBase from '../lib/DetailBase';
|
||||
import ItemTypeField from '../lib/field';
|
||||
import OpenNeuroFreeKeyword from '../lib/field/OpenNeuroFreeKeyword';
|
||||
import OpenNeuroRelatedTo from '../lib/field/OpenNeuroRelatedTo';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
item: ItemBinder;
|
||||
}
|
||||
// OpenNeuro extensions: labels of the binder categories, keyed by the stored value.
|
||||
const RESEARCH_AREAS: Record<string, string> = {
|
||||
visiome: 'Visiome Platform',
|
||||
bmi: 'Brain Machine Interface Platform',
|
||||
invbrain: 'Invertebrate Brain Platform',
|
||||
braintx: 'Brain Transcriptome Database',
|
||||
cerebellum: 'Cerebellar Platform',
|
||||
nimg: 'NeuroImaging Platform',
|
||||
dynamicbrain: 'Dynamic Brain Platform',
|
||||
cbsn: 'Comprehensive Brain Science Platform',
|
||||
'mouse-phenotype': 'Mouse Phenotype Database',
|
||||
sim: 'Simulation Platform',
|
||||
bsd: 'Brain Science Dictionary',
|
||||
vibrism: 'ViBrism Database',
|
||||
'cbs-ni': 'CBS Neuroinformatics',
|
||||
cns: 'Comparative Neuroscience Platform',
|
||||
};
|
||||
|
||||
const SPECIES: Record<string, string> = {
|
||||
_human_: '[ja]ヒト[/ja][en]Human[/en]',
|
||||
'_nonhuman primate_': '[ja]非ヒト霊長目[/ja][en]Nonhuman Primate[/en]',
|
||||
_rodents_: '[ja]齧歯目[/ja][en]Rodents[/en]',
|
||||
'_other vertebrates_': '[ja]上記以外の脊椎動物[/ja][en]Other Vertebrates[/en]',
|
||||
_arthropod_: '[ja]節足動物[/ja][en]Arthropod[/en]',
|
||||
'_other invertebrates_': '[ja]節足動物以外の無脊椎動物[/ja][en]Other Invertebrates[/en]',
|
||||
_plant_: '[ja]植物[/ja][en]Plant[/en]',
|
||||
_others_: '[ja]その他[/ja][en]Others[/en]',
|
||||
};
|
||||
|
||||
const SCALES: Record<string, string> = {
|
||||
'molecular level': '[ja]分子レベル[/ja][en]Molecular level[/en]',
|
||||
'cellular level': '[ja]細胞レベル[/ja][en]Cellular level[/en]',
|
||||
'tissue/network level': '[ja]組織・ネットワークレベル[/ja][en]Tissue/Network level[/en]',
|
||||
'individual level': '[ja]個体レベル[/ja][en]Individual level[/en]',
|
||||
'population level': '[ja]個体集団レベル[/ja][en]Population level[/en]',
|
||||
'multiple levels': '[ja]マルチレベル[/ja][en]Multiple levels[/en]',
|
||||
others: '[ja]その他[/ja][en]Others[/en]',
|
||||
};
|
||||
|
||||
const METHODS: Record<string, string> = {
|
||||
electrophysiology: '[ja]電気生理[/ja][en]Electrophysiology[/en]',
|
||||
'brain imaging': '[ja]脳イメージング[/ja][en]Brain Imaging[/en]',
|
||||
'mathematical modeling/analysis': '[ja]数理モデル・解析[/ja][en]Mathematical Modeling/Analysis[/en]',
|
||||
simulation: '[ja]シミュレーション[/ja][en]Simulation[/en]',
|
||||
'molecular biology': '[ja]分子生物学[/ja][en]Molecular Biology[/en]',
|
||||
'cell signaling': '[ja]細胞シグナル伝達[/ja][en]Cell Signaling[/en]',
|
||||
psychophysics: '[ja]心理物理学[/ja][en]Psychophysics[/en]',
|
||||
neurogenetics: '[ja]神経遺伝学[/ja][en]Neurogenetics[/en]',
|
||||
'computational neuroscience': '[ja]計算論的神経科学[/ja][en]Computational Neuroscience[/en]',
|
||||
'behavioral neuroscience': '[ja]行動学的神経科学[/ja][en]Behavioral Neuroscience[/en]',
|
||||
'clinical neuroscience': '[ja]臨床神経科学[/ja][en]Clinical Neuroscience[/en]',
|
||||
'integrative neuroscience': '[ja]統合神経科学[/ja][en]Integrative Neuroscience[/en]',
|
||||
'brain machine interface': '[ja]ブレインマシンインタフェイス[/ja][en]Brain Machine Interface[/en]',
|
||||
};
|
||||
|
||||
class BinderDetail extends DetailBase {
|
||||
|
||||
getFields() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemBinder;
|
||||
// OPENNEURO:
|
||||
const getResearchAreas = (value: string) => {
|
||||
const list: { [key: string]: string } = {
|
||||
visiome: "Visiome Platform",
|
||||
bmi: "Brain Machine Interface Platform",
|
||||
invbrain: "Invertebrate Brain Platform",
|
||||
braintx: "Brain Transcriptome Database",
|
||||
cerebellum: "Cerebellar Platform",
|
||||
nimg: "NeuroImaging Platform",
|
||||
dynamicbrain: "Dynamic Brain Platform",
|
||||
cbsn: "Comprehensive Brain Science Platform",
|
||||
"mouse-phenotype": "Mouse Phenotype Database",
|
||||
sim: "Simulation Platform",
|
||||
bsd: "Brain Science Dictionary",
|
||||
vibrism: "ViBrism Database",
|
||||
"cbs-ni": "CBS Neuroinformatics",
|
||||
cns: "Comparative Neuroscience Platform"
|
||||
};
|
||||
return list.hasOwnProperty(value) ? Functions.mlang(list[value], lang) : '';
|
||||
};
|
||||
const getSpecies = (value: string) => {
|
||||
const list: { [key: string]: string } = {
|
||||
"_human_": "[ja]ヒト[/ja][en]Human[/en]",
|
||||
"_nonhuman primate_": "[ja]非ヒト霊長目[/ja][en]Nonhuman Primate[/en]",
|
||||
"_rodents_": "[ja]齧歯目[/ja][en]Rodents[/en]",
|
||||
"_other vertebrates_": "[ja]上記以外の脊椎動物[/ja][en]Other Vertebrates[/en]",
|
||||
"_arthropod_": "[ja]節足動物[/ja][en]Arthropod[/en]",
|
||||
"_other invertebrates_": "[ja]節足動物以外の無脊椎動物[/ja][en]Other Invertebrates[/en]",
|
||||
"_plant_": "[ja]植物[/ja][en]Plant[/en]",
|
||||
"_others_": "[ja]その他[/ja][en]Others[/en]"
|
||||
};
|
||||
return list.hasOwnProperty(value) ? Functions.mlang(list[value], lang) : '';
|
||||
}
|
||||
const getScale = (value: string) => {
|
||||
const list: { [key: string]: string } = {
|
||||
"molecular level": "[ja]分子レベル[/ja][en]Molecular level[/en]",
|
||||
"cellular level": "[ja]細胞レベル[/ja][en]Cellular level[/en]",
|
||||
"tissue/network level": "[ja]組織・ネットワークレベル[/ja][en]Tissue/Network level[/en]",
|
||||
"individual level": "[ja]個体レベル[/ja][en]Individual level[/en]",
|
||||
"population level": "[ja]個体集団レベル[/ja][en]Population level[/en]",
|
||||
"multiple levels": "[ja]マルチレベル[/ja][en]Multiple levels[/en]",
|
||||
"others": "[ja]その他[/ja][en]Others[/en]"
|
||||
|
||||
};
|
||||
return list.hasOwnProperty(value) ? Functions.mlang(list[value], lang) : '';
|
||||
}
|
||||
const getMethod = (value: string) => {
|
||||
const list: { [key: string]: string } = {
|
||||
"electrophysiology": "[ja]電気生理[/ja][en]Electrophysiology[/en]",
|
||||
"brain imaging": "[ja]脳イメージング[/ja][en]Brain Imaging[/en]",
|
||||
"mathematical modeling/analysis": "[ja]数理モデル・解析[/ja][en]Mathematical Modeling/Analysis[/en]",
|
||||
"simulation": "[ja]シミュレーション[/ja][en]Simulation[/en]",
|
||||
"molecular biology": "[ja]分子生物学[/ja][en]Molecular Biology[/en]",
|
||||
"cell signaling": "[ja]細胞シグナル伝達[/ja][en]Cell Signaling[/en]",
|
||||
"psychophysics": "[ja]心理物理学[/ja][en]Psychophysics[/en]",
|
||||
"neurogenetics": "[ja]神経遺伝学[/ja][en]Neurogenetics[/en]",
|
||||
"computational neuroscience": "[ja]計算論的神経科学[/ja][en]Computational Neuroscience[/en]",
|
||||
"behavioral neuroscience": "[ja]行動学的神経科学[/ja][en]Behavioral Neuroscience[/en]",
|
||||
"clinical neuroscience": "[ja]臨床神経科学[/ja][en]Clinical Neuroscience[/en]",
|
||||
"integrative neuroscience": "[ja]統合神経科学[/ja][en]Integrative Neuroscience[/en]",
|
||||
"brain machine interface": "[ja]ブレインマシンインタフェイス[/ja][en]Brain Machine Interface[/en]"
|
||||
};
|
||||
return list.hasOwnProperty(value) ? Functions.mlang(list[value], lang) : '';
|
||||
}
|
||||
const label = (list: Record<string, string>, value: string) =>
|
||||
Object.hasOwn(list, value) ? Functions.mlang(list[value], lang) : '';
|
||||
return [
|
||||
{ label: '[en]Item Type[/en][ja]アイテムタイプ[/ja]', value: item.item_type_display_name },
|
||||
{ label: 'ID', value: item.doi },
|
||||
{ label: '[en]Title[/en][ja]タイトル[/ja]', value: Functions.mlang(item.title, lang) },
|
||||
{ label: '[ja]コンテンツ分野[/ja][en]Research Areas[/en]', value: getResearchAreas(item.research_area) },
|
||||
{ label: '[ja]動物種[/ja][en]Species[/en]', value: getSpecies(item.species) },
|
||||
{ label: '[ja]スケール[/ja][en]Scale[/en]', value: getScale(item.scale) },
|
||||
{ label: '[ja]メソッド[/ja][en]Method[/en]', value: getMethod(item.method) },
|
||||
{ label: '[ja]サマリー[/ja][en]Summary[/en]', value: <ItemTypeField.Description lang={lang} description={item.description} /> },
|
||||
{ label: '[ja]フリーキーワード[/ja][en]Free keyword[/en]', value: <OpenNeuroFreeKeyword lang={lang} keyword={item.keyword} /> },
|
||||
{
|
||||
label: '[ja]コンテンツ分野[/ja][en]Research Areas[/en]',
|
||||
value: label(RESEARCH_AREAS, item.research_area),
|
||||
},
|
||||
{ label: '[ja]動物種[/ja][en]Species[/en]', value: label(SPECIES, item.species) },
|
||||
{ label: '[ja]スケール[/ja][en]Scale[/en]', value: label(SCALES, item.scale) },
|
||||
{ label: '[ja]メソッド[/ja][en]Method[/en]', value: label(METHODS, item.method) },
|
||||
{
|
||||
label: '[ja]サマリー[/ja][en]Summary[/en]',
|
||||
value: <ItemTypeField.Description lang={lang} description={item.description} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]フリーキーワード[/ja][en]Free keyword[/en]',
|
||||
value: <OpenNeuroFreeKeyword lang={lang} keyword={item.keyword} />,
|
||||
},
|
||||
{ label: '[ja]同意[/ja][en]Agree[/en]', value: Functions.mlang(item.agree, lang) },
|
||||
{ label: '[ja]最終更新日[/ja][en]Last Update Date[/en]', value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
|
||||
{ label: '[ja]作成日[/ja][en]Creation Date[/en]', value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
|
||||
{ label: '[ja]所有者[/ja][en]Contributor[/en]', value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
|
||||
{
|
||||
label: '[ja]最終更新日[/ja][en]Last Update Date[/en]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]作成日[/ja][en]Creation Date[/en]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]所有者[/ja][en]Contributor[/en]',
|
||||
value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} />,
|
||||
},
|
||||
{ label: '[ja]査読者[/ja][en]Referee[/en]', value: Functions.mlang(item.referee, lang) },
|
||||
{ label: '[ja]更新履歴[/ja][en]Change Log[/en]', value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
|
||||
{ label: '[ja]関連アイテム[/ja][en]Related Item[/en]', value: <OpenNeuroRelatedTo lang={lang} relatedTo={item.item_link} /> },
|
||||
{
|
||||
label: '[ja]更新履歴[/ja][en]Change Log[/en]',
|
||||
value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]関連アイテム[/ja][en]Related Item[/en]',
|
||||
value: <OpenNeuroRelatedTo lang={lang} relatedTo={item.item_link} />,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default BinderDetail;
|
||||
export default BinderDetail;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link } from 'react-router';
|
||||
import Functions from '../../../functions';
|
||||
import iconFile from '../../assets/images/icon_binder.gif';
|
||||
import { ItemBinder } from '../../lib/ItemUtil';
|
||||
import ListBase, { ListBaseProps } from '../lib/ListBase';
|
||||
import type { ItemBinder } from '../../lib/ItemUtil';
|
||||
import ListBase, { type ListBaseProps } from '../lib/ListBase';
|
||||
|
||||
class BinderList extends ListBase {
|
||||
|
||||
constructor(props: ListBaseProps) {
|
||||
super(props);
|
||||
this.label = 'Binder';
|
||||
@@ -16,12 +14,7 @@ class BinderList extends ListBase {
|
||||
renderBody() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemBinder;
|
||||
// OPENNEURO:
|
||||
return (
|
||||
<>
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
|
||||
</>
|
||||
);
|
||||
return <Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import TopBase, { TopBaseProps } from '../lib/TopBase';
|
||||
import iconFile from '../../assets/images/icon_binder.gif';
|
||||
import TopBase, { type TopBaseProps } from '../lib/TopBase';
|
||||
|
||||
class BinderTop extends TopBase {
|
||||
|
||||
constructor(props: TopBaseProps) {
|
||||
super(props);
|
||||
this.type = 'binder';
|
||||
this.label = 'Binder';
|
||||
this.icon = iconFile;
|
||||
// OPENNEURO:
|
||||
this.description = 'Binder';
|
||||
}
|
||||
}
|
||||
|
||||
export default BinderTop;
|
||||
export default BinderTop;
|
||||
|
||||
@@ -1,35 +1,43 @@
|
||||
import AdvancedSearchBase, { AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
import AdvancedSearchBase, { type AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
|
||||
class BookAdvancedSearch extends AdvancedSearchBase {
|
||||
|
||||
constructor(props: AdvancedSearchBaseProps) {
|
||||
super(props);
|
||||
this.type = 'book';
|
||||
this.title = 'Book';
|
||||
this.state.values['title'] = '';
|
||||
this.state.values['keyword'] = '';
|
||||
this.state.values['description'] = '';
|
||||
this.state.values['doi'] = '';
|
||||
this.state.values['author'] = '';
|
||||
this.state.values['editor'] = '';
|
||||
this.state.values['publisher'] = '';
|
||||
this.state.values['publication_year'] = '';
|
||||
this.state.values['isbn'] = '';
|
||||
this.state.values.title = '';
|
||||
this.state.values.keyword = '';
|
||||
this.state.values.description = '';
|
||||
this.state.values.doi = '';
|
||||
this.state.values.author = '';
|
||||
this.state.values.editor = '';
|
||||
this.state.values.publisher = '';
|
||||
this.state.values.publication_year = '';
|
||||
this.state.values.isbn = '';
|
||||
this.state.values['file.book_pdf.original_file_name'] = '';
|
||||
}
|
||||
|
||||
getRows() {
|
||||
const rows = [
|
||||
{ label: '[en]Book Title[/en][ja]著書名[/ja]', value: this.renderFieldInputText('title', 50) },
|
||||
{ label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]', value: this.renderFieldInputText('keyword', 50) },
|
||||
{
|
||||
label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]',
|
||||
value: this.renderFieldInputText('keyword', 50),
|
||||
},
|
||||
{ label: '[en]Description[/en][ja]概要[/ja]', value: this.renderFieldInputText('description', 50) },
|
||||
{ label: 'ID', value: this.renderFieldInputText('doi', 50) },
|
||||
{ label: '[en]Author[/en][ja]著者[/ja]', value: this.renderFieldInputText('author', 50) },
|
||||
{ label: '[en]Editor[/en][ja]編集者[/ja]', value: this.renderFieldInputText('editor', 50) },
|
||||
{ label: '[en]Publisher[/en][ja]出版社[/ja]', value: this.renderFieldInputText('publisher', 50) },
|
||||
{ label: '[en]Publication Year[/en][ja]出版年[/ja]', value: this.renderFieldInputText('publication_year', 10) },
|
||||
{
|
||||
label: '[en]Publication Year[/en][ja]出版年[/ja]',
|
||||
value: this.renderFieldInputText('publication_year', 10),
|
||||
},
|
||||
{ label: 'ISBN', value: this.renderFieldInputText('isbn', 50) },
|
||||
{ label: '[en]PDF File[/en][ja]PDF ファイル[/ja]', value: this.renderFieldInputText('file.book_pdf.original_file_name', 50) },
|
||||
{
|
||||
label: '[en]PDF File[/en][ja]PDF ファイル[/ja]',
|
||||
value: this.renderFieldInputText('file.book_pdf.original_file_name', 50),
|
||||
},
|
||||
];
|
||||
return rows;
|
||||
}
|
||||
|
||||
@@ -1,33 +1,72 @@
|
||||
import React from 'react';
|
||||
import Functions from '../../../functions';
|
||||
import { ItemBook } from '../../lib/ItemUtil';
|
||||
import type { ItemBook } from '../../lib/ItemUtil';
|
||||
import DetailBase from '../lib/DetailBase';
|
||||
import ItemTypeField from '../lib/field';
|
||||
|
||||
class BookDetail extends DetailBase {
|
||||
|
||||
getFields() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemBook;
|
||||
return [
|
||||
{ label: 'ID', value: item.doi },
|
||||
{ label: '[en]Language[/en][ja]言語[/ja]', value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
|
||||
{
|
||||
label: '[en]Language[/en][ja]言語[/ja]',
|
||||
value: <ItemTypeField.Language lang={lang} itemLang={item.lang} />,
|
||||
},
|
||||
{ label: '[en]Book Title[/en][ja]著書名[/ja]', value: Functions.mlang(item.title, lang) },
|
||||
{ label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]', value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
|
||||
{ label: '[en]Description[/en][ja]概要[/ja]', value: <ItemTypeField.Description lang={lang} description={item.description} /> },
|
||||
{ label: '[en]Last Modified Date[/en][ja]最終更新日[/ja]', value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
|
||||
{ label: '[en]Created Date[/en][ja]作成日[/ja]', value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
|
||||
{ label: '[en]Contributor[/en][ja]登録者[/ja]', value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
|
||||
{
|
||||
label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]',
|
||||
value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Description[/en][ja]概要[/ja]',
|
||||
value: <ItemTypeField.Description lang={lang} description={item.description} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Last Modified Date[/en][ja]最終更新日[/ja]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Created Date[/en][ja]作成日[/ja]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Contributor[/en][ja]登録者[/ja]',
|
||||
value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} />,
|
||||
},
|
||||
{ label: '[en]Item Type[/en][ja]アイテムタイプ[/ja]', value: item.item_type_display_name },
|
||||
{ label: '[en]Change Log(History)[/en][ja]変更履歴[/ja]', value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
|
||||
{
|
||||
label: '[en]Change Log(History)[/en][ja]変更履歴[/ja]',
|
||||
value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} />,
|
||||
},
|
||||
{ label: '[en]Author[/en][ja]著者[/ja]', value: <ItemTypeField.Author lang={lang} author={item.author} /> },
|
||||
{ label: '[en]Editor[/en][ja]編集者[/ja]', value: Functions.mlang(item.editor, lang) },
|
||||
{ label: '[en]Publisher[/en][ja]出版社[/ja]', value: Functions.mlang(item.publisher, lang) },
|
||||
{ label: '[en]Publication Year[/en][ja]出版年[/ja]', value: item.publication_year },
|
||||
{ label: 'URL', value: <a href={item.url} target="_blank" rel="noopener noreferrer">{item.url}</a> },
|
||||
{ label: '[en]PDF File[/en][ja]PDF ファイル[/ja]', value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="book_pdf" downloadLimit={item.attachment_dl_limit} /> },
|
||||
{
|
||||
label: 'URL',
|
||||
value: (
|
||||
<a href={item.url} target="_blank" rel="noopener noreferrer">
|
||||
{item.url}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: '[en]PDF File[/en][ja]PDF ファイル[/ja]',
|
||||
value: (
|
||||
<ItemTypeField.ItemFile
|
||||
lang={lang}
|
||||
file={item.file}
|
||||
type="book_pdf"
|
||||
downloadLimit={item.attachment_dl_limit}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{ label: 'Index', value: <ItemTypeField.ItemIndex lang={lang} index={item.index} /> },
|
||||
{ label: '[en]Related to[/en][ja]関連アイテム[/ja]', value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} /> },
|
||||
{
|
||||
label: '[en]Related to[/en][ja]関連アイテム[/ja]',
|
||||
value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} />,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link } from 'react-router';
|
||||
import Functions from '../../../functions';
|
||||
import iconFile from '../../assets/images/icon_book.gif';
|
||||
import { ItemBook } from '../../lib/ItemUtil';
|
||||
import ListBase, { ListBaseProps } from '../lib/ListBase';
|
||||
import type { ItemBook } from '../../lib/ItemUtil';
|
||||
import ListBase, { type ListBaseProps } from '../lib/ListBase';
|
||||
|
||||
class BookList extends ListBase {
|
||||
|
||||
constructor(props: ListBaseProps) {
|
||||
super(props);
|
||||
this.label = 'Book';
|
||||
@@ -16,12 +14,11 @@ class BookList extends ListBase {
|
||||
renderBody() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemBook;
|
||||
const authors = item.author.map((author, i) => {
|
||||
return <Fragment key={i}>{i > 0 && ', '}{Functions.mlang(author, lang)}</Fragment>
|
||||
});
|
||||
const authors = item.author.map((author) => Functions.mlang(author, lang)).join(', ');
|
||||
return (
|
||||
<>
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link><br />
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
|
||||
<br />
|
||||
{authors}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import TopBase, { TopBaseProps } from '../lib/TopBase';
|
||||
import iconFile from '../../assets/images/icon_book.gif';
|
||||
import TopBase, { type TopBaseProps } from '../lib/TopBase';
|
||||
|
||||
class BookTop extends TopBase {
|
||||
|
||||
constructor(props: TopBaseProps) {
|
||||
super(props);
|
||||
this.type = 'book';
|
||||
@@ -12,4 +11,4 @@ class BookTop extends TopBase {
|
||||
}
|
||||
}
|
||||
|
||||
export default BookTop;
|
||||
export default BookTop;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import BookTop from './BookTop';
|
||||
import BookList from './BookList';
|
||||
import BookDetail from './BookDetail';
|
||||
import BookAdvancedSearch from './BookAdvancedSearch';
|
||||
import BookDetail from './BookDetail';
|
||||
import BookList from './BookList';
|
||||
import BookTop from './BookTop';
|
||||
|
||||
const ItemTypeBook = {
|
||||
Top: BookTop,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React from 'react';
|
||||
import { ItemConferenceSubTypes } from '../../lib/ItemUtil';
|
||||
import AdvancedSearchBase, { AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
import AdvancedSearchBase, { type AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
|
||||
class ConferenceAdvancedSearch extends AdvancedSearchBase {
|
||||
|
||||
constructor(props: AdvancedSearchBaseProps) {
|
||||
super(props);
|
||||
this.type = 'conference';
|
||||
@@ -12,15 +10,15 @@ class ConferenceAdvancedSearch extends AdvancedSearchBase {
|
||||
const year = String(now.getFullYear());
|
||||
const month = String(now.getMonth() + 1);
|
||||
const mday = String(now.getDate());
|
||||
this.state.values['title'] = '';
|
||||
this.state.values['presentation_type'] = '';
|
||||
this.state.values['author'] = '';
|
||||
this.state.values['conference_from_year'] = year;
|
||||
this.state.values['conference_from_month'] = month;
|
||||
this.state.values['conference_from_mday'] = mday;
|
||||
this.state.values['conference_to_year'] = year;
|
||||
this.state.values['conference_to_month'] = month;
|
||||
this.state.values['conference_to_mday'] = mday;
|
||||
this.state.values.title = '';
|
||||
this.state.values.presentation_type = '';
|
||||
this.state.values.author = '';
|
||||
this.state.values.conference_from_year = year;
|
||||
this.state.values.conference_from_month = month;
|
||||
this.state.values.conference_from_mday = mday;
|
||||
this.state.values.conference_to_year = year;
|
||||
this.state.values.conference_to_month = month;
|
||||
this.state.values.conference_to_mday = mday;
|
||||
this.setIgnoreKey('conference_from_year');
|
||||
this.setIgnoreKey('conference_from_month');
|
||||
this.setIgnoreKey('conference_from_mday');
|
||||
@@ -33,8 +31,12 @@ class ConferenceAdvancedSearch extends AdvancedSearchBase {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{this.renderFieldDate('From', 'conference_from_year', 'conference_from_month', 'conference_from_mday')}
|
||||
|
||||
{this.renderFieldDate(
|
||||
'From',
|
||||
'conference_from_year',
|
||||
'conference_from_month',
|
||||
'conference_from_mday'
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{this.renderFieldDate('To', 'conference_to_year', 'conference_to_month', 'conference_to_mday')}
|
||||
@@ -46,12 +48,15 @@ class ConferenceAdvancedSearch extends AdvancedSearchBase {
|
||||
getRows() {
|
||||
const rows = [
|
||||
{ label: '[en]Presentation Title[/en][ja]発表議題[/ja]', value: this.renderFieldInputText('title', 50) },
|
||||
{ label: '[en]Presentation Type[/en][ja]発表資料ファイル形式[/ja]', value: this.renderFieldSelect('presentation_type', ItemConferenceSubTypes) },
|
||||
{
|
||||
label: '[en]Presentation Type[/en][ja]発表資料ファイル形式[/ja]',
|
||||
value: this.renderFieldSelect('presentation_type', ItemConferenceSubTypes),
|
||||
},
|
||||
{ label: '[en]Author[/en][ja]発表者[/ja]', value: this.renderFieldInputText('author', 50) },
|
||||
{ label: '[en]Date[/en][ja]日付[/ja]', value: this.renderDate() }
|
||||
{ label: '[en]Date[/en][ja]日付[/ja]', value: this.renderDate() },
|
||||
];
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
export default ConferenceAdvancedSearch;
|
||||
export default ConferenceAdvancedSearch;
|
||||
|
||||
@@ -1,36 +1,67 @@
|
||||
import React from 'react';
|
||||
import Functions from '../../../functions';
|
||||
import { ItemConference } from '../../lib/ItemUtil';
|
||||
import type { ItemConference } from '../../lib/ItemUtil';
|
||||
import DetailBase from '../lib/DetailBase';
|
||||
import ItemTypeField from '../lib/field';
|
||||
import ConferenceUtil from './ConferenceUtil';
|
||||
|
||||
class ConferenceDetail extends DetailBase {
|
||||
|
||||
getFields() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemConference;
|
||||
return [
|
||||
{ label: 'ID', value: item.doi },
|
||||
{ label: '[en]Language[/en][ja]言語[/ja]', value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
|
||||
{
|
||||
label: '[en]Language[/en][ja]言語[/ja]',
|
||||
value: <ItemTypeField.Language lang={lang} itemLang={item.lang} />,
|
||||
},
|
||||
{ label: '[en]Conference Title[/en][ja]学会名[/ja]', value: Functions.mlang(item.conference_title, lang) },
|
||||
{ label: '[en]Place[/en][ja]開催地[/ja]', value: item.place },
|
||||
{ label: '[en]Date[/en][ja]日付[/ja]', value: <ConferenceUtil.ConferenceDate lang={lang} item={item} /> },
|
||||
{ label: '[en]Last Modified Date[/en][ja]最終更新日[/ja]', value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
|
||||
{ label: '[en]Created Date[/en][ja]作成日[/ja]', value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
|
||||
{ label: '[en]Contributor[/en][ja]登録者[/ja]', value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
|
||||
{
|
||||
label: '[en]Last Modified Date[/en][ja]最終更新日[/ja]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Created Date[/en][ja]作成日[/ja]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Contributor[/en][ja]登録者[/ja]',
|
||||
value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} />,
|
||||
},
|
||||
{ label: '[en]Item Type[/en][ja]アイテムタイプ[/ja]', value: item.item_type_display_name },
|
||||
{ label: '[en]Change Log(History)[/en][ja]変更履歴[/ja]', value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
|
||||
{
|
||||
label: '[en]Change Log(History)[/en][ja]変更履歴[/ja]',
|
||||
value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} />,
|
||||
},
|
||||
{ label: '[en]Presentation Title[/en][ja]発表議題[/ja]', value: Functions.mlang(item.title, lang) },
|
||||
{ label: '[en]Author[/en][ja]発表者[/ja]', value: <ItemTypeField.Author lang={lang} author={item.author} /> },
|
||||
{ label: '[en]Abstract[/en][ja]要約[/ja]', value: <ItemTypeField.Description lang={lang} description={item.abstract} /> },
|
||||
{ label: '[en]Presentation File[/en][ja]発表資料[/ja]', value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="conference_file" /> },
|
||||
{ label: '[en]Presentation Type[/en][ja]発表資料ファイル形式[/ja]', value: <ConferenceUtil.PresentationType lang={lang} type={item.presentation_type} /> },
|
||||
{ label: '[en]Conference Paper[/en][ja]学会資料[/ja]', value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="conference_paper" /> },
|
||||
{
|
||||
label: '[en]Author[/en][ja]発表者[/ja]',
|
||||
value: <ItemTypeField.Author lang={lang} author={item.author} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Abstract[/en][ja]要約[/ja]',
|
||||
value: <ItemTypeField.Description lang={lang} description={item.abstract} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Presentation File[/en][ja]発表資料[/ja]',
|
||||
value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="conference_file" />,
|
||||
},
|
||||
{
|
||||
label: '[en]Presentation Type[/en][ja]発表資料ファイル形式[/ja]',
|
||||
value: <ConferenceUtil.PresentationType lang={lang} type={item.presentation_type} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Conference Paper[/en][ja]学会資料[/ja]',
|
||||
value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="conference_paper" />,
|
||||
},
|
||||
{ label: 'Index', value: <ItemTypeField.ItemIndex lang={lang} index={item.index} /> },
|
||||
{ label: '[en]Related to[/en][ja]関連アイテム[/ja]', value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} /> },
|
||||
{
|
||||
label: '[en]Related to[/en][ja]関連アイテム[/ja]',
|
||||
value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} />,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default ConferenceDetail;
|
||||
export default ConferenceDetail;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ItemConference } from '../../lib/ItemUtil';
|
||||
import ListBase, { ListBaseProps } from '../lib/ListBase';
|
||||
import ConferenceUtil from './ConferenceUtil';
|
||||
import iconFile from '../../assets/images/icon_conference.gif';
|
||||
import { Link } from 'react-router';
|
||||
import Functions from '../../../functions';
|
||||
import iconFile from '../../assets/images/icon_conference.gif';
|
||||
import type { ItemConference } from '../../lib/ItemUtil';
|
||||
import ListBase, { type ListBaseProps } from '../lib/ListBase';
|
||||
import ConferenceUtil from './ConferenceUtil';
|
||||
|
||||
class ConferenceList extends ListBase {
|
||||
|
||||
constructor(props: ListBaseProps) {
|
||||
super(props);
|
||||
this.label = 'Conference';
|
||||
@@ -17,17 +15,17 @@ class ConferenceList extends ListBase {
|
||||
renderBody() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemConference;
|
||||
const authors = item.author.map((author, i) => {
|
||||
return <Fragment key={i}>{i > 0 && ', '}{Functions.mlang(author, lang)}</Fragment>
|
||||
});
|
||||
const authors = item.author.map((author) => Functions.mlang(author, lang)).join(', ');
|
||||
return (
|
||||
<>
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link><br />
|
||||
{Functions.mlang(item.conference_title, lang)} (<ConferenceUtil.PresentationType lang={lang} type={item.presentation_type} />)<br />
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
|
||||
<br />
|
||||
{Functions.mlang(item.conference_title, lang)} (
|
||||
<ConferenceUtil.PresentationType lang={lang} type={item.presentation_type} />)<br />
|
||||
{authors}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConferenceList;
|
||||
export default ConferenceList;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ItemConferenceSubTypes } from '../../lib/ItemUtil';
|
||||
import TopBase, { TopBaseProps } from '../lib/TopBase';
|
||||
import iconFile from '../../assets/images/icon_conference.gif';
|
||||
import { ItemConferenceSubTypes } from '../../lib/ItemUtil';
|
||||
import TopBase, { type TopBaseProps } from '../lib/TopBase';
|
||||
|
||||
class ConferenceTop extends TopBase {
|
||||
|
||||
constructor(props: TopBaseProps) {
|
||||
super(props);
|
||||
this.type = 'conference';
|
||||
@@ -14,4 +13,4 @@ class ConferenceTop extends TopBase {
|
||||
}
|
||||
}
|
||||
|
||||
export default ConferenceTop;
|
||||
export default ConferenceTop;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../config';
|
||||
import { ItemConference, ItemConferenceSubType, ItemConferenceSubTypes } from '../../lib/ItemUtil';
|
||||
import type { MultiLang } from '../../../config';
|
||||
import { type ItemConference, type ItemConferenceSubType, ItemConferenceSubTypes } from '../../lib/ItemUtil';
|
||||
|
||||
interface PresentationTypeProps {
|
||||
lang: MultiLang;
|
||||
@@ -9,12 +8,14 @@ interface PresentationTypeProps {
|
||||
|
||||
const PresentationType = (props: PresentationTypeProps) => {
|
||||
const { type } = props;
|
||||
const subtype = ItemConferenceSubTypes.find((value) => { return value.type === type; });
|
||||
const subtype = ItemConferenceSubTypes.find((value) => {
|
||||
return value.type === type;
|
||||
});
|
||||
if (typeof subtype === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return (<span>{subtype.label}</span>);
|
||||
}
|
||||
return <span>{subtype.label}</span>;
|
||||
};
|
||||
|
||||
interface ConferenceDateProps {
|
||||
lang: MultiLang;
|
||||
@@ -24,14 +25,30 @@ interface ConferenceDateProps {
|
||||
const ConferenceDate = (props: ConferenceDateProps) => {
|
||||
const { item } = props;
|
||||
const monthStr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const from = 'From: ' + monthStr[item.conference_from_month - 1] + ' ' + item.conference_from_mday + ', ' + item.conference_from_year;
|
||||
const to = 'To: ' + monthStr[item.conference_to_month - 1] + ' ' + item.conference_to_mday + ', ' + item.conference_to_year;
|
||||
return (<span>{from} {to}</span>);
|
||||
}
|
||||
const from =
|
||||
'From: ' +
|
||||
monthStr[item.conference_from_month - 1] +
|
||||
' ' +
|
||||
item.conference_from_mday +
|
||||
', ' +
|
||||
item.conference_from_year;
|
||||
const to =
|
||||
'To: ' +
|
||||
monthStr[item.conference_to_month - 1] +
|
||||
' ' +
|
||||
item.conference_to_mday +
|
||||
', ' +
|
||||
item.conference_to_year;
|
||||
return (
|
||||
<span>
|
||||
{from} {to}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const ConferenceUtil = {
|
||||
PresentationType,
|
||||
ConferenceDate,
|
||||
}
|
||||
};
|
||||
|
||||
export default ConferenceUtil;
|
||||
export default ConferenceUtil;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ConferenceTop from './ConferenceTop';
|
||||
import ConferenceList from './ConferenceList';
|
||||
import ConferenceDetail from './ConferenceDetail';
|
||||
import ConferenceAdvancedSearch from './ConferenceAdvancedSearch';
|
||||
import ConferenceDetail from './ConferenceDetail';
|
||||
import ConferenceList from './ConferenceList';
|
||||
import ConferenceTop from './ConferenceTop';
|
||||
|
||||
const ItemTypeConference = {
|
||||
Top: ConferenceTop,
|
||||
@@ -10,4 +10,4 @@ const ItemTypeConference = {
|
||||
AdvancedSearch: ConferenceAdvancedSearch,
|
||||
};
|
||||
|
||||
export default ItemTypeConference;
|
||||
export default ItemTypeConference;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ItemDataSubTypes } from '../../lib/ItemUtil';
|
||||
import AdvancedSearchBase, { AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
import AdvancedSearchBase, { type AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
|
||||
class DataAdvancedSearch extends AdvancedSearchBase {
|
||||
|
||||
constructor(props: AdvancedSearchBaseProps) {
|
||||
super(props);
|
||||
this.type = 'data';
|
||||
@@ -11,15 +10,15 @@ class DataAdvancedSearch extends AdvancedSearchBase {
|
||||
const year = String(now.getFullYear());
|
||||
const month = String(now.getMonth() + 1);
|
||||
const mday = String(now.getDate());
|
||||
this.state.values['title'] = '';
|
||||
this.state.values['keyword'] = '';
|
||||
this.state.values['description'] = '';
|
||||
this.state.values['doi'] = '';
|
||||
this.state.values['data_type'] = '';
|
||||
this.state.values['experimenter'] = '';
|
||||
this.state.values['publication_year'] = year;
|
||||
this.state.values['publication_month'] = month;
|
||||
this.state.values['publication_mday'] = mday;
|
||||
this.state.values.title = '';
|
||||
this.state.values.keyword = '';
|
||||
this.state.values.description = '';
|
||||
this.state.values.doi = '';
|
||||
this.state.values.data_type = '';
|
||||
this.state.values.experimenter = '';
|
||||
this.state.values.publication_year = year;
|
||||
this.state.values.publication_month = month;
|
||||
this.state.values.publication_mday = mday;
|
||||
this.state.values['file.preview.caption'] = '';
|
||||
this.state.values['file.data_file.original_file_name'] = '';
|
||||
this.setIgnoreKey('publication_year');
|
||||
@@ -30,17 +29,32 @@ class DataAdvancedSearch extends AdvancedSearchBase {
|
||||
getRows() {
|
||||
const rows = [
|
||||
{ label: '[en]Title[/en][ja]タイトル[/ja]', value: this.renderFieldInputText('title', 50) },
|
||||
{ label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]', value: this.renderFieldInputText('keyword', 50) },
|
||||
{
|
||||
label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]',
|
||||
value: this.renderFieldInputText('keyword', 50),
|
||||
},
|
||||
{ label: '[en]Description[/en][ja]概要[/ja]', value: this.renderFieldInputText('description', 50) },
|
||||
{ label: 'ID', value: this.renderFieldInputText('doi', 50) },
|
||||
{ label: '[en]Data Type[/en][ja]データタイプ[/ja]', value: this.renderFieldSelect('data_type', ItemDataSubTypes) },
|
||||
{
|
||||
label: '[en]Data Type[/en][ja]データタイプ[/ja]',
|
||||
value: this.renderFieldSelect('data_type', ItemDataSubTypes),
|
||||
},
|
||||
{ label: '[en]Experimenter[/en][ja]実験者[/ja]', value: this.renderFieldInputText('experimenter', 50) },
|
||||
{ label: '[en]Date[/en][ja]日付[/ja]', value: this.renderFieldDate('', 'publication_year', 'publication_month', 'publication_mday') },
|
||||
{ label: '[en]Caption[/en][ja]キャプション[/ja]', value: this.renderFieldInputText('file.preview.caption', 50) },
|
||||
{ label: '[en]Data File[/en][ja]データファイル[/ja]', value: this.renderFieldInputText('file.data_file.original_file_name', 50) },
|
||||
{
|
||||
label: '[en]Date[/en][ja]日付[/ja]',
|
||||
value: this.renderFieldDate('', 'publication_year', 'publication_month', 'publication_mday'),
|
||||
},
|
||||
{
|
||||
label: '[en]Caption[/en][ja]キャプション[/ja]',
|
||||
value: this.renderFieldInputText('file.preview.caption', 50),
|
||||
},
|
||||
{
|
||||
label: '[en]Data File[/en][ja]データファイル[/ja]',
|
||||
value: this.renderFieldInputText('file.data_file.original_file_name', 50),
|
||||
},
|
||||
];
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
export default DataAdvancedSearch;
|
||||
export default DataAdvancedSearch;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import Functions from '../../../functions';
|
||||
import ItemUtil, { ItemData } from '../../lib/ItemUtil';
|
||||
import ItemUtil, { type ItemData } from '../../lib/ItemUtil';
|
||||
import DetailBase from '../lib/DetailBase';
|
||||
import ItemTypeField from '../lib/field';
|
||||
import OpenNeuroAuthor from '../lib/field/OpenNeuroAuthor';
|
||||
@@ -10,39 +9,108 @@ import SimPFLinkIcon from '../lib/field/SimPFLinkIcon';
|
||||
import DataUtil from './DataUtil';
|
||||
|
||||
class DataDetail extends DetailBase {
|
||||
|
||||
getFields() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemData;
|
||||
// OPENNEURO:
|
||||
const fields = [
|
||||
{ label: '[en]Item Type[/en][ja]アイテムタイプ[/ja]', value: item.item_type_display_name },
|
||||
{ label: 'ID', value: item.doi },
|
||||
{ label: '[en]Language[/en][ja]言語[/ja]', value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
|
||||
{
|
||||
label: '[en]Language[/en][ja]言語[/ja]',
|
||||
value: <ItemTypeField.Language lang={lang} itemLang={item.lang} />,
|
||||
},
|
||||
{ label: '[en]Title[/en][ja]タイトル[/ja]', value: Functions.mlang(item.title, lang) },
|
||||
{ label: '[ja]概要[/ja][en]Outline[/en]', value: <ItemTypeField.Description lang={lang} description={item.description} /> },
|
||||
{ label: '[ja]プレビュー[/ja][en]Preview[/en]', value: <ItemTypeField.Preview lang={lang} file={item.file} /> },
|
||||
{ label: '[ja]データファイル[/ja][en]Data File[/en]', value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="data_file" rights={item.rights} useCc={item.use_cc} ccCommercialUse={item.cc_commercial_use} ccModification={item.cc_modification} downloadLimit={item.attachment_dl_limit} /> },
|
||||
{ label: '[ja]日付[/ja][en]Date[/en]', value: <ItemTypeField.PublicationDate lang={lang} year={item.publication_year} month={item.publication_month} mday={item.publication_mday} /> },
|
||||
{ label: '[ja]データタイプ[/ja][en]Data Type[/en]', value: <DataUtil.DataType lang={lang} type={item.data_type} /> },
|
||||
{ label: '[ja]実験者[/ja][en]Experimenter[/en]', value: <OpenNeuroAuthor lang={lang} author={item.experimenter} /> },
|
||||
{
|
||||
label: '[ja]概要[/ja][en]Outline[/en]',
|
||||
value: <ItemTypeField.Description lang={lang} description={item.description} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]プレビュー[/ja][en]Preview[/en]',
|
||||
value: <ItemTypeField.Preview lang={lang} file={item.file} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]データファイル[/ja][en]Data File[/en]',
|
||||
value: (
|
||||
<ItemTypeField.ItemFile
|
||||
lang={lang}
|
||||
file={item.file}
|
||||
type="data_file"
|
||||
rights={item.rights}
|
||||
useCc={item.use_cc}
|
||||
ccCommercialUse={item.cc_commercial_use}
|
||||
ccModification={item.cc_modification}
|
||||
downloadLimit={item.attachment_dl_limit}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: '[ja]日付[/ja][en]Date[/en]',
|
||||
value: (
|
||||
<ItemTypeField.PublicationDate
|
||||
lang={lang}
|
||||
year={item.publication_year}
|
||||
month={item.publication_month}
|
||||
mday={item.publication_mday}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: '[ja]データタイプ[/ja][en]Data Type[/en]',
|
||||
value: <DataUtil.DataType lang={lang} type={item.data_type} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]実験者[/ja][en]Experimenter[/en]',
|
||||
value: <OpenNeuroAuthor lang={lang} author={item.experimenter} />,
|
||||
},
|
||||
{ label: 'Readme', value: <ItemTypeField.Readme lang={lang} readme={item.readme} /> },
|
||||
{ label: 'Rights', value: <ItemTypeField.Rights lang={lang} rights={item.rights} useCc={item.use_cc} ccCommercialUse={item.cc_commercial_use} ccModification={item.cc_modification} /> },
|
||||
{ label: '[ja]フリーキーワード[/ja][en]Free keyword[/en]', value: <OpenNeuroFreeKeyword lang={lang} keyword={item.keyword} /> },
|
||||
{ label: '[ja]最終更新日[/ja][en]Last Update Date[/en]', value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
|
||||
{ label: '[ja]作成日[/ja][en]Creation Date[/en]', value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
|
||||
{ label: '[ja]所有者[/ja][en]Contributor[/en]', value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
|
||||
{
|
||||
label: 'Rights',
|
||||
value: (
|
||||
<ItemTypeField.Rights
|
||||
lang={lang}
|
||||
rights={item.rights}
|
||||
useCc={item.use_cc}
|
||||
ccCommercialUse={item.cc_commercial_use}
|
||||
ccModification={item.cc_modification}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: '[ja]フリーキーワード[/ja][en]Free keyword[/en]',
|
||||
value: <OpenNeuroFreeKeyword lang={lang} keyword={item.keyword} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]最終更新日[/ja][en]Last Update Date[/en]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]作成日[/ja][en]Creation Date[/en]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]所有者[/ja][en]Contributor[/en]',
|
||||
value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} />,
|
||||
},
|
||||
{ label: '[ja]査読者[/ja][en]Referee[/en]', value: Functions.mlang(item.referee, lang) },
|
||||
{ label: '[ja]更新履歴[/ja][en]Change Log[/en]', value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
|
||||
{ label: '[ja]関連アイテム[/ja][en]Related Item[/en]', value: <OpenNeuroRelatedTo lang={lang} relatedTo={item.related_to} /> },
|
||||
{
|
||||
label: '[ja]更新履歴[/ja][en]Change Log[/en]',
|
||||
value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} />,
|
||||
},
|
||||
{
|
||||
label: '[ja]関連アイテム[/ja][en]Related Item[/en]',
|
||||
value: <OpenNeuroRelatedTo lang={lang} relatedTo={item.related_to} />,
|
||||
},
|
||||
];
|
||||
const simpfLinkUrl = ItemUtil.getSimPFLinkUrl(item.item_id);
|
||||
if (simpfLinkUrl !== '') {
|
||||
const field = { label: 'Online Simulation', value: <SimPFLinkIcon lang={lang} url={simpfLinkUrl} isDetail={true} /> };
|
||||
const field = {
|
||||
label: 'Online Simulation',
|
||||
value: <SimPFLinkIcon lang={lang} url={simpfLinkUrl} isDetail={true} />,
|
||||
};
|
||||
fields.splice(14, 0, field);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
|
||||
export default DataDetail;
|
||||
export default DataDetail;
|
||||
|
||||
@@ -1,53 +1,43 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link } from 'react-router';
|
||||
import Functions from '../../../functions';
|
||||
import iconFile from '../../assets/images/icon_data.gif';
|
||||
import ItemUtil, { ItemData } from '../../lib/ItemUtil';
|
||||
import ListBase, { ListBaseProps } from '../lib/ListBase';
|
||||
import ItemUtil, { type ItemData } from '../../lib/ItemUtil';
|
||||
import ListBase, { type ListBaseProps } from '../lib/ListBase';
|
||||
import DataUtil from './DataUtil';
|
||||
|
||||
class DataList extends ListBase {
|
||||
|
||||
constructor(props: ListBaseProps) {
|
||||
super(props);
|
||||
this.label = 'Data';
|
||||
this.icon = iconFile;
|
||||
}
|
||||
|
||||
// OpenNeuro extensions: the preview image, when there is one, stands in for the icon.
|
||||
renderIcon() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemData;
|
||||
const data = item.file.filter((value) => {
|
||||
return value.file_type_name === 'preview';
|
||||
});
|
||||
let imgSrc = this.icon;
|
||||
let imgAlt = this.label;
|
||||
if (data.length > 0) {
|
||||
const preview = data.pop();
|
||||
if ('undefined' !== typeof preview) {
|
||||
imgSrc = ItemUtil.getPreviewFileUrl(preview);
|
||||
if ('' !== preview.caption) {
|
||||
imgAlt = Functions.mlang(preview.caption, lang);
|
||||
}
|
||||
}
|
||||
const preview = item.file.filter((value) => value.file_type_name === 'preview').pop();
|
||||
if (typeof preview === 'undefined') {
|
||||
return super.renderIcon();
|
||||
}
|
||||
return <img src={imgSrc} alt={imgAlt} />;
|
||||
const alt = preview.caption !== '' ? Functions.mlang(preview.caption, lang) : this.label;
|
||||
return <img src={ItemUtil.getPreviewFileUrl(preview)} alt={alt} />;
|
||||
}
|
||||
|
||||
renderBody() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemData;
|
||||
const authors = item.experimenter.map((author, i) => {
|
||||
return <Fragment key={i}>{i > 0 && ', '}{Functions.mlang(author, lang)}</Fragment>
|
||||
});
|
||||
const authors = item.experimenter.map((author) => Functions.mlang(author, lang)).join(', ');
|
||||
return (
|
||||
<>
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link><br />
|
||||
<DataUtil.DataType lang={lang} type={item.data_type} /><br />
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
|
||||
<br />
|
||||
<DataUtil.DataType lang={lang} type={item.data_type} />
|
||||
<br />
|
||||
{authors}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DataList;
|
||||
export default DataList;
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ItemDataSubTypes } from '../../lib/ItemUtil';
|
||||
import TopBase, { TopBaseProps } from '../lib/TopBase';
|
||||
import iconFile from '../../assets/images/icon_data.gif';
|
||||
import { ItemDataSubTypes } from '../../lib/ItemUtil';
|
||||
import TopBase, { type TopBaseProps } from '../lib/TopBase';
|
||||
|
||||
class DataTop extends TopBase {
|
||||
|
||||
constructor(props: TopBaseProps) {
|
||||
super(props);
|
||||
this.type = 'data';
|
||||
this.label = 'Data';
|
||||
this.icon = iconFile;
|
||||
// OPENNEURO:
|
||||
this.description = '[en]Experimental data, Numerical data/Image/Movie/etc...[/en][ja]実験結果の数値データ/画像/動画など[/ja]';
|
||||
this.description =
|
||||
'[en]Experimental data, Numerical data/Image/Movie/etc...[/en][ja]実験結果の数値データ/画像/動画など[/ja]';
|
||||
this.subTypes = ItemDataSubTypes;
|
||||
}
|
||||
}
|
||||
|
||||
export default DataTop;
|
||||
export default DataTop;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../config';
|
||||
import { ItemDataSubType, ItemDataSubTypes } from '../../lib/ItemUtil';
|
||||
import type { MultiLang } from '../../../config';
|
||||
import { type ItemDataSubType, ItemDataSubTypes } from '../../lib/ItemUtil';
|
||||
|
||||
interface DataTypeProps {
|
||||
lang: MultiLang;
|
||||
@@ -9,15 +8,17 @@ interface DataTypeProps {
|
||||
|
||||
const DataType = (props: DataTypeProps) => {
|
||||
const { type } = props;
|
||||
const subtype = ItemDataSubTypes.find((value) => { return value.type === type; });
|
||||
const subtype = ItemDataSubTypes.find((value) => {
|
||||
return value.type === type;
|
||||
});
|
||||
if (typeof subtype === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return (<span>{subtype.label}</span>);
|
||||
}
|
||||
return <span>{subtype.label}</span>;
|
||||
};
|
||||
|
||||
const DataUtil = {
|
||||
DataType,
|
||||
}
|
||||
};
|
||||
|
||||
export default DataUtil;
|
||||
export default DataUtil;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import DataTop from './DataTop';
|
||||
import DataList from './DataList';
|
||||
import DataDetail from './DataDetail';
|
||||
import DataAdvancedSearch from './DataAdvancedSearch';
|
||||
import DataDetail from './DataDetail';
|
||||
import DataList from './DataList';
|
||||
import DataTop from './DataTop';
|
||||
|
||||
const ItemTypeData = {
|
||||
Top: DataTop,
|
||||
@@ -10,4 +10,4 @@ const ItemTypeData = {
|
||||
AdvancedSearch: DataAdvancedSearch,
|
||||
};
|
||||
|
||||
export default ItemTypeData;
|
||||
export default ItemTypeData;
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
import AdvancedSearchBase, { AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
import AdvancedSearchBase, { type AdvancedSearchBaseProps } from '../lib/AdvancedSearchBase';
|
||||
|
||||
class FilesAdvancedSearch extends AdvancedSearchBase {
|
||||
|
||||
constructor(props: AdvancedSearchBaseProps) {
|
||||
super(props);
|
||||
this.type = 'files';
|
||||
this.title = 'Files';
|
||||
this.state.values['title'] = '';
|
||||
this.state.values['data_file_name'] = '';
|
||||
this.state.values['data_file_mimetype'] = '';
|
||||
this.state.values['data_file_filetype'] = '';
|
||||
this.state.values['keyword'] = '';
|
||||
this.state.values['description'] = '';
|
||||
this.state.values.title = '';
|
||||
this.state.values.data_file_name = '';
|
||||
this.state.values.data_file_mimetype = '';
|
||||
this.state.values.data_file_filetype = '';
|
||||
this.state.values.keyword = '';
|
||||
this.state.values.description = '';
|
||||
}
|
||||
|
||||
getRows() {
|
||||
const rows = [
|
||||
{ label: '[en]Title[/en][ja]タイトル[/ja]', value: this.renderFieldInputText('title', 50) },
|
||||
{ label: '- [en]File Name[/en][ja]ファイル名[/ja]', value: this.renderFieldInputText('data_file_name', 50) },
|
||||
{ label: '- [en]MIME Type[/en][ja]MIMEタイプ[/ja]', value: this.renderFieldInputText('data_file_mimetype', 50) },
|
||||
{ label: '- [en]File Type[/en][ja]ファイルタイプ[/ja]', value: this.renderFieldInputText('data_file_filetype', 20) },
|
||||
{ label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]', value: this.renderFieldInputText('keyword', 50) },
|
||||
{
|
||||
label: '- [en]File Name[/en][ja]ファイル名[/ja]',
|
||||
value: this.renderFieldInputText('data_file_name', 50),
|
||||
},
|
||||
{
|
||||
label: '- [en]MIME Type[/en][ja]MIMEタイプ[/ja]',
|
||||
value: this.renderFieldInputText('data_file_mimetype', 50),
|
||||
},
|
||||
{
|
||||
label: '- [en]File Type[/en][ja]ファイルタイプ[/ja]',
|
||||
value: this.renderFieldInputText('data_file_filetype', 20),
|
||||
},
|
||||
{
|
||||
label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]',
|
||||
value: this.renderFieldInputText('keyword', 50),
|
||||
},
|
||||
{ label: '[en]Description[/en][ja]概要[/ja]', value: this.renderFieldInputText('description', 50) },
|
||||
];
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
export default FilesAdvancedSearch;
|
||||
export default FilesAdvancedSearch;
|
||||
|
||||
@@ -1,31 +1,56 @@
|
||||
import React from 'react';
|
||||
import Functions from '../../../functions';
|
||||
import { ItemFiles } from '../../lib/ItemUtil';
|
||||
import type { ItemFiles } from '../../lib/ItemUtil';
|
||||
import DetailBase from '../lib/DetailBase';
|
||||
import ItemTypeField from '../lib/field';
|
||||
|
||||
class FilesDetail extends DetailBase {
|
||||
|
||||
getFields() {
|
||||
const { lang } = this.props;
|
||||
const item = this.props.item as ItemFiles;
|
||||
return [
|
||||
{ label: 'ID', value: item.doi },
|
||||
{ label: '[en]Language[/en][ja]言語[/ja]', value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
|
||||
{
|
||||
label: '[en]Language[/en][ja]言語[/ja]',
|
||||
value: <ItemTypeField.Language lang={lang} itemLang={item.lang} />,
|
||||
},
|
||||
{ label: '[en]Title[/en][ja]タイトル[/ja]', value: Functions.mlang(item.title, lang) },
|
||||
{ label: '[en]Last Modified Date[/en][ja]最終更新日[/ja]', value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
|
||||
{ label: '[en]Created Date[/en][ja]作成日[/ja]', value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
|
||||
{ label: '[en]Contributor[/en][ja]登録者[/ja]', value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
|
||||
{
|
||||
label: '[en]Last Modified Date[/en][ja]最終更新日[/ja]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Created Date[/en][ja]作成日[/ja]',
|
||||
value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Contributor[/en][ja]登録者[/ja]',
|
||||
value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} />,
|
||||
},
|
||||
{ label: '[en]Item Type[/en][ja]アイテムタイプ[/ja]', value: item.item_type_display_name },
|
||||
{ label: '[en]Change Log(History)[/en][ja]変更履歴[/ja]', value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
|
||||
{ label: '[en]Data File[/en][ja]データファイル[/ja]', value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="files_file" /> },
|
||||
{
|
||||
label: '[en]Change Log(History)[/en][ja]変更履歴[/ja]',
|
||||
value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Data File[/en][ja]データファイル[/ja]',
|
||||
value: <ItemTypeField.ItemFile lang={lang} file={item.file} type="files_file" />,
|
||||
},
|
||||
{ label: '- [en]File Name[/en][ja]ファイル名[/ja]', value: item.data_file_name },
|
||||
{ label: '- [en]MIME Type[/en][ja]MIMEタイプ[/ja]', value: item.data_file_mimetype },
|
||||
{ label: '- [en]File Type[/en][ja]ファイルタイプ[/ja]', value: item.data_file_filetype },
|
||||
{ label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]', value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
|
||||
{ label: '[en]Description[/en][ja]概要[/ja]', value: <ItemTypeField.Description lang={lang} description={item.description} /> },
|
||||
{
|
||||
label: '[en]Free Keywords[/en][ja]フリーキーワード[/ja]',
|
||||
value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} />,
|
||||
},
|
||||
{
|
||||
label: '[en]Description[/en][ja]概要[/ja]',
|
||||
value: <ItemTypeField.Description lang={lang} description={item.description} />,
|
||||
},
|
||||
{ label: 'Index', value: <ItemTypeField.ItemIndex lang={lang} index={item.index} /> },
|
||||
{ label: '[en]Related to[/en][ja]関連アイテム[/ja]', value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} /> },
|
||||
{
|
||||
label: '[en]Related to[/en][ja]関連アイテム[/ja]',
|
||||
value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} />,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link } from 'react-router';
|
||||
import Functions from '../../../functions';
|
||||
import iconFile from '../../assets/images/icon_files.gif';
|
||||
import { ItemFiles } from '../../lib/ItemUtil';
|
||||
import type { ItemFiles } from '../../lib/ItemUtil';
|
||||
import Contributer from '../lib/field/Contributer';
|
||||
import ListBase, { ListBaseProps } from '../lib/ListBase';
|
||||
import ListBase, { type ListBaseProps } from '../lib/ListBase';
|
||||
|
||||
class FilesList extends ListBase {
|
||||
|
||||
constructor(props: ListBaseProps) {
|
||||
super(props);
|
||||
this.label = 'Files';
|
||||
@@ -19,12 +17,14 @@ class FilesList extends ListBase {
|
||||
const item = this.props.item as ItemFiles;
|
||||
return (
|
||||
<>
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link><br />
|
||||
<Contributer lang={lang} uname={item.uname} name={item.name} /><br />
|
||||
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
|
||||
<br />
|
||||
<Contributer lang={lang} uname={item.uname} name={item.name} />
|
||||
<br />
|
||||
{item.data_file_mimetype}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FilesList;
|
||||
export default FilesList;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import TopBase, { TopBaseProps } from '../lib/TopBase';
|
||||
import iconFile from '../../assets/images/icon_files.gif';
|
||||
import { ItemFilesSubTypes } from '../../lib/ItemUtil';
|
||||
import TopBase, { type TopBaseProps } from '../lib/TopBase';
|
||||
|
||||
class FilesTop extends TopBase {
|
||||
|
||||
constructor(props: TopBaseProps) {
|
||||
super(props);
|
||||
this.type = 'files';
|
||||
@@ -14,4 +13,4 @@ class FilesTop extends TopBase {
|
||||
}
|
||||
}
|
||||
|
||||
export default FilesTop;
|
||||
export default FilesTop;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const FilesUtil = {};
|
||||
|
||||
const FilesUtil = {
|
||||
}
|
||||
|
||||
export default FilesUtil;
|
||||
export default FilesUtil;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import FilesTop from './FilesTop';
|
||||
import FilesList from './FilesList';
|
||||
import FilesDetail from './FilesDetail';
|
||||
import FilesAdvancedSearch from './FilesAdvancedSearch';
|
||||
import FilesDetail from './FilesDetail';
|
||||
import FilesList from './FilesList';
|
||||
import FilesTop from './FilesTop';
|
||||
|
||||
const ItemTypeFiles = {
|
||||
Top: FilesTop,
|
||||
@@ -10,4 +10,4 @@ const ItemTypeFiles = {
|
||||
AdvancedSearch: FilesAdvancedSearch,
|
||||
};
|
||||
|
||||
export default ItemTypeFiles;
|
||||
export default ItemTypeFiles;
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../config';
|
||||
import AdvancedSearchQuery from '../lib/AdvancedSearchQuery';
|
||||
import { Item, ItemBinder, ItemBook, ItemConference, ItemData, ItemFiles, ItemModel, ItemPaper, ItemPresentation, ItemSimulator, ItemStimulus, ItemTool, ItemUrl, ItemMemo } from '../lib/ItemUtil';
|
||||
import type { MultiLang } from '../../config';
|
||||
import type AdvancedSearchQuery from '../lib/AdvancedSearchQuery';
|
||||
import type {
|
||||
Item,
|
||||
ItemBinder,
|
||||
ItemBook,
|
||||
ItemConference,
|
||||
ItemData,
|
||||
ItemFiles,
|
||||
ItemMemo,
|
||||
ItemModel,
|
||||
ItemPaper,
|
||||
ItemPresentation,
|
||||
ItemSimulator,
|
||||
ItemStimulus,
|
||||
ItemTool,
|
||||
ItemUrl,
|
||||
} from '../lib/ItemUtil';
|
||||
import ItemTypeBinder from './binder';
|
||||
import ItemTypeBook from './book';
|
||||
import ItemTypeConference from './conference';
|
||||
@@ -52,7 +66,7 @@ const Top = (props: TopProps) => {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface ListProps {
|
||||
lang: MultiLang;
|
||||
@@ -90,7 +104,7 @@ const List = (props: ListProps) => {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface DetailProps {
|
||||
lang: MultiLang;
|
||||
@@ -128,7 +142,7 @@ const Detail = (props: DetailProps) => {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface AdvancedSearchProps {
|
||||
lang: MultiLang;
|
||||
@@ -167,13 +181,13 @@ const AdvancedSearch = (props: AdvancedSearchProps) => {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ItemType = {
|
||||
Top,
|
||||
List,
|
||||
Detail,
|
||||
AdvancedSearch
|
||||
}
|
||||
AdvancedSearch,
|
||||
};
|
||||
|
||||
export default ItemType;
|
||||
export default ItemType;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { ChangeEvent, Component } from 'react';
|
||||
import { MultiLang } from '../../../config';
|
||||
import type { ReactElement } from 'react';
|
||||
import { type ChangeEvent, Component } from 'react';
|
||||
import type { MultiLang } from '../../../config';
|
||||
import Functions from '../../../functions';
|
||||
import AdvancedSearchQuery from '../../lib/AdvancedSearchQuery';
|
||||
import { ItemSubTypes } from '../../lib/ItemUtil';
|
||||
import type AdvancedSearchQuery from '../../lib/AdvancedSearchQuery';
|
||||
import type { ItemSubTypes } from '../../lib/ItemUtil';
|
||||
|
||||
export interface AdvancedSearchBaseProps {
|
||||
lang: MultiLang;
|
||||
@@ -11,12 +12,11 @@ export interface AdvancedSearchBaseProps {
|
||||
|
||||
interface State {
|
||||
show: boolean;
|
||||
values: any;
|
||||
values: Record<string, string>;
|
||||
}
|
||||
|
||||
class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
|
||||
protected type: string = 'base'
|
||||
protected type: string = 'base';
|
||||
protected title: string = 'Base';
|
||||
protected query: AdvancedSearchQuery;
|
||||
protected ignoreKeys: string[] = [];
|
||||
@@ -26,7 +26,7 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
this.state = {
|
||||
show: false,
|
||||
values: {},
|
||||
}
|
||||
};
|
||||
this.query = props.query;
|
||||
this.handleChangeTitleCheck = this.handleChangeTitleCheck.bind(this);
|
||||
}
|
||||
@@ -49,14 +49,14 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
deleteIgnoreKey(key: string) {
|
||||
if (this.ignoreKeys.includes(key)) {
|
||||
this.ignoreKeys = this.ignoreKeys.filter((v) => {
|
||||
return (v !== key);
|
||||
return v !== key;
|
||||
});
|
||||
}
|
||||
this.query.set(this.type, key, this.state.values[key]);
|
||||
}
|
||||
|
||||
updateField(key: string, value: string) {
|
||||
let values = Object.assign({}, this.state.values);
|
||||
const values = Object.assign({}, this.state.values);
|
||||
values[key] = value;
|
||||
this.updateQuery(key, value);
|
||||
this.setState({ values });
|
||||
@@ -75,7 +75,7 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
this.setState({ show });
|
||||
}
|
||||
|
||||
getRows(): { label: string, value: JSX.Element }[] {
|
||||
getRows(): { label: string; value: ReactElement }[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -84,20 +84,30 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
this.updateField(key, e.target.value);
|
||||
};
|
||||
return (
|
||||
<input className="fieldInputText" type="text" value={this.state.values[key]} size={size} onChange={onChange} />
|
||||
<input
|
||||
className="fieldInputText"
|
||||
type="text"
|
||||
value={this.state.values[key]}
|
||||
size={size}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderFieldSelect(key: string, values: ItemSubTypes<any>) {
|
||||
renderFieldSelect(key: string, values: ItemSubTypes<string>) {
|
||||
const onChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
this.updateField(key, e.target.value);
|
||||
};
|
||||
const options = values.map(({ type, label }, i) => {
|
||||
return <option key={i} value={type}>{label}</option>;
|
||||
const options = values.map(({ type, label }) => {
|
||||
return (
|
||||
<option key={type} value={type}>
|
||||
{label}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<select className="fieldSelect" value={this.state.values[key]} onChange={onChange}>
|
||||
<option value=''>Any</option>
|
||||
<option value="">Any</option>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
@@ -107,37 +117,68 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.checked) {
|
||||
this.deleteIgnoreKey(keyYear);
|
||||
keyMonth !== '' && this.deleteIgnoreKey(keyMonth);
|
||||
keyMday !== '' && this.deleteIgnoreKey(keyMday);
|
||||
if (keyMonth !== '') {
|
||||
this.deleteIgnoreKey(keyMonth);
|
||||
}
|
||||
if (keyMday !== '') {
|
||||
this.deleteIgnoreKey(keyMday);
|
||||
}
|
||||
} else {
|
||||
this.setIgnoreKey(keyYear);
|
||||
keyMonth !== '' && this.setIgnoreKey(keyMonth);
|
||||
keyMday !== '' && this.setIgnoreKey(keyMday);
|
||||
if (keyMonth !== '') {
|
||||
this.setIgnoreKey(keyMonth);
|
||||
}
|
||||
if (keyMday !== '') {
|
||||
this.setIgnoreKey(keyMday);
|
||||
}
|
||||
}
|
||||
};
|
||||
const month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const monthOptions = month.map((value, i) => {
|
||||
return <option key={i} value={i + 1}>{value}</option>;
|
||||
})
|
||||
let mdayOptions: JSX.Element[] = [];
|
||||
return (
|
||||
<option key={value} value={i + 1}>
|
||||
{value}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
const mdayOptions: ReactElement[] = [];
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
mdayOptions.push(<option key={i} value={i}>{i}</option>)
|
||||
mdayOptions.push(
|
||||
<option key={i} value={i}>
|
||||
{i}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="fieldDate">
|
||||
<input type="checkbox" onChange={onChange} />
|
||||
{label.length !== 0 && <label className="fieldDateLabel">{label}</label>}
|
||||
{keyMonth !== '' &&
|
||||
<select value={this.state.values[keyMonth]} onChange={(e) => this.updateField(keyMonth, e.target.value)}>
|
||||
<input type="checkbox" id={keyYear} onChange={onChange} />
|
||||
{label.length !== 0 && (
|
||||
<label className="fieldDateLabel" htmlFor={keyYear}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
{keyMonth !== '' && (
|
||||
<select
|
||||
value={this.state.values[keyMonth]}
|
||||
onChange={(e) => this.updateField(keyMonth, e.target.value)}
|
||||
>
|
||||
{monthOptions}
|
||||
</select>
|
||||
}
|
||||
{keyMday !== '' &&
|
||||
<select value={this.state.values[keyMday]} onChange={(e) => this.updateField(keyMday, e.target.value)}>
|
||||
)}
|
||||
{keyMday !== '' && (
|
||||
<select
|
||||
value={this.state.values[keyMday]}
|
||||
onChange={(e) => this.updateField(keyMday, e.target.value)}
|
||||
>
|
||||
{mdayOptions}
|
||||
</select>
|
||||
}
|
||||
<input type="text" value={this.state.values[keyYear]} size={5} onChange={(e) => this.updateField(keyYear, e.target.value)} />
|
||||
)}
|
||||
<input
|
||||
type="text"
|
||||
value={this.state.values[keyYear]}
|
||||
size={5}
|
||||
onChange={(e) => this.updateField(keyYear, e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -151,7 +192,7 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
const fields = rows.map((value, idx) => {
|
||||
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<tr key={value.label}>
|
||||
<td className="head">{Functions.mlang(value.label, lang)}</td>
|
||||
<td className={evenodd}>{value.value}</td>
|
||||
</tr>
|
||||
@@ -159,9 +200,7 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
});
|
||||
return (
|
||||
<table className="itemtypeFields outer">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
<tbody>{fields}</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
@@ -173,7 +212,11 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
<tbody>
|
||||
<tr>
|
||||
<th align="left">
|
||||
<input type="checkbox" checked={this.state.show} onChange={this.handleChangeTitleCheck} />
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.state.show}
|
||||
onChange={this.handleChangeTitleCheck}
|
||||
/>
|
||||
{this.title}
|
||||
</th>
|
||||
</tr>
|
||||
@@ -183,7 +226,6 @@ class AdvancedSearchBase extends Component<AdvancedSearchBaseProps, State> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AdvancedSearchBase;
|
||||
export default AdvancedSearchBase;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import { MultiLang } from '../../../config';
|
||||
import { Component, type ReactNode } from 'react';
|
||||
import type { MultiLang } from '../../../config';
|
||||
import Functions from '../../../functions';
|
||||
import { Item } from '../../lib/ItemUtil';
|
||||
import type { Item } from '../../lib/ItemUtil';
|
||||
|
||||
export interface DetailBaseField {
|
||||
label: string;
|
||||
@@ -10,11 +10,10 @@ export interface DetailBaseField {
|
||||
|
||||
export interface DetailBaseProps {
|
||||
lang: MultiLang;
|
||||
item: Item
|
||||
item: Item;
|
||||
}
|
||||
|
||||
class DetailBase extends Component<DetailBaseProps> {
|
||||
|
||||
getFields(): DetailBaseField[] {
|
||||
return [];
|
||||
}
|
||||
@@ -24,20 +23,18 @@ class DetailBase extends Component<DetailBaseProps> {
|
||||
const elements = this.getFields().map((value, idx) => {
|
||||
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<tr key={value.label}>
|
||||
<td className="head">{Functions.mlang(value.label, lang)}</td>
|
||||
<td className={evenodd}>{value.value}</td>
|
||||
</tr>
|
||||
)
|
||||
);
|
||||
});
|
||||
return (
|
||||
<table className="outer itemDetail">
|
||||
<tbody>
|
||||
{elements}
|
||||
</tbody>
|
||||
<tbody>{elements}</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DetailBase;
|
||||
export default DetailBase;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import { MultiLang } from '../../../config';
|
||||
import ItemUtil, { Item } from '../../lib/ItemUtil';
|
||||
import { Component } from 'react';
|
||||
import type { MultiLang } from '../../../config';
|
||||
import ItemUtil, { type Item } from '../../lib/ItemUtil';
|
||||
import SimPFLinkIcon from './field/SimPFLinkIcon';
|
||||
|
||||
export interface ListBaseProps {
|
||||
lang: MultiLang;
|
||||
item: Item
|
||||
};
|
||||
item: Item;
|
||||
}
|
||||
|
||||
class ListBase extends Component<ListBaseProps> {
|
||||
|
||||
protected label = '';
|
||||
protected icon = '';
|
||||
protected url: string;
|
||||
@@ -21,12 +20,12 @@ class ListBase extends Component<ListBaseProps> {
|
||||
this.simpfLinkUrl = ItemUtil.getSimPFLinkUrl(props.item.item_id);
|
||||
}
|
||||
|
||||
renderIcon() {
|
||||
renderIcon(): React.ReactNode {
|
||||
return <img src={this.icon} alt={this.label} />;
|
||||
}
|
||||
|
||||
renderBody() {
|
||||
return <></>;
|
||||
renderBody(): React.ReactNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -35,12 +34,8 @@ class ListBase extends Component<ListBaseProps> {
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="listIcon">
|
||||
{this.renderIcon()}
|
||||
</td>
|
||||
<td className="listBody">
|
||||
{this.renderBody()}
|
||||
</td>
|
||||
<td className="listIcon">{this.renderIcon()}</td>
|
||||
<td className="listBody">{this.renderBody()}</td>
|
||||
<td className="listExtra">
|
||||
<SimPFLinkIcon lang={lang} url={this.simpfLinkUrl} isDetail={false} />
|
||||
</td>
|
||||
@@ -51,4 +46,4 @@ class ListBase extends Component<ListBaseProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export default ListBase;
|
||||
export default ListBase;
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../../config';
|
||||
import { Component, Fragment } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../../config';
|
||||
import Functions from '../../../functions';
|
||||
import ItemUtil, { ItemSubTypes } from '../../lib/ItemUtil';
|
||||
import ItemUtil, { type ItemSubTypes } from '../../lib/ItemUtil';
|
||||
|
||||
export interface TopBaseProps {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class TopBase extends Component<TopBaseProps> {
|
||||
|
||||
protected type: string = '';
|
||||
protected label: string = '';
|
||||
protected icon: string = '';
|
||||
protected description: string = '';
|
||||
protected subTypes: ItemSubTypes<any> = [];
|
||||
protected subTypes: ItemSubTypes<string> = [];
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const url = ItemUtil.getItemTypeSearchUrl(this.type);
|
||||
const links = this.subTypes.map(({ type, label }, i) => {
|
||||
return <Fragment key={i}>{i > 0 && ' / '}<Link to={url + '/' + type}>{label}</Link></Fragment>;
|
||||
return (
|
||||
<Fragment key={type}>
|
||||
{i > 0 && ' / '}
|
||||
<Link to={`${url}/${type}`}>{label}</Link>
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
@@ -44,4 +48,4 @@ class TopBase extends Component<TopBaseProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export default TopBase;
|
||||
export default TopBase;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -13,9 +12,17 @@ const Author = (props: Props) => {
|
||||
}
|
||||
const elements = author.map((value, idx) => {
|
||||
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
|
||||
return <tr key={idx}><td className={evenodd}>{value}</td></tr>;
|
||||
return (
|
||||
<tr key={value}>
|
||||
<td className={evenodd}>{value}</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
return <table><tbody>{elements}</tbody></table>;
|
||||
}
|
||||
return (
|
||||
<table>
|
||||
<tbody>{elements}</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
export default Author;
|
||||
export default Author;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
import { ItemBasicChangeLog } from '../../../lib/ItemUtil';
|
||||
import type { ItemBasicChangeLog } from '../../../lib/ItemUtil';
|
||||
import DateTime from './DateTime';
|
||||
|
||||
interface Props {
|
||||
@@ -14,15 +13,21 @@ const ChangeLog = (props: Props) => {
|
||||
if (changelog.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const elements = changelog.map((value, i) => {
|
||||
const elements = changelog.map((value) => {
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td><DateTime lang={lang} date={value.log_date} onlyDate={true} /></td>
|
||||
<tr key={`${value.log_date}:${value.log}`}>
|
||||
<td>
|
||||
<DateTime lang={lang} date={value.log_date} onlyDate={true} />
|
||||
</td>
|
||||
<td>{Functions.mlang(value.log, lang)}</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
return (<table><tbody>{elements}</tbody></table>);
|
||||
}
|
||||
return (
|
||||
<table>
|
||||
<tbody>{elements}</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangeLog;
|
||||
export default ChangeLog;
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
uname: string;
|
||||
name: string
|
||||
name: string;
|
||||
}
|
||||
|
||||
const Contributer = (props: Props) => {
|
||||
const { lang, name, uname } = props;
|
||||
const unsubscribed = '([en]Unsubscribed User[/en][ja]退会済みユーザ[/ja])';
|
||||
const label = uname === '' ? unsubscribed : (name === '' ? uname : name + ' (' + uname + ')');
|
||||
const label = uname === '' ? unsubscribed : name === '' ? uname : `${name} (${uname})`;
|
||||
return <span>{Functions.mlang(label, lang)}</span>;
|
||||
}
|
||||
};
|
||||
|
||||
export default Contributer;
|
||||
export default Contributer;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
|
||||
export type CreativeCommonsType = 'by' | 'by-nc' | 'by-nc-nd' | 'by-nc-sa' | 'by-nd' | 'by-sa';
|
||||
|
||||
@@ -16,11 +15,10 @@ export const getCreativeCommonsType = (ccCommercialUse: number, ccModification:
|
||||
return 'by-nd';
|
||||
case 11:
|
||||
return 'by-sa';
|
||||
case 12:
|
||||
default:
|
||||
return 'by';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -29,28 +27,41 @@ interface Props {
|
||||
|
||||
const CreativeCommons = (props: Props) => {
|
||||
const { type } = props;
|
||||
const url = 'http://creativecommons.org/licenses/' + type + '/4.0/';
|
||||
const logoUrl = 'https://i.creativecommons.org/l/' + type + '/4.0/88x31.png';
|
||||
const url = `http://creativecommons.org/licenses/${type}/4.0/`;
|
||||
const logoUrl = `https://i.creativecommons.org/l/${type}/4.0/88x31.png`;
|
||||
const labels = {
|
||||
by: 'Attribution',
|
||||
nc: 'NonCommercial',
|
||||
nd: 'NoDerivatives',
|
||||
sa: 'ShareAlike',
|
||||
}
|
||||
const label = type.split('-').map((value) => {
|
||||
const prop = value as 'by' | 'nc' | 'nd' | 'sa';
|
||||
return labels[prop];
|
||||
}).join('-');
|
||||
};
|
||||
const label = type
|
||||
.split('-')
|
||||
.map((value) => {
|
||||
const prop = value as 'by' | 'nc' | 'nd' | 'sa';
|
||||
return labels[prop];
|
||||
})
|
||||
.join('-');
|
||||
return (
|
||||
<table style={{borderCollapse: 'separate', borderSpacing: '5px'}}>
|
||||
<table style={{ borderCollapse: 'separate', borderSpacing: '5px' }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href={url} target="_blank" rel="license noopener noreferrer"><img alt="Creative Commons License" src={logoUrl} /></a></td>
|
||||
<td>This work is licensed under a <a href={url} target="_blank" rel="license noopener noreferrer">Criative Commons {label} 4.0 International License</a>.</td>
|
||||
<td>
|
||||
<a href={url} target="_blank" rel="license noopener noreferrer">
|
||||
<img alt="Creative Commons License" src={logoUrl} />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
This work is licensed under a{' '}
|
||||
<a href={url} target="_blank" rel="license noopener noreferrer">
|
||||
Criative Commons {label} 4.0 International License
|
||||
</a>
|
||||
.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default CreativeCommons;
|
||||
export default CreativeCommons;
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import { format } from 'date-fns';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
date: number;
|
||||
onlyDate?: boolean
|
||||
onlyDate?: boolean;
|
||||
}
|
||||
|
||||
const DateTime = (props: Props) => {
|
||||
const { date, onlyDate } = props;
|
||||
const d = moment(new Date(date * 1000));
|
||||
let format = 'MMM D, Y';
|
||||
if (typeof onlyDate === 'undefined' || !onlyDate) {
|
||||
format += ' HH:mm:ss';
|
||||
}
|
||||
return <span>{d.format(format)}</span>;
|
||||
}
|
||||
const pattern = onlyDate ? 'MMM d, y' : 'MMM d, y HH:mm:ss';
|
||||
return <span>{format(new Date(date * 1000), pattern)}</span>;
|
||||
};
|
||||
|
||||
export default DateTime;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import XoopsCode from '../../../../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -12,7 +11,7 @@ const Description = (props: Props) => {
|
||||
const { lang, description, className } = props;
|
||||
const textarea = <XoopsCode lang={lang} text={description} dobr={true} />;
|
||||
const name = typeof className === 'undefined' ? 'description' : className;
|
||||
return (<div className={name}>{textarea}</div>);
|
||||
}
|
||||
return <div className={name}>{textarea}</div>;
|
||||
};
|
||||
|
||||
export default Description;
|
||||
export default Description;
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
.downloadButton {
|
||||
display: inline-block;
|
||||
padding: 7px 20px;
|
||||
text-decoration: none !important;
|
||||
font-weight: normal !important;
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
background: #f0f0f0;
|
||||
color: #000 !important;
|
||||
color: #000;
|
||||
border: solid 1px #e0e0e0;
|
||||
box-shadow: 2px 2px #bbbbbb;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.downloadButton:hover {
|
||||
background: #e8e8e8 !important;
|
||||
background: #e8e8e8;
|
||||
border: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
.downloadButton:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import ItemUtil, { ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import { Component, type MouseEvent } from 'react';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import ItemUtil, { type ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import styles from './FileDownloadButton.module.css';
|
||||
import LicenseAgreementDialog from './LicenseAgreementDialog';
|
||||
|
||||
@@ -18,7 +18,6 @@ interface State {
|
||||
}
|
||||
|
||||
class FileDownloadButton extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -28,7 +27,7 @@ class FileDownloadButton extends Component<Props, State> {
|
||||
this.unsetShow = this.unsetShow.bind(this);
|
||||
}
|
||||
|
||||
handleClickDownload(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
|
||||
handleClickDownload(e: MouseEvent<HTMLAnchorElement>) {
|
||||
if (this.props.rights !== '') {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@@ -45,11 +44,29 @@ class FileDownloadButton extends Component<Props, State> {
|
||||
const url = ItemUtil.getFileUrl(this.props.file);
|
||||
return (
|
||||
<>
|
||||
<a className={styles.downloadButton} href={url} download={file.original_file_name} target="_blank" rel="noopener noreferrer" onClick={this.handleClickDownload}>Download</a>
|
||||
<LicenseAgreementDialog lang={lang} file={file} rights={rights} useCc={useCc} ccCommercialUse={ccCommercialUse} ccModification={ccModification} show={this.state.show} unsetShow={this.unsetShow} />
|
||||
<a
|
||||
className={styles.downloadButton}
|
||||
href={url}
|
||||
download={file.original_file_name}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={this.handleClickDownload}
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
<LicenseAgreementDialog
|
||||
lang={lang}
|
||||
file={file}
|
||||
rights={rights}
|
||||
useCc={useCc}
|
||||
ccCommercialUse={ccCommercialUse}
|
||||
ccModification={ccModification}
|
||||
show={this.state.show}
|
||||
unsetShow={this.unsetShow}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FileDownloadButton;
|
||||
export default FileDownloadButton;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -10,8 +9,8 @@ const FileSize = (props: Props) => {
|
||||
const { size } = props;
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const power = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0;
|
||||
const label = Math.round(size / Math.pow(1024, power) * 10) / 10 + ' ' + units[power]
|
||||
const label = `${Math.round((size / 1024 ** power) * 10) / 10} ${units[power]}`;
|
||||
return <span>{label}</span>;
|
||||
}
|
||||
};
|
||||
|
||||
export default FileSize;
|
||||
export default FileSize;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -12,7 +11,7 @@ const FreeKeyword = (props: Props) => {
|
||||
return null;
|
||||
}
|
||||
const label = keyword.join(', ');
|
||||
return (<span>{label}</span>);
|
||||
}
|
||||
return <span>{label}</span>;
|
||||
};
|
||||
|
||||
export default FreeKeyword;
|
||||
export default FreeKeyword;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
import { ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import type { ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import DateTime from './DateTime';
|
||||
import FileSize from './FileSize';
|
||||
import OpenNeuroFileDownloadButton from './OpenNeuroFileDownloadButton';
|
||||
@@ -32,17 +31,47 @@ const ItemFile = (props: Props) => {
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{data.original_file_name}<br />
|
||||
{data.original_file_name}
|
||||
<br />
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><td>Type</td><td>: {data.mime_type}</td><td rowSpan={4}>{downloadLimit === 0 && <OpenNeuroFileDownloadButton lang={lang} file={data} rights={rights} useCc={useCc} ccCommercialUse={ccCommercialUse} ccModification={ccModification} />}</td></tr>
|
||||
<tr><td>Size</td><td>: <FileSize lang={lang} size={data.file_size} /></td></tr>
|
||||
<tr><td>Last updated</td><td>: <DateTime lang={lang} date={data.timestamp} onlyDate={true} /></td></tr>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>: {data.mime_type}</td>
|
||||
<td rowSpan={4}>
|
||||
{downloadLimit === 0 && (
|
||||
<OpenNeuroFileDownloadButton
|
||||
lang={lang}
|
||||
file={data}
|
||||
rights={rights}
|
||||
useCc={useCc}
|
||||
ccCommercialUse={ccCommercialUse}
|
||||
ccModification={ccModification}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>
|
||||
: <FileSize lang={lang} size={data.file_size} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last updated</td>
|
||||
<td>
|
||||
: <DateTime lang={lang} date={data.timestamp} onlyDate={true} />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{downloadLimit === 1 && <><br />({Functions.mlang('[en]File has been removed[/en][ja]ファイルは削除されました[/ja]', lang)})</>}
|
||||
{downloadLimit === 1 && (
|
||||
<>
|
||||
<br />({Functions.mlang('[en]File has been removed[/en][ja]ファイルは削除されました[/ja]', lang)})
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ItemFile;
|
||||
export default ItemFile;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
import IndexUtil from '../../../lib/IndexUtil';
|
||||
import { ItemBasicIndex } from '../../../lib/ItemUtil';
|
||||
import type { ItemBasicIndex } from '../../../lib/ItemUtil';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -18,9 +17,19 @@ const ItemIndex = (props: Props) => {
|
||||
const elements = index.map((value, idx) => {
|
||||
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
|
||||
const url = IndexUtil.getUrl(value.index_id);
|
||||
return (<tr key={value.index_id}><td className={evenodd}><Link to={url}>{Functions.mlang(value.title, lang)}</Link></td></tr>);
|
||||
return (
|
||||
<tr key={value.index_id}>
|
||||
<td className={evenodd}>
|
||||
<Link to={url}>{Functions.mlang(value.title, lang)}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
return (<table><tbody>{elements}</tbody></table>);
|
||||
}
|
||||
return (
|
||||
<table>
|
||||
<tbody>{elements}</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemIndex;
|
||||
export default ItemIndex;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ItemBasicLang } from '../../../lib/ItemUtil';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
import type { ItemBasicLang } from '../../../lib/ItemUtil';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -25,11 +24,11 @@ const Language = (props: Props) => {
|
||||
por: '[en]Portuguese[/en][ja]ポルトガル語[/ja]',
|
||||
chi: '[en]Chinese[/en][ja]中国語[/ja]',
|
||||
kor: '[en]Korean[/en][ja]韓国語[/ja]',
|
||||
}
|
||||
};
|
||||
if (!(itemLang in langStr)) {
|
||||
return null;
|
||||
}
|
||||
return (<span>{Functions.mlang(langStr[itemLang], lang)}</span>);
|
||||
}
|
||||
return <span>{Functions.mlang(langStr[itemLang], lang)}</span>;
|
||||
};
|
||||
|
||||
export default Language;
|
||||
export default Language;
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
|
||||
.download button {
|
||||
margin: 5px 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import React, { ChangeEvent, Component, MouseEvent } from 'react';
|
||||
import { Modal } from 'react-overlays';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import { type ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
import ItemUtil, { ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import ItemUtil, { type ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import DateTime from './DateTime';
|
||||
import FileSize from './FileSize';
|
||||
import styles from './LicenseAgreementDialog.module.css';
|
||||
import Rights from './Rights';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
file: ItemBasicFile;
|
||||
rights: string;
|
||||
@@ -20,96 +18,128 @@ interface Props extends RouteComponentProps {
|
||||
unsetShow: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
show: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
const LicenseAgreementDialog = (props: Props) => {
|
||||
const { lang, file, rights, useCc, ccCommercialUse, ccModification, show, unsetShow } = props;
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
const [disabled, setDisabled] = useState(true);
|
||||
const [wasShown, setWasShown] = useState(show);
|
||||
|
||||
class LicenseAgreementDialog extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
show: props.show,
|
||||
disabled: false,
|
||||
};
|
||||
this.handleChangeCheckbox = this.handleChangeCheckbox.bind(this);
|
||||
this.handleClickDownload = this.handleClickDownload.bind(this);
|
||||
this.handleClickCancel = this.handleClickCancel.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
if (nextProps.show && !prevState.show) {
|
||||
return { disabled: true, show: nextProps.show };
|
||||
// Every time the dialog opens the licence has to be accepted again.
|
||||
if (show !== wasShown) {
|
||||
setWasShown(show);
|
||||
if (show) {
|
||||
setDisabled(true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
handleChangeCheckbox(e: ChangeEvent<HTMLInputElement>) {
|
||||
const disabled = e.target.value === '0';
|
||||
this.setState({ disabled });
|
||||
}
|
||||
useEffect(() => {
|
||||
const dialog = dialogRef.current;
|
||||
if (dialog === null) {
|
||||
return;
|
||||
}
|
||||
if (show) {
|
||||
if (!dialog.open) {
|
||||
dialog.showModal();
|
||||
}
|
||||
} else if (dialog.open) {
|
||||
dialog.close();
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
handleClickDownload(e: MouseEvent<HTMLButtonElement>) {
|
||||
this.props.unsetShow();
|
||||
this.setState({ show: false });
|
||||
}
|
||||
const handleChangeCheckbox = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setDisabled(e.target.value === '0');
|
||||
};
|
||||
|
||||
handleClickCancel() {
|
||||
this.props.unsetShow();
|
||||
this.setState({ show: false });
|
||||
}
|
||||
|
||||
renderBackdrop(props: any) {
|
||||
return <div className={styles.overlay} {...props} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const date = new Date(this.props.file.timestamp);
|
||||
const timestamp = Math.floor(date.valueOf() / 1000);
|
||||
const url = ItemUtil.getFileUrl(this.props.file);
|
||||
return (
|
||||
<Modal className={styles.dialog} show={this.state.show} onHide={this.handleClickCancel} renderBackdrop={this.renderBackdrop}>
|
||||
const url = ItemUtil.getFileUrl(file);
|
||||
return (
|
||||
<dialog ref={dialogRef} className={styles.dialog} onCancel={unsetShow} onClose={unsetShow}>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
{Functions.mlang('[en]Download file information[/en][ja]ダウンロードするファイルの情報[/ja]', lang)}
|
||||
<div className={styles.box}>
|
||||
{this.props.file.original_file_name}<br />
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><td>Type</td><td>: {this.props.file.mime_type}</td></tr>
|
||||
<tr><td>Size</td><td>: <FileSize lang={lang} size={this.props.file.file_size} /></td></tr>
|
||||
<tr><td>Last updated</td><td>: <DateTime lang={lang} date={timestamp} onlyDate={true} /></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
{Functions.mlang('[en]License agreement[/en][ja]ファイルのライセンス[/ja]', lang)}
|
||||
<div className={styles.box}>
|
||||
{Functions.mlang('[en]Please read the following license agreement carefully.[/en][ja]このファイルには下記のライセンスが設定されています。[/ja]', lang)}
|
||||
<div>
|
||||
<Rights lang={lang} rights={this.props.rights} useCc={this.props.useCc} ccCommercialUse={this.props.ccCommercialUse} ccModification={this.props.ccModification} />
|
||||
<input type="radio" name="radio_license" value="1" onChange={this.handleChangeCheckbox} checked={!this.state.disabled} />{Functions.mlang('[en]I accept the terms in the license agreement.[/en][ja]ライセンスに同意します。[/ja]', lang)}<br />
|
||||
<input type="radio" name="radio_license" value="0" onChange={this.handleChangeCheckbox} checked={this.state.disabled} />{Functions.mlang('[en]I do not accept the terms in the license agreement.[/en][ja]ライセンスに同意しません。[/ja]', lang)}<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div className={styles.download}>
|
||||
Acceptance is needed to download this file.
|
||||
{Functions.mlang('[en]Download file information[/en][ja]ダウンロードするファイルの情報[/ja]', lang)}
|
||||
<div className={styles.box}>
|
||||
{file.original_file_name}
|
||||
<br />
|
||||
<a href={url} download={this.props.file.original_file_name}>
|
||||
<button className="formButton" onClick={this.handleClickDownload} disabled={this.state.disabled}>Download</button>
|
||||
</a>
|
||||
<button className="formButton" onClick={this.handleClickCancel}>Cancel</button>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>: {file.mime_type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>
|
||||
: <FileSize lang={lang} size={file.file_size} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last updated</td>
|
||||
<td>
|
||||
: <DateTime lang={lang} date={file.timestamp} onlyDate={true} />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
<br />
|
||||
<div>
|
||||
{Functions.mlang('[en]License agreement[/en][ja]ファイルのライセンス[/ja]', lang)}
|
||||
<div className={styles.box}>
|
||||
{Functions.mlang(
|
||||
'[en]Please read the following license agreement carefully.[/en][ja]このファイルには下記のライセンスが設定されています。[/ja]',
|
||||
lang
|
||||
)}
|
||||
<div>
|
||||
<Rights
|
||||
lang={lang}
|
||||
rights={rights}
|
||||
useCc={useCc}
|
||||
ccCommercialUse={ccCommercialUse}
|
||||
ccModification={ccModification}
|
||||
/>
|
||||
<input
|
||||
type="radio"
|
||||
name="radio_license"
|
||||
value="1"
|
||||
onChange={handleChangeCheckbox}
|
||||
checked={!disabled}
|
||||
/>
|
||||
{Functions.mlang(
|
||||
'[en]I accept the terms in the license agreement.[/en][ja]ライセンスに同意します。[/ja]',
|
||||
lang
|
||||
)}
|
||||
<br />
|
||||
<input
|
||||
type="radio"
|
||||
name="radio_license"
|
||||
value="0"
|
||||
onChange={handleChangeCheckbox}
|
||||
checked={disabled}
|
||||
/>
|
||||
{Functions.mlang(
|
||||
'[en]I do not accept the terms in the license agreement.[/en][ja]ライセンスに同意しません。[/ja]',
|
||||
lang
|
||||
)}
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div className={styles.download}>
|
||||
Acceptance is needed to download this file.
|
||||
<br />
|
||||
<a href={url} download={file.original_file_name}>
|
||||
<button type="button" className="formButton" onClick={unsetShow} disabled={disabled}>
|
||||
Download
|
||||
</button>
|
||||
</a>
|
||||
<button type="button" className="formButton" onClick={unsetShow}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(LicenseAgreementDialog);
|
||||
export default LicenseAgreementDialog;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
|
||||
interface Props {
|
||||
@@ -12,8 +11,12 @@ const OpenNeuroAuthor = (props: Props) => {
|
||||
if (author.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const label = author.map((value, idx) => <div key={idx}><span>{Functions.mlang(value, lang)}</span></div>);
|
||||
const label = author.map((value) => (
|
||||
<div key={value}>
|
||||
<span>{Functions.mlang(value, lang)}</span>
|
||||
</div>
|
||||
));
|
||||
return <div className="openneuroAuthor">{label}</div>;
|
||||
}
|
||||
};
|
||||
|
||||
export default OpenNeuroAuthor;
|
||||
export default OpenNeuroAuthor;
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
.downloadButton {
|
||||
display: inline-block;
|
||||
padding: 7px 20px;
|
||||
text-decoration: none !important;
|
||||
font-weight: normal !important;
|
||||
background: chocolate;
|
||||
color: #ffffff !important;
|
||||
border: solid 1px #cccccc;
|
||||
box-shadow: 2px 2px #bbbbbb;
|
||||
border-radius: 5px;
|
||||
font-weight: normal;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
background: chocolate;
|
||||
border: solid 1px #cccccc;
|
||||
border-radius: 5px;
|
||||
box-shadow: 2px 2px #bbbbbb;
|
||||
}
|
||||
|
||||
.downloadButton:hover {
|
||||
background: orangered !important;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
background: orangered;
|
||||
border: solid 1px #888888;
|
||||
}
|
||||
|
||||
.downloadButton:active {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { faDownload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { Component } from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import ItemUtil, { ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import { Component, type MouseEvent } from 'react';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import ItemUtil, { type ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import LicenseAgreementDialog from './LicenseAgreementDialog';
|
||||
import styles from './OpenNeuroFileDownloadButton.module.css';
|
||||
|
||||
@@ -20,7 +20,6 @@ interface State {
|
||||
}
|
||||
|
||||
class OpenNeuroFileDownloadButton extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -30,7 +29,7 @@ class OpenNeuroFileDownloadButton extends Component<Props, State> {
|
||||
this.unsetShow = this.unsetShow.bind(this);
|
||||
}
|
||||
|
||||
handleClickDownload(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
|
||||
handleClickDownload(e: MouseEvent<HTMLAnchorElement>) {
|
||||
if (this.props.rights !== '') {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@@ -47,11 +46,29 @@ class OpenNeuroFileDownloadButton extends Component<Props, State> {
|
||||
const url = ItemUtil.getFileUrl(this.props.file);
|
||||
return (
|
||||
<>
|
||||
<a className={styles.downloadButton} href={url} download={file.original_file_name} target="_blank" rel="noopener noreferrer" onClick={this.handleClickDownload}><FontAwesomeIcon icon={faDownload} /> Download</a>
|
||||
<LicenseAgreementDialog lang={lang} file={file} rights={rights} useCc={useCc} ccCommercialUse={ccCommercialUse} ccModification={ccModification} show={this.state.show} unsetShow={this.unsetShow} />
|
||||
<a
|
||||
className={styles.downloadButton}
|
||||
href={url}
|
||||
download={file.original_file_name}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={this.handleClickDownload}
|
||||
>
|
||||
<FontAwesomeIcon icon={faDownload} /> Download
|
||||
</a>
|
||||
<LicenseAgreementDialog
|
||||
lang={lang}
|
||||
file={file}
|
||||
rights={rights}
|
||||
useCc={useCc}
|
||||
ccCommercialUse={ccCommercialUse}
|
||||
ccModification={ccModification}
|
||||
show={this.state.show}
|
||||
unsetShow={this.unsetShow}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenNeuroFileDownloadButton;
|
||||
export default OpenNeuroFileDownloadButton;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
|
||||
interface Props {
|
||||
@@ -12,8 +11,12 @@ const OpenNeuroFreeKeyword = (props: Props) => {
|
||||
if (keyword.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const label = keyword.map((value, idx) => <div key={idx}><span>{Functions.mlang(value, lang)}</span></div>);
|
||||
const label = keyword.map((value) => (
|
||||
<div key={value}>
|
||||
<span>{Functions.mlang(value, lang)}</span>
|
||||
</div>
|
||||
));
|
||||
return <div className="openneuroFreeKeyword">{label}</div>;
|
||||
}
|
||||
};
|
||||
|
||||
export default OpenNeuroFreeKeyword;
|
||||
export default OpenNeuroFreeKeyword;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React, { Component } from 'react';
|
||||
import ItemType from '../..';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import type { ReactElement } from 'react';
|
||||
import { Component } from 'react';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import ItemType from '../../../item-type';
|
||||
import ItemUtil from '../../../lib/ItemUtil';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang
|
||||
lang: MultiLang;
|
||||
relatedTo: number[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
elements: JSX.Element[];
|
||||
elements: ReactElement[];
|
||||
}
|
||||
|
||||
class OpenNeuroRelatedTo extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
@@ -46,8 +46,14 @@ class OpenNeuroRelatedTo extends Component<Props, State> {
|
||||
} else {
|
||||
ItemUtil.getList(relatedTo, (results) => {
|
||||
const elements = results.data.map((item, idx) => {
|
||||
const evenodd = 0 === idx % 2 ? 'even' : 'odd';
|
||||
return <tr key={item.item_id}><td className={evenodd}><ItemType.List lang={lang} item={item} /></td></tr>;
|
||||
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
|
||||
return (
|
||||
<tr key={item.item_id}>
|
||||
<td className={evenodd}>
|
||||
<ItemType.List lang={lang} item={item} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
if (this.isActive) {
|
||||
this.setState({ elements });
|
||||
@@ -62,12 +68,10 @@ class OpenNeuroRelatedTo extends Component<Props, State> {
|
||||
}
|
||||
return (
|
||||
<table className="openneuroRelatedTo">
|
||||
<tbody>
|
||||
{this.state.elements}
|
||||
</tbody>
|
||||
<tbody>{this.state.elements}</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenNeuroRelatedTo;
|
||||
export default OpenNeuroRelatedTo;
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
width: 200px;
|
||||
margin: 5px;
|
||||
font-size: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { Component } from 'react';
|
||||
import Lightbox from 'react-image-lightbox';
|
||||
import 'react-image-lightbox/style.css';
|
||||
import { MultiLang } from '../../../../config';
|
||||
import { useState } from 'react';
|
||||
import Lightbox from 'yet-another-react-lightbox';
|
||||
import 'yet-another-react-lightbox/styles.css';
|
||||
import type { MultiLang } from '../../../../config';
|
||||
import Functions from '../../../../functions';
|
||||
import ItemUtil, { ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import ItemUtil, { type ItemBasicFile } from '../../../lib/ItemUtil';
|
||||
import styles from './Preview.module.css';
|
||||
|
||||
interface Props {
|
||||
@@ -11,74 +11,48 @@ interface Props {
|
||||
file: ItemBasicFile[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
isOpen: boolean;
|
||||
imageIndex: number;
|
||||
}
|
||||
|
||||
class Preview extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
imageIndex: 0,
|
||||
};
|
||||
const Preview = (props: Props) => {
|
||||
const { lang, file } = props;
|
||||
const [imageIndex, setImageIndex] = useState(-1);
|
||||
const data = file.filter((value) => value.file_type_name === 'preview');
|
||||
if (data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const slides = data.map((value) => ({
|
||||
src: ItemUtil.getFileUrl(value),
|
||||
alt: Functions.mlang(value.caption, lang),
|
||||
title: Functions.mlang(value.caption, lang),
|
||||
}));
|
||||
return (
|
||||
<>
|
||||
<div className={styles.previewBox}>
|
||||
{data.map((value, idx) => {
|
||||
const caption = Functions.mlang(value.caption, lang);
|
||||
return (
|
||||
<figure key={value.file_id} className={styles.preview}>
|
||||
<a
|
||||
href={ItemUtil.getFileUrl(value)}
|
||||
download={value.original_file_name}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setImageIndex(idx);
|
||||
}}
|
||||
>
|
||||
<img src={ItemUtil.getPreviewFileUrl(value)} alt={caption} />
|
||||
</a>
|
||||
<figcaption>{caption}</figcaption>
|
||||
</figure>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Lightbox
|
||||
open={imageIndex >= 0}
|
||||
index={imageIndex < 0 ? 0 : imageIndex}
|
||||
close={() => setImageIndex(-1)}
|
||||
slides={slides}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang, file } = this.props;
|
||||
const data = file.filter((value) => {
|
||||
return value.file_type_name === 'preview';
|
||||
});
|
||||
if (data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
let imageUrls: string[] = [];
|
||||
const previews = data.map((value, idx) => {
|
||||
const fileUrl = ItemUtil.getFileUrl(value);
|
||||
const previewUrl = ItemUtil.getPreviewFileUrl(value);
|
||||
const caption = Functions.mlang(value.caption, lang);
|
||||
imageUrls.push(fileUrl);
|
||||
return (
|
||||
<figure key={value.file_id} className={styles.preview}>
|
||||
<a href={fileUrl} download={value.original_file_name} onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ isOpen: true, imageIndex: idx });
|
||||
}}>
|
||||
<img src={previewUrl} alt={caption} />
|
||||
</a>
|
||||
<figcaption>{caption}</figcaption>
|
||||
</figure>
|
||||
);
|
||||
});
|
||||
const { isOpen, imageIndex } = this.state;
|
||||
return (
|
||||
<>
|
||||
<div className={styles.previewBox}>
|
||||
{previews}
|
||||
</div>
|
||||
{isOpen &&
|
||||
<Lightbox
|
||||
mainSrc={imageUrls[imageIndex]}
|
||||
nextSrc={imageUrls[(imageIndex + 1) % data.length]}
|
||||
prevSrc={imageUrls[(imageIndex + data.length - 1) % data.length]}
|
||||
onCloseRequest={() => this.setState({ isOpen: false })}
|
||||
onMovePrevRequest={() =>
|
||||
this.setState({
|
||||
imageIndex: (imageIndex + data.length - 1) % data.length,
|
||||
})
|
||||
}
|
||||
onMoveNextRequest={() =>
|
||||
this.setState({
|
||||
imageIndex: (imageIndex + 1) % data.length,
|
||||
})
|
||||
}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Preview;
|
||||
export default Preview;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user