@evolution-james/evolution-theme-engine 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/LICENSE.txt +13 -0
- package/README.md +407 -0
- package/dist/components/ThemeNavBar.js +99 -0
- package/dist/components/ThemeSelector.js +86 -0
- package/dist/context/ThemeContext.js +175 -0
- package/dist/index.js +44 -0
- package/dist/styles/themes.css +278 -0
- package/package.json +46 -0
- package/src/components/ThemeNavBar.jsx +86 -0
- package/src/components/ThemeSelector.jsx +68 -0
- package/src/context/ThemeContext.jsx +159 -0
- package/src/index.js +20 -0
- package/src/styles/themes.css +278 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2026 Evolution Coding Academy (@evolution-james / @james-evolution)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, merge, and distribute the Software in source or binary forms, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
1. Commercial Use Restriction: The Software may not be sold, sublicensed, or otherwise distributed as a standalone product, or as a substantially similar derivative, for direct commercial gain. You may not offer the Software, with or without modification, as a paid product or as part of a paid library, toolkit, or component collection.
|
|
6
|
+
|
|
7
|
+
2. Permitted Commercial Use: The Software may be used as part of a larger application, product, or service, including commercial products, provided that the Software is not the primary, sole, or core component being sold or licensed. The Software must not be the main value or selling point of the product.
|
|
8
|
+
|
|
9
|
+
3. Attribution: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
+
|
|
11
|
+
4. No Warranty: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
12
|
+
|
|
13
|
+
For questions or commercial licensing, contact: contact@evolutioncoding.net
|
package/README.md
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# Evolution Theme Engine
|
|
2
|
+
|
|
3
|
+
A plug-and-play React theme engine built on CSS custom properties. It ships with five polished built-in themes, a standalone theme-selector dropdown, and an optional barebones navbar — all wired together through React Context with automatic `localStorage` persistence.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Five built-in themes** — Light, Dark, Forest, Tron, Midnight
|
|
8
|
+
- **CSS variable-based** — every color in your UI traces back to a single `--color-*` variable, so a theme change affects the entire page instantly
|
|
9
|
+
- **localStorage persistence** — the user's chosen theme survives page refreshes and new tabs automatically
|
|
10
|
+
- **No flash on reload** — the theme is applied before the first paint by setting `data-theme` on `<html>` at initialisation
|
|
11
|
+
- **Two integration options** — use the standalone `ThemeSelector` dropdown wherever you like, or drop in `ThemeNavBar` for a ready-made header with the selector already inside
|
|
12
|
+
- **Runtime theme registration** — call `registerTheme()` to inject a custom theme at runtime without editing any CSS files
|
|
13
|
+
- **Zero UI-framework dependency** — no Bootstrap, no MUI, no Tailwind required
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @evolution-james/evolution-theme-engine
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> **Peer dependencies:** React 17+ and ReactDOM 17+ must already be installed in your project.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### 1. Wrap your app with `ThemeProvider`
|
|
30
|
+
|
|
31
|
+
`ThemeProvider` is the context source. Everything else in this package must be rendered inside it.
|
|
32
|
+
|
|
33
|
+
```jsx
|
|
34
|
+
// src/index.jsx (or src/main.jsx)
|
|
35
|
+
import React from 'react';
|
|
36
|
+
import ReactDOM from 'react-dom/client';
|
|
37
|
+
import App from './App';
|
|
38
|
+
import { ThemeProvider } from '@evolution-james/evolution-theme-engine';
|
|
39
|
+
|
|
40
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
41
|
+
<ThemeProvider>
|
|
42
|
+
<App />
|
|
43
|
+
</ThemeProvider>
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Choose your integration style
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Integration Option A — Standalone `ThemeSelector`
|
|
52
|
+
|
|
53
|
+
Use this when you already have your own navbar or header and just want to drop the theme-switcher inside it.
|
|
54
|
+
|
|
55
|
+
```jsx
|
|
56
|
+
import { ThemeSelector } from '@evolution-james/evolution-theme-engine';
|
|
57
|
+
|
|
58
|
+
function MyHeader() {
|
|
59
|
+
return (
|
|
60
|
+
<header>
|
|
61
|
+
<span>My App</span>
|
|
62
|
+
{/* Render the selector wherever you like */}
|
|
63
|
+
<ThemeSelector />
|
|
64
|
+
</header>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`ThemeSelector` reads and writes the theme through context — you don't pass anything to it unless you want to customise the available options (see [Props reference](#themenavbar-props)).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Integration Option B — `ThemeNavBar`
|
|
74
|
+
|
|
75
|
+
Use this when you want a complete, ready-made navbar with the theme selector pre-rendered inside it.
|
|
76
|
+
|
|
77
|
+
```jsx
|
|
78
|
+
import { ThemeNavBar } from '@evolution-james/evolution-theme-engine';
|
|
79
|
+
|
|
80
|
+
function App() {
|
|
81
|
+
return (
|
|
82
|
+
<>
|
|
83
|
+
<ThemeNavBar
|
|
84
|
+
title="My App"
|
|
85
|
+
links={[
|
|
86
|
+
{ label: 'Home', href: '/' },
|
|
87
|
+
{ label: 'About', href: '/about' },
|
|
88
|
+
{ label: 'Contact', href: '/contact' },
|
|
89
|
+
]}
|
|
90
|
+
/>
|
|
91
|
+
{/* rest of your app */}
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`ThemeNavBar` is intentionally barebones — it applies `var(--color-*)` variables for all colors, which means it automatically adapts to whichever theme is active.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Ensuring Themes Apply to Your Application
|
|
102
|
+
|
|
103
|
+
After adding `ThemeProvider` and either `ThemeSelector` or `ThemeNavBar`, you may notice that switching themes changes the navbar colors but nothing else in your app. **This is expected behaviour** — and easy to fix.
|
|
104
|
+
|
|
105
|
+
### How it works under the hood
|
|
106
|
+
|
|
107
|
+
The theme engine works by setting a `data-theme` attribute on the `<html>` element:
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<html data-theme="dark">
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`themes.css` (automatically imported by the components) then activates a matching block of CSS variables:
|
|
114
|
+
|
|
115
|
+
```css
|
|
116
|
+
[data-theme="dark"] {
|
|
117
|
+
--color-bg: #212529;
|
|
118
|
+
--color-text: #f8f9fa;
|
|
119
|
+
/* … */
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
These variables are now available **globally** across your entire page. However, your own components only respond to theme changes if they **use those variables** in their CSS. If your styles are hardcoded colors (e.g. `background: white`), they will not change.
|
|
124
|
+
|
|
125
|
+
### Making your app theme-aware
|
|
126
|
+
|
|
127
|
+
The first thing to do is wire the page background and text color to the theme. Add this to your global stylesheet (e.g. `index.css` or `App.css`):
|
|
128
|
+
|
|
129
|
+
```css
|
|
130
|
+
/* Apply theme colors to the whole page */
|
|
131
|
+
body {
|
|
132
|
+
background-color: var(--color-bg);
|
|
133
|
+
color: var(--color-text);
|
|
134
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
From there, use the CSS variables anywhere in your own component styles:
|
|
139
|
+
|
|
140
|
+
```css
|
|
141
|
+
.my-card {
|
|
142
|
+
background-color: var(--color-card-bg);
|
|
143
|
+
color: var(--color-text);
|
|
144
|
+
border: 1px solid var(--color-card-border);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.my-button {
|
|
148
|
+
background-color: var(--color-primary);
|
|
149
|
+
color: var(--color-on-primary);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.my-link {
|
|
153
|
+
color: var(--color-link);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
hr {
|
|
157
|
+
border-color: var(--color-divider);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Because the variables are set on `<html>`, they cascade down to every element on the page — you just need to opt each style into using them.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## CSS Variable Reference
|
|
166
|
+
|
|
167
|
+
| Variable | Purpose |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `--color-bg` | Page / app background |
|
|
170
|
+
| `--color-text` | Primary body text |
|
|
171
|
+
| `--color-card-bg` | Card / panel surface background |
|
|
172
|
+
| `--color-card-border` | Card / panel border color |
|
|
173
|
+
| `--color-btn-dark-bg` | Background for "dark" style buttons |
|
|
174
|
+
| `--color-btn-dark-text` | Text on "dark" style buttons |
|
|
175
|
+
| `--color-btn-light-bg` | Background for "light" style buttons |
|
|
176
|
+
| `--color-btn-light-text` | Text on "light" style buttons |
|
|
177
|
+
| `--color-divider` | Horizontal rules / separators |
|
|
178
|
+
| `--color-primary` | Primary accent / brand color |
|
|
179
|
+
| `--color-on-primary` | Text rendered on top of `--color-primary` |
|
|
180
|
+
| `--color-link` | Hyperlink color |
|
|
181
|
+
| `--color-hover-bg` | Subtle hover-state background tint |
|
|
182
|
+
| `--color-code-bg` | Code block background |
|
|
183
|
+
| `--color-code-text` | Code block text color |
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## How the Engine Works
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
User picks a theme
|
|
191
|
+
│
|
|
192
|
+
▼
|
|
193
|
+
setTheme(newTheme) ← called by ThemeSelector's onChange
|
|
194
|
+
│
|
|
195
|
+
├──▶ React state update ← triggers re-render in ThemeProvider
|
|
196
|
+
│
|
|
197
|
+
└──▶ localStorage.setItem() ← persists selection across sessions
|
|
198
|
+
│
|
|
199
|
+
▼
|
|
200
|
+
useEffect (inside ThemeProvider)
|
|
201
|
+
│
|
|
202
|
+
▼
|
|
203
|
+
document.documentElement
|
|
204
|
+
.setAttribute('data-theme', newTheme)
|
|
205
|
+
│
|
|
206
|
+
▼
|
|
207
|
+
CSS picks up [data-theme="dark"] { … }
|
|
208
|
+
and all --color-* variables update
|
|
209
|
+
site-wide, instantly.
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
1. **`themes.css`** declares all `--color-*` variables per theme inside `[data-theme="name"]` blocks. The `:root` block (Light theme) comes first because `:root` and `[data-theme]` share equal CSS specificity — whichever appears later in the file wins.
|
|
213
|
+
2. **`ThemeProvider`** stores the active theme in React state, writes it to `localStorage` for persistence, and sets the `data-theme` attribute on `<html>` via `useEffect`.
|
|
214
|
+
3. **`useTheme()`** hook exposes `{ theme, setTheme }` to any component inside the provider.
|
|
215
|
+
4. **`ThemeSelector`** calls `setTheme` when the user picks a different option from the dropdown.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Adding Your Own Themes
|
|
220
|
+
|
|
221
|
+
There are two ways to add a custom theme.
|
|
222
|
+
|
|
223
|
+
### Method 1 — Edit `themes.css` (recommended for permanent themes)
|
|
224
|
+
|
|
225
|
+
Copy any existing `[data-theme]` block in `themes.css`, change the selector name, and update the variable values:
|
|
226
|
+
|
|
227
|
+
```css
|
|
228
|
+
/* themes.css */
|
|
229
|
+
[data-theme="ocean"] {
|
|
230
|
+
--color-bg: #0a1628;
|
|
231
|
+
--color-text: #e0f0ff;
|
|
232
|
+
--color-card-bg: #0d2137;
|
|
233
|
+
--color-card-border: #1a3a5c;
|
|
234
|
+
--color-btn-dark-bg: #1a3a5c;
|
|
235
|
+
--color-btn-dark-text: #e0f0ff;
|
|
236
|
+
--color-btn-light-bg: #0d2137;
|
|
237
|
+
--color-btn-light-text: #e0f0ff;
|
|
238
|
+
--color-divider: #1a3a5c;
|
|
239
|
+
|
|
240
|
+
--color-primary: #00b4d8;
|
|
241
|
+
--color-on-primary: #0a1628;
|
|
242
|
+
--color-link: #90e0ef;
|
|
243
|
+
--color-hover-bg: rgba(0, 180, 216, 0.1);
|
|
244
|
+
--color-code-bg: #070f1a;
|
|
245
|
+
--color-code-text: #e0f0ff;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Then surface it in the UI by passing a custom `themes` prop to `ThemeSelector` or `ThemeNavBar`:
|
|
250
|
+
|
|
251
|
+
```jsx
|
|
252
|
+
const MY_THEMES = {
|
|
253
|
+
'Light Theme': 'light',
|
|
254
|
+
'Dark Theme': 'dark',
|
|
255
|
+
'Ocean': 'ocean',
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
<ThemeSelector themes={MY_THEMES} />
|
|
259
|
+
// or
|
|
260
|
+
<ThemeNavBar themes={MY_THEMES} />
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Method 2 — `registerTheme()` (runtime injection, no file editing)
|
|
264
|
+
|
|
265
|
+
Call `registerTheme` once during app initialisation. It dynamically injects a `<style>` tag into `<head>`.
|
|
266
|
+
|
|
267
|
+
```jsx
|
|
268
|
+
import { registerTheme, THEMES } from '@evolution-james/evolution-theme-engine';
|
|
269
|
+
|
|
270
|
+
// Call before or after ThemeProvider mounts — works either way.
|
|
271
|
+
registerTheme('ocean', {
|
|
272
|
+
'color-bg': '#0a1628',
|
|
273
|
+
'color-text': '#e0f0ff',
|
|
274
|
+
'color-primary': '#00b4d8',
|
|
275
|
+
'color-on-primary': '#0a1628',
|
|
276
|
+
'color-card-bg': '#0d2137',
|
|
277
|
+
'color-card-border': '#1a3a5c',
|
|
278
|
+
'color-divider': '#1a3a5c',
|
|
279
|
+
'color-btn-dark-bg': '#1a3a5c',
|
|
280
|
+
'color-btn-dark-text': '#e0f0ff',
|
|
281
|
+
'color-btn-light-bg': '#0d2137',
|
|
282
|
+
'color-btn-light-text': '#e0f0ff',
|
|
283
|
+
'color-link': '#90e0ef',
|
|
284
|
+
'color-hover-bg': 'rgba(0,180,216,0.1)',
|
|
285
|
+
'color-code-bg': '#070f1a',
|
|
286
|
+
'color-code-text': '#e0f0ff',
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
> Note: keys in the `vars` object should omit the leading `--` — `registerTheme` adds it for you.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## API Reference
|
|
295
|
+
|
|
296
|
+
### `<ThemeProvider>`
|
|
297
|
+
|
|
298
|
+
| Prop | Type | Default | Description |
|
|
299
|
+
|---|---|---|---|
|
|
300
|
+
| `children` | `ReactNode` | — | Required. Your app subtree. |
|
|
301
|
+
| `defaultTheme` | `string` | `'light'` | Fallback theme when localStorage has no saved value. |
|
|
302
|
+
| `storageKey` | `string` | `'etn-theme'` | localStorage key used to persist the selection. |
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### `<ThemeSelector>`
|
|
307
|
+
|
|
308
|
+
| Prop | Type | Default | Description |
|
|
309
|
+
|---|---|---|---|
|
|
310
|
+
| `themes` | `object` | All 5 built-in themes | Map of `{ 'Display Label': 'theme-key' }` shown in the dropdown. |
|
|
311
|
+
| `className` | `string` | `''` | Extra CSS classes added to the `<select>` (alongside `etn-theme-selector`). |
|
|
312
|
+
| `style` | `object` | `{}` | Inline styles for the `<select>`. |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### `<ThemeNavBar>`
|
|
317
|
+
|
|
318
|
+
| Prop | Type | Default | Description |
|
|
319
|
+
|---|---|---|---|
|
|
320
|
+
| `title` | `string` | `'My App'` | Brand text shown on the left of the navbar. |
|
|
321
|
+
| `links` | `Array<{ label, href }>` | Placeholder links | Navigation links rendered in the center. |
|
|
322
|
+
| `themes` | `object` | All 5 built-in themes | Forwarded to the internal `<ThemeSelector>`. |
|
|
323
|
+
| `className` | `string` | `''` | Extra CSS classes added to the `<nav>` (alongside `etn-navbar`). |
|
|
324
|
+
| `style` | `object` | `{}` | Inline styles for the `<nav>`. |
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
### `useTheme()`
|
|
329
|
+
|
|
330
|
+
Returns `{ theme: string, setTheme: (newTheme: string) => void }`.
|
|
331
|
+
|
|
332
|
+
```jsx
|
|
333
|
+
import { useTheme } from '@evolution-james/evolution-theme-engine';
|
|
334
|
+
|
|
335
|
+
function MyComponent() {
|
|
336
|
+
const { theme, setTheme } = useTheme();
|
|
337
|
+
|
|
338
|
+
return (
|
|
339
|
+
<div>
|
|
340
|
+
<p>Active theme: {theme}</p>
|
|
341
|
+
<button onClick={() => setTheme('dark')}>Go Dark</button>
|
|
342
|
+
</div>
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
### `THEMES`
|
|
350
|
+
|
|
351
|
+
A convenience object that maps friendly JS keys to their `data-theme` string values.
|
|
352
|
+
|
|
353
|
+
```js
|
|
354
|
+
import { THEMES } from '@evolution-james/evolution-theme-engine';
|
|
355
|
+
|
|
356
|
+
console.log(THEMES);
|
|
357
|
+
// {
|
|
358
|
+
// light: 'light',
|
|
359
|
+
// dark: 'dark',
|
|
360
|
+
// forest: 'forest',
|
|
361
|
+
// tron: 'tron',
|
|
362
|
+
// midnight: 'midnight',
|
|
363
|
+
// }
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
### `registerTheme(name, vars)`
|
|
369
|
+
|
|
370
|
+
Injects a custom `[data-theme]` CSS block at runtime.
|
|
371
|
+
|
|
372
|
+
| Parameter | Type | Description |
|
|
373
|
+
|---|---|---|
|
|
374
|
+
| `name` | `string` | Theme key (used as the `data-theme` value). |
|
|
375
|
+
| `vars` | `object` | CSS variable declarations. Keys omit the leading `--`. |
|
|
376
|
+
|
|
377
|
+
Calling `registerTheme` again with the same `name` replaces the previous injection.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Built-in Themes
|
|
382
|
+
|
|
383
|
+
| Key | Name | Background | Primary Accent |
|
|
384
|
+
|---|---|---|---|
|
|
385
|
+
| `light` | Light | `#f8f9fa` | `#1976d2` (blue) |
|
|
386
|
+
| `dark` | Dark | `#212529` | `#90caf9` (light blue) |
|
|
387
|
+
| `forest` | Forest | `#1b2e22` | `#4caf70` (green) |
|
|
388
|
+
| `tron` | Tron | `#0f172a` | `#0ea5e9` (cyan) |
|
|
389
|
+
| `midnight` | Midnight | `#0b1016` | `#5ce1b5` (teal) |
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
Copyright (c) 2026 Evolution Coding Academy (@evolution-james / @james-evolution)
|
|
396
|
+
|
|
397
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, merge, and distribute the Software in source or binary forms, subject to the following conditions:
|
|
398
|
+
|
|
399
|
+
1. Commercial Use Restriction: The Software may not be sold, sublicensed, or otherwise distributed as a standalone product, or as a substantially similar derivative, for direct commercial gain. You may not offer the Software, with or without modification, as a paid product or as part of a paid library, toolkit, or component collection.
|
|
400
|
+
|
|
401
|
+
2. Permitted Commercial Use: The Software may be used as part of a larger application, product, or service, including commercial products, provided that the Software is not the primary, sole, or core component being sold or licensed. The Software must not be the main value or selling point of the product.
|
|
402
|
+
|
|
403
|
+
3. Attribution: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
404
|
+
|
|
405
|
+
4. No Warranty: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
406
|
+
|
|
407
|
+
For questions or commercial licensing, contact: contact@evolutioncoding.net
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ThemeNavBar = ThemeNavBar;
|
|
7
|
+
var _ThemeSelector = require("./ThemeSelector.js");
|
|
8
|
+
require("../styles/themes.css");
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
/*
|
|
11
|
+
* ============================================================
|
|
12
|
+
* ThemeNavBar.jsx — Evolution Theme Engine
|
|
13
|
+
* ============================================================
|
|
14
|
+
* An optional, barebones navigation bar with the ThemeSelector
|
|
15
|
+
* component already rendered inside it.
|
|
16
|
+
*
|
|
17
|
+
* This is the "batteries included" option — import ThemeNavBar
|
|
18
|
+
* if you want a ready-made header without wiring up ThemeSelector
|
|
19
|
+
* yourself. Under the hood it simply renders <ThemeSelector />,
|
|
20
|
+
* so everything still flows through ThemeContext.
|
|
21
|
+
*
|
|
22
|
+
* The navbar is intentionally minimal and styled purely via
|
|
23
|
+
* CSS variables (no Bootstrap, no third-party UI libraries).
|
|
24
|
+
* Customize it by passing props or overriding .etn-navbar CSS
|
|
25
|
+
* classes in your own stylesheet.
|
|
26
|
+
*
|
|
27
|
+
* Props:
|
|
28
|
+
* title (string) — Brand/title text shown on the
|
|
29
|
+
* left side of the navbar.
|
|
30
|
+
* Defaults to 'My App'.
|
|
31
|
+
* links (Array<object>) — Navigation links rendered to
|
|
32
|
+
* the right of the title. Each
|
|
33
|
+
* entry: { label, href }.
|
|
34
|
+
* Defaults to a few placeholder
|
|
35
|
+
* links.
|
|
36
|
+
* themes (object) — Forwarded to <ThemeSelector>.
|
|
37
|
+
* Defaults to all 5 built-in themes.
|
|
38
|
+
* className (string) — Extra class(es) added to the
|
|
39
|
+
* root <nav> element alongside
|
|
40
|
+
* 'etn-navbar'.
|
|
41
|
+
* style (object) — Inline styles for the root <nav>.
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* import { ThemeNavBar } from 'evolution-theme-engine';
|
|
45
|
+
*
|
|
46
|
+
* <ThemeNavBar
|
|
47
|
+
* title="My Cool App"
|
|
48
|
+
* links={[
|
|
49
|
+
* { label: 'Home', href: '/' },
|
|
50
|
+
* { label: 'About', href: '/about' },
|
|
51
|
+
* ]}
|
|
52
|
+
* />
|
|
53
|
+
* ============================================================
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
var DEFAULT_LINKS = [{
|
|
57
|
+
label: 'Home',
|
|
58
|
+
href: '#'
|
|
59
|
+
}, {
|
|
60
|
+
label: 'About',
|
|
61
|
+
href: '#'
|
|
62
|
+
}, {
|
|
63
|
+
label: 'Docs',
|
|
64
|
+
href: '#'
|
|
65
|
+
}];
|
|
66
|
+
function ThemeNavBar(_ref) {
|
|
67
|
+
var _ref$title = _ref.title,
|
|
68
|
+
title = _ref$title === void 0 ? 'My App' : _ref$title,
|
|
69
|
+
_ref$links = _ref.links,
|
|
70
|
+
links = _ref$links === void 0 ? DEFAULT_LINKS : _ref$links,
|
|
71
|
+
themes = _ref.themes,
|
|
72
|
+
_ref$className = _ref.className,
|
|
73
|
+
className = _ref$className === void 0 ? '' : _ref$className,
|
|
74
|
+
_ref$style = _ref.style,
|
|
75
|
+
style = _ref$style === void 0 ? {} : _ref$style;
|
|
76
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("nav", {
|
|
77
|
+
className: "etn-navbar".concat(className ? " ".concat(className) : ''),
|
|
78
|
+
style: style,
|
|
79
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
80
|
+
className: "etn-navbar-brand",
|
|
81
|
+
children: title
|
|
82
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("ul", {
|
|
83
|
+
className: "etn-navbar-links",
|
|
84
|
+
children: links.map(function (_ref2) {
|
|
85
|
+
var label = _ref2.label,
|
|
86
|
+
href = _ref2.href;
|
|
87
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
88
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("a", {
|
|
89
|
+
href: href,
|
|
90
|
+
className: "etn-navbar-link",
|
|
91
|
+
children: label
|
|
92
|
+
})
|
|
93
|
+
}, label);
|
|
94
|
+
})
|
|
95
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ThemeSelector.ThemeSelector, {
|
|
96
|
+
themes: themes
|
|
97
|
+
})]
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ThemeSelector = ThemeSelector;
|
|
7
|
+
var _ThemeContext = require("../context/ThemeContext.js");
|
|
8
|
+
require("../styles/themes.css");
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
11
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
12
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
13
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
14
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
15
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } /*
|
|
16
|
+
* ============================================================
|
|
17
|
+
* ThemeSelector.jsx — Evolution Theme Engine
|
|
18
|
+
* ============================================================
|
|
19
|
+
* A standalone, dependency-free <select> dropdown that lets
|
|
20
|
+
* users switch between themes.
|
|
21
|
+
*
|
|
22
|
+
* This component has NO required props — it reads the current
|
|
23
|
+
* theme and setter from ThemeContext via useTheme(). All you
|
|
24
|
+
* need to do is render it anywhere inside a <ThemeProvider>.
|
|
25
|
+
*
|
|
26
|
+
* Props:
|
|
27
|
+
* themes (object) — Map of { label: themeKey } entries
|
|
28
|
+
* displayed in the dropdown.
|
|
29
|
+
* Defaults to all 5 built-in themes.
|
|
30
|
+
* className (string) — Extra CSS class(es) added to the
|
|
31
|
+
* <select> element alongside the
|
|
32
|
+
* default 'etn-theme-selector' class.
|
|
33
|
+
* style (object) — Inline styles applied to the select.
|
|
34
|
+
*
|
|
35
|
+
* Usage:
|
|
36
|
+
* import { ThemeSelector } from 'evolution-theme-engine';
|
|
37
|
+
*
|
|
38
|
+
* // Standalone — render anywhere inside ThemeProvider:
|
|
39
|
+
* <ThemeSelector />
|
|
40
|
+
*
|
|
41
|
+
* // With custom theme list:
|
|
42
|
+
* <ThemeSelector
|
|
43
|
+
* themes={{ 'Light': 'light', 'Dark': 'dark', 'Ocean': 'ocean' }}
|
|
44
|
+
* />
|
|
45
|
+
* ============================================================
|
|
46
|
+
*/ /*
|
|
47
|
+
* DEFAULT_THEMES is what shows up in the dropdown when no
|
|
48
|
+
* `themes` prop is passed. Keys are display labels; values
|
|
49
|
+
* are the data-theme attribute strings defined in themes.css.
|
|
50
|
+
*/
|
|
51
|
+
var DEFAULT_THEMES = {
|
|
52
|
+
'Light Theme': 'light',
|
|
53
|
+
'Dark Theme': 'dark',
|
|
54
|
+
'Forest': 'forest',
|
|
55
|
+
'Tron': 'tron',
|
|
56
|
+
'Midnight': 'midnight'
|
|
57
|
+
};
|
|
58
|
+
function ThemeSelector(_ref) {
|
|
59
|
+
var _ref$themes = _ref.themes,
|
|
60
|
+
themes = _ref$themes === void 0 ? DEFAULT_THEMES : _ref$themes,
|
|
61
|
+
_ref$className = _ref.className,
|
|
62
|
+
className = _ref$className === void 0 ? '' : _ref$className,
|
|
63
|
+
_ref$style = _ref.style,
|
|
64
|
+
style = _ref$style === void 0 ? {} : _ref$style;
|
|
65
|
+
var _useTheme = (0, _ThemeContext.useTheme)(),
|
|
66
|
+
theme = _useTheme.theme,
|
|
67
|
+
setTheme = _useTheme.setTheme;
|
|
68
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("select", {
|
|
69
|
+
className: "etn-theme-selector".concat(className ? " ".concat(className) : ''),
|
|
70
|
+
value: theme,
|
|
71
|
+
onChange: function onChange(e) {
|
|
72
|
+
return setTheme(e.target.value);
|
|
73
|
+
},
|
|
74
|
+
"aria-label": "Select theme",
|
|
75
|
+
style: style,
|
|
76
|
+
children: Object.entries(themes).map(function (_ref2) {
|
|
77
|
+
var _ref3 = _slicedToArray(_ref2, 2),
|
|
78
|
+
label = _ref3[0],
|
|
79
|
+
value = _ref3[1];
|
|
80
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("option", {
|
|
81
|
+
value: value,
|
|
82
|
+
children: label
|
|
83
|
+
}, value);
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|