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