visualifyjs 2.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. package/.github/workflows/static.yml.bak +51 -0
  2. package/LICENSE +674 -0
  3. package/README.md +59 -0
  4. package/config-overrides.js +31 -0
  5. package/dist/visualify.js +188 -0
  6. package/docs/.nojekyll +0 -0
  7. package/docs/docs/CLI.md +34 -0
  8. package/docs/docs/README.md +65 -0
  9. package/docs/docs/Rechart/bar.md +190 -0
  10. package/docs/docs/Rechart/funnel.md +193 -0
  11. package/docs/docs/Rechart/geo.md +0 -0
  12. package/docs/docs/Rechart/line.md +355 -0
  13. package/docs/docs/Rechart/liquidfill.md +0 -0
  14. package/docs/docs/Rechart/pie.md +225 -0
  15. package/docs/docs/Rechart/polar.md +0 -0
  16. package/docs/docs/Rechart/radar.md +253 -0
  17. package/docs/docs/Rechart/sankey.md +0 -0
  18. package/docs/docs/Rechart/scatter.md +0 -0
  19. package/docs/docs/Rechart/sunburst.md +0 -0
  20. package/docs/docs/Rechart/tree.md +0 -0
  21. package/docs/docs/Rechart/wordcloud.md +0 -0
  22. package/docs/docs/_404.md +52 -0
  23. package/docs/docs/_coverpage.md +11 -0
  24. package/docs/docs/_sidebar.md +43 -0
  25. package/docs/docs/components/dotBio.md +34 -0
  26. package/docs/docs/components/echart.md +82 -0
  27. package/docs/docs/components/html.md +34 -0
  28. package/docs/docs/components/macaron.md +145 -0
  29. package/docs/docs/components/markdown.md +0 -0
  30. package/docs/docs/components/more.md +142 -0
  31. package/docs/docs/components/plotly.md +62 -0
  32. package/docs/docs/components/scatterL.md +70 -0
  33. package/docs/docs/components/visium.md +57 -0
  34. package/docs/docs/configuration.md +123 -0
  35. package/docs/docs/deploy.md +31 -0
  36. package/docs/docs/log.md +1 -0
  37. package/docs/docs/more-pages.md +23 -0
  38. package/docs/docs/quickstart.md +119 -0
  39. package/docs/docs/rechart-attributes.md +74 -0
  40. package/docs/docs/rechart-basic-usage.md +162 -0
  41. package/docs/docs/static/_images/deploy-github-pages.png +0 -0
  42. package/docs/docs/static/logo/favicon.ico +0 -0
  43. package/docs/docs/static/logo/logo_128x128.png +0 -0
  44. package/docs/docs/static/logo/logo_192x192.png +0 -0
  45. package/docs/docs/static/logo/logo_256x256.png +0 -0
  46. package/docs/docs/static/logo/logo_512x512.png +0 -0
  47. package/docs/docs/static/logo/logo_64x64.png +0 -0
  48. package/docs/docs/theme.md +5 -0
  49. package/docs/index.html +71 -0
  50. package/docs/manifest.json +24 -0
  51. package/docs/static/css/fluff-stuff.css +170 -0
  52. package/docs/static/css/font-awesome.min.css +4 -0
  53. package/docs/static/css/visualify.css +25 -0
  54. package/docs/static/fonts/fontawesome-webfont.woff2 +0 -0
  55. package/docs/static/images/star.png +0 -0
  56. package/docs/static/js/configuration.js +448 -0
  57. package/docs/static/js/fluff.js +1 -0
  58. package/docs/static/js/visualify.js +188 -0
  59. package/docs/static/logo/favicon.ico +0 -0
  60. package/docs/static/logo/logo_128x128.png +0 -0
  61. package/docs/static/logo/logo_192x192.png +0 -0
  62. package/docs/static/logo/logo_256x256.png +0 -0
  63. package/docs/static/logo/logo_512x512.png +0 -0
  64. package/docs/static/logo/logo_64x64.png +0 -0
  65. package/package.json +84 -0
  66. package/rollup.config.mjs +76 -0
  67. package/src/_css/404.css +116 -0
  68. package/src/_css/App.css +38 -0
  69. package/src/_css/autoSuggestion.css +27 -0
  70. package/src/_css/circular-progress.css +33 -0
  71. package/src/_css/index.css +37 -0
  72. package/src/_css/modern.css +25 -0
  73. package/src/_media/404.png +0 -0
  74. package/src/_media/corner.svg +8 -0
  75. package/src/_media/download.svg +3 -0
  76. package/src/_media/icon.svg +1 -0
  77. package/src/_media/logo.svg +14 -0
  78. package/src/_test/App.test.js +15 -0
  79. package/src/_utils/reportWebVitals.js +13 -0
  80. package/src/core/appContext.js +27 -0
  81. package/src/core/components/Scatter.js +188 -0
  82. package/src/core/components/ScatterBio.js +572 -0
  83. package/src/core/components/VisiumPlot.js +165 -0
  84. package/src/core/components/browser.js +42 -0
  85. package/src/core/components/dotplot.js +413 -0
  86. package/src/core/components/html.js +29 -0
  87. package/src/core/components/list.js +178 -0
  88. package/src/core/components/macaron.js +201 -0
  89. package/src/core/components/markdown.js +56 -0
  90. package/src/core/components/parser.scatterBio.js +579 -0
  91. package/src/core/components/ratio.js +80 -0
  92. package/src/core/components/scatterL.js +173 -0
  93. package/src/core/components/searchbar.js +131 -0
  94. package/src/core/components/selection.js +193 -0
  95. package/src/core/components/timeline.js +281 -0
  96. package/src/core/components/visium.js +97 -0
  97. package/src/core/fetch/condfetch.js +82 -0
  98. package/src/core/fetch/fetch.js +92 -0
  99. package/src/core/fetch/json.js +29 -0
  100. package/src/core/fetch/vfetch.js +42 -0
  101. package/src/core/liveEditor.js +44 -0
  102. package/src/core/modules/codeEditorWithPreview.js +104 -0
  103. package/src/core/modules/echarts/common.js +20 -0
  104. package/src/core/modules/echarts/presetHandler.js +41 -0
  105. package/src/core/modules/echarts/presets/esodev.chromium.js +172 -0
  106. package/src/core/modules/echarts/presets/esodev.codex.js +130 -0
  107. package/src/core/modules/echarts/presets/esodev.visium.js +123 -0
  108. package/src/core/modules/echarts/presets/mmtrbc.js +186 -0
  109. package/src/core/modules/echarts.js +71 -0
  110. package/src/core/modules/echartsUtils.js +43 -0
  111. package/src/core/modules/echartswitcher.js +152 -0
  112. package/src/core/modules/replotly/presetHandler.js +24 -0
  113. package/src/core/modules/replotly/presets/minimum.js +18 -0
  114. package/src/core/modules/replotly/presets/mmtrbc.dot.js +114 -0
  115. package/src/core/modules/replotly/presets/mmtrbc.violin.js +100 -0
  116. package/src/core/modules/replotly.js +71 -0
  117. package/src/core/pages/404.js +50 -0
  118. package/src/core/pages/error.js +27 -0
  119. package/src/core/pages/jsonPage.js +62 -0
  120. package/src/core/pages/loading.js +44 -0
  121. package/src/core/parser/echart.data.js +183 -0
  122. package/src/core/parser/echart.features.js +125 -0
  123. package/src/core/parser/echart.general.js +143 -0
  124. package/src/core/parser/echart.hilbert.js +57 -0
  125. package/src/core/parser/echart.parser.js +210 -0
  126. package/src/core/parser/echart.series.js +67 -0
  127. package/src/core/parser/echart.types.js +76 -0
  128. package/src/core/parser/plotly.config.js +10 -0
  129. package/src/core/parser/plotly.data.js +132 -0
  130. package/src/core/parser/plotly.layout.js +10 -0
  131. package/src/core/parser/plotly.violin.js +18 -0
  132. package/src/core/recharts.js +62 -0
  133. package/src/core/router/alias.js +49 -0
  134. package/src/core/router/jsonRouter.js +31 -0
  135. package/src/core/themes/modern.js +32 -0
  136. package/src/core/themes/themeSelector.js +33 -0
  137. package/src/core/visualify.js +47 -0
  138. package/src/core/widgets/circularProgress.js +24 -0
  139. package/src/core/widgets/controller.js +83 -0
  140. package/src/core/widgets/errorBoundary.js +36 -0
  141. package/src/core/widgets/footer.js +177 -0
  142. package/src/core/widgets/header.js +234 -0
  143. package/src/core/widgets/layout/Grid.js +31 -0
  144. package/src/core/widgets/layout.js +36 -0
  145. package/src/core/widgets/mapping.js +42 -0
  146. package/src/index.js +62 -0
  147. 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;