lightview 1.8.2 → 2.0.1

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 (262) hide show
  1. package/.codacy/cli.sh +149 -0
  2. package/.codacy/codacy.yaml +15 -0
  3. package/.github/instructions/codacy.instructions.md +72 -0
  4. package/.wranglerignore +21 -0
  5. package/README.md +1330 -19
  6. package/_headers +4 -0
  7. package/build.js +70 -0
  8. package/components/actions/button.js +151 -0
  9. package/components/actions/dropdown.js +120 -0
  10. package/components/actions/modal.js +146 -0
  11. package/components/actions/swap.js +118 -0
  12. package/components/daisyui.js +288 -0
  13. package/components/data-display/accordion.js +128 -0
  14. package/components/data-display/alert.js +112 -0
  15. package/components/data-display/avatar.js +170 -0
  16. package/components/data-display/badge.js +82 -0
  17. package/components/data-display/card.js +151 -0
  18. package/components/data-display/carousel.js +94 -0
  19. package/components/data-display/chart.js +220 -0
  20. package/components/data-display/chat.js +128 -0
  21. package/components/data-display/collapse.js +103 -0
  22. package/components/data-display/countdown.js +69 -0
  23. package/components/data-display/diff.js +111 -0
  24. package/components/data-display/kbd.js +65 -0
  25. package/components/data-display/loading.js +75 -0
  26. package/components/data-display/progress.js +79 -0
  27. package/components/data-display/radial-progress.js +88 -0
  28. package/components/data-display/skeleton.js +66 -0
  29. package/components/data-display/stats.js +159 -0
  30. package/components/data-display/table.js +146 -0
  31. package/components/data-display/timeline.js +146 -0
  32. package/components/data-display/toast.js +72 -0
  33. package/components/data-display/tooltip.js +74 -0
  34. package/components/data-input/checkbox.js +253 -0
  35. package/components/data-input/file-input.js +224 -0
  36. package/components/data-input/input.js +264 -0
  37. package/components/data-input/radio.js +338 -0
  38. package/components/data-input/range.js +204 -0
  39. package/components/data-input/rating.js +219 -0
  40. package/components/data-input/select.js +287 -0
  41. package/components/data-input/textarea.js +287 -0
  42. package/components/data-input/toggle.js +201 -0
  43. package/components/index.js +137 -0
  44. package/components/layout/divider.js +72 -0
  45. package/components/layout/drawer.js +142 -0
  46. package/components/layout/footer.js +100 -0
  47. package/components/layout/hero.js +109 -0
  48. package/components/layout/indicator.js +90 -0
  49. package/components/layout/join.js +78 -0
  50. package/components/layout/navbar.js +110 -0
  51. package/components/navigation/breadcrumbs.js +91 -0
  52. package/components/navigation/dock.js +103 -0
  53. package/components/navigation/menu.js +126 -0
  54. package/components/navigation/pagination.js +105 -0
  55. package/components/navigation/steps.js +89 -0
  56. package/components/navigation/tabs.css +177 -0
  57. package/components/navigation/tabs.js +123 -0
  58. package/components/theme/theme-switch.css +65 -0
  59. package/components/theme/theme-switch.js +177 -0
  60. package/docs/about.html +164 -0
  61. package/docs/api/computed.html +184 -0
  62. package/docs/api/effects.html +173 -0
  63. package/docs/api/elements.html +180 -0
  64. package/docs/api/enhance.html +225 -0
  65. package/docs/api/hypermedia.html +165 -0
  66. package/docs/api/index.html +178 -0
  67. package/docs/api/nav.html +18 -0
  68. package/docs/api/signals.html +136 -0
  69. package/docs/api/state.html +217 -0
  70. package/docs/assets/images/logo-favicon.svg +42 -0
  71. package/docs/assets/images/logo-static.svg +40 -0
  72. package/docs/assets/images/logo.svg +66 -0
  73. package/docs/assets/js/examplify.js +395 -0
  74. package/docs/assets/styles/site.css +1102 -0
  75. package/docs/assets/styles/themes.css +236 -0
  76. package/docs/components/accordion.html +439 -0
  77. package/docs/components/alert.html +528 -0
  78. package/docs/components/avatar.html +586 -0
  79. package/docs/components/badge.html +531 -0
  80. package/docs/components/breadcrumbs.html +278 -0
  81. package/docs/components/button.html +579 -0
  82. package/docs/components/card.html +561 -0
  83. package/docs/components/carousel.html +286 -0
  84. package/docs/components/chart-area.html +702 -0
  85. package/docs/components/chart-bar.html +782 -0
  86. package/docs/components/chart-column.html +735 -0
  87. package/docs/components/chart-line.html +794 -0
  88. package/docs/components/chart-pie.html +823 -0
  89. package/docs/components/chart.html +610 -15
  90. package/docs/components/chat.html +547 -0
  91. package/docs/components/checkbox.html +641 -0
  92. package/docs/components/collapse.html +536 -0
  93. package/docs/components/component-nav.html +53 -0
  94. package/docs/components/countdown.html +470 -0
  95. package/docs/components/diff.html +245 -0
  96. package/docs/components/divider.html +240 -0
  97. package/docs/components/dock.html +277 -0
  98. package/docs/components/drawer.html +515 -0
  99. package/docs/components/dropdown.html +479 -0
  100. package/docs/components/file-input.html +591 -0
  101. package/docs/components/footer.html +301 -0
  102. package/docs/components/gallery.html +504 -0
  103. package/docs/components/hero.html +264 -0
  104. package/docs/components/index.css +840 -0
  105. package/docs/components/index.html +735 -0
  106. package/docs/components/indicator.html +342 -0
  107. package/docs/components/input.html +644 -0
  108. package/docs/components/join.html +285 -0
  109. package/docs/components/kbd.html +322 -0
  110. package/docs/components/loading.html +521 -0
  111. package/docs/components/menu.html +461 -0
  112. package/docs/components/modal.html +639 -0
  113. package/docs/components/navbar.html +321 -0
  114. package/docs/components/pagination.html +279 -0
  115. package/docs/components/progress.html +514 -0
  116. package/docs/components/radial-progress.html +434 -0
  117. package/docs/components/radio.html +655 -0
  118. package/docs/components/range.html +611 -0
  119. package/docs/components/rating.html +642 -0
  120. package/docs/components/select.html +696 -0
  121. package/docs/components/sidebar-setup.js +93 -0
  122. package/docs/components/skeleton.html +447 -0
  123. package/docs/components/spinner.html +68 -0
  124. package/docs/components/stats.html +486 -0
  125. package/docs/components/steps.html +356 -0
  126. package/docs/components/swap.html +517 -0
  127. package/docs/components/switch.html +68 -0
  128. package/docs/components/table.html +668 -0
  129. package/docs/components/tabs.html +506 -0
  130. package/docs/components/text-input.html +68 -0
  131. package/docs/components/textarea.html +603 -0
  132. package/docs/components/timeline.html +485 -42
  133. package/docs/components/toast.html +474 -0
  134. package/docs/components/toggle.html +564 -0
  135. package/docs/components/tooltip.html +423 -0
  136. package/docs/examples/getting-started-example.html +40 -0
  137. package/docs/examples/index.html +93 -0
  138. package/docs/getting-started/index.html +739 -0
  139. package/docs/getting-started/reviews.html +23 -0
  140. package/docs/getting-started/reviews.odom +108 -0
  141. package/docs/getting-started/reviews.vdom +84 -0
  142. package/docs/index.html +132 -42
  143. package/docs/playground.html +416 -0
  144. package/docs/router.html +285 -0
  145. package/docs/styles/index.html +190 -0
  146. package/functions/_middleware.js +32 -0
  147. package/index.html +309 -0
  148. package/lightview-router.js +364 -0
  149. package/lightview-x.js +1577 -0
  150. package/lightview.js +659 -1200
  151. package/middleware/locale.js +25 -0
  152. package/middleware/markdown.js +44 -0
  153. package/middleware/notFound.js +37 -0
  154. package/package.json +27 -41
  155. package/watch.js +92 -0
  156. package/wrangler.toml +12 -0
  157. package/.idea/lightview.iml +0 -12
  158. package/.idea/modules.xml +0 -8
  159. package/.idea/vcs.xml +0 -6
  160. package/LICENSE +0 -21
  161. package/codepen-no-tabs-embed.css +0 -2
  162. package/docs/CNAME +0 -1
  163. package/docs/api.html +0 -674
  164. package/docs/blank.html +0 -10
  165. package/docs/comparedto.html +0 -89
  166. package/docs/components/chart-repl.html +0 -69
  167. package/docs/components/components.js +0 -113
  168. package/docs/components/contents.html +0 -17
  169. package/docs/components/gantt-repl.html +0 -61
  170. package/docs/components/gantt.html +0 -42
  171. package/docs/components/gauge-repl.html +0 -66
  172. package/docs/components/gauge.html +0 -20
  173. package/docs/components/orgchart-repl.html +0 -64
  174. package/docs/components/orgchart.html +0 -41
  175. package/docs/components/repl-as-src.html +0 -17
  176. package/docs/components/repl-repl.html +0 -95
  177. package/docs/components/repl.html +0 -527
  178. package/docs/components/timeline-repl.html +0 -72
  179. package/docs/components.html +0 -14
  180. package/docs/css/highlightjs.min.css +0 -9
  181. package/docs/css/tutorial.css +0 -35
  182. package/docs/examples/anchor.html +0 -11
  183. package/docs/examples/chart.html +0 -34
  184. package/docs/examples/counter.html +0 -26
  185. package/docs/examples/counter.test.mjs +0 -47
  186. package/docs/examples/counter2.html +0 -26
  187. package/docs/examples/directives.html +0 -79
  188. package/docs/examples/foreign.html +0 -50
  189. package/docs/examples/forgeinform.html +0 -98
  190. package/docs/examples/form.html +0 -61
  191. package/docs/examples/gauge.html +0 -18
  192. package/docs/examples/invalid-template-literals.html +0 -44
  193. package/docs/examples/medium/remote.html +0 -60
  194. package/docs/examples/message.html +0 -18
  195. package/docs/examples/nested.html +0 -11
  196. package/docs/examples/object-bound-form.html +0 -34
  197. package/docs/examples/remote-server.js +0 -51
  198. package/docs/examples/remote.html +0 -34
  199. package/docs/examples/remote.json +0 -1
  200. package/docs/examples/scratch.html +0 -69
  201. package/docs/examples/sensors/index.html +0 -44
  202. package/docs/examples/sensors/sensor-server.js +0 -30
  203. package/docs/examples/shared.html +0 -41
  204. package/docs/examples/template.html +0 -33
  205. package/docs/examples/timeline.html +0 -21
  206. package/docs/examples/todo.html +0 -40
  207. package/docs/examples/top.html +0 -10
  208. package/docs/examples/types.html +0 -94
  209. package/docs/examples/xor.html +0 -62
  210. package/docs/examples.html +0 -25
  211. package/docs/javascript/codejar.min.js +0 -8
  212. package/docs/javascript/highlightjs.min.js +0 -1173
  213. package/docs/javascript/isomorphic-git.js +0 -9
  214. package/docs/javascript/json5.min.js +0 -1
  215. package/docs/javascript/lightning-fs.js +0 -1
  216. package/docs/javascript/lightview.js +0 -1285
  217. package/docs/javascript/marked.min.js +0 -6
  218. package/docs/javascript/peerjs.min.js +0 -70
  219. package/docs/javascript/turndown.js +0 -973
  220. package/docs/javascript/types.js +0 -606
  221. package/docs/javascript/utils.js +0 -45
  222. package/docs/lightview.html +0 -63
  223. package/docs/old_index.html +0 -965
  224. package/docs/old_index.md +0 -1132
  225. package/docs/slidein.html +0 -51
  226. package/docs/tutorial/0-getting-started.html +0 -67
  227. package/docs/tutorial/1-intro-to-variables.html +0 -103
  228. package/docs/tutorial/10-template-components.html +0 -80
  229. package/docs/tutorial/11-linked-components.html +0 -76
  230. package/docs/tutorial/12-imported-components.html +0 -67
  231. package/docs/tutorial/13-input-binding.html +0 -94
  232. package/docs/tutorial/14-automatic-variable-creation.html +0 -74
  233. package/docs/tutorial/15-form-binding.html +0 -110
  234. package/docs/tutorial/16-if-directive.html +0 -60
  235. package/docs/tutorial/17-loop-directives.html +0 -83
  236. package/docs/tutorial/18-sanitizing-and-escaping-input.html +0 -79
  237. package/docs/tutorial/2-imported-and-exported-variables.html +0 -80
  238. package/docs/tutorial/3-data-types.html +0 -89
  239. package/docs/tutorial/4-extended-data-types.html +0 -83
  240. package/docs/tutorial/5-extended-functional-types.html +0 -96
  241. package/docs/tutorial/5.1-extended-functional-types.html +0 -79
  242. package/docs/tutorial/5.2-extended-functional-types.html +0 -70
  243. package/docs/tutorial/6-conventional-javascript.html +0 -75
  244. package/docs/tutorial/7-monitoring-with-observers.html +0 -107
  245. package/docs/tutorial/8-event-listeners.html +0 -65
  246. package/docs/tutorial/9-intro-to-components.html +0 -91
  247. package/docs/tutorial/contents.html +0 -32
  248. package/docs/tutorial/my-component.html +0 -29
  249. package/docs/tutorial/remote-value.json +0 -4
  250. package/docs/websiterepl.html +0 -46
  251. package/jest-puppeteer.config.js +0 -5
  252. package/jest.config.json +0 -12
  253. package/lightview.min.js +0 -1
  254. package/lightview_good.js +0 -1267
  255. package/lightview_optimized.js +0 -1274
  256. package/repl_hold.html +0 -320
  257. package/test/basic.html +0 -104
  258. package/test/basic.test.mjs +0 -315
  259. package/test/extended.html +0 -29
  260. package/test/extended.test.mjs +0 -448
  261. package/types.js +0 -607
  262. package/unsplash.key +0 -1
package/_headers ADDED
@@ -0,0 +1,4 @@
1
+ /*
2
+ Access-Control-Allow-Origin: *
3
+ Access-Control-Allow-Methods: GET, OPTIONS
4
+ Access-Control-Allow-Headers: *
package/build.js ADDED
@@ -0,0 +1,70 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const rootDir = __dirname;
5
+ const distDir = path.join(rootDir, 'dist');
6
+ const docsDir = path.join(rootDir, 'docs');
7
+ const componentsDir = path.join(rootDir, 'components');
8
+ const middlewareDir = path.join(rootDir, 'middleware');
9
+
10
+ // Configuration
11
+ // Files in root that should be copied
12
+ const allowedExtensions = ['.html', '.js', '.css', '.txt', '.xml', '.ico', '.png', '.svg', '.jpg', '.jpeg', '.md'];
13
+ const includeFiles = ['_headers']; // specific files to always include
14
+ const excludeFiles = ['build.js', 'package.json', 'package-lock.json', 'wrangler.toml'];
15
+
16
+ console.log('Building for deployment...');
17
+
18
+ // 1. Clean/Create dist
19
+ if (fs.existsSync(distDir)) {
20
+ fs.rmSync(distDir, { recursive: true, force: true });
21
+ }
22
+ fs.mkdirSync(distDir);
23
+ console.log('Created dist directory.');
24
+
25
+ // 2. Copy Root Files
26
+ const files = fs.readdirSync(rootDir);
27
+ files.forEach(file => {
28
+ const srcPath = path.join(rootDir, file);
29
+ const stat = fs.statSync(srcPath);
30
+
31
+ if (stat.isFile()) {
32
+ const ext = path.extname(file).toLowerCase();
33
+
34
+ // Check if file should be copied
35
+ const isAllowedExt = allowedExtensions.includes(ext);
36
+ const isExplicitInclude = includeFiles.includes(file);
37
+ const isExcluded = excludeFiles.includes(file) || file.startsWith('.');
38
+
39
+ if ((isAllowedExt || isExplicitInclude) && !isExcluded) {
40
+ fs.copyFileSync(srcPath, path.join(distDir, file));
41
+ console.log(`Copied: ${file}`);
42
+ }
43
+ }
44
+ });
45
+
46
+ // 3. Copy Docs Directory
47
+ if (fs.existsSync(docsDir)) {
48
+ fs.cpSync(docsDir, path.join(distDir, 'docs'), { recursive: true });
49
+ console.log('Copied: docs directory');
50
+ } else {
51
+ console.warn('Warning: docs directory not found.');
52
+ }
53
+
54
+ // 4. Copy Components Directory
55
+ if (fs.existsSync(componentsDir)) {
56
+ fs.cpSync(componentsDir, path.join(distDir, 'components'), { recursive: true });
57
+ console.log('Copied: components directory');
58
+ } else {
59
+ console.warn('Warning: components directory not found.');
60
+ }
61
+
62
+ // 5. Copy Middleware Directory
63
+ if (fs.existsSync(middlewareDir)) {
64
+ fs.cpSync(middlewareDir, path.join(distDir, 'middleware'), { recursive: true });
65
+ console.log('Copied: middleware directory');
66
+ } else {
67
+ console.warn('Warning: middleware directory not found.');
68
+ }
69
+
70
+ console.log('Build complete! Assets are ready in ./dist');
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Lightview Button Component (DaisyUI)
3
+ * @see https://daisyui.com/components/button/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Button Component
10
+ * @param {Object} props
11
+ * @param {string} props.color - 'primary' | 'secondary' | 'accent' | 'neutral' | 'info' | 'success' | 'warning' | 'error' | 'ghost' | 'link'
12
+ * @param {string} props.size - 'xs' | 'sm' | 'md' | 'lg'
13
+ * @param {string} props.variant - 'outline' | 'soft' | 'dash' | 'wide' | 'block' | 'square' | 'circle'
14
+ * @param {boolean} props.disabled - Disable the button
15
+ * @param {boolean} props.loading - Show loading state
16
+ * @param {boolean} props.active - Force active state
17
+ * @param {boolean} props.glass - Glass morphism effect
18
+ * @param {boolean} props.noAnimation - Disable click animation
19
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
20
+ * @param {...children} children - Button content
21
+ */
22
+ const Button = (props = {}, ...children) => {
23
+ const { tags } = window.Lightview || {};
24
+ const LVX = window.LightviewX || {};
25
+
26
+ if (!tags) {
27
+ console.error('Lightview not found');
28
+ return null;
29
+ }
30
+
31
+ const { button, div, span, shadowDOM } = tags;
32
+
33
+ const {
34
+ color,
35
+ size,
36
+ variant,
37
+ disabled = false,
38
+ loading = false,
39
+ active = false,
40
+ glass = false,
41
+ noAnimation = false,
42
+ useShadow,
43
+ theme, // Explicit theme override
44
+ class: className = '',
45
+ ...rest
46
+ } = props;
47
+
48
+ const getClassList = () => {
49
+ const classes = ['btn'];
50
+
51
+ // Color
52
+ if (['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error', 'ghost', 'link'].includes(color)) classes.push(`btn-${color}`);
53
+
54
+ // Size
55
+ if (size) classes.push(`btn-${size}`);
56
+
57
+ // Variant
58
+ if (variant === 'outline') classes.push('btn-outline');
59
+ else if (variant === 'soft') classes.push('btn-soft');
60
+ else if (variant === 'dash') classes.push('btn-dash');
61
+ else if (variant === 'wide') classes.push('btn-wide');
62
+ else if (variant === 'block') classes.push('btn-block');
63
+ else if (variant === 'square') classes.push('btn-square');
64
+ else if (variant === 'circle') classes.push('btn-circle');
65
+
66
+ // States
67
+ const isDisabled = typeof disabled === 'function' ? disabled() : disabled;
68
+ const isLoading = typeof loading === 'function' ? loading() : loading;
69
+ const isActive = typeof active === 'function' ? active() : active;
70
+
71
+ if (isDisabled) classes.push('btn-disabled');
72
+ if (isActive) classes.push('btn-active');
73
+ if (glass) classes.push('glass');
74
+ if (noAnimation) classes.push('no-animation');
75
+
76
+ if (className) classes.push(className);
77
+
78
+ return classes.join(' ');
79
+ };
80
+
81
+ const buildContent = () => {
82
+ const isLoading = typeof loading === 'function' ? loading() : loading;
83
+ if (isLoading) {
84
+ return [
85
+ tags.span({ class: 'loading loading-spinner' }),
86
+ ...children
87
+ ];
88
+ }
89
+ return children;
90
+ };
91
+
92
+ if (color && !['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error', 'ghost', 'link'].includes(color)) {
93
+ // set the background color using style
94
+ rest.style = rest.style || '';
95
+ if (typeof rest.style === 'object') {
96
+ rest.style.backgroundColor = color;
97
+ } else {
98
+ rest.style += `;background-color: ${color};`;
99
+ }
100
+ }
101
+ const buttonEl = button({
102
+ class: typeof disabled === 'function' || typeof loading === 'function' || typeof active === 'function'
103
+ ? () => getClassList()
104
+ : getClassList(),
105
+ disabled: typeof disabled === 'function'
106
+ ? () => disabled() || (typeof loading === 'function' ? loading() : loading)
107
+ : disabled || loading,
108
+ ...rest
109
+ }, ...(typeof loading === 'function' ? [() => buildContent()] : buildContent()));
110
+
111
+ // Check if we should use shadow DOM
112
+ let usesShadow = false;
113
+ if (LVX.shouldUseShadow) {
114
+ usesShadow = LVX.shouldUseShadow(useShadow);
115
+ } else {
116
+ usesShadow = useShadow === true;
117
+ }
118
+
119
+ if (usesShadow) {
120
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
121
+
122
+ // If theme is explicitly provided, wrap in a div with data-theme
123
+ // Otherwise, return button directly in shadow root to allow inheritance from host
124
+ // Use reactive theme signal if available, otherwise fallback to explicit 'theme' prop or default
125
+ const themeValue = theme || (LVX.themeSignal ? () => LVX.themeSignal.value : 'light');
126
+
127
+ return div({ class: 'content', style: 'display: inline-block' },
128
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
129
+ div({ 'data-theme': themeValue },
130
+ buttonEl
131
+ )
132
+ )
133
+ );
134
+ }
135
+
136
+ return buttonEl;
137
+ };
138
+
139
+
140
+ window.Lightview.tags.Button = Button;
141
+
142
+ // Register as Custom Element
143
+ if (window.LightviewX?.createCustomElement) {
144
+ const ButtonElement = window.LightviewX.createCustomElement(Button);
145
+ if (!customElements.get('lv-button')) {
146
+ customElements.define('lv-button', ButtonElement);
147
+ }
148
+ }
149
+
150
+
151
+ export default Button;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Lightview Dropdown Component (DaisyUI)
3
+ * @see https://daisyui.com/components/dropdown/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Dropdown Component
10
+ * @param {Object} props
11
+ * @param {string} props.position - 'top' | 'bottom' | 'left' | 'right' | 'end'
12
+ * @param {boolean} props.hover - Open on hover
13
+ * @param {boolean} props.open - Force open state
14
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
15
+ */
16
+ const Dropdown = (props = {}, ...children) => {
17
+ const { tags } = window.Lightview || {};
18
+ const LVX = window.LightviewX || {};
19
+
20
+ if (!tags) return null;
21
+
22
+ const { div, shadowDOM } = tags;
23
+
24
+ const {
25
+ position,
26
+ hover = false,
27
+ open = false,
28
+ useShadow,
29
+ class: className = '',
30
+ ...rest
31
+ } = props;
32
+
33
+ const classes = ['dropdown'];
34
+ if (position === 'top') classes.push('dropdown-top');
35
+ else if (position === 'bottom') classes.push('dropdown-bottom');
36
+ else if (position === 'left') classes.push('dropdown-left');
37
+ else if (position === 'right') classes.push('dropdown-right');
38
+ else if (position === 'end') classes.push('dropdown-end');
39
+
40
+ if (hover) classes.push('dropdown-hover');
41
+ if (open) classes.push('dropdown-open');
42
+ if (className) classes.push(className);
43
+
44
+ const dropdownEl = div({ class: classes.join(' '), ...rest }, ...children);
45
+
46
+ // Check if we should use shadow DOM
47
+ let usesShadow = false;
48
+ if (LVX.shouldUseShadow) {
49
+ usesShadow = LVX.shouldUseShadow(useShadow);
50
+ } else {
51
+ usesShadow = useShadow === true;
52
+ }
53
+
54
+ if (usesShadow) {
55
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
56
+
57
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
58
+
59
+ return div({ class: 'contents' },
60
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
61
+ div({ 'data-theme': themeValue },
62
+ dropdownEl
63
+ )
64
+ )
65
+ );
66
+ }
67
+
68
+ return dropdownEl;
69
+ };
70
+
71
+ /**
72
+ * Dropdown Trigger - the element that toggles the dropdown
73
+ */
74
+ Dropdown.Trigger = (props = {}, ...children) => {
75
+ const { tags } = window.Lightview || {};
76
+ if (!tags) return null;
77
+
78
+ const { class: className = '', ...rest } = props;
79
+
80
+ return tags.div({
81
+ tabindex: '0',
82
+ role: 'button',
83
+ class: `btn m-1 ${className}`.trim(),
84
+ ...rest
85
+ }, ...children);
86
+ };
87
+
88
+ /**
89
+ * Dropdown Content - the menu that appears
90
+ */
91
+ Dropdown.Content = (props = {}, ...children) => {
92
+ const { tags } = window.Lightview || {};
93
+ if (!tags) return null;
94
+
95
+ const { class: className = '', ...rest } = props;
96
+
97
+ return tags.ul({
98
+ tabindex: '0',
99
+ class: `dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm ${className}`.trim(),
100
+ ...rest
101
+ }, ...children);
102
+ };
103
+
104
+ /**
105
+ * Dropdown Item
106
+ */
107
+ Dropdown.Item = (props = {}, ...children) => {
108
+ const { tags } = window.Lightview || {};
109
+ if (!tags) return null;
110
+
111
+ return tags.li(props, tags.a({}, ...children));
112
+ };
113
+
114
+ const tags = window.Lightview.tags;
115
+ tags.Dropdown = Dropdown;
116
+ tags['Dropdown.Trigger'] = Dropdown.Trigger;
117
+ tags['Dropdown.Content'] = Dropdown.Content;
118
+ tags['Dropdown.Item'] = Dropdown.Item;
119
+
120
+ export default Dropdown;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Lightview Modal Component (DaisyUI)
3
+ * @see https://daisyui.com/components/modal/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Modal Component using the dialog element
10
+ * @param {Object} props
11
+ * @param {string} props.id - Required unique ID for the modal
12
+ * @param {boolean|function} props.open - Control open state reactively
13
+ * @param {string} props.position - 'top' | 'bottom' | 'middle' (default)
14
+ * @param {function} props.onClose - Callback when modal closes
15
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
16
+ */
17
+ const Modal = (props = {}, ...children) => {
18
+ const { tags } = window.Lightview || {};
19
+ const LVX = window.LightviewX || {};
20
+
21
+ if (!tags) return null;
22
+
23
+ const { dialog, div, shadowDOM } = tags;
24
+
25
+ const {
26
+ id,
27
+ open = false,
28
+ position,
29
+ onClose,
30
+ useShadow = false,
31
+ class: className = '',
32
+ ...rest
33
+ } = props;
34
+
35
+ const classes = ['modal'];
36
+ if (position === 'top') classes.push('modal-top');
37
+ else if (position === 'bottom') classes.push('modal-bottom');
38
+ else if (position === 'middle') classes.push('modal-middle');
39
+ if (className) classes.push(className);
40
+
41
+ const modalEl = dialog({
42
+ id,
43
+ class: classes.join(' '),
44
+ ...rest
45
+ }, ...children);
46
+
47
+ // Handle reactive open state
48
+ if (typeof open === 'function') {
49
+ const checkOpen = () => {
50
+ if (open()) {
51
+ modalEl.domEl?.showModal?.();
52
+ } else {
53
+ modalEl.domEl?.close?.();
54
+ }
55
+ };
56
+ // Set up effect after element is in DOM
57
+ setTimeout(checkOpen, 0);
58
+ }
59
+
60
+ // Check if we should use shadow DOM
61
+ let usesShadow = false;
62
+ if (LVX.shouldUseShadow) {
63
+ usesShadow = LVX.shouldUseShadow(useShadow);
64
+ } else {
65
+ usesShadow = useShadow === true;
66
+ }
67
+
68
+ if (usesShadow) {
69
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
70
+
71
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
72
+
73
+ return div({ class: 'contents' },
74
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
75
+ div({ 'data-theme': themeValue },
76
+ modalEl
77
+ )
78
+ )
79
+ );
80
+ }
81
+
82
+ return modalEl;
83
+ };
84
+
85
+ /**
86
+ * Modal Box - the content container
87
+ */
88
+ Modal.Box = (props = {}, ...children) => {
89
+ const { tags } = window.Lightview || {};
90
+ if (!tags) return null;
91
+
92
+ const { class: className = '', ...rest } = props;
93
+
94
+ return tags.div({
95
+ class: `modal-box ${className}`.trim(),
96
+ ...rest
97
+ }, ...children);
98
+ };
99
+
100
+ /**
101
+ * Modal Action - container for action buttons
102
+ */
103
+ Modal.Action = (props = {}, ...children) => {
104
+ const { tags } = window.Lightview || {};
105
+ if (!tags) return null;
106
+
107
+ const { class: className = '', ...rest } = props;
108
+
109
+ return tags.div({
110
+ class: `modal-action ${className}`.trim(),
111
+ ...rest
112
+ }, ...children);
113
+ };
114
+
115
+ /**
116
+ * Modal Backdrop - click to close
117
+ */
118
+ Modal.Backdrop = (props = {}) => {
119
+ const { tags } = window.Lightview || {};
120
+ if (!tags) return null;
121
+
122
+ return tags.form({ method: 'dialog', class: 'modal-backdrop' },
123
+ tags.button({}, 'close')
124
+ );
125
+ };
126
+
127
+ /**
128
+ * Open a modal by ID
129
+ */
130
+ Modal.open = (id) => {
131
+ document.getElementById(id)?.showModal?.();
132
+ };
133
+
134
+ /**
135
+ * Close a modal by ID
136
+ */
137
+ Modal.close = (id) => {
138
+ document.getElementById(id)?.close?.();
139
+ };
140
+
141
+ window.Lightview.tags.Modal = Modal;
142
+ window.Lightview.tags['Modal.Box'] = Modal.Box;
143
+ window.Lightview.tags['Modal.Action'] = Modal.Action;
144
+ window.Lightview.tags['Modal.Backdrop'] = Modal.Backdrop;
145
+
146
+ export default Modal;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Lightview Swap Component (DaisyUI)
3
+ * @see https://daisyui.com/components/swap/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Swap Component - toggle between two elements
10
+ * @param {Object} props
11
+ * @param {boolean|function} props.active - Control swap state
12
+ * @param {string} props.effect - 'rotate' | 'flip'
13
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
14
+ */
15
+ const Swap = (props = {}, ...children) => {
16
+ const { tags, signal } = window.Lightview || {};
17
+ const LVX = window.LightviewX || {};
18
+
19
+ if (!tags) return null;
20
+
21
+ const { label, input, div, shadowDOM } = tags;
22
+
23
+ const {
24
+ active = false,
25
+ effect,
26
+ useShadow,
27
+ class: className = '',
28
+ onChange,
29
+ ...rest
30
+ } = props;
31
+
32
+ const classes = ['swap'];
33
+ if (effect === 'rotate') classes.push('swap-rotate');
34
+ else if (effect === 'flip') classes.push('swap-flip');
35
+ if (className) classes.push(className);
36
+
37
+ // Handle reactive active state
38
+ const isActive = typeof active === 'function' ? active : () => active;
39
+
40
+ const swapEl = label({
41
+ class: () => {
42
+ const base = [...classes];
43
+ if (isActive()) base.push('swap-active');
44
+ return base.join(' ');
45
+ },
46
+ ...rest
47
+ },
48
+ input({
49
+ type: 'checkbox',
50
+ checked: isActive,
51
+ onchange: (e) => {
52
+ if (onChange) onChange(e.target.checked);
53
+ }
54
+ }),
55
+ ...children
56
+ );
57
+
58
+ // Check if we should use shadow DOM
59
+ let usesShadow = false;
60
+ if (LVX.shouldUseShadow) {
61
+ usesShadow = LVX.shouldUseShadow(useShadow);
62
+ } else {
63
+ usesShadow = useShadow === true;
64
+ }
65
+
66
+ if (usesShadow) {
67
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
68
+
69
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
70
+
71
+ return div({ class: 'contents' },
72
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
73
+ div({ 'data-theme': themeValue },
74
+ swapEl
75
+ )
76
+ )
77
+ );
78
+ }
79
+
80
+ return swapEl;
81
+ };
82
+
83
+ /**
84
+ * Swap On - visible when active
85
+ */
86
+ Swap.On = (props = {}, ...children) => {
87
+ const { tags } = window.Lightview || {};
88
+ if (!tags) return null;
89
+
90
+ const { class: className = '', ...rest } = props;
91
+
92
+ return tags.div({
93
+ class: `swap-on ${className}`.trim(),
94
+ ...rest
95
+ }, ...children);
96
+ };
97
+
98
+ /**
99
+ * Swap Off - visible when inactive
100
+ */
101
+ Swap.Off = (props = {}, ...children) => {
102
+ const { tags } = window.Lightview || {};
103
+ if (!tags) return null;
104
+
105
+ const { class: className = '', ...rest } = props;
106
+
107
+ return tags.div({
108
+ class: `swap-off ${className}`.trim(),
109
+ ...rest
110
+ }, ...children);
111
+ };
112
+
113
+ const tags = window.Lightview.tags;
114
+ tags.Swap = Swap;
115
+ tags['Swap.On'] = Swap.On;
116
+ tags['Swap.Off'] = Swap.Off;
117
+
118
+ export default Swap;