lightview 1.8.1-b → 2.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/.agent/workflows/daisyui-component-migration.md +155 -0
- package/.codacy/cli.sh +149 -0
- package/.codacy/codacy.yaml +15 -0
- package/.github/instructions/codacy.instructions.md +72 -0
- package/.wranglerignore +21 -0
- package/README.md +1331 -21
- package/_headers +4 -0
- package/build.js +70 -0
- package/components/actions/button.js +151 -0
- package/components/actions/dropdown.js +120 -0
- package/components/actions/modal.js +146 -0
- package/components/actions/swap.js +118 -0
- package/components/daisyui.js +288 -0
- package/components/data-display/accordion.js +128 -0
- package/components/data-display/alert.js +112 -0
- package/components/data-display/avatar.js +170 -0
- package/components/data-display/badge.js +82 -0
- package/components/data-display/card.js +151 -0
- package/components/data-display/carousel.js +94 -0
- package/components/data-display/chart.js +220 -0
- package/components/data-display/chat.js +128 -0
- package/components/data-display/collapse.js +103 -0
- package/components/data-display/countdown.js +69 -0
- package/components/data-display/diff.js +111 -0
- package/components/data-display/kbd.js +65 -0
- package/components/data-display/loading.js +75 -0
- package/components/data-display/progress.js +79 -0
- package/components/data-display/radial-progress.js +88 -0
- package/components/data-display/skeleton.js +66 -0
- package/components/data-display/stats.js +159 -0
- package/components/data-display/table.js +146 -0
- package/components/data-display/timeline.js +146 -0
- package/components/data-display/toast.js +72 -0
- package/components/data-display/tooltip.js +74 -0
- package/components/data-input/checkbox.js +253 -0
- package/components/data-input/file-input.js +224 -0
- package/components/data-input/input.js +264 -0
- package/components/data-input/radio.js +338 -0
- package/components/data-input/range.js +204 -0
- package/components/data-input/rating.js +219 -0
- package/components/data-input/select.js +287 -0
- package/components/data-input/textarea.js +287 -0
- package/components/data-input/toggle.js +201 -0
- package/components/index.js +137 -0
- package/components/layout/divider.js +72 -0
- package/components/layout/drawer.js +142 -0
- package/components/layout/footer.js +100 -0
- package/components/layout/hero.js +109 -0
- package/components/layout/indicator.js +90 -0
- package/components/layout/join.js +78 -0
- package/components/layout/navbar.js +110 -0
- package/components/navigation/breadcrumbs.js +91 -0
- package/components/navigation/dock.js +103 -0
- package/components/navigation/menu.js +126 -0
- package/components/navigation/pagination.js +105 -0
- package/components/navigation/steps.js +89 -0
- package/components/navigation/tabs.css +177 -0
- package/components/navigation/tabs.js +123 -0
- package/components/theme/theme-switch.css +65 -0
- package/components/theme/theme-switch.js +177 -0
- package/docs/about.html +164 -0
- package/docs/api/computed.html +184 -0
- package/docs/api/effects.html +173 -0
- package/docs/api/elements.html +180 -0
- package/docs/api/enhance.html +225 -0
- package/docs/api/hypermedia.html +165 -0
- package/docs/api/index.html +178 -0
- package/docs/api/nav.html +18 -0
- package/docs/api/signals.html +136 -0
- package/docs/api/state.html +217 -0
- package/docs/assets/images/logo-favicon.svg +42 -0
- package/docs/assets/images/logo-static.svg +40 -0
- package/docs/assets/images/logo.svg +66 -0
- package/docs/assets/js/examplify.js +395 -0
- package/docs/assets/styles/site.css +1102 -0
- package/docs/assets/styles/themes.css +236 -0
- package/docs/components/accordion.html +439 -0
- package/docs/components/alert.html +528 -0
- package/docs/components/avatar.html +586 -0
- package/docs/components/badge.html +531 -0
- package/docs/components/breadcrumbs.html +278 -0
- package/docs/components/button.html +579 -0
- package/docs/components/card.html +561 -0
- package/docs/components/carousel.html +286 -0
- package/docs/components/chart-area.html +702 -0
- package/docs/components/chart-bar.html +782 -0
- package/docs/components/chart-column.html +735 -0
- package/docs/components/chart-line.html +794 -0
- package/docs/components/chart-pie.html +823 -0
- package/docs/components/chart.html +612 -0
- package/docs/components/chat.html +547 -0
- package/docs/components/checkbox.html +641 -0
- package/docs/components/collapse.html +536 -0
- package/docs/components/component-nav.html +53 -0
- package/docs/components/countdown.html +470 -0
- package/docs/components/diff.html +245 -0
- package/docs/components/divider.html +240 -0
- package/docs/components/dock.html +277 -0
- package/docs/components/drawer.html +515 -0
- package/docs/components/dropdown.html +479 -0
- package/docs/components/file-input.html +591 -0
- package/docs/components/footer.html +301 -0
- package/docs/components/gallery.html +504 -0
- package/docs/components/hero.html +264 -0
- package/docs/components/index.css +840 -0
- package/docs/components/index.html +735 -0
- package/docs/components/indicator.html +342 -0
- package/docs/components/input.html +644 -0
- package/docs/components/join.html +285 -0
- package/docs/components/kbd.html +322 -0
- package/docs/components/loading.html +521 -0
- package/docs/components/menu.html +461 -0
- package/docs/components/modal.html +639 -0
- package/docs/components/navbar.html +321 -0
- package/docs/components/pagination.html +279 -0
- package/docs/components/progress.html +514 -0
- package/docs/components/radial-progress.html +434 -0
- package/docs/components/radio.html +655 -0
- package/docs/components/range.html +611 -0
- package/docs/components/rating.html +642 -0
- package/docs/components/select.html +696 -0
- package/docs/components/sidebar-setup.js +93 -0
- package/docs/components/skeleton.html +447 -0
- package/docs/components/spinner.html +68 -0
- package/docs/components/stats.html +486 -0
- package/docs/components/steps.html +356 -0
- package/docs/components/swap.html +517 -0
- package/docs/components/switch.html +68 -0
- package/docs/components/table.html +668 -0
- package/docs/components/tabs.html +506 -0
- package/docs/components/text-input.html +68 -0
- package/docs/components/textarea.html +603 -0
- package/docs/components/timeline.html +487 -0
- package/docs/components/toast.html +474 -0
- package/docs/components/toggle.html +564 -0
- package/docs/components/tooltip.html +423 -0
- package/docs/examples/getting-started-example.html +40 -0
- package/docs/examples/index.html +93 -0
- package/docs/getting-started/index.html +739 -0
- package/docs/getting-started/reviews.html +23 -0
- package/docs/getting-started/reviews.odom +108 -0
- package/docs/getting-started/reviews.vdom +84 -0
- package/docs/index.html +134 -0
- package/docs/playground.html +416 -0
- package/docs/router.html +285 -0
- package/docs/styles/index.html +190 -0
- package/functions/_middleware.js +32 -0
- package/index.html +309 -0
- package/lightview-router.js +364 -0
- package/lightview-x.js +1577 -0
- package/lightview.js +658 -1109
- package/lightview.js.backup +793 -0
- package/middleware/locale.js +25 -0
- package/middleware/markdown.js +44 -0
- package/middleware/notFound.js +37 -0
- package/package.json +27 -41
- package/watch.js +92 -0
- package/wrangler.toml +12 -0
- package/.idea/lightview.iml +0 -12
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/LICENSE +0 -21
- package/codepen-no-tabs-embed.css +0 -2
- package/components/chart/chart.html +0 -17
- package/components/chart/example.html +0 -32
- package/components/chart.html +0 -83
- package/components/components.js +0 -113
- package/components/gantt/example.html +0 -22
- package/components/gantt/gantt.html +0 -42
- package/components/gauge/example.html +0 -28
- package/components/gauge/gauge.html +0 -20
- package/components/gauge.html +0 -60
- package/components/orgchart/example.html +0 -25
- package/components/orgchart/orgchart.html +0 -41
- package/components/repl/code-editor.html +0 -64
- package/components/repl/editor.html +0 -37
- package/components/repl/editorjs-inline-tool/index.js +0 -3
- package/components/repl/editorjs-inline-tool/inline-tools.js +0 -28
- package/components/repl/editorjs-inline-tool/tool.js +0 -175
- package/components/repl/repl-with-wysiwyg.html +0 -355
- package/components/repl/repl.html +0 -345
- package/components/repl/sup.js +0 -44
- package/components/repl/wysiwyg-repl.html +0 -258
- package/components/timeline/example.html +0 -33
- package/components/timeline/timeline.html +0 -44
- package/components/timeline.html +0 -81
- package/examples/anchor.html +0 -11
- package/examples/chart.html +0 -34
- package/examples/counter.html +0 -26
- package/examples/counter.test.mjs +0 -47
- package/examples/counter2.html +0 -26
- package/examples/directives.html +0 -79
- package/examples/foreign.html +0 -50
- package/examples/forgeinform.html +0 -98
- package/examples/form.html +0 -61
- package/examples/gauge.html +0 -18
- package/examples/invalid-template-literals.html +0 -44
- package/examples/medium/remote.html +0 -60
- package/examples/message.html +0 -18
- package/examples/nested.html +0 -11
- package/examples/object-bound-form.html +0 -34
- package/examples/remote-server.js +0 -51
- package/examples/remote.html +0 -34
- package/examples/remote.json +0 -1
- package/examples/scratch.html +0 -69
- package/examples/sensors/index.html +0 -30
- package/examples/sensors/sensor-server.js +0 -30
- package/examples/shared.html +0 -41
- package/examples/template.html +0 -33
- package/examples/timeline.html +0 -21
- package/examples/todo.html +0 -38
- package/examples/top.html +0 -10
- package/examples/types.html +0 -94
- package/examples/xor.html +0 -62
- package/jest-puppeteer.config.js +0 -5
- package/jest.config.json +0 -12
- package/sites/client.html +0 -48
- package/sites/index.html +0 -247
- package/test/basic.html +0 -93
- package/test/basic.test.mjs +0 -315
- package/test/extended.html +0 -29
- package/test/extended.test.mjs +0 -448
- package/types.js +0 -534
- package/unsplash.key +0 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightview Tabs Component (DaisyUI)
|
|
3
|
+
* @see https://daisyui.com/components/tab/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import '../daisyui.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tabs Component
|
|
10
|
+
* @param {Object} props
|
|
11
|
+
* @param {string} props.variant - 'boxed' | 'bordered' | 'lifted'
|
|
12
|
+
* @param {string} props.size - 'xs' | 'sm' | 'md' | 'lg'
|
|
13
|
+
* @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
|
|
14
|
+
*/
|
|
15
|
+
const Tabs = (props = {}, ...children) => {
|
|
16
|
+
const { tags } = window.Lightview || {};
|
|
17
|
+
const LVX = window.LightviewX || {};
|
|
18
|
+
|
|
19
|
+
if (!tags) return null;
|
|
20
|
+
|
|
21
|
+
const { div, shadowDOM } = tags;
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
variant,
|
|
25
|
+
size,
|
|
26
|
+
useShadow,
|
|
27
|
+
class: className = '',
|
|
28
|
+
...rest
|
|
29
|
+
} = props;
|
|
30
|
+
|
|
31
|
+
const classes = ['tabs'];
|
|
32
|
+
if (variant === 'boxed') classes.push('tabs-boxed');
|
|
33
|
+
else if (variant === 'bordered') classes.push('tabs-bordered');
|
|
34
|
+
else if (variant === 'lifted') classes.push('tabs-lifted');
|
|
35
|
+
if (size) classes.push(`tabs-${size}`);
|
|
36
|
+
if (className) classes.push(className);
|
|
37
|
+
|
|
38
|
+
const tabsEl = div({
|
|
39
|
+
role: 'tablist',
|
|
40
|
+
class: classes.join(' '),
|
|
41
|
+
...rest
|
|
42
|
+
}, ...children);
|
|
43
|
+
|
|
44
|
+
// Check if we should use shadow DOM
|
|
45
|
+
let usesShadow = false;
|
|
46
|
+
if (LVX.shouldUseShadow) {
|
|
47
|
+
usesShadow = LVX.shouldUseShadow(useShadow);
|
|
48
|
+
} else {
|
|
49
|
+
usesShadow = useShadow === true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (usesShadow) {
|
|
53
|
+
const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
|
|
54
|
+
|
|
55
|
+
const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
|
|
56
|
+
|
|
57
|
+
return div({ class: 'contents' },
|
|
58
|
+
shadowDOM({ mode: 'open', adoptedStyleSheets },
|
|
59
|
+
div({ 'data-theme': themeValue },
|
|
60
|
+
tabsEl
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return tabsEl;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Tab Item
|
|
71
|
+
*/
|
|
72
|
+
Tabs.Tab = (props = {}, ...children) => {
|
|
73
|
+
const { tags } = window.Lightview || {};
|
|
74
|
+
if (!tags) return null;
|
|
75
|
+
|
|
76
|
+
const {
|
|
77
|
+
active = false,
|
|
78
|
+
disabled = false,
|
|
79
|
+
class: className = '',
|
|
80
|
+
...rest
|
|
81
|
+
} = props;
|
|
82
|
+
|
|
83
|
+
const getClasses = () => {
|
|
84
|
+
const classes = ['tab'];
|
|
85
|
+
const isActive = typeof active === 'function' ? active() : active;
|
|
86
|
+
const isDisabled = typeof disabled === 'function' ? disabled() : disabled;
|
|
87
|
+
if (isActive) classes.push('tab-active');
|
|
88
|
+
if (isDisabled) classes.push('tab-disabled');
|
|
89
|
+
if (className) classes.push(className);
|
|
90
|
+
return classes.join(' ');
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return tags.button({
|
|
94
|
+
role: 'tab',
|
|
95
|
+
class: typeof active === 'function' || typeof disabled === 'function'
|
|
96
|
+
? () => getClasses()
|
|
97
|
+
: getClasses(),
|
|
98
|
+
...rest
|
|
99
|
+
}, ...children);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Tab Content - for lifted tabs with content panels
|
|
104
|
+
*/
|
|
105
|
+
Tabs.Content = (props = {}, ...children) => {
|
|
106
|
+
const { tags } = window.Lightview || {};
|
|
107
|
+
if (!tags) return null;
|
|
108
|
+
|
|
109
|
+
const { class: className = '', ...rest } = props;
|
|
110
|
+
|
|
111
|
+
return tags.div({
|
|
112
|
+
role: 'tabpanel',
|
|
113
|
+
class: `tab-content bg-base-100 border-base-300 p-6 ${className}`.trim(),
|
|
114
|
+
...rest
|
|
115
|
+
}, ...children);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const tags = window.Lightview.tags;
|
|
119
|
+
tags.Tabs = Tabs;
|
|
120
|
+
tags['Tabs.Tab'] = Tabs.Tab;
|
|
121
|
+
tags['Tabs.Content'] = Tabs.Content;
|
|
122
|
+
|
|
123
|
+
export default Tabs;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
.lv-theme-switch {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
width: 2.5rem;
|
|
7
|
+
height: 2.5rem;
|
|
8
|
+
padding: 0;
|
|
9
|
+
background: none;
|
|
10
|
+
border: none;
|
|
11
|
+
border-radius: var(--lv-radius-md);
|
|
12
|
+
color: var(--lv-color-text);
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
transition: all var(--lv-transition-fast);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.lv-theme-switch:hover {
|
|
18
|
+
background-color: var(--lv-color-neutral-light);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.lv-theme-switch:focus-visible {
|
|
22
|
+
outline: 2px solid var(--lv-color-primary);
|
|
23
|
+
outline-offset: 2px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.lv-theme-switch__icon {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
transition: transform var(--lv-transition);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.lv-theme-switch__icon[data-icon="sun"] {
|
|
34
|
+
transform: rotate(0deg);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.lv-theme-switch__icon[data-icon="moon"] {
|
|
38
|
+
transform: rotate(0deg);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Animated variant */
|
|
42
|
+
.lv-theme-switch[data-animated="true"] .lv-theme-switch__icon {
|
|
43
|
+
position: absolute;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.lv-theme-switch[data-animated="true"] .lv-theme-switch__icon[data-state="hidden"] {
|
|
47
|
+
transform: scale(0) rotate(-90deg);
|
|
48
|
+
opacity: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.lv-theme-switch[data-animated="true"] .lv-theme-switch__icon[data-state="visible"] {
|
|
52
|
+
transform: scale(1) rotate(0deg);
|
|
53
|
+
opacity: 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Size variants */
|
|
57
|
+
.lv-theme-switch[data-size="sm"] {
|
|
58
|
+
width: 2rem;
|
|
59
|
+
height: 2rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.lv-theme-switch[data-size="lg"] {
|
|
63
|
+
width: 3rem;
|
|
64
|
+
height: 3rem;
|
|
65
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightview Components - ThemeSwitch
|
|
3
|
+
* A toggle for switching between light and dark themes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { loadStylesheetSync } from '../utils/styles.js';
|
|
7
|
+
import { getIcon } from '../utils/icons.js';
|
|
8
|
+
|
|
9
|
+
// Load styles from external CSS file
|
|
10
|
+
loadStylesheetSync(import.meta.url);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ThemeSwitch Component
|
|
14
|
+
* @param {Object} props
|
|
15
|
+
* @param {string|Signal} props.theme - Current theme ('light' | 'dark')
|
|
16
|
+
* @param {string} props.defaultTheme - Default theme if uncontrolled
|
|
17
|
+
* @param {string} props.size - 'sm' | 'md' | 'lg' (default: 'md')
|
|
18
|
+
* @param {boolean} props.animated - Use animated transition (default: true)
|
|
19
|
+
* @param {string} props.storageKey - localStorage key to persist theme (default: 'lv-theme')
|
|
20
|
+
* @param {Function} props.onChange - Theme change handler
|
|
21
|
+
*/
|
|
22
|
+
const ThemeSwitch = (props = {}) => {
|
|
23
|
+
|
|
24
|
+
const { tags, signal, effect } = window.Lightview || {};
|
|
25
|
+
if (!tags) {
|
|
26
|
+
console.error('Lightview not found');
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { button, span } = tags;
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
theme,
|
|
34
|
+
defaultTheme,
|
|
35
|
+
size = 'md',
|
|
36
|
+
animated = true,
|
|
37
|
+
storageKey = 'lv-theme',
|
|
38
|
+
onChange,
|
|
39
|
+
class: className = '',
|
|
40
|
+
...rest
|
|
41
|
+
} = props;
|
|
42
|
+
|
|
43
|
+
// Detect system preference
|
|
44
|
+
const getSystemTheme = () => {
|
|
45
|
+
if (typeof window === 'undefined') return 'light';
|
|
46
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Get initial theme
|
|
50
|
+
const getInitialTheme = () => {
|
|
51
|
+
if (typeof window === 'undefined') return defaultTheme || 'light';
|
|
52
|
+
|
|
53
|
+
// Check localStorage first
|
|
54
|
+
const stored = localStorage.getItem(storageKey);
|
|
55
|
+
if (stored === 'light' || stored === 'dark') return stored;
|
|
56
|
+
|
|
57
|
+
// Then default or system
|
|
58
|
+
return defaultTheme || getSystemTheme();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Internal state
|
|
62
|
+
const internalTheme = signal ? signal(getInitialTheme()) : { value: getInitialTheme() };
|
|
63
|
+
|
|
64
|
+
const isControlled = theme !== undefined;
|
|
65
|
+
|
|
66
|
+
const getTheme = () => {
|
|
67
|
+
if (isControlled) {
|
|
68
|
+
return typeof theme === 'function' ? theme() :
|
|
69
|
+
(theme && typeof theme.value !== 'undefined') ? theme.value : theme;
|
|
70
|
+
}
|
|
71
|
+
return internalTheme.value;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const applyTheme = (newTheme) => {
|
|
75
|
+
document.documentElement.setAttribute('data-theme', newTheme);
|
|
76
|
+
|
|
77
|
+
// Also update meta theme-color for mobile browsers
|
|
78
|
+
let metaTheme = document.querySelector('meta[name="theme-color"]');
|
|
79
|
+
if (!metaTheme) {
|
|
80
|
+
metaTheme = document.createElement('meta');
|
|
81
|
+
metaTheme.name = 'theme-color';
|
|
82
|
+
document.head.appendChild(metaTheme);
|
|
83
|
+
}
|
|
84
|
+
metaTheme.content = newTheme === 'dark' ? '#0f172a' : '#ffffff';
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const toggle = () => {
|
|
88
|
+
const currentTheme = getTheme();
|
|
89
|
+
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
90
|
+
|
|
91
|
+
if (!isControlled) {
|
|
92
|
+
internalTheme.value = newTheme;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isControlled && theme && typeof theme.value !== 'undefined') {
|
|
96
|
+
theme.value = newTheme;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Persist to localStorage
|
|
100
|
+
if (storageKey) {
|
|
101
|
+
localStorage.setItem(storageKey, newTheme);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
applyTheme(newTheme);
|
|
105
|
+
|
|
106
|
+
if (onChange) {
|
|
107
|
+
onChange(newTheme);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Apply theme on mount
|
|
112
|
+
if (typeof window !== 'undefined') {
|
|
113
|
+
// Apply immediately
|
|
114
|
+
applyTheme(getTheme());
|
|
115
|
+
|
|
116
|
+
// Set up effect to watch for changes
|
|
117
|
+
if (effect) {
|
|
118
|
+
effect(() => {
|
|
119
|
+
applyTheme(getTheme());
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Listen for system theme changes
|
|
124
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
|
125
|
+
if (!localStorage.getItem(storageKey)) {
|
|
126
|
+
const newTheme = e.matches ? 'dark' : 'light';
|
|
127
|
+
if (!isControlled) {
|
|
128
|
+
internalTheme.value = newTheme;
|
|
129
|
+
}
|
|
130
|
+
applyTheme(newTheme);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const iconSize = size === 'sm' ? 16 : size === 'lg' ? 28 : 20;
|
|
136
|
+
|
|
137
|
+
const classes = ['lv-theme-switch', `lv-theme-switch--${size}`];
|
|
138
|
+
if (animated) classes.push('lv-theme-switch--animated');
|
|
139
|
+
if (className) classes.push(className);
|
|
140
|
+
|
|
141
|
+
if (animated) {
|
|
142
|
+
return button({
|
|
143
|
+
class: classes.join(' '),
|
|
144
|
+
onclick: toggle,
|
|
145
|
+
'aria-label': () => `Switch to ${getTheme() === 'light' ? 'dark' : 'light'} mode`,
|
|
146
|
+
title: () => `Switch to ${getTheme() === 'light' ? 'dark' : 'light'} mode`,
|
|
147
|
+
...rest
|
|
148
|
+
},
|
|
149
|
+
span({
|
|
150
|
+
class: () => `lv-theme-switch__icon ${getTheme() === 'light' ? 'lv-theme-switch__icon--visible' : 'lv-theme-switch__icon--hidden'}`,
|
|
151
|
+
innerHTML: getIcon('sun', { size: iconSize })
|
|
152
|
+
}),
|
|
153
|
+
span({
|
|
154
|
+
class: () => `lv-theme-switch__icon ${getTheme() === 'dark' ? 'lv-theme-switch__icon--visible' : 'lv-theme-switch__icon--hidden'}`,
|
|
155
|
+
innerHTML: getIcon('moon', { size: iconSize })
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return button({
|
|
161
|
+
class: classes.join(' '),
|
|
162
|
+
onclick: toggle,
|
|
163
|
+
'aria-label': () => `Switch to ${getTheme() === 'light' ? 'dark' : 'light'} mode`,
|
|
164
|
+
title: () => `Switch to ${getTheme() === 'light' ? 'dark' : 'light'} mode`,
|
|
165
|
+
...rest
|
|
166
|
+
},
|
|
167
|
+
span({
|
|
168
|
+
class: 'lv-theme-switch__icon',
|
|
169
|
+
innerHTML: () => getIcon(getTheme() === 'light' ? 'sun' : 'moon', { size: iconSize })
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Auto-register
|
|
175
|
+
window.Lightview.tags.ThemeSwitch = ThemeSwitch;
|
|
176
|
+
|
|
177
|
+
export default ThemeSwitch;
|
package/docs/about.html
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<!-- SEO-friendly SPA Shim -->
|
|
2
|
+
<script src="/lightview-router.js"></script>
|
|
3
|
+
<script>
|
|
4
|
+
if (window.LightviewRouter) {
|
|
5
|
+
LightviewRouter.base('/index.html');
|
|
6
|
+
}
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<div class="section">
|
|
10
|
+
<div class="section-content" style="max-width: 800px;">
|
|
11
|
+
<h1>About Lightview</h1>
|
|
12
|
+
<p class="text-secondary" style="font-size: 1.125rem;">
|
|
13
|
+
A lighter way to build.
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<div
|
|
17
|
+
style="background: var(--site-bg-alt); border-radius: var(--site-radius-lg); padding: 2rem; margin: 2rem 0; text-align: center;">
|
|
18
|
+
<img src="/docs/assets/images/logo.svg" alt="Lightview"
|
|
19
|
+
style="width: 80px; height: 80px; margin-bottom: 1rem;">
|
|
20
|
+
<p style="font-size: 1.25rem; font-style: italic; color: var(--site-text-secondary); margin: 0;">
|
|
21
|
+
"The view is beautiful from here."
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<h2>Genesis</h2>
|
|
26
|
+
|
|
27
|
+
<p>
|
|
28
|
+
It all started with a mix of admiration and frustration. I liked <strong>Bau</strong>,
|
|
29
|
+
<strong>HTMX</strong>, and
|
|
30
|
+
<strong>Juris</strong>. They were elegant, but they were also opinionated—they forced a choice.
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<p>
|
|
34
|
+
I didn't want to choose. I wanted a framework that supported <em>all</em> approaches. None of them quite hit
|
|
35
|
+
the mark on their own. Plus, I wanted something none provided, template literals right inside HTML. I also
|
|
36
|
+
wanted a rich component library (with
|
|
37
|
+
charts!) without having to build everything from scratch available as both JavaScript and custom HTML
|
|
38
|
+
elements.
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p>
|
|
42
|
+
I wanted a framework that was easy to use and learn, as close to regular HTML and JavaScript as possible,
|
|
43
|
+
something that felt more like regular HTML than even HTMX does.
|
|
44
|
+
This would also require thorough and fully interactive documentation accessible via direct URLs and an SPA
|
|
45
|
+
like experience.
|
|
46
|
+
This manadated a router with at least partial "automatic" SSR—where code could execute and replace itself on
|
|
47
|
+
the server with a simple
|
|
48
|
+
DOM emulation and "have-your-cake-and-eat-it-too" single page apps that are SEO enabled without any extra
|
|
49
|
+
work.
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
<p>
|
|
53
|
+
In today's age, the ability of an LLM to generate cohensive applications using the library is also important
|
|
54
|
+
to me. If figured that if an LLM generated it and it followed conventional patterns, it will be able to use
|
|
55
|
+
it well and perhaps generate a little less slop.
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
<p>
|
|
59
|
+
So, I decided to run an experiment. I told Claude Opus 4.5 via Google Antigravity to "create me the
|
|
60
|
+
combo of HTMX, Bau, and
|
|
61
|
+
Juris with no special attribute names plus HTML template literals, and an SPA router. And, give me a UI
|
|
62
|
+
component library like
|
|
63
|
+
Bau's with automatic custom elment creation but don't build it from scratch. Finally, make it possible to
|
|
64
|
+
create SEO enabled apps with no extra work" I also provied a few examples of the kind of code I wanted to
|
|
65
|
+
write
|
|
66
|
+
using the library.
|
|
67
|
+
</p>
|
|
68
|
+
|
|
69
|
+
<p>
|
|
70
|
+
Naturally, it took a little iterating, but the basic framework was in place after just a few hours of
|
|
71
|
+
working with both Claude Opus 4.5 and Gemini 3 Pro. I then asked for
|
|
72
|
+
an extensive interactive website for documenting and promoting Lightview that also used Lightview, despsite
|
|
73
|
+
the fact that in the age of LLM code generation the need for extensive documentation websites is limited.
|
|
74
|
+
This took a lot more work, 3
|
|
75
|
+
weeks in fact. Very little of this time was spent addressing issues with or enhancing Ligntview. Most of it
|
|
76
|
+
was spent adding, debugginng and testing all the UI components. The LLM would get locked in on a particular
|
|
77
|
+
approach that worked form simple components bu no more complex ones.
|
|
78
|
+
But, that is a story for another time. Here we are 3 weeks later with about 2,000 lines of code for
|
|
79
|
+
Lightview
|
|
80
|
+
as a whole across three
|
|
81
|
+
files, 25,000 lines of component code, and 30,000 lines of documentation.
|
|
82
|
+
</p>
|
|
83
|
+
|
|
84
|
+
<p>
|
|
85
|
+
The resulting Lightview 2.0 library is a replacement for an experiment I ran 4 years ago to create a linrary
|
|
86
|
+
with
|
|
87
|
+
remote state storage and extended type declarations for state management. It is specific yet flexible.
|
|
88
|
+
Although it drops some of the features of Livghtview v1.0 and is not syntax compatible, it brings together
|
|
89
|
+
the features I craved without
|
|
90
|
+
locking me into a single paradigm. I hope you like it.
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
<h2>Design Principles</h2>
|
|
94
|
+
|
|
95
|
+
<div
|
|
96
|
+
style="background: var(--site-accent-light); border-left: 4px solid var(--site-accent); padding: 1.5rem; border-radius: 0 var(--site-radius) var(--site-radius) 0; margin: 1.5rem 0;">
|
|
97
|
+
<p style="margin: 0; color: var(--site-text-primary); font-size: 1.25rem; font-style: italic;">
|
|
98
|
+
"My opinion, don't be opinionated!"
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div class="feature-grid" style="margin-top: 1.5rem;">
|
|
103
|
+
<div class="feature-card">
|
|
104
|
+
<h3 class="feature-title">🪶 Keep It Light</h3>
|
|
105
|
+
<p class="feature-description">
|
|
106
|
+
Life's too short for heavy frameworks. Although Ligheview is not tiny (core is 8K), I tried to keep
|
|
107
|
+
it lean and modular so your apps stay fast.
|
|
108
|
+
</p>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="feature-card">
|
|
111
|
+
<h3 class="feature-title">🔌 Zero Dependencies</h3>
|
|
112
|
+
<p class="feature-description">
|
|
113
|
+
No external dependencies. No supply chain worries. No version conflicts. Just works.
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="feature-card">
|
|
117
|
+
<h3 class="feature-title">🚀 No Build Step</h3>
|
|
118
|
+
<p class="feature-description">
|
|
119
|
+
Drop in a script tag and go. Instant gratification. Your bundler can take the day off.
|
|
120
|
+
</p>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="feature-card">
|
|
123
|
+
<h3 class="feature-title">🎨 Your Way</h3>
|
|
124
|
+
<p class="feature-description">
|
|
125
|
+
Four syntaxes, one system. Incremental adoption by enhancing just the HTML you want. Pick what
|
|
126
|
+
feels right. See the function <code>enhance</code> in the <a href="./api/enhance">API docs</a>.
|
|
127
|
+
</p>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="feature-card">
|
|
130
|
+
<h3 class="feature-title">😊 Have Fun</h3>
|
|
131
|
+
<p class="feature-description">
|
|
132
|
+
Coding should spark joy. Simple API, clear and interactive docs, helpful errors. Built for humans
|
|
133
|
+
who like to smile.
|
|
134
|
+
</p>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<h2>Credits & Inspiration</h2>
|
|
139
|
+
<p>
|
|
140
|
+
Lightview stands on the shoulders of giants. Big thanks to:
|
|
141
|
+
</p>
|
|
142
|
+
<ul>
|
|
143
|
+
<li><strong>Bau.js</strong> — The elegant tagged template API</li>
|
|
144
|
+
<li><strong>HTMX</strong> — Hypermedia as the engine of application state</li>
|
|
145
|
+
<li><strong>Juris.js</strong> — Simplicity and progressive enhancement</li>
|
|
146
|
+
</ul>
|
|
147
|
+
|
|
148
|
+
<h2>License</h2>
|
|
149
|
+
<p>
|
|
150
|
+
Lightview is open source software licensed under the <a href="https://opensource.org/licenses/MIT"
|
|
151
|
+
target="_blank" rel="noopener">MIT License</a>.
|
|
152
|
+
</p>
|
|
153
|
+
<p>
|
|
154
|
+
Free to use, modify, and distribute. Go forth and build light things. ✨
|
|
155
|
+
</p>
|
|
156
|
+
|
|
157
|
+
<div style="margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--site-border); text-align: center;">
|
|
158
|
+
<p style="font-size: 1.125rem; margin-bottom: 1rem;">
|
|
159
|
+
Ready to lighten up?
|
|
160
|
+
</p>
|
|
161
|
+
<a href="./getting-started" class="btn btn-primary">Enlighten Your UI</a>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|