baremetal.js 1.0.0
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/.gitattributes +2 -0
- package/.github/workflows/npm-publish.yml +34 -0
- package/CHANGELOG.md +18 -0
- package/CODE_OF_CONDUCT.md +122 -0
- package/CONTRIBUTING.md +53 -0
- package/LICENSE +674 -0
- package/README.md +154 -0
- package/SECURITY.md +18 -0
- package/demo/assets/audio/darren_hirst-20-474737.mp3 +0 -0
- package/demo/assets/js/media_player.js +9 -0
- package/demo/assets/js/page1_specific.js +23 -0
- package/demo/assets/js/page2_specific.js +15 -0
- package/demo/assets/js/shared.js +56 -0
- package/demo/assets/js/sidebar.js +49 -0
- package/demo/main.js +18 -0
- package/demo/page1.html +139 -0
- package/demo/page2.html +132 -0
- package/demo/page3_normal.html +26 -0
- package/docs/api.md +109 -0
- package/package.json +29 -0
- package/src/index.js +25 -0
- package/src/loader.js +149 -0
- package/src/router.js +240 -0
- package/src/state.js +83 -0
- package/src/transition.js +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# BareMetal.js
|
|
2
|
+
|
|
3
|
+
A lightweight, dependency-free Vanilla JavaScript SPA engine prioritizing extreme performance, native browser features, and explicit lifecycle management.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
BareMetal.js is designed to bring Single Page Application (SPA) functionality to vanilla HTML/JS projects without the overhead of heavy frameworks or build steps. It intercepts navigations to provide instantaneous page transitions while giving developers precise control over module lifecycles and state.
|
|
10
|
+
|
|
11
|
+
## Key Features
|
|
12
|
+
|
|
13
|
+
- **Hover Pre-fetching (0ms Latency):** BareMetal anticipates user actions by fetching HTML in the background when a user hovers over an internal link, resulting in instant transitions upon click.
|
|
14
|
+
- **Smart Module Keep-Alive:** Share state and complex UI modules across page transitions without destroying them. Native `<video>` and `<audio>` tags can be preserved and re-injected into the new DOM seamlessly.
|
|
15
|
+
- **Scroll Memory & Programmatic Back:** Maintains a persistent history stack, restoring your exact scroll depth instantly when navigating backward.
|
|
16
|
+
- **Reactive State Management:** Includes a built-in publish/subscribe Signals pattern, preventing race conditions and keeping your UI synced.
|
|
17
|
+
- **Custom Page Transitions:** Build and integrate your own loading animations and transition effects hooking into the routing lifecycle.
|
|
18
|
+
- **Error Boundaries:** Handles navigation failures gracefully with configurable fallback UIs.
|
|
19
|
+
- **Auto-Wrap Module Loader:** Can automatically wrap simple scripts into conformant lifecycle modules to prevent errors.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Installation
|
|
24
|
+
|
|
25
|
+
No installation required! Just clone the repository or copy the `src` folder into your project.
|
|
26
|
+
|
|
27
|
+
### Running the Demo
|
|
28
|
+
|
|
29
|
+
To test the demo and advanced features across page navigation, serve the directory via any local static server.
|
|
30
|
+
|
|
31
|
+
Using `npx`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx serve .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then visit: `http://localhost:3000/demo/page1.html`
|
|
38
|
+
|
|
39
|
+
## Project Structure
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
├── src/ # The BareMetal Engine Source Code
|
|
43
|
+
│ ├── index.js
|
|
44
|
+
│ ├── router.js
|
|
45
|
+
│ ├── loader.js
|
|
46
|
+
│ ├── state.js
|
|
47
|
+
│ └── transition.js
|
|
48
|
+
├── demo/ # Demo Application
|
|
49
|
+
│ ├── page1.html
|
|
50
|
+
│ ├── page2.html
|
|
51
|
+
│ ├── page3_normal.html
|
|
52
|
+
│ ├── main.js
|
|
53
|
+
│ └── assets/ # Page-specific widgets and media
|
|
54
|
+
├── docs/
|
|
55
|
+
│ └── api.md # Comprehensive API Reference
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
### 1. Engine Initialization
|
|
61
|
+
|
|
62
|
+
Initialize the engine in your main entry file (e.g., `main.js`) to toggle advanced features:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
import { BareMetal } from '../src/index.js';
|
|
66
|
+
|
|
67
|
+
BareMetal.init({
|
|
68
|
+
debug: false,
|
|
69
|
+
keepAliveSameModules: true,
|
|
70
|
+
autoWrap: false,
|
|
71
|
+
hoverPrefetch: true, // 0ms Latency Pre-fetching
|
|
72
|
+
transition: {
|
|
73
|
+
enabled: true,
|
|
74
|
+
simulatedDelay: 0
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Module Definition
|
|
80
|
+
|
|
81
|
+
A BareMetal module must export a `mount` function. The engine injects the `state` manager into `mount`. It must return an object containing a `destroy()` function to prevent memory leaks when navigating away.
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// my-widget.js
|
|
85
|
+
export function mount({ state }) {
|
|
86
|
+
const btn = document.getElementById('my-btn');
|
|
87
|
+
btn.onclick = () => alert('Clicked!');
|
|
88
|
+
|
|
89
|
+
// Return cleanup methods
|
|
90
|
+
return {
|
|
91
|
+
destroy: () => {
|
|
92
|
+
btn.onclick = null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Page Configuration
|
|
99
|
+
|
|
100
|
+
On your HTML pages, instruct BareMetal on which modules are required for that specific page using the inline `loader()`.
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<script type="module">
|
|
104
|
+
import { loader } from '../src/index.js';
|
|
105
|
+
loader({
|
|
106
|
+
sharedSidebar: "/demo/assets/js/sidebar.js",
|
|
107
|
+
dashboard: "/demo/assets/js/dashboard.js"
|
|
108
|
+
});
|
|
109
|
+
</script>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Configuration Reference
|
|
113
|
+
|
|
114
|
+
The `BareMetal.init(config)` method accepts the following configuration object:
|
|
115
|
+
|
|
116
|
+
| Option | Type | Default | Description |
|
|
117
|
+
|--------|------|---------|-------------|
|
|
118
|
+
| `debug` | boolean | `false` | Enable verbose console logs. |
|
|
119
|
+
| `keepAliveSameModules` | boolean | `true` | Prevent destruction of modules shared between routes. |
|
|
120
|
+
| `autoWrap` | boolean | `true` | Automatically wrap modules that do not export a `mount` function. |
|
|
121
|
+
| `hoverPrefetch` | boolean | `false` | Enable 0ms latency pre-fetching on link hover. |
|
|
122
|
+
| `transition.enabled` | boolean | `false` | Enable the protected transition module. |
|
|
123
|
+
| `transition.module` | string | `null` | Path to a custom transition module. |
|
|
124
|
+
| `transition.simulatedDelay` | number | `0` | Artificial delay (ms) for testing transitions. |
|
|
125
|
+
|
|
126
|
+
## API Reference
|
|
127
|
+
|
|
128
|
+
For detailed documentation on the Router, State Manager, and Events, please see the [API Documentation](docs/api.md).
|
|
129
|
+
|
|
130
|
+
## Custom Transitions
|
|
131
|
+
|
|
132
|
+
You can replace the default progress bar and fade overlay by pointing `config.transition.module` to your own JavaScript file. A custom transition is a standard BareMetal module that listens to routing events (`ROUTE_START`, `ROUTE_PROGRESS`, `ROUTE_END`, `ROUTE_ERROR`). Ensure your transition UI is injected into an element with `id="baremetal-transition-root"`.
|
|
133
|
+
|
|
134
|
+
See the API Documentation for a complete example.
|
|
135
|
+
|
|
136
|
+
## Browser Support
|
|
137
|
+
|
|
138
|
+
BareMetal.js uses ES Modules natively in the browser. It supports all modern browsers (Chrome, Firefox, Safari, Edge) without any build step required.
|
|
139
|
+
|
|
140
|
+
## Contributing
|
|
141
|
+
|
|
142
|
+
We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for more details on how to get started.
|
|
143
|
+
|
|
144
|
+
## Code of Conduct
|
|
145
|
+
|
|
146
|
+
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
|
147
|
+
|
|
148
|
+
## Security
|
|
149
|
+
|
|
150
|
+
If you discover a security vulnerability, please refer to our [Security Policy](SECURITY.md).
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
This project is licensed under the [GNU General Public License v3.0](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Currently, only the latest version of BareMetal.js is supported with security updates.
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| ------- | ------------------ |
|
|
9
|
+
| 1.0.x | :white_check_mark: |
|
|
10
|
+
| < 1.0 | :x: |
|
|
11
|
+
|
|
12
|
+
## Reporting a Vulnerability
|
|
13
|
+
|
|
14
|
+
Please do not report security vulnerabilities through public GitHub issues.
|
|
15
|
+
|
|
16
|
+
Instead, please report them to the project maintainers via email at coc@dkydivyansh.com.
|
|
17
|
+
|
|
18
|
+
You should receive a response within 48 hours. If for some reason you do not, please follow up via email to ensure we received your message.
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function mount() {
|
|
2
|
+
|
|
3
|
+
let timer;
|
|
4
|
+
let count = 0;
|
|
5
|
+
|
|
6
|
+
const container = document.getElementById('page1-widget');
|
|
7
|
+
if (container) {
|
|
8
|
+
const display = document.createElement('p');
|
|
9
|
+
display.innerText = `Active for 0 seconds`;
|
|
10
|
+
container.appendChild(display);
|
|
11
|
+
|
|
12
|
+
timer = setInterval(() => {
|
|
13
|
+
count++;
|
|
14
|
+
display.innerText = `Active for ${count} seconds`;
|
|
15
|
+
}, 1000);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
destroy: () => {
|
|
20
|
+
clearInterval(timer);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function mount({ state }) {
|
|
2
|
+
const container = document.getElementById('page2-widget');
|
|
3
|
+
if (container) {
|
|
4
|
+
const btn = document.createElement('button');
|
|
5
|
+
btn.innerText = "Click Me";
|
|
6
|
+
btn.onclick = () => alert("Hello from Page 2 specific module!");
|
|
7
|
+
container.appendChild(btn);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
destroy: () => {
|
|
12
|
+
// Cleanup if needed
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function mount({ state }) {
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// Initialize state if not exists
|
|
5
|
+
state.init('transCount', 0);
|
|
6
|
+
|
|
7
|
+
// Re-attach UI to state
|
|
8
|
+
const btn = document.getElementById('btn-translate');
|
|
9
|
+
const display = document.getElementById('trans-count');
|
|
10
|
+
|
|
11
|
+
// We store the handler on the module level so we can remove it on destroy if needed
|
|
12
|
+
// But wait, if this module is kept alive across page transitions where the DOM body is swapped,
|
|
13
|
+
// the old DOM elements are removed and replaced by new identical ones from the fetch!
|
|
14
|
+
// So a keep-alive module must re-bind its DOM elements whenever the DOM changes.
|
|
15
|
+
|
|
16
|
+
// To handle the SPA HTML-swap paradigm:
|
|
17
|
+
// When a module is kept alive, its `mount` is NOT called again.
|
|
18
|
+
// Instead, the framework should either preserve the exact DOM elements during the swap,
|
|
19
|
+
// OR the module needs an `update()` lifecycle method.
|
|
20
|
+
|
|
21
|
+
// For this simple demo, let's assume the router replaces the whole body.
|
|
22
|
+
// Thus, we need a way to re-bind. We can listen to a global event 'PAGE_CHANGED'.
|
|
23
|
+
|
|
24
|
+
const bindUI = () => {
|
|
25
|
+
const btn = document.getElementById('btn-translate');
|
|
26
|
+
const display = document.getElementById('trans-count');
|
|
27
|
+
|
|
28
|
+
if (btn) {
|
|
29
|
+
// Avoid duplicate listeners
|
|
30
|
+
btn.onclick = () => {
|
|
31
|
+
const current = state.get('transCount');
|
|
32
|
+
state.update('transCount', current + 1);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Subscribe to state to update UI
|
|
37
|
+
// We use a unique listener to update whatever the current DOM element is
|
|
38
|
+
state.subscribe('transCount', (val) => {
|
|
39
|
+
const currentDisplay = document.getElementById('trans-count');
|
|
40
|
+
if (currentDisplay) currentDisplay.innerText = val;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
bindUI();
|
|
45
|
+
|
|
46
|
+
// Listen for route changes to re-bind UI since DOM body is swapped
|
|
47
|
+
state.on('DOM_SWAPPED', bindUI);
|
|
48
|
+
|
|
49
|
+
// Return destroy method
|
|
50
|
+
return {
|
|
51
|
+
destroy: () => {
|
|
52
|
+
|
|
53
|
+
state.off('DOM_SWAPPED', bindUI);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function mount({ state }) {
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// Initialize state (true = expanded, false = collapsed)
|
|
5
|
+
state.init('sidebarExpanded', true);
|
|
6
|
+
|
|
7
|
+
const bindUI = () => {
|
|
8
|
+
const sidebar = document.getElementById('sidebar');
|
|
9
|
+
const toggleBtn = document.getElementById('sidebar-toggle');
|
|
10
|
+
const content = document.getElementById('main-content');
|
|
11
|
+
|
|
12
|
+
if (!sidebar || !toggleBtn || !content) return;
|
|
13
|
+
|
|
14
|
+
// Remove old listeners just in case
|
|
15
|
+
toggleBtn.onclick = null;
|
|
16
|
+
|
|
17
|
+
toggleBtn.onclick = () => {
|
|
18
|
+
const isExpanded = state.get('sidebarExpanded');
|
|
19
|
+
state.update('sidebarExpanded', !isExpanded);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Make sure we update the DOM when the state changes
|
|
23
|
+
state.subscribe('sidebarExpanded', (expanded) => {
|
|
24
|
+
const el = document.getElementById('sidebar');
|
|
25
|
+
const mainEl = document.getElementById('main-content');
|
|
26
|
+
if (el && mainEl) {
|
|
27
|
+
if (expanded) {
|
|
28
|
+
el.style.width = '250px';
|
|
29
|
+
mainEl.style.marginLeft = '250px';
|
|
30
|
+
} else {
|
|
31
|
+
el.style.width = '60px';
|
|
32
|
+
mainEl.style.marginLeft = '60px';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
bindUI();
|
|
39
|
+
|
|
40
|
+
// Rebind UI when DOM is swapped by Router
|
|
41
|
+
state.on('DOM_SWAPPED', bindUI);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
destroy: () => {
|
|
45
|
+
|
|
46
|
+
state.off('DOM_SWAPPED', bindUI);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
package/demo/main.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BareMetal } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
// Initialize the BareMetal engine with our desired config
|
|
4
|
+
BareMetal.init({
|
|
5
|
+
debug: false,
|
|
6
|
+
keepAliveSameModules: true,
|
|
7
|
+
autoWrap: false,
|
|
8
|
+
hoverPrefetch: false,
|
|
9
|
+
showErrorNotification: false,
|
|
10
|
+
transition: {
|
|
11
|
+
enabled: true,
|
|
12
|
+
simulatedDelay: 500 // Adds a half-second artificial delay to see the progress bar in local dev
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// For demonstration purposes, we will expose state to the window
|
|
17
|
+
// so we can see the reactive updates in action via console if desired.
|
|
18
|
+
window.appState = BareMetal.state;
|
package/demo/page1.html
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>BareMetal Demo - Page 1</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: sans-serif;
|
|
10
|
+
margin: 0;
|
|
11
|
+
background-color: #f5f5f5;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#sidebar {
|
|
15
|
+
position: fixed;
|
|
16
|
+
left: 0;
|
|
17
|
+
top: 0;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
width: 250px;
|
|
20
|
+
background-color: #2c3e50;
|
|
21
|
+
color: white;
|
|
22
|
+
transition: width 0.3s ease;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
padding-top: 15px;
|
|
25
|
+
text-align: center;
|
|
26
|
+
z-index: 1000;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#sidebar-toggle {
|
|
30
|
+
background: none;
|
|
31
|
+
border: none;
|
|
32
|
+
color: white;
|
|
33
|
+
font-size: 24px;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
margin-bottom: 20px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.nav-links a {
|
|
39
|
+
display: block;
|
|
40
|
+
padding: 15px;
|
|
41
|
+
color: #ecf0f1;
|
|
42
|
+
text-decoration: none;
|
|
43
|
+
font-weight: bold;
|
|
44
|
+
transition: background 0.2s;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.nav-links a:hover {
|
|
48
|
+
background-color: #34495e;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#main-content {
|
|
52
|
+
margin-left: 250px;
|
|
53
|
+
/* default expanded */
|
|
54
|
+
padding: 30px;
|
|
55
|
+
transition: margin-left 0.3s ease;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.widget {
|
|
59
|
+
border: 1px solid #ccc;
|
|
60
|
+
padding: 20px;
|
|
61
|
+
margin-bottom: 20px;
|
|
62
|
+
border-radius: 8px;
|
|
63
|
+
background: white;
|
|
64
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.shared {
|
|
68
|
+
border-left: 4px solid #27ae60;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
|
|
73
|
+
<body>
|
|
74
|
+
|
|
75
|
+
<div id="sidebar">
|
|
76
|
+
<button id="sidebar-toggle">☰</button>
|
|
77
|
+
<div class="nav-links">
|
|
78
|
+
<a href="/demo/page1.html" data-link>Page 1</a>
|
|
79
|
+
<a href="/demo/page2.html" data-link>Page 2</a>
|
|
80
|
+
<a href="/demo/page3_normal.html">Page 3 (No SPA)</a>
|
|
81
|
+
<a href="https://example.com" target="_blank">External</a>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div id="main-content">
|
|
86
|
+
<h1>Welcome to Page 1</h1>
|
|
87
|
+
<p>This page uses a shared Translation Checker module, a Sidebar, and a specific Page 1 module.</p>
|
|
88
|
+
|
|
89
|
+
<div id="shared-widget" class="widget shared">
|
|
90
|
+
<h3>Shared Translation Module</h3>
|
|
91
|
+
<p>Translation count: <span id="trans-count">0</span></p>
|
|
92
|
+
<button id="btn-translate">Simulate Translation</button>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div id="page1-widget" class="widget">
|
|
96
|
+
<h3>Page 1 Specific Module</h3>
|
|
97
|
+
<p>This module only exists on Page 1 and will be destroyed when navigating away.</p>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="widget" style="border-color: #e74c3c;">
|
|
101
|
+
<h3>Error Boundary Demo</h3>
|
|
102
|
+
<p>Click below to navigate to a page that doesn't exist.</p>
|
|
103
|
+
<a href="/does-not-exist.html"
|
|
104
|
+
style="display: inline-block; padding: 10px; background: #e74c3c; color: white; text-decoration: none; border-radius: 4px;">Trigger
|
|
105
|
+
404 Error</a>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div id="media-container"
|
|
110
|
+
style="position: fixed; bottom: 20px; right: 20px; background: white; padding: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.2); z-index: 1000; border-radius: 8px; transition: all 0.5s ease;">
|
|
111
|
+
<h4 style="margin-top:0; margin-bottom: 10px;">Uninterrupted Native Media</h4>
|
|
112
|
+
<video id="demo-video" data-baremetal-preserve width="300" controls autoplay muted loop>
|
|
113
|
+
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
|
|
114
|
+
Your browser does not support HTML video.
|
|
115
|
+
</video>
|
|
116
|
+
<br />
|
|
117
|
+
<audio id="demo-audio" data-baremetal-preserve controls style="width: 300px; margin-top: 10px;">
|
|
118
|
+
<source src="./assets/audio/darren_hirst-20-474737.mp3" type="audio/mpeg">
|
|
119
|
+
Your browser does not support the audio element.
|
|
120
|
+
</audio>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- BareMetal Engine -->
|
|
124
|
+
<script type="module" src="./main.js"></script>
|
|
125
|
+
|
|
126
|
+
<!-- Page Configuration -->
|
|
127
|
+
<script type="module">
|
|
128
|
+
import { loader } from '../src/index.js';
|
|
129
|
+
loader({
|
|
130
|
+
sidebar: "/demo/assets/js/sidebar.js",
|
|
131
|
+
shared: "/demo/assets/js/shared.js",
|
|
132
|
+
page1: "/demo/assets/js/page1_specific.js",
|
|
133
|
+
mediaPlayer: "/demo/assets/js/media_player.js"
|
|
134
|
+
});
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
</body>
|
|
138
|
+
|
|
139
|
+
</html>
|
package/demo/page2.html
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>BareMetal Demo - Page 2</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: sans-serif;
|
|
10
|
+
margin: 0;
|
|
11
|
+
background-color: #f5f5f5;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#sidebar {
|
|
15
|
+
position: fixed;
|
|
16
|
+
left: 0;
|
|
17
|
+
top: 0;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
width: 250px;
|
|
20
|
+
background-color: #2c3e50;
|
|
21
|
+
color: white;
|
|
22
|
+
transition: width 0.3s ease;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
padding-top: 15px;
|
|
25
|
+
text-align: center;
|
|
26
|
+
z-index: 1000;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#sidebar-toggle {
|
|
30
|
+
background: none;
|
|
31
|
+
border: none;
|
|
32
|
+
color: white;
|
|
33
|
+
font-size: 24px;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
margin-bottom: 20px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.nav-links a {
|
|
39
|
+
display: block;
|
|
40
|
+
padding: 15px;
|
|
41
|
+
color: #ecf0f1;
|
|
42
|
+
text-decoration: none;
|
|
43
|
+
font-weight: bold;
|
|
44
|
+
transition: background 0.2s;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.nav-links a:hover {
|
|
48
|
+
background-color: #34495e;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#main-content {
|
|
52
|
+
margin-left: 250px; /* default expanded */
|
|
53
|
+
padding: 30px;
|
|
54
|
+
transition: margin-left 0.3s ease;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.widget {
|
|
58
|
+
border: 1px solid #ccc;
|
|
59
|
+
padding: 20px;
|
|
60
|
+
margin-bottom: 20px;
|
|
61
|
+
border-radius: 8px;
|
|
62
|
+
background: white;
|
|
63
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.shared {
|
|
67
|
+
border-left: 4px solid #27ae60;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
</head>
|
|
71
|
+
|
|
72
|
+
<body>
|
|
73
|
+
|
|
74
|
+
<div id="sidebar">
|
|
75
|
+
<button id="sidebar-toggle">☰</button>
|
|
76
|
+
<div class="nav-links">
|
|
77
|
+
<a href="/demo/page1.html" data-link>Page 1</a>
|
|
78
|
+
<a href="/demo/page2.html" data-link>Page 2</a>
|
|
79
|
+
<a href="/demo/page3_normal.html">Page 3 (No SPA)</a>
|
|
80
|
+
<a href="https://example.com" target="_blank">External</a>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div id="main-content">
|
|
85
|
+
<h1>Welcome to Page 2</h1>
|
|
86
|
+
<p>This is a completely different page, but notice the sidebar and translation widget state are maintained perfectly!</p>
|
|
87
|
+
|
|
88
|
+
<div id="media-container" class="widget" style="background: #333; color: white; border-left: 4px solid #e74c3c; transition: all 0.5s ease;">
|
|
89
|
+
<h3 style="margin-top:0;">Deeply Nested & Transformed!</h3>
|
|
90
|
+
<p>I am still embedded in the main content flow, but my container's text and CSS synced seamlessly!</p>
|
|
91
|
+
<video id="demo-video" data-baremetal-preserve width="100%" controls autoplay muted loop>
|
|
92
|
+
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
|
|
93
|
+
Your browser does not support HTML video.
|
|
94
|
+
</video>
|
|
95
|
+
<br/>
|
|
96
|
+
<audio id="demo-audio" data-baremetal-preserve controls style="width: 100%; margin-top: 10px;">
|
|
97
|
+
<source src="./assets/audio/darren_hirst-20-474737.mp3" type="audio/mpeg">
|
|
98
|
+
Your browser does not support the audio element.
|
|
99
|
+
</audio>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div id="shared-widget" class="widget shared">
|
|
103
|
+
<h3>Shared Translation Module</h3>
|
|
104
|
+
<p>Translation count: <span id="trans-count">0</span></p>
|
|
105
|
+
<button id="btn-translate">Simulate Translation</button>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div id="page2-widget" class="widget">
|
|
109
|
+
<h3>Page 2 Specific Module</h3>
|
|
110
|
+
<p>This is a completely different module loaded dynamically.</p>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<!-- BareMetal Engine -->
|
|
117
|
+
<script type="module" src="./main.js"></script>
|
|
118
|
+
|
|
119
|
+
<!-- Page Configuration -->
|
|
120
|
+
<script type="module">
|
|
121
|
+
import { loader } from '../src/index.js';
|
|
122
|
+
loader({
|
|
123
|
+
sidebar: "/demo/assets/js/sidebar.js",
|
|
124
|
+
shared: "/demo/assets/js/shared.js",
|
|
125
|
+
page2: "/demo/assets/js/page2_specific.js",
|
|
126
|
+
mediaPlayer: "/demo/assets/js/media_player.js"
|
|
127
|
+
});
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
</body>
|
|
131
|
+
|
|
132
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>BareMetal Demo - Normal Page</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: sans-serif; padding: 20px; background-color: #f9f9f9; }
|
|
8
|
+
.nav { margin-bottom: 20px; padding: 10px; background: #eee; }
|
|
9
|
+
.nav a { margin-right: 15px; text-decoration: none; color: blue; }
|
|
10
|
+
</style>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<div class="nav">
|
|
15
|
+
<strong>Navigation:</strong>
|
|
16
|
+
<a href="/demo/page1.html">Back to Page 1</a>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<h1>Welcome to Page 3 (Normal Page)</h1>
|
|
20
|
+
<p>This page <strong>does not</strong> contain a BareMetal <code>loader({...})</code> configuration.</p>
|
|
21
|
+
<p>Because of this, the BareMetal Router detected it and performed a standard browser navigation (hard reload) to get here.</p>
|
|
22
|
+
|
|
23
|
+
<p>Notice that your translation counter from Page 1 and 2 has been reset because we left the SPA environment!</p>
|
|
24
|
+
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|