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.
Files changed (224) hide show
  1. package/.agent/workflows/daisyui-component-migration.md +155 -0
  2. package/.codacy/cli.sh +149 -0
  3. package/.codacy/codacy.yaml +15 -0
  4. package/.github/instructions/codacy.instructions.md +72 -0
  5. package/.wranglerignore +21 -0
  6. package/README.md +1331 -21
  7. package/_headers +4 -0
  8. package/build.js +70 -0
  9. package/components/actions/button.js +151 -0
  10. package/components/actions/dropdown.js +120 -0
  11. package/components/actions/modal.js +146 -0
  12. package/components/actions/swap.js +118 -0
  13. package/components/daisyui.js +288 -0
  14. package/components/data-display/accordion.js +128 -0
  15. package/components/data-display/alert.js +112 -0
  16. package/components/data-display/avatar.js +170 -0
  17. package/components/data-display/badge.js +82 -0
  18. package/components/data-display/card.js +151 -0
  19. package/components/data-display/carousel.js +94 -0
  20. package/components/data-display/chart.js +220 -0
  21. package/components/data-display/chat.js +128 -0
  22. package/components/data-display/collapse.js +103 -0
  23. package/components/data-display/countdown.js +69 -0
  24. package/components/data-display/diff.js +111 -0
  25. package/components/data-display/kbd.js +65 -0
  26. package/components/data-display/loading.js +75 -0
  27. package/components/data-display/progress.js +79 -0
  28. package/components/data-display/radial-progress.js +88 -0
  29. package/components/data-display/skeleton.js +66 -0
  30. package/components/data-display/stats.js +159 -0
  31. package/components/data-display/table.js +146 -0
  32. package/components/data-display/timeline.js +146 -0
  33. package/components/data-display/toast.js +72 -0
  34. package/components/data-display/tooltip.js +74 -0
  35. package/components/data-input/checkbox.js +253 -0
  36. package/components/data-input/file-input.js +224 -0
  37. package/components/data-input/input.js +264 -0
  38. package/components/data-input/radio.js +338 -0
  39. package/components/data-input/range.js +204 -0
  40. package/components/data-input/rating.js +219 -0
  41. package/components/data-input/select.js +287 -0
  42. package/components/data-input/textarea.js +287 -0
  43. package/components/data-input/toggle.js +201 -0
  44. package/components/index.js +137 -0
  45. package/components/layout/divider.js +72 -0
  46. package/components/layout/drawer.js +142 -0
  47. package/components/layout/footer.js +100 -0
  48. package/components/layout/hero.js +109 -0
  49. package/components/layout/indicator.js +90 -0
  50. package/components/layout/join.js +78 -0
  51. package/components/layout/navbar.js +110 -0
  52. package/components/navigation/breadcrumbs.js +91 -0
  53. package/components/navigation/dock.js +103 -0
  54. package/components/navigation/menu.js +126 -0
  55. package/components/navigation/pagination.js +105 -0
  56. package/components/navigation/steps.js +89 -0
  57. package/components/navigation/tabs.css +177 -0
  58. package/components/navigation/tabs.js +123 -0
  59. package/components/theme/theme-switch.css +65 -0
  60. package/components/theme/theme-switch.js +177 -0
  61. package/docs/about.html +164 -0
  62. package/docs/api/computed.html +184 -0
  63. package/docs/api/effects.html +173 -0
  64. package/docs/api/elements.html +180 -0
  65. package/docs/api/enhance.html +225 -0
  66. package/docs/api/hypermedia.html +165 -0
  67. package/docs/api/index.html +178 -0
  68. package/docs/api/nav.html +18 -0
  69. package/docs/api/signals.html +136 -0
  70. package/docs/api/state.html +217 -0
  71. package/docs/assets/images/logo-favicon.svg +42 -0
  72. package/docs/assets/images/logo-static.svg +40 -0
  73. package/docs/assets/images/logo.svg +66 -0
  74. package/docs/assets/js/examplify.js +395 -0
  75. package/docs/assets/styles/site.css +1102 -0
  76. package/docs/assets/styles/themes.css +236 -0
  77. package/docs/components/accordion.html +439 -0
  78. package/docs/components/alert.html +528 -0
  79. package/docs/components/avatar.html +586 -0
  80. package/docs/components/badge.html +531 -0
  81. package/docs/components/breadcrumbs.html +278 -0
  82. package/docs/components/button.html +579 -0
  83. package/docs/components/card.html +561 -0
  84. package/docs/components/carousel.html +286 -0
  85. package/docs/components/chart-area.html +702 -0
  86. package/docs/components/chart-bar.html +782 -0
  87. package/docs/components/chart-column.html +735 -0
  88. package/docs/components/chart-line.html +794 -0
  89. package/docs/components/chart-pie.html +823 -0
  90. package/docs/components/chart.html +612 -0
  91. package/docs/components/chat.html +547 -0
  92. package/docs/components/checkbox.html +641 -0
  93. package/docs/components/collapse.html +536 -0
  94. package/docs/components/component-nav.html +53 -0
  95. package/docs/components/countdown.html +470 -0
  96. package/docs/components/diff.html +245 -0
  97. package/docs/components/divider.html +240 -0
  98. package/docs/components/dock.html +277 -0
  99. package/docs/components/drawer.html +515 -0
  100. package/docs/components/dropdown.html +479 -0
  101. package/docs/components/file-input.html +591 -0
  102. package/docs/components/footer.html +301 -0
  103. package/docs/components/gallery.html +504 -0
  104. package/docs/components/hero.html +264 -0
  105. package/docs/components/index.css +840 -0
  106. package/docs/components/index.html +735 -0
  107. package/docs/components/indicator.html +342 -0
  108. package/docs/components/input.html +644 -0
  109. package/docs/components/join.html +285 -0
  110. package/docs/components/kbd.html +322 -0
  111. package/docs/components/loading.html +521 -0
  112. package/docs/components/menu.html +461 -0
  113. package/docs/components/modal.html +639 -0
  114. package/docs/components/navbar.html +321 -0
  115. package/docs/components/pagination.html +279 -0
  116. package/docs/components/progress.html +514 -0
  117. package/docs/components/radial-progress.html +434 -0
  118. package/docs/components/radio.html +655 -0
  119. package/docs/components/range.html +611 -0
  120. package/docs/components/rating.html +642 -0
  121. package/docs/components/select.html +696 -0
  122. package/docs/components/sidebar-setup.js +93 -0
  123. package/docs/components/skeleton.html +447 -0
  124. package/docs/components/spinner.html +68 -0
  125. package/docs/components/stats.html +486 -0
  126. package/docs/components/steps.html +356 -0
  127. package/docs/components/swap.html +517 -0
  128. package/docs/components/switch.html +68 -0
  129. package/docs/components/table.html +668 -0
  130. package/docs/components/tabs.html +506 -0
  131. package/docs/components/text-input.html +68 -0
  132. package/docs/components/textarea.html +603 -0
  133. package/docs/components/timeline.html +487 -0
  134. package/docs/components/toast.html +474 -0
  135. package/docs/components/toggle.html +564 -0
  136. package/docs/components/tooltip.html +423 -0
  137. package/docs/examples/getting-started-example.html +40 -0
  138. package/docs/examples/index.html +93 -0
  139. package/docs/getting-started/index.html +739 -0
  140. package/docs/getting-started/reviews.html +23 -0
  141. package/docs/getting-started/reviews.odom +108 -0
  142. package/docs/getting-started/reviews.vdom +84 -0
  143. package/docs/index.html +134 -0
  144. package/docs/playground.html +416 -0
  145. package/docs/router.html +285 -0
  146. package/docs/styles/index.html +190 -0
  147. package/functions/_middleware.js +32 -0
  148. package/index.html +309 -0
  149. package/lightview-router.js +364 -0
  150. package/lightview-x.js +1577 -0
  151. package/lightview.js +658 -1109
  152. package/lightview.js.backup +793 -0
  153. package/middleware/locale.js +25 -0
  154. package/middleware/markdown.js +44 -0
  155. package/middleware/notFound.js +37 -0
  156. package/package.json +27 -41
  157. package/watch.js +92 -0
  158. package/wrangler.toml +12 -0
  159. package/.idea/lightview.iml +0 -12
  160. package/.idea/modules.xml +0 -8
  161. package/.idea/vcs.xml +0 -6
  162. package/LICENSE +0 -21
  163. package/codepen-no-tabs-embed.css +0 -2
  164. package/components/chart/chart.html +0 -17
  165. package/components/chart/example.html +0 -32
  166. package/components/chart.html +0 -83
  167. package/components/components.js +0 -113
  168. package/components/gantt/example.html +0 -22
  169. package/components/gantt/gantt.html +0 -42
  170. package/components/gauge/example.html +0 -28
  171. package/components/gauge/gauge.html +0 -20
  172. package/components/gauge.html +0 -60
  173. package/components/orgchart/example.html +0 -25
  174. package/components/orgchart/orgchart.html +0 -41
  175. package/components/repl/code-editor.html +0 -64
  176. package/components/repl/editor.html +0 -37
  177. package/components/repl/editorjs-inline-tool/index.js +0 -3
  178. package/components/repl/editorjs-inline-tool/inline-tools.js +0 -28
  179. package/components/repl/editorjs-inline-tool/tool.js +0 -175
  180. package/components/repl/repl-with-wysiwyg.html +0 -355
  181. package/components/repl/repl.html +0 -345
  182. package/components/repl/sup.js +0 -44
  183. package/components/repl/wysiwyg-repl.html +0 -258
  184. package/components/timeline/example.html +0 -33
  185. package/components/timeline/timeline.html +0 -44
  186. package/components/timeline.html +0 -81
  187. package/examples/anchor.html +0 -11
  188. package/examples/chart.html +0 -34
  189. package/examples/counter.html +0 -26
  190. package/examples/counter.test.mjs +0 -47
  191. package/examples/counter2.html +0 -26
  192. package/examples/directives.html +0 -79
  193. package/examples/foreign.html +0 -50
  194. package/examples/forgeinform.html +0 -98
  195. package/examples/form.html +0 -61
  196. package/examples/gauge.html +0 -18
  197. package/examples/invalid-template-literals.html +0 -44
  198. package/examples/medium/remote.html +0 -60
  199. package/examples/message.html +0 -18
  200. package/examples/nested.html +0 -11
  201. package/examples/object-bound-form.html +0 -34
  202. package/examples/remote-server.js +0 -51
  203. package/examples/remote.html +0 -34
  204. package/examples/remote.json +0 -1
  205. package/examples/scratch.html +0 -69
  206. package/examples/sensors/index.html +0 -30
  207. package/examples/sensors/sensor-server.js +0 -30
  208. package/examples/shared.html +0 -41
  209. package/examples/template.html +0 -33
  210. package/examples/timeline.html +0 -21
  211. package/examples/todo.html +0 -38
  212. package/examples/top.html +0 -10
  213. package/examples/types.html +0 -94
  214. package/examples/xor.html +0 -62
  215. package/jest-puppeteer.config.js +0 -5
  216. package/jest.config.json +0 -12
  217. package/sites/client.html +0 -48
  218. package/sites/index.html +0 -247
  219. package/test/basic.html +0 -93
  220. package/test/basic.test.mjs +0 -315
  221. package/test/extended.html +0 -29
  222. package/test/extended.test.mjs +0 -448
  223. package/types.js +0 -534
  224. package/unsplash.key +0 -1
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Lightview Components - DaisyUI Integration
3
+ * This module ensures DaisyUI CSS is loaded and provides utilities for components
4
+ */
5
+
6
+ const DAISYUI_CDN = 'https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.min.css';
7
+ const TAILWIND_CDN = 'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4';
8
+
9
+ let daisyLoaded = false;
10
+ let tailwindLoaded = false;
11
+
12
+ // ============= ADOPTED STYLESHEETS SUPPORT =============
13
+ // Cached CSSStyleSheet objects for efficient shadow DOM usage
14
+
15
+ let daisyStyleSheet = null;
16
+ let daisyStyleSheetPromise = null;
17
+ const componentStyleSheets = new Map(); // Cache for component CSS
18
+ const componentStyleSheetPromises = new Map();
19
+
20
+ /**
21
+ * Get a CSSStyleSheet for DaisyUI (for use with adoptedStyleSheets)
22
+ * Fetches and parses CSS once, caches for reuse
23
+ * @returns {Promise<CSSStyleSheet>}
24
+ */
25
+ export const getDaisyStyleSheet = async () => {
26
+ // Return cached sheet if available
27
+ if (daisyStyleSheet) {
28
+ return daisyStyleSheet;
29
+ }
30
+
31
+ // Return existing promise if fetch is in progress
32
+ if (daisyStyleSheetPromise) {
33
+ return daisyStyleSheetPromise;
34
+ }
35
+
36
+ // Fetch and create the stylesheet
37
+ daisyStyleSheetPromise = (async () => {
38
+ try {
39
+ const response = await fetch(DAISYUI_CDN);
40
+ if (!response.ok) {
41
+ throw new Error(`Failed to fetch DaisyUI CSS: ${response.status}`);
42
+ }
43
+ const cssText = await response.text();
44
+
45
+ const sheet = new CSSStyleSheet();
46
+ sheet.replaceSync(cssText);
47
+ daisyStyleSheet = sheet;
48
+ return sheet;
49
+ } catch (e) {
50
+ console.error('Lightview: Failed to create DaisyUI adoptedStyleSheet:', e);
51
+ daisyStyleSheetPromise = null; // Allow retry
52
+ throw e;
53
+ }
54
+ })();
55
+
56
+ return daisyStyleSheetPromise;
57
+ };
58
+
59
+ /**
60
+ * Get a CSSStyleSheet for a component's CSS file
61
+ * @param {string} cssUrl - URL to the component's CSS file
62
+ * @returns {Promise<CSSStyleSheet>}
63
+ */
64
+ export const getComponentStyleSheet = async (cssUrl) => {
65
+ // Return cached sheet if available
66
+ if (componentStyleSheets.has(cssUrl)) {
67
+ return componentStyleSheets.get(cssUrl);
68
+ }
69
+
70
+ // Return existing promise if fetch is in progress
71
+ if (componentStyleSheetPromises.has(cssUrl)) {
72
+ return componentStyleSheetPromises.get(cssUrl);
73
+ }
74
+
75
+ // Fetch and create the stylesheet
76
+ const promise = (async () => {
77
+ try {
78
+ const response = await fetch(cssUrl);
79
+ if (!response.ok) {
80
+ throw new Error(`Failed to fetch component CSS: ${response.status}`);
81
+ }
82
+ const cssText = await response.text();
83
+
84
+ const sheet = new CSSStyleSheet();
85
+ sheet.replaceSync(cssText);
86
+ componentStyleSheets.set(cssUrl, sheet);
87
+ return sheet;
88
+ } catch (e) {
89
+ console.error(`Lightview: Failed to create adoptedStyleSheet for ${cssUrl}:`, e);
90
+ componentStyleSheetPromises.delete(cssUrl); // Allow retry
91
+ throw e;
92
+ }
93
+ })();
94
+
95
+ componentStyleSheetPromises.set(cssUrl, promise);
96
+ return promise;
97
+ };
98
+
99
+ /**
100
+ * Synchronously get cached DaisyUI stylesheet (returns null if not yet loaded)
101
+ * Use getDaisyStyleSheet() first to ensure it's loaded
102
+ * @returns {CSSStyleSheet|null}
103
+ */
104
+ export const getDaisyStyleSheetSync = () => daisyStyleSheet;
105
+
106
+ /**
107
+ * Synchronously get cached component stylesheet (returns null if not yet loaded)
108
+ * @param {string} cssUrl
109
+ * @returns {CSSStyleSheet|null}
110
+ */
111
+ export const getComponentStyleSheetSync = (cssUrl) => componentStyleSheets.get(cssUrl) || null;
112
+
113
+ /**
114
+ * Pre-load all stylesheets needed for shadow DOM components
115
+ * Call this early in your app initialization for best performance
116
+ * @param {string[]} componentCssUrls - Optional array of component CSS URLs to preload
117
+ * @returns {Promise<void>}
118
+ */
119
+ export const preloadShadowStyles = async (componentCssUrls = []) => {
120
+ const promises = [getDaisyStyleSheet()];
121
+ for (const url of componentCssUrls) {
122
+ promises.push(getComponentStyleSheet(url));
123
+ }
124
+ await Promise.all(promises);
125
+ };
126
+
127
+ /**
128
+ * Ensure DaisyUI CSS is loaded from CDN
129
+ */
130
+ export const ensureDaisyUI = () => {
131
+ if (daisyLoaded) return Promise.resolve();
132
+
133
+ return new Promise((resolve) => {
134
+ // Check if already loaded
135
+ if (document.querySelector('link[href*="daisyui"]')) {
136
+ daisyLoaded = true;
137
+ resolve();
138
+ return;
139
+ }
140
+
141
+ const link = document.createElement('link');
142
+ link.rel = 'stylesheet';
143
+ link.href = DAISYUI_CDN;
144
+ link.id = 'daisyui-styles';
145
+ link.onload = () => {
146
+ daisyLoaded = true;
147
+ resolve();
148
+ };
149
+ link.onerror = () => {
150
+ console.warn('Failed to load DaisyUI from CDN');
151
+ resolve();
152
+ };
153
+ document.head.appendChild(link);
154
+ });
155
+ };
156
+
157
+ /**
158
+ * Ensure Tailwind CSS Browser is loaded (for utility classes)
159
+ */
160
+ export const ensureTailwind = () => {
161
+ if (tailwindLoaded) return Promise.resolve();
162
+
163
+ return new Promise((resolve) => {
164
+ // Check if already loaded
165
+ if (document.querySelector('script[src*="tailwindcss/browser"]')) {
166
+ tailwindLoaded = true;
167
+ resolve();
168
+ return;
169
+ }
170
+
171
+ const script = document.createElement('script');
172
+ script.src = TAILWIND_CDN;
173
+ script.id = 'tailwind-browser';
174
+ script.onload = () => {
175
+ tailwindLoaded = true;
176
+ resolve();
177
+ };
178
+ script.onerror = () => {
179
+ console.warn('Failed to load Tailwind Browser from CDN');
180
+ resolve();
181
+ };
182
+ document.head.appendChild(script);
183
+ });
184
+ };
185
+
186
+ /**
187
+ * Ensure all themes are loaded
188
+ */
189
+ export const ensureThemes = () => {
190
+ return new Promise((resolve) => {
191
+ if (document.querySelector('link[href*="daisyui"][href*="themes"]')) {
192
+ resolve();
193
+ return;
194
+ }
195
+
196
+ const link = document.createElement('link');
197
+ link.rel = 'stylesheet';
198
+ link.href = `${DAISYUI_CDN}/themes.css`;
199
+ link.id = 'daisyui-themes';
200
+ link.onload = () => resolve();
201
+ link.onerror = () => resolve();
202
+ document.head.appendChild(link);
203
+ });
204
+ };
205
+
206
+ /**
207
+ * Initialize DaisyUI with optional Tailwind utilities
208
+ * @param {Object} options
209
+ * @param {boolean} options.tailwind - Whether to load Tailwind Browser (default: true)
210
+ * @param {boolean} options.themes - Whether to load all themes (default: false)
211
+ */
212
+ export const init = async (options = {}) => {
213
+ const { tailwind = true, themes = false } = options;
214
+
215
+ const promises = [ensureDaisyUI()];
216
+ if (tailwind) promises.push(ensureTailwind());
217
+ if (themes) promises.push(ensureThemes());
218
+
219
+ await Promise.all(promises);
220
+ };
221
+
222
+ /**
223
+ * Set the current theme
224
+ * @param {string} theme - Theme name (e.g., 'light', 'dark', 'cupcake', 'cyberpunk')
225
+ */
226
+ export const setTheme = (theme) => {
227
+ document.documentElement.setAttribute('data-theme', theme);
228
+ };
229
+
230
+ /**
231
+ * Get the current theme
232
+ * @returns {string}
233
+ */
234
+ export const getTheme = () => {
235
+ return document.documentElement.getAttribute('data-theme') || 'light';
236
+ };
237
+
238
+ /**
239
+ * Toggle between light and dark themes
240
+ */
241
+ export const toggleTheme = () => {
242
+ const current = getTheme();
243
+ setTheme(current === 'dark' ? 'light' : 'dark');
244
+ };
245
+
246
+ /**
247
+ * Available DaisyUI themes
248
+ */
249
+ export const themes = [
250
+ 'light', 'dark', 'cupcake', 'bumblebee', 'emerald', 'corporate',
251
+ 'synthwave', 'retro', 'cyberpunk', 'valentine', 'halloween', 'garden',
252
+ 'forest', 'aqua', 'lofi', 'pastel', 'fantasy', 'wireframe', 'black',
253
+ 'luxury', 'dracula', 'cmyk', 'autumn', 'business', 'acid', 'lemonade',
254
+ 'night', 'coffee', 'winter', 'dim', 'nord', 'sunset'
255
+ ];
256
+
257
+ /**
258
+ * Color variants available for components
259
+ */
260
+ export const colors = ['primary', 'secondary', 'accent', 'neutral', 'info', 'success', 'warning', 'error'];
261
+
262
+ /**
263
+ * Size variants available for components
264
+ */
265
+ export const sizes = ['xs', 'sm', 'md', 'lg'];
266
+
267
+ // Note: Auto-initialization removed. Use one of:
268
+ // - LightviewX.initComponents() for shadow DOM components (recommended)
269
+ // - daisyui.init() for light DOM usage with global DaisyUI styles
270
+
271
+ export default {
272
+ init,
273
+ ensureDaisyUI,
274
+ ensureTailwind,
275
+ ensureThemes,
276
+ setTheme,
277
+ getTheme,
278
+ toggleTheme,
279
+ themes,
280
+ colors,
281
+ sizes,
282
+ // Shadow DOM / Adopted Stylesheets
283
+ getDaisyStyleSheet,
284
+ getComponentStyleSheet,
285
+ getDaisyStyleSheetSync,
286
+ getComponentStyleSheetSync,
287
+ preloadShadowStyles
288
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Lightview Accordion Component (DaisyUI)
3
+ * @see https://daisyui.com/components/accordion/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Accordion - show/hide content, one item at a time
10
+ * @param {Object} props
11
+ * @param {boolean} props.join - Use join style (connected items)
12
+ * @param {string} props.icon - 'arrow' | 'plus'
13
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
14
+ */
15
+ const Accordion = (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
+ join = false,
25
+ useShadow,
26
+ class: className = '',
27
+ ...rest
28
+ } = props;
29
+
30
+ const classes = join ? ['join join-vertical w-full'] : ['space-y-2'];
31
+ if (className) classes.push(className);
32
+
33
+ const accordionEl = div({ class: classes.join(' '), ...rest }, ...children);
34
+
35
+ // Check if we should use shadow DOM
36
+ let usesShadow = false;
37
+ if (LVX.shouldUseShadow) {
38
+ usesShadow = LVX.shouldUseShadow(useShadow);
39
+ } else {
40
+ usesShadow = useShadow === true;
41
+ }
42
+
43
+ if (usesShadow) {
44
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
45
+
46
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
47
+
48
+ return div({ class: 'contents' },
49
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
50
+ div({ 'data-theme': themeValue },
51
+ accordionEl
52
+ )
53
+ )
54
+ );
55
+ }
56
+
57
+ return accordionEl;
58
+ };
59
+
60
+ /**
61
+ * Accordion Item
62
+ */
63
+ Accordion.Item = (props = {}, ...children) => {
64
+ const { tags } = window.Lightview || {};
65
+ if (!tags) return null;
66
+
67
+ const { div, input } = tags;
68
+
69
+ const {
70
+ name = 'accordion',
71
+ icon = 'arrow',
72
+ join = false,
73
+ checked = false,
74
+ class: className = '',
75
+ ...rest
76
+ } = props;
77
+
78
+ const classes = ['collapse'];
79
+ if (icon === 'arrow') classes.push('collapse-arrow');
80
+ else if (icon === 'plus') classes.push('collapse-plus');
81
+ classes.push('bg-base-100');
82
+ if (join) classes.push('join-item border-base-300 border');
83
+ else classes.push('border border-base-300 rounded-box');
84
+ if (className) classes.push(className);
85
+
86
+ return div({ class: classes.join(' '), ...rest },
87
+ input({ type: 'radio', name, checked }),
88
+ ...children
89
+ );
90
+ };
91
+
92
+ /**
93
+ * Accordion Title
94
+ */
95
+ Accordion.Title = (props = {}, ...children) => {
96
+ const { tags } = window.Lightview || {};
97
+ if (!tags) return null;
98
+
99
+ const { class: className = '', ...rest } = props;
100
+
101
+ return tags.div({
102
+ class: `collapse-title font-semibold ${className}`.trim(),
103
+ ...rest
104
+ }, ...children);
105
+ };
106
+
107
+ /**
108
+ * Accordion Content
109
+ */
110
+ Accordion.Content = (props = {}, ...children) => {
111
+ const { tags } = window.Lightview || {};
112
+ if (!tags) return null;
113
+
114
+ const { class: className = '', ...rest } = props;
115
+
116
+ return tags.div({
117
+ class: `collapse-content text-sm ${className}`.trim(),
118
+ ...rest
119
+ }, ...children);
120
+ };
121
+
122
+ const tags = window.Lightview.tags;
123
+ tags.Accordion = Accordion;
124
+ tags['Accordion.Item'] = Accordion.Item;
125
+ tags['Accordion.Title'] = Accordion.Title;
126
+ tags['Accordion.Content'] = Accordion.Content;
127
+
128
+ export default Accordion;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Lightview Alert Component (DaisyUI)
3
+ * @see https://daisyui.com/components/alert/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Alert Component
10
+ * @param {Object} props
11
+ * @param {string} props.color - 'info' | 'success' | 'warning' | 'error'
12
+ * @param {boolean} props.soft - Use soft/light variant
13
+ * @param {boolean} props.dash - Use dashed border
14
+ * @param {boolean} props.outline - Use outline style
15
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
16
+ */
17
+ const Alert = (props = {}, ...children) => {
18
+ const { tags } = window.Lightview || {};
19
+ const LVX = window.LightviewX || {};
20
+
21
+ if (!tags) return null;
22
+
23
+ const { div, shadowDOM } = tags;
24
+
25
+ const {
26
+ color,
27
+ soft = false,
28
+ dash = false,
29
+ outline = false,
30
+ icon,
31
+ useShadow,
32
+ class: className = '',
33
+ ...rest
34
+ } = props;
35
+
36
+ const classes = ['alert'];
37
+ if (color) classes.push(`alert-${color}`);
38
+ if (soft) classes.push('alert-soft');
39
+ if (dash) classes.push('alert-dash');
40
+ if (outline) classes.push('alert-outline');
41
+ if (className) classes.push(className);
42
+
43
+ const iconSvg = icon ? getAlertIcon(icon || color) : null;
44
+
45
+ let alertEl;
46
+ if (iconSvg) {
47
+ alertEl = div({ role: 'alert', class: classes.join(' '), ...rest },
48
+ iconSvg,
49
+ ...children
50
+ );
51
+ } else {
52
+ alertEl = div({ role: 'alert', class: classes.join(' '), ...rest }, ...children);
53
+ }
54
+
55
+ // Check if we should use shadow DOM
56
+ let usesShadow = false;
57
+ if (LVX.shouldUseShadow) {
58
+ usesShadow = LVX.shouldUseShadow(useShadow);
59
+ } else {
60
+ usesShadow = useShadow === true;
61
+ }
62
+
63
+ if (usesShadow) {
64
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
65
+
66
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
67
+
68
+ return div({ class: 'contents' },
69
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
70
+ div({ 'data-theme': themeValue },
71
+ alertEl
72
+ )
73
+ )
74
+ );
75
+ }
76
+
77
+ return alertEl;
78
+ };
79
+
80
+ function getAlertIcon(type) {
81
+ const { tags } = window.Lightview || {};
82
+ if (!tags) return null;
83
+
84
+ const icons = {
85
+ info: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>',
86
+ success: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>',
87
+ warning: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>',
88
+ error: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>'
89
+ };
90
+
91
+ if (!icons[type]) return null;
92
+
93
+ return tags.svg({
94
+ xmlns: 'http://www.w3.org/2000/svg',
95
+ class: 'h-6 w-6 shrink-0 stroke-current',
96
+ fill: 'none',
97
+ viewBox: '0 0 24 24',
98
+ innerHTML: icons[type]
99
+ });
100
+ }
101
+
102
+ window.Lightview.tags.Alert = Alert;
103
+
104
+ // Register as Custom Element
105
+ if (window.LightviewX?.createCustomElement) {
106
+ const AlertElement = window.LightviewX.createCustomElement(Alert);
107
+ if (!customElements.get('lv-alert')) {
108
+ customElements.define('lv-alert', AlertElement);
109
+ }
110
+ }
111
+
112
+ export default Alert;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Lightview Avatar Component (DaisyUI)
3
+ * @see https://daisyui.com/components/avatar/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Avatar Component
10
+ * @param {Object} props
11
+ * @param {string} props.src - Image source URL
12
+ * @param {string} props.alt - Alt text for image
13
+ * @param {string} props.placeholder - Text to show when no image
14
+ * @param {string} props.size - Size in pixels or class
15
+ * @param {string} props.shape - 'circle' | 'rounded' | 'squircle' | 'hexagon' | 'triangle'
16
+ * @param {boolean} props.ring - Add ring border
17
+ * @param {string} props.ringColor - Ring color class
18
+ * @param {boolean} props.online - Show online indicator
19
+ * @param {boolean} props.offline - Show offline indicator
20
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
21
+ */
22
+ const Avatar = (props = {}, ...children) => {
23
+ const { tags } = window.Lightview || {};
24
+ const LVX = window.LightviewX || {};
25
+
26
+ if (!tags) return null;
27
+
28
+ const { div, img, shadowDOM } = tags;
29
+
30
+ const {
31
+ src,
32
+ alt = 'Avatar',
33
+ placeholder,
34
+ size = 'w-12',
35
+ shape = 'circle',
36
+ ring = false,
37
+ ringColor = 'ring-primary',
38
+ online = false,
39
+ offline = false,
40
+ useShadow,
41
+ class: className = '',
42
+ ...rest
43
+ } = props;
44
+
45
+ const wrapperClasses = ['avatar'];
46
+ if (online) wrapperClasses.push('online');
47
+ if (offline) wrapperClasses.push('offline');
48
+ if (placeholder && !src) wrapperClasses.push('placeholder');
49
+ if (className) wrapperClasses.push(className);
50
+
51
+ const innerClasses = [];
52
+ const innerStyle = {};
53
+
54
+ // Check if size is a Tailwind class (w-*) or a direct measurement
55
+ if (size) {
56
+ if (/^w-(\d+)$/.test(size)) {
57
+ // Workaround: Map w- classes to explicit styles as Tailwind classes might be missing
58
+ const num = parseInt(size.split('-')[1]);
59
+ const remValue = num * 0.25;
60
+ innerStyle.width = `${remValue}rem`;
61
+ innerStyle.height = `${remValue}rem`;
62
+ innerClasses.push(size);
63
+ } else if (/^w-/.test(size)) {
64
+ innerClasses.push(size);
65
+ } else {
66
+ // Treat as direct measurement (e.g., 64px, 4rem)
67
+ innerStyle.width = size;
68
+ innerStyle.height = size;
69
+ }
70
+ } else {
71
+ innerClasses.push('w-12');
72
+ innerStyle.width = '3rem';
73
+ innerStyle.height = '3rem';
74
+ }
75
+
76
+ if (ring) {
77
+ innerClasses.push('ring', ringColor, 'ring-offset-base-100', 'ring-offset-2');
78
+ }
79
+
80
+ // Shape classes
81
+ if (shape === 'circle') innerClasses.push('rounded-full');
82
+ else if (shape === 'rounded') innerClasses.push('rounded');
83
+ else if (shape === 'squircle') innerClasses.push('mask mask-squircle');
84
+ else if (shape === 'hexagon') innerClasses.push('mask mask-hexagon');
85
+ else if (shape === 'triangle') innerClasses.push('mask mask-triangle');
86
+
87
+ let avatarEl;
88
+ if (src) {
89
+ avatarEl = div({ class: wrapperClasses.join(' '), ...rest },
90
+ div({ class: innerClasses.join(' '), style: innerStyle },
91
+ img({ src, alt, style: 'width: 100%; height: 100%; object-fit: cover;' })
92
+ )
93
+ );
94
+ } else {
95
+ // Placeholder avatar
96
+ // Use flexbox to center the placeholder text
97
+ innerClasses.push('bg-neutral text-neutral-content flex items-center justify-center');
98
+
99
+ // Support custom background/text colors via CSS variables if provided in style
100
+ innerStyle.backgroundColor = 'var(--lv-avatar-bg)';
101
+ innerStyle.color = 'var(--lv-avatar-text)';
102
+ innerStyle.display = 'flex';
103
+ innerStyle.alignItems = 'center';
104
+ innerStyle.justifyContent = 'center';
105
+
106
+ avatarEl = div({ class: wrapperClasses.join(' '), ...rest },
107
+ div({
108
+ class: innerClasses.join(' '),
109
+ style: innerStyle
110
+ },
111
+ tags.span({}, placeholder || children[0] || '?')
112
+ )
113
+ );
114
+ }
115
+
116
+ // Check if we should use shadow DOM
117
+ let usesShadow = false;
118
+ if (LVX.shouldUseShadow) {
119
+ usesShadow = LVX.shouldUseShadow(useShadow);
120
+ } else {
121
+ usesShadow = useShadow === true;
122
+ }
123
+
124
+ if (usesShadow) {
125
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
126
+
127
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
128
+
129
+ return div({ class: 'contents' },
130
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
131
+ div({ 'data-theme': themeValue },
132
+ avatarEl
133
+ )
134
+ )
135
+ );
136
+ }
137
+
138
+ return avatarEl;
139
+ };
140
+
141
+ /**
142
+ * Avatar Group
143
+ */
144
+ Avatar.Group = (props = {}, ...children) => {
145
+ const { tags } = window.Lightview || {};
146
+ if (!tags) return null;
147
+
148
+ const { class: className = '', ...rest } = props;
149
+
150
+ return tags.div({
151
+ class: `avatar-group -space-x-6 rtl:space-x-reverse ${className}`.trim(),
152
+ ...rest
153
+ }, ...children);
154
+ };
155
+
156
+ const tags = window.Lightview.tags;
157
+ tags.Avatar = Avatar;
158
+ tags['Avatar.Group'] = Avatar.Group;
159
+
160
+ // Register as Custom Elements
161
+ if (window.LightviewX?.createCustomElement) {
162
+ if (!customElements.get('lv-avatar')) {
163
+ customElements.define('lv-avatar', window.LightviewX.createCustomElement(Avatar));
164
+ }
165
+ if (!customElements.get('lv-avatar-group')) {
166
+ customElements.define('lv-avatar-group', window.LightviewX.createCustomElement(Avatar.Group));
167
+ }
168
+ }
169
+
170
+ export default Avatar;