visualifyjs 2.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/static.yml.bak +51 -0
- package/LICENSE +674 -0
- package/README.md +59 -0
- package/config-overrides.js +31 -0
- package/dist/visualify.js +188 -0
- package/docs/.nojekyll +0 -0
- package/docs/docs/CLI.md +34 -0
- package/docs/docs/README.md +65 -0
- package/docs/docs/Rechart/bar.md +190 -0
- package/docs/docs/Rechart/funnel.md +193 -0
- package/docs/docs/Rechart/geo.md +0 -0
- package/docs/docs/Rechart/line.md +355 -0
- package/docs/docs/Rechart/liquidfill.md +0 -0
- package/docs/docs/Rechart/pie.md +225 -0
- package/docs/docs/Rechart/polar.md +0 -0
- package/docs/docs/Rechart/radar.md +253 -0
- package/docs/docs/Rechart/sankey.md +0 -0
- package/docs/docs/Rechart/scatter.md +0 -0
- package/docs/docs/Rechart/sunburst.md +0 -0
- package/docs/docs/Rechart/tree.md +0 -0
- package/docs/docs/Rechart/wordcloud.md +0 -0
- package/docs/docs/_404.md +52 -0
- package/docs/docs/_coverpage.md +11 -0
- package/docs/docs/_sidebar.md +43 -0
- package/docs/docs/components/dotBio.md +34 -0
- package/docs/docs/components/echart.md +82 -0
- package/docs/docs/components/html.md +34 -0
- package/docs/docs/components/macaron.md +145 -0
- package/docs/docs/components/markdown.md +0 -0
- package/docs/docs/components/more.md +142 -0
- package/docs/docs/components/plotly.md +62 -0
- package/docs/docs/components/scatterL.md +70 -0
- package/docs/docs/components/visium.md +57 -0
- package/docs/docs/configuration.md +123 -0
- package/docs/docs/deploy.md +31 -0
- package/docs/docs/log.md +1 -0
- package/docs/docs/more-pages.md +23 -0
- package/docs/docs/quickstart.md +119 -0
- package/docs/docs/rechart-attributes.md +74 -0
- package/docs/docs/rechart-basic-usage.md +162 -0
- package/docs/docs/static/_images/deploy-github-pages.png +0 -0
- package/docs/docs/static/logo/favicon.ico +0 -0
- package/docs/docs/static/logo/logo_128x128.png +0 -0
- package/docs/docs/static/logo/logo_192x192.png +0 -0
- package/docs/docs/static/logo/logo_256x256.png +0 -0
- package/docs/docs/static/logo/logo_512x512.png +0 -0
- package/docs/docs/static/logo/logo_64x64.png +0 -0
- package/docs/docs/theme.md +5 -0
- package/docs/index.html +71 -0
- package/docs/manifest.json +24 -0
- package/docs/static/css/fluff-stuff.css +170 -0
- package/docs/static/css/font-awesome.min.css +4 -0
- package/docs/static/css/visualify.css +25 -0
- package/docs/static/fonts/fontawesome-webfont.woff2 +0 -0
- package/docs/static/images/star.png +0 -0
- package/docs/static/js/configuration.js +448 -0
- package/docs/static/js/fluff.js +1 -0
- package/docs/static/js/visualify.js +188 -0
- package/docs/static/logo/favicon.ico +0 -0
- package/docs/static/logo/logo_128x128.png +0 -0
- package/docs/static/logo/logo_192x192.png +0 -0
- package/docs/static/logo/logo_256x256.png +0 -0
- package/docs/static/logo/logo_512x512.png +0 -0
- package/docs/static/logo/logo_64x64.png +0 -0
- package/package.json +84 -0
- package/rollup.config.mjs +76 -0
- package/src/_css/404.css +116 -0
- package/src/_css/App.css +38 -0
- package/src/_css/autoSuggestion.css +27 -0
- package/src/_css/circular-progress.css +33 -0
- package/src/_css/index.css +37 -0
- package/src/_css/modern.css +25 -0
- package/src/_media/404.png +0 -0
- package/src/_media/corner.svg +8 -0
- package/src/_media/download.svg +3 -0
- package/src/_media/icon.svg +1 -0
- package/src/_media/logo.svg +14 -0
- package/src/_test/App.test.js +15 -0
- package/src/_utils/reportWebVitals.js +13 -0
- package/src/core/appContext.js +27 -0
- package/src/core/components/Scatter.js +188 -0
- package/src/core/components/ScatterBio.js +572 -0
- package/src/core/components/VisiumPlot.js +165 -0
- package/src/core/components/browser.js +42 -0
- package/src/core/components/dotplot.js +413 -0
- package/src/core/components/html.js +29 -0
- package/src/core/components/list.js +178 -0
- package/src/core/components/macaron.js +201 -0
- package/src/core/components/markdown.js +56 -0
- package/src/core/components/parser.scatterBio.js +579 -0
- package/src/core/components/ratio.js +80 -0
- package/src/core/components/scatterL.js +173 -0
- package/src/core/components/searchbar.js +131 -0
- package/src/core/components/selection.js +193 -0
- package/src/core/components/timeline.js +281 -0
- package/src/core/components/visium.js +97 -0
- package/src/core/fetch/condfetch.js +82 -0
- package/src/core/fetch/fetch.js +92 -0
- package/src/core/fetch/json.js +29 -0
- package/src/core/fetch/vfetch.js +42 -0
- package/src/core/liveEditor.js +44 -0
- package/src/core/modules/codeEditorWithPreview.js +104 -0
- package/src/core/modules/echarts/common.js +20 -0
- package/src/core/modules/echarts/presetHandler.js +41 -0
- package/src/core/modules/echarts/presets/esodev.chromium.js +172 -0
- package/src/core/modules/echarts/presets/esodev.codex.js +130 -0
- package/src/core/modules/echarts/presets/esodev.visium.js +123 -0
- package/src/core/modules/echarts/presets/mmtrbc.js +186 -0
- package/src/core/modules/echarts.js +71 -0
- package/src/core/modules/echartsUtils.js +43 -0
- package/src/core/modules/echartswitcher.js +152 -0
- package/src/core/modules/replotly/presetHandler.js +24 -0
- package/src/core/modules/replotly/presets/minimum.js +18 -0
- package/src/core/modules/replotly/presets/mmtrbc.dot.js +114 -0
- package/src/core/modules/replotly/presets/mmtrbc.violin.js +100 -0
- package/src/core/modules/replotly.js +71 -0
- package/src/core/pages/404.js +50 -0
- package/src/core/pages/error.js +27 -0
- package/src/core/pages/jsonPage.js +62 -0
- package/src/core/pages/loading.js +44 -0
- package/src/core/parser/echart.data.js +183 -0
- package/src/core/parser/echart.features.js +125 -0
- package/src/core/parser/echart.general.js +143 -0
- package/src/core/parser/echart.hilbert.js +57 -0
- package/src/core/parser/echart.parser.js +210 -0
- package/src/core/parser/echart.series.js +67 -0
- package/src/core/parser/echart.types.js +76 -0
- package/src/core/parser/plotly.config.js +10 -0
- package/src/core/parser/plotly.data.js +132 -0
- package/src/core/parser/plotly.layout.js +10 -0
- package/src/core/parser/plotly.violin.js +18 -0
- package/src/core/recharts.js +62 -0
- package/src/core/router/alias.js +49 -0
- package/src/core/router/jsonRouter.js +31 -0
- package/src/core/themes/modern.js +32 -0
- package/src/core/themes/themeSelector.js +33 -0
- package/src/core/visualify.js +47 -0
- package/src/core/widgets/circularProgress.js +24 -0
- package/src/core/widgets/controller.js +83 -0
- package/src/core/widgets/errorBoundary.js +36 -0
- package/src/core/widgets/footer.js +177 -0
- package/src/core/widgets/header.js +234 -0
- package/src/core/widgets/layout/Grid.js +31 -0
- package/src/core/widgets/layout.js +36 -0
- package/src/core/widgets/mapping.js +42 -0
- package/src/index.js +62 -0
- package/src/setupTests.js +5 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-11-29 21:58:14
|
4
|
+
* @FilePath : /visualifyjs/src/core/router/jsonRouter.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
import React from 'react';
|
9
|
+
import { HashRouter, Route, Routes } from 'react-router-dom';
|
10
|
+
import AliasRoute from './alias';
|
11
|
+
|
12
|
+
function JsonRouter({ config, children }) {
|
13
|
+
const { alias } = config;
|
14
|
+
|
15
|
+
return (
|
16
|
+
<HashRouter>
|
17
|
+
<Routes>
|
18
|
+
<Route
|
19
|
+
path='/'
|
20
|
+
element={children}
|
21
|
+
/>
|
22
|
+
<Route
|
23
|
+
path='/*'
|
24
|
+
element={<AliasRoute alias={alias}>{children}</AliasRoute>}
|
25
|
+
/>
|
26
|
+
</Routes>
|
27
|
+
</HashRouter>
|
28
|
+
);
|
29
|
+
}
|
30
|
+
|
31
|
+
export default JsonRouter;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-11-29 22:03:28
|
4
|
+
* @FilePath : /visualifyjs/src/core/themes/modern.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
|
9
|
+
import React from 'react';
|
10
|
+
import Vheader from '../widgets/header';
|
11
|
+
import Vfooter from '../widgets/footer';
|
12
|
+
import { Container } from 'react-bootstrap';
|
13
|
+
import JsonPage from '../pages/jsonPage';
|
14
|
+
|
15
|
+
import '../../_css/modern.css';
|
16
|
+
|
17
|
+
function Modern({ config, children }) {
|
18
|
+
//console.log('Modern config', config);
|
19
|
+
const { homepage = 'home.json' } = config;
|
20
|
+
return (
|
21
|
+
<>
|
22
|
+
<Vheader config={config} />
|
23
|
+
<Container className='text-center'>
|
24
|
+
<JsonPage homepage={homepage} />
|
25
|
+
{children}
|
26
|
+
</Container>
|
27
|
+
<Vfooter config={config} />
|
28
|
+
</>
|
29
|
+
);
|
30
|
+
}
|
31
|
+
|
32
|
+
export default Modern;
|
@@ -0,0 +1,33 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-11-29 22:52:11
|
4
|
+
* @FilePath : /visualifyjs/src/core/themes/themeSelector.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
import React from 'react';
|
9
|
+
import Modern from './modern';
|
10
|
+
|
11
|
+
function ThemeSelector({ theme, config, children }) {
|
12
|
+
switch (theme) {
|
13
|
+
case 'modern':
|
14
|
+
return (
|
15
|
+
<Modern
|
16
|
+
config={config}
|
17
|
+
children={children}
|
18
|
+
/>
|
19
|
+
);
|
20
|
+
// ... more cases as needed
|
21
|
+
default:
|
22
|
+
console.warn('Unsupported theme:', theme, 'using default theme');
|
23
|
+
// Fallback to default theme
|
24
|
+
return (
|
25
|
+
<Modern
|
26
|
+
config={config}
|
27
|
+
children={children}
|
28
|
+
/>
|
29
|
+
);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
export default ThemeSelector;
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-11-29 15:35:28
|
4
|
+
* @FilePath : /visualifyjs/src/core/visualify.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
import React from 'react';
|
9
|
+
import ReactDOM from 'react-dom/client';
|
10
|
+
import ThemeSelector from './themes/themeSelector';
|
11
|
+
import JsonRouter from './router/jsonRouter';
|
12
|
+
import { VisualifyProvider } from './appContext';
|
13
|
+
|
14
|
+
function Visualify({ config }) {
|
15
|
+
// global variable
|
16
|
+
const { theme = 'modern' } = config;
|
17
|
+
|
18
|
+
return (
|
19
|
+
<VisualifyProvider>
|
20
|
+
<JsonRouter config={config}>
|
21
|
+
<ThemeSelector
|
22
|
+
theme={theme}
|
23
|
+
config={config}
|
24
|
+
/>
|
25
|
+
</JsonRouter>
|
26
|
+
</VisualifyProvider>
|
27
|
+
);
|
28
|
+
}
|
29
|
+
|
30
|
+
function CreateApp(config) {
|
31
|
+
if (!config) throw new Error('Missing configuration.');
|
32
|
+
const el = document.querySelector(config.el || '#root');
|
33
|
+
if (!el) throw new Error('el not found. Please check your `el` option.');
|
34
|
+
const app = ReactDOM.createRoot(el);
|
35
|
+
|
36
|
+
// deletion used configuration
|
37
|
+
delete config.el;
|
38
|
+
delete config.mode;
|
39
|
+
|
40
|
+
app.render(
|
41
|
+
<React.StrictMode>
|
42
|
+
<Visualify config={{ ...config }} />
|
43
|
+
</React.StrictMode>,
|
44
|
+
);
|
45
|
+
}
|
46
|
+
|
47
|
+
export default CreateApp;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-12-23 13:24:11
|
4
|
+
* @FilePath : /visualifyjs/src/core/widgets/CircularProgress.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
import React from 'react';
|
9
|
+
import PropTypes from 'prop-types';
|
10
|
+
import '../../_css/circular-progress.css';
|
11
|
+
|
12
|
+
function CircularProgress({ color }) {
|
13
|
+
return (
|
14
|
+
<div className='circular-progress'>
|
15
|
+
<div className={`spinner ${color}`} />
|
16
|
+
</div>
|
17
|
+
);
|
18
|
+
}
|
19
|
+
|
20
|
+
CircularProgress.propTypes = {
|
21
|
+
color: PropTypes.oneOf(['primary', 'secondary']),
|
22
|
+
};
|
23
|
+
|
24
|
+
export default CircularProgress;
|
@@ -0,0 +1,83 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-12-21 13:07:59
|
4
|
+
* @FilePath : /visualifyjs/src/core/widgets/controller.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
import React from 'react';
|
9
|
+
import widgetMapping from './mapping';
|
10
|
+
|
11
|
+
function Vcontroller({ components = [], layout }) {
|
12
|
+
// ----------------------------------------------------------------------
|
13
|
+
const UUIDs = new Set();
|
14
|
+
function generateUniqueId() {
|
15
|
+
let uniqueId;
|
16
|
+
do {
|
17
|
+
uniqueId = 'xxxx-xxxx-4xxx-yxxx-xxxx-xxxx'.replace(
|
18
|
+
/[xy]/g,
|
19
|
+
function (c) {
|
20
|
+
const r = (Math.random() * 16) | 0;
|
21
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
22
|
+
return v.toString(16);
|
23
|
+
},
|
24
|
+
);
|
25
|
+
} while (UUIDs.has(uniqueId));
|
26
|
+
UUIDs.add(uniqueId);
|
27
|
+
return uniqueId;
|
28
|
+
}
|
29
|
+
|
30
|
+
const _components = {};
|
31
|
+
// ----------------------------------------------------------------------
|
32
|
+
// Iterate over the config array and render the components based on their type and position
|
33
|
+
const Allcomponents = components.map((componentConfig, index) => {
|
34
|
+
const {
|
35
|
+
row = 1,
|
36
|
+
col = 1,
|
37
|
+
colspan = 1,
|
38
|
+
rowspan = 1,
|
39
|
+
debug = false,
|
40
|
+
} = componentConfig;
|
41
|
+
|
42
|
+
const componentStyle =
|
43
|
+
layout === 'grid'
|
44
|
+
? {
|
45
|
+
gridColumn: `${col} / span ${colspan}`,
|
46
|
+
gridRow: `${row} / span ${rowspan}`,
|
47
|
+
border: debug ? '1px solid red' : 'none',
|
48
|
+
}
|
49
|
+
: {};
|
50
|
+
|
51
|
+
const Component = widgetMapping[componentConfig.type];
|
52
|
+
|
53
|
+
// Generate a unique ID for the component using both ID and index
|
54
|
+
const uniqueId = generateUniqueId();
|
55
|
+
|
56
|
+
//console.log(uniqueId);
|
57
|
+
|
58
|
+
if (!Component) {
|
59
|
+
return (
|
60
|
+
<span
|
61
|
+
key={uniqueId}
|
62
|
+
style={componentStyle}>
|
63
|
+
Error: Component{' '}
|
64
|
+
<b style={{ color: 'red' }}>{componentConfig.type}</b> not
|
65
|
+
found.
|
66
|
+
</span>
|
67
|
+
);
|
68
|
+
}
|
69
|
+
|
70
|
+
const _Component = React.cloneElement(<Component key={uniqueId} />, {
|
71
|
+
style: componentStyle,
|
72
|
+
props: componentConfig,
|
73
|
+
});
|
74
|
+
|
75
|
+
_components[uniqueId] = _Component;
|
76
|
+
// Clone the Component element with the necessary styles and props
|
77
|
+
return _Component;
|
78
|
+
});
|
79
|
+
|
80
|
+
return <>{Allcomponents}</>;
|
81
|
+
}
|
82
|
+
|
83
|
+
export default Vcontroller;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/*
|
2
|
+
* @Author : Lihao leolihao@arizona.edu
|
3
|
+
* @Date : 2023-12-02 16:34:41
|
4
|
+
* @FilePath : /visualifyjs/src/core/widgets/errorBoundary.js
|
5
|
+
* @Description :
|
6
|
+
* Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
|
7
|
+
*/
|
8
|
+
import React from 'react';
|
9
|
+
|
10
|
+
class ErrorBoundary extends React.Component {
|
11
|
+
constructor(props) {
|
12
|
+
super(props);
|
13
|
+
this.state = { hasError: false, error: null };
|
14
|
+
}
|
15
|
+
|
16
|
+
static getDerivedStateFromError(error) {
|
17
|
+
// Update state so the next render will show the fallback UI.
|
18
|
+
return { hasError: true, error };
|
19
|
+
}
|
20
|
+
|
21
|
+
componentDidCatch(error, errorInfo) {
|
22
|
+
// You can also log the error to an error reporting service
|
23
|
+
console.error('Caught an error:', error, errorInfo);
|
24
|
+
}
|
25
|
+
|
26
|
+
render() {
|
27
|
+
if (this.state.hasError) {
|
28
|
+
// You can render any custom fallback UI
|
29
|
+
return <h1>Something went wrong: {this.state.error.message}</h1>;
|
30
|
+
}
|
31
|
+
|
32
|
+
return this.props.children;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
export default ErrorBoundary;
|
@@ -0,0 +1,177 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import DOMPurify from 'dompurify';
|
3
|
+
import { Container, Row, Col } from 'react-bootstrap';
|
4
|
+
import { ReactComponent as ICON } from '../../_media/icon.svg';
|
5
|
+
import pkg from '../../../package.json';
|
6
|
+
|
7
|
+
function Vfooter({ config }) {
|
8
|
+
// Download -------------------------------------------------------------------------
|
9
|
+
const { download, download_style } = config;
|
10
|
+
|
11
|
+
const _renderDownloads = () => {
|
12
|
+
if (download === undefined) return null;
|
13
|
+
if (Object.keys(download).length === 0) return null;
|
14
|
+
|
15
|
+
let button_style = {
|
16
|
+
marginRight: '1rem',
|
17
|
+
};
|
18
|
+
|
19
|
+
if (
|
20
|
+
download_style !== undefined &&
|
21
|
+
typeof download_style === 'object'
|
22
|
+
) {
|
23
|
+
button_style = download_style;
|
24
|
+
}
|
25
|
+
|
26
|
+
return (
|
27
|
+
<Row>
|
28
|
+
<Col>
|
29
|
+
<b style={{ marginRight: '1rem' }}>Download: </b>
|
30
|
+
{Object.keys(download).map((key) => (
|
31
|
+
<a
|
32
|
+
key={key}
|
33
|
+
href={download[key]}
|
34
|
+
download>
|
35
|
+
<button style={button_style}>{`${key}`}</button>
|
36
|
+
</a>
|
37
|
+
))}
|
38
|
+
</Col>
|
39
|
+
</Row>
|
40
|
+
);
|
41
|
+
};
|
42
|
+
|
43
|
+
// References -------------------------------------------------------------------------
|
44
|
+
// get references config for rendering references
|
45
|
+
const { references, references_style } = config;
|
46
|
+
|
47
|
+
const _renderRefs = () => {
|
48
|
+
let refs = [];
|
49
|
+
var ref_list_style = references_style;
|
50
|
+
if (references === undefined || references.length === 0) return null;
|
51
|
+
if (references_style === undefined || references_style === '')
|
52
|
+
ref_list_style = 'circle';
|
53
|
+
|
54
|
+
references.map((reference, index) =>
|
55
|
+
refs.push(
|
56
|
+
<li
|
57
|
+
key={index}
|
58
|
+
className={`reference-item ${ref_list_style}`}
|
59
|
+
dangerouslySetInnerHTML={{
|
60
|
+
__html: DOMPurify.sanitize(reference),
|
61
|
+
}}
|
62
|
+
/>,
|
63
|
+
),
|
64
|
+
);
|
65
|
+
|
66
|
+
return (
|
67
|
+
<Row>
|
68
|
+
<Col>
|
69
|
+
<ul className='reference-list'>{refs}</ul>
|
70
|
+
</Col>
|
71
|
+
</Row>
|
72
|
+
);
|
73
|
+
};
|
74
|
+
|
75
|
+
// CopyRight -------------------------------------------------------------------------
|
76
|
+
const { copyright = pkg.name } = config;
|
77
|
+
const _renderCopyRight = () => {
|
78
|
+
return (
|
79
|
+
<Col>
|
80
|
+
<p
|
81
|
+
className='mb-0'
|
82
|
+
dangerouslySetInnerHTML={{
|
83
|
+
__html: DOMPurify.sanitize(
|
84
|
+
`${copyright} Copyright © ${new Date().getFullYear()} powered by ${
|
85
|
+
pkg.name
|
86
|
+
}@<a href="${pkg.homepage}">${pkg.version}</a>`,
|
87
|
+
),
|
88
|
+
}}
|
89
|
+
/>
|
90
|
+
</Col>
|
91
|
+
);
|
92
|
+
};
|
93
|
+
|
94
|
+
// Icons -------------------------------------------------------------------------
|
95
|
+
const { icons } = config;
|
96
|
+
|
97
|
+
const _renderIcons = () => {
|
98
|
+
let Icons = [];
|
99
|
+
|
100
|
+
if (icons === undefined) {
|
101
|
+
Icons.push(
|
102
|
+
<li
|
103
|
+
key='visualify'
|
104
|
+
className='list-inline-item'>
|
105
|
+
<a href='https://visualify.pharmacy.arizona.edu/'>
|
106
|
+
<ICON />
|
107
|
+
</a>
|
108
|
+
</li>,
|
109
|
+
);
|
110
|
+
|
111
|
+
Icons.push(
|
112
|
+
<li
|
113
|
+
key='v_github'
|
114
|
+
className='list-inline-item'>
|
115
|
+
<a href='https://github.com/visualify/visualify.js'>
|
116
|
+
<i className='fa-brands fa-github fa-lg'></i>
|
117
|
+
</a>
|
118
|
+
</li>,
|
119
|
+
);
|
120
|
+
} else {
|
121
|
+
for (const [icon, link] of Object.entries(icons)) {
|
122
|
+
let IconElement = null;
|
123
|
+
if (icon.startsWith('fa')) {
|
124
|
+
IconElement = <i className={`${icon}`} />;
|
125
|
+
} else if (icon.startsWith('<svg')) {
|
126
|
+
IconElement = (
|
127
|
+
<div
|
128
|
+
dangerouslySetInnerHTML={{
|
129
|
+
__html: DOMPurify.sanitize(icon),
|
130
|
+
}}
|
131
|
+
/>
|
132
|
+
);
|
133
|
+
} else {
|
134
|
+
IconElement = (
|
135
|
+
<img
|
136
|
+
src={icon}
|
137
|
+
alt=''
|
138
|
+
/>
|
139
|
+
);
|
140
|
+
}
|
141
|
+
Icons.push(
|
142
|
+
<li
|
143
|
+
key={link}
|
144
|
+
className='list-inline-item'>
|
145
|
+
<a href={link}>{IconElement}</a>
|
146
|
+
</li>,
|
147
|
+
);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
return (
|
152
|
+
<Col>
|
153
|
+
<ul
|
154
|
+
id='footer_icon'
|
155
|
+
className='list-inline mb-0'>
|
156
|
+
{Icons}
|
157
|
+
</ul>
|
158
|
+
</Col>
|
159
|
+
);
|
160
|
+
};
|
161
|
+
|
162
|
+
return (
|
163
|
+
<footer className='v-footer'>
|
164
|
+
<Container className='py-4 py-lg-5'>
|
165
|
+
{_renderDownloads()}
|
166
|
+
{_renderRefs()}
|
167
|
+
<hr />
|
168
|
+
<Row className='text-muted d-flex justify-content-between align-items-center pt-3 list-inline mb-0'>
|
169
|
+
{_renderCopyRight()}
|
170
|
+
{_renderIcons()}
|
171
|
+
</Row>
|
172
|
+
</Container>
|
173
|
+
</footer>
|
174
|
+
);
|
175
|
+
}
|
176
|
+
|
177
|
+
export default Vfooter;
|
@@ -0,0 +1,234 @@
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
2
|
+
import { Container } from 'react-bootstrap';
|
3
|
+
import { ReactComponent as Corner } from '../../_media/corner.svg';
|
4
|
+
import { useLocation, Link } from 'react-router-dom';
|
5
|
+
import { ReactComponent as Logo } from '../../_media/logo.svg';
|
6
|
+
|
7
|
+
function Vheader({ config }) {
|
8
|
+
const location = useLocation();
|
9
|
+
const [Navgation, setNavgation] = useState(null);
|
10
|
+
const [activeNav, setActiveNav] = useState('home');
|
11
|
+
|
12
|
+
const {
|
13
|
+
logo = <Logo />,
|
14
|
+
logolink = 'https://www.lgcyaxi.net/visualify',
|
15
|
+
name = 'Visualify',
|
16
|
+
nav = false,
|
17
|
+
alias = {},
|
18
|
+
nav_alignment = 'start',
|
19
|
+
repo,
|
20
|
+
} = config;
|
21
|
+
|
22
|
+
// Function to get the current directory path
|
23
|
+
const getCurrentDirectoryPath = () => {
|
24
|
+
// Extract the path from the hash, removing the leading '#/'
|
25
|
+
const hashPath = window.location.hash.slice(2);
|
26
|
+
const pathSegments = hashPath.split('/');
|
27
|
+
// Remove the last segment (file name or empty string if it ends with '/')
|
28
|
+
pathSegments.pop();
|
29
|
+
// Rejoin the remaining segments to form the directory path
|
30
|
+
let directoryPath = pathSegments.join('/');
|
31
|
+
// Ensure the directory path ends with '/'
|
32
|
+
if (!directoryPath.endsWith('/')) {
|
33
|
+
directoryPath += '/';
|
34
|
+
}
|
35
|
+
return directoryPath;
|
36
|
+
};
|
37
|
+
|
38
|
+
const renderLogo = () => {
|
39
|
+
return (
|
40
|
+
<a
|
41
|
+
className='navbar-brand d-flex align-items-center'
|
42
|
+
href={logolink}
|
43
|
+
target='_blank'
|
44
|
+
rel='noreferrer'>
|
45
|
+
{typeof logo === 'string' ? (
|
46
|
+
<img
|
47
|
+
src={logo}
|
48
|
+
alt='logo'
|
49
|
+
width='65'
|
50
|
+
height='65'
|
51
|
+
className='d-inline-block align-top'
|
52
|
+
/>
|
53
|
+
) : (
|
54
|
+
logo
|
55
|
+
)}
|
56
|
+
</a>
|
57
|
+
);
|
58
|
+
};
|
59
|
+
|
60
|
+
const renderTitle = () => {
|
61
|
+
if (typeof name === 'string') {
|
62
|
+
return <h1 className='rainbow-text'>{name}</h1>;
|
63
|
+
}
|
64
|
+
const {
|
65
|
+
text = 'Visualify',
|
66
|
+
font_weight = 'bold',
|
67
|
+
color = 'rainbow',
|
68
|
+
...rest
|
69
|
+
} = name;
|
70
|
+
|
71
|
+
if (color === 'rainbow') {
|
72
|
+
return (
|
73
|
+
<h1
|
74
|
+
className='rainbow-text'
|
75
|
+
style={{ fontWeight: font_weight }}>
|
76
|
+
{text}
|
77
|
+
</h1>
|
78
|
+
);
|
79
|
+
} else {
|
80
|
+
return (
|
81
|
+
<h1 style={{ fontWeight: font_weight, color: color, ...rest }}>
|
82
|
+
{text}
|
83
|
+
</h1>
|
84
|
+
);
|
85
|
+
}
|
86
|
+
};
|
87
|
+
|
88
|
+
useEffect(() => {
|
89
|
+
const resolveNavUrl = () => {
|
90
|
+
let pathSegments = location.pathname.split('/');
|
91
|
+
//console.log('pathseg:' + pathSegments);
|
92
|
+
pathSegments.pop();
|
93
|
+
let directoryPath = pathSegments.join('/');
|
94
|
+
|
95
|
+
if (directoryPath === '') directoryPath = window.location.pathname;
|
96
|
+
//console.log('directoryPath:' + directoryPath,'windows.pathname:' + window.location.pathname,);
|
97
|
+
|
98
|
+
if (!directoryPath.endsWith('/')) directoryPath += '/';
|
99
|
+
|
100
|
+
const fullNavPath = directoryPath + '_nav.json';
|
101
|
+
const aliasPath = alias && alias[fullNavPath];
|
102
|
+
|
103
|
+
return aliasPath ? aliasPath : fullNavPath;
|
104
|
+
};
|
105
|
+
|
106
|
+
if (nav && typeof nav === 'boolean') {
|
107
|
+
const navUrl = resolveNavUrl();
|
108
|
+
|
109
|
+
// Fetch the navigation data only if the URL has changed
|
110
|
+
if (navUrl !== previousNavUrl.current) {
|
111
|
+
fetch(navUrl)
|
112
|
+
.then((res) => res.json())
|
113
|
+
.then((data) => {
|
114
|
+
// Check if the fetched data is different from the current state
|
115
|
+
if (
|
116
|
+
JSON.stringify(data) !== JSON.stringify(Navgation)
|
117
|
+
) {
|
118
|
+
setNavgation(data);
|
119
|
+
}
|
120
|
+
})
|
121
|
+
.catch((err) => {
|
122
|
+
console.error(
|
123
|
+
`Failed to fetch navigation data: ${err}`,
|
124
|
+
);
|
125
|
+
setNavgation(null);
|
126
|
+
});
|
127
|
+
|
128
|
+
// Update the ref with the new URL
|
129
|
+
previousNavUrl.current = navUrl;
|
130
|
+
}
|
131
|
+
} else if (
|
132
|
+
nav &&
|
133
|
+
typeof nav === 'object' &&
|
134
|
+
JSON.stringify(nav) !== JSON.stringify(Navgation)
|
135
|
+
) {
|
136
|
+
setNavgation(nav);
|
137
|
+
} else if (!nav) {
|
138
|
+
setNavgation(null);
|
139
|
+
}
|
140
|
+
}, [nav, alias, Navgation, location.pathname]); // Use location.pathname if only the path is relevant
|
141
|
+
|
142
|
+
// Use a ref to store the previous navigation URL
|
143
|
+
const previousNavUrl = useRef();
|
144
|
+
|
145
|
+
useEffect(() => {
|
146
|
+
// Update the active nav item based on the current location
|
147
|
+
let path = location.pathname === '/' ? '/home' : location.pathname;
|
148
|
+
setActiveNav(path.slice(1));
|
149
|
+
}, [location]);
|
150
|
+
|
151
|
+
const renderNav = () => {
|
152
|
+
if (!Navgation) return <Container />;
|
153
|
+
const currentDirectoryPath = getCurrentDirectoryPath();
|
154
|
+
const navs = Navgation.map((navItem, index) => {
|
155
|
+
let fullPath = `${currentDirectoryPath}${navItem.link}`;
|
156
|
+
if (fullPath.startsWith('/')) {
|
157
|
+
fullPath = fullPath.slice(1);
|
158
|
+
}
|
159
|
+
const isActive = activeNav === fullPath;
|
160
|
+
let navLinkClass = 'nav-link';
|
161
|
+
if (navItem.type === 'primary')
|
162
|
+
navLinkClass = 'btn btn-primary shadow no-border-radius';
|
163
|
+
if (isActive) navLinkClass += ' active';
|
164
|
+
const isExternal = navItem.link.startsWith('http');
|
165
|
+
|
166
|
+
return (
|
167
|
+
<li
|
168
|
+
className='nav-item'
|
169
|
+
key={index}>
|
170
|
+
{isExternal ? (
|
171
|
+
<a
|
172
|
+
className={navLinkClass}
|
173
|
+
href={fullPath}
|
174
|
+
target='_blank'
|
175
|
+
rel='noreferrer'>
|
176
|
+
{navItem.title}
|
177
|
+
</a>
|
178
|
+
) : (
|
179
|
+
<Link
|
180
|
+
className={navLinkClass}
|
181
|
+
to={fullPath}>
|
182
|
+
{navItem.title}
|
183
|
+
</Link>
|
184
|
+
)}
|
185
|
+
</li>
|
186
|
+
);
|
187
|
+
});
|
188
|
+
|
189
|
+
return (
|
190
|
+
<Container>
|
191
|
+
<ul
|
192
|
+
className={`navbar-nav d-flex justify-content-${nav_alignment} mx-auto`}
|
193
|
+
id='navtabs'>
|
194
|
+
{navs}
|
195
|
+
</ul>
|
196
|
+
</Container>
|
197
|
+
);
|
198
|
+
};
|
199
|
+
|
200
|
+
const renderGitHubCorner = () => {
|
201
|
+
if (!repo) return null;
|
202
|
+
|
203
|
+
// Check if it's a full URL or a "username/repo" string
|
204
|
+
const isFullUrl =
|
205
|
+
repo.startsWith('http://') || repo.startsWith('https://');
|
206
|
+
const githubUrl = isFullUrl ? repo : `https://github.com/${repo}`;
|
207
|
+
|
208
|
+
return (
|
209
|
+
<a
|
210
|
+
href={githubUrl}
|
211
|
+
className='github-corner'
|
212
|
+
aria-label='View source on GitHub'
|
213
|
+
target='_blank'
|
214
|
+
rel='noreferrer'>
|
215
|
+
{<Corner />}
|
216
|
+
</a>
|
217
|
+
);
|
218
|
+
};
|
219
|
+
|
220
|
+
return (
|
221
|
+
<header className='v-header'>
|
222
|
+
<nav className='navbar navbar-light navbar-expand-md py-3'>
|
223
|
+
<Container>
|
224
|
+
{renderLogo()}
|
225
|
+
{renderTitle()}
|
226
|
+
{renderNav()}
|
227
|
+
</Container>
|
228
|
+
</nav>
|
229
|
+
{renderGitHubCorner()}
|
230
|
+
</header>
|
231
|
+
);
|
232
|
+
}
|
233
|
+
|
234
|
+
export default Vheader;
|