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,287 @@
1
+ /**
2
+ * Lightview Components - Textarea
3
+ * A multi-line text input component using DaisyUI 5 styling with validation support
4
+ * @see https://daisyui.com/components/textarea/
5
+ *
6
+ * Uses DaisyUI's fieldset pattern:
7
+ * <fieldset class="fieldset">
8
+ * <legend class="fieldset-legend">Label</legend>
9
+ * <textarea class="textarea" />
10
+ * <p class="label">Helper text</p>
11
+ * </fieldset>
12
+ */
13
+
14
+ import '../daisyui.js';
15
+
16
+ /**
17
+ * Textarea Component
18
+ * @param {Object} props - Textarea properties
19
+ * @param {string|Signal} props.value - Textarea value (controlled)
20
+ * @param {string} props.defaultValue - Default value (uncontrolled)
21
+ * @param {string} props.placeholder - Placeholder text
22
+ * @param {string} props.size - 'xs' | 'sm' | 'md' | 'lg' (default: 'md')
23
+ * @param {string} props.color - 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
24
+ * @param {boolean} props.ghost - Ghost style (no background)
25
+ * @param {boolean} props.disabled - Disable textarea
26
+ * @param {boolean} props.required - Required field
27
+ * @param {boolean} props.readOnly - Make read-only
28
+ * @param {string} props.label - Label text (rendered as fieldset legend)
29
+ * @param {string} props.helper - Helper text (rendered below textarea)
30
+ * @param {string|Function} props.error - Error message (string or validation function)
31
+ * @param {Function} props.validate - Validation function (value) => errorMessage | null
32
+ * @param {number} props.rows - Number of visible rows (default: 3)
33
+ * @param {number} props.maxLength - Maximum character length
34
+ * @param {boolean} props.showCount - Show character count
35
+ * @param {Function} props.onChange - Change handler
36
+ * @param {Function} props.onBlur - Blur handler
37
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
38
+ */
39
+ const Textarea = (props = {}) => {
40
+ const { tags, signal } = window.Lightview || {};
41
+ const LVX = window.LightviewX || {};
42
+
43
+ if (!tags) {
44
+ console.error('Lightview not found');
45
+ return null;
46
+ }
47
+
48
+ const { div, textarea, fieldset, legend, p, span, shadowDOM } = tags;
49
+
50
+ const {
51
+ value,
52
+ defaultValue = '',
53
+ placeholder,
54
+ size = 'md',
55
+ color,
56
+ ghost = false,
57
+ disabled = false,
58
+ readOnly = false,
59
+ required = false,
60
+ label: labelText,
61
+ helper,
62
+ error,
63
+ validate,
64
+ rows = 3,
65
+ maxLength,
66
+ showCount = false,
67
+ onChange,
68
+ onBlur,
69
+ onInput,
70
+ name,
71
+ id,
72
+ class: className = '',
73
+ useShadow,
74
+ ...rest
75
+ } = props;
76
+
77
+ // Generate unique ID if not provided
78
+ const textareaId = id || `textarea-${Math.random().toString(36).slice(2, 9)}`;
79
+ const textareaName = name || textareaId;
80
+
81
+ // Internal state
82
+ const internalValue = signal ? signal(defaultValue) : { value: defaultValue };
83
+ const internalError = signal ? signal(null) : { value: null };
84
+ const touched = signal ? signal(false) : { value: false };
85
+
86
+ const isControlled = value !== undefined;
87
+
88
+ const getValue = () => {
89
+ if (isControlled) {
90
+ return typeof value === 'function' ? value() :
91
+ (value && typeof value.value !== 'undefined') ? value.value : value;
92
+ }
93
+ return internalValue.value;
94
+ };
95
+
96
+ const getError = () => {
97
+ // External error takes priority
98
+ if (error) {
99
+ const err = typeof error === 'function' ? error() : error;
100
+ if (err) return err;
101
+ }
102
+ // Then internal validation error
103
+ return internalError.value;
104
+ };
105
+
106
+ const runValidation = (val) => {
107
+ if (!validate) return null;
108
+ const result = validate(val);
109
+ internalError.value = result;
110
+ return result;
111
+ };
112
+
113
+ const handleInput = (e) => {
114
+ const newValue = e.target.value;
115
+
116
+ if (!isControlled) {
117
+ internalValue.value = newValue;
118
+ }
119
+
120
+ if (isControlled && value && typeof value.value !== 'undefined') {
121
+ value.value = newValue;
122
+ }
123
+
124
+ // Validate on input if already touched
125
+ if (touched.value && validate) {
126
+ runValidation(newValue);
127
+ }
128
+
129
+ if (onInput) onInput(e);
130
+ if (onChange) onChange(newValue, e);
131
+ };
132
+
133
+ const handleBlur = (e) => {
134
+ touched.value = true;
135
+ runValidation(e.target.value);
136
+ if (onBlur) onBlur(e);
137
+ };
138
+
139
+ // Build DaisyUI textarea classes
140
+ const getTextareaClass = () => {
141
+ const classes = ['textarea', 'w-full'];
142
+
143
+ // Ghost style
144
+ if (ghost) {
145
+ classes.push('textarea-ghost');
146
+ }
147
+
148
+ // Size
149
+ if (size && size !== 'md') {
150
+ classes.push(`textarea-${size}`);
151
+ }
152
+
153
+ // Color
154
+ if (color) {
155
+ classes.push(`textarea-${color}`);
156
+ }
157
+
158
+ // Error state
159
+ const currentError = getError();
160
+ if (currentError) {
161
+ classes.push('textarea-error');
162
+ }
163
+
164
+ return classes.join(' ');
165
+ };
166
+
167
+ const getCharCount = () => {
168
+ const val = getValue() || '';
169
+ return val.length;
170
+ };
171
+
172
+ // Build textarea attributes
173
+ const textareaAttrs = {
174
+ class: validate || error ? () => getTextareaClass() : getTextareaClass(),
175
+ value: isControlled
176
+ ? (typeof value === 'function' ? value : () => getValue())
177
+ : () => internalValue.value,
178
+ disabled: typeof disabled === 'function' ? disabled : disabled,
179
+ readonly: readOnly,
180
+ required,
181
+ rows,
182
+ name: textareaName,
183
+ id: textareaId,
184
+ oninput: handleInput,
185
+ onblur: handleBlur,
186
+ 'aria-invalid': () => !!getError(),
187
+ ...rest
188
+ };
189
+
190
+ // Only add placeholder if defined
191
+ if (placeholder !== undefined) {
192
+ textareaAttrs.placeholder = placeholder;
193
+ }
194
+
195
+ // Only add maxlength if defined
196
+ if (maxLength !== undefined) {
197
+ textareaAttrs.maxlength = maxLength;
198
+ }
199
+
200
+ const textareaEl = textarea(textareaAttrs);
201
+
202
+ // Build the component using DaisyUI fieldset pattern
203
+ const fieldsetContent = [];
204
+
205
+ // Legend/Label (DaisyUI fieldset-legend)
206
+ if (labelText) {
207
+ fieldsetContent.push(
208
+ legend({ class: 'fieldset-legend' },
209
+ labelText,
210
+ required ? span({ class: 'text-error' }, ' *') : null
211
+ )
212
+ );
213
+ }
214
+
215
+ // Textarea element
216
+ fieldsetContent.push(textareaEl);
217
+
218
+ // Footer with helper/error and character count
219
+ const hasFooter = helper || validate || error || showCount || maxLength;
220
+ if (hasFooter) {
221
+ fieldsetContent.push(
222
+ div({ class: 'flex justify-between items-center' },
223
+ () => {
224
+ const currentError = getError();
225
+ if (currentError) {
226
+ return p({
227
+ class: 'label text-error flex-1',
228
+ role: 'alert'
229
+ }, currentError);
230
+ }
231
+ if (helper) {
232
+ return p({
233
+ class: 'label flex-1'
234
+ }, helper);
235
+ }
236
+ return span({ class: 'flex-1' });
237
+ },
238
+ (showCount || maxLength) ? span({
239
+ class: () => {
240
+ const count = getCharCount();
241
+ let classes = 'label text-xs';
242
+ if (maxLength) {
243
+ if (count > maxLength) classes += ' text-error';
244
+ else if (count > maxLength * 0.9) classes += ' text-warning';
245
+ }
246
+ return classes;
247
+ }
248
+ }, () => maxLength ? `${getCharCount()}/${maxLength}` : getCharCount()) : null
249
+ )
250
+ );
251
+ }
252
+
253
+ // Wrapper with DaisyUI fieldset class
254
+ const wrapperEl = fieldset({
255
+ class: `fieldset ${className}`.trim()
256
+ }, ...fieldsetContent);
257
+
258
+ // Check if we should use shadow DOM
259
+ let usesShadow = false;
260
+ if (LVX.shouldUseShadow) {
261
+ usesShadow = LVX.shouldUseShadow(useShadow);
262
+ } else {
263
+ usesShadow = useShadow === true;
264
+ }
265
+
266
+ if (usesShadow) {
267
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
268
+
269
+ // Get current theme from document
270
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
271
+
272
+ return div({ class: 'content', style: 'display: inline-block' },
273
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
274
+ div({ 'data-theme': themeValue },
275
+ wrapperEl
276
+ )
277
+ )
278
+ );
279
+ }
280
+
281
+ return wrapperEl;
282
+ };
283
+
284
+ // Auto-register
285
+ window.Lightview.tags.Textarea = Textarea;
286
+
287
+ export default Textarea;
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Lightview Components - Toggle
3
+ * A toggle switch component using DaisyUI 5 styling
4
+ * @see https://daisyui.com/components/toggle/
5
+ *
6
+ * Uses DaisyUI's form-control pattern:
7
+ * <div class="form-control">
8
+ * <label class="label cursor-pointer">
9
+ * <span class="label-text">Label</span>
10
+ * <input type="checkbox" class="toggle" />
11
+ * </label>
12
+ * </div>
13
+ */
14
+
15
+ import '../daisyui.js';
16
+
17
+ /**
18
+ * Toggle Component
19
+ * @param {Object} props - Toggle properties
20
+ * @param {boolean|Signal} props.checked - Controlled checked state
21
+ * @param {boolean} props.defaultChecked - Initial checked state (uncontrolled)
22
+ * @param {string} props.size - 'xs' | 'sm' | 'md' | 'lg' (default: 'md')
23
+ * @param {string} props.color - 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
24
+ * @param {boolean} props.disabled - Disable toggle
25
+ * @param {string} props.label - Label text
26
+ * @param {string} props.labelPosition - 'left' | 'right' (default: 'left')
27
+ * @param {string} props.description - Description text below label
28
+ * @param {Function} props.onChange - Change handler
29
+ * @param {boolean} props.useShadow - Render in Shadow DOM
30
+ */
31
+ const Toggle = (props = {}) => {
32
+ const { tags, signal } = window.Lightview || {};
33
+ const LVX = window.LightviewX || {};
34
+
35
+ if (!tags) {
36
+ console.error('Lightview not found');
37
+ return null;
38
+ }
39
+
40
+ const { div, input, label, span, shadowDOM } = tags;
41
+
42
+ const {
43
+ checked,
44
+ defaultChecked = false,
45
+ size = 'md',
46
+ color,
47
+ disabled = false,
48
+ label: labelText,
49
+ labelPosition = 'left',
50
+ description,
51
+ onChange,
52
+ name,
53
+ id,
54
+ class: className = '',
55
+ useShadow,
56
+ theme, // Explicit theme override
57
+ ...rest
58
+ } = props;
59
+
60
+ const toggleId = id || `toggle-${Math.random().toString(36).slice(2, 9)}`;
61
+
62
+ // Internal state for uncontrolled mode
63
+ const internalChecked = signal ? signal(defaultChecked) : { value: defaultChecked };
64
+
65
+ const isControlled = checked !== undefined;
66
+
67
+ const getChecked = () => {
68
+ if (isControlled) {
69
+ return typeof checked === 'function' ? checked() :
70
+ (checked && typeof checked.value !== 'undefined') ? checked.value : checked;
71
+ }
72
+ return internalChecked.value;
73
+ };
74
+
75
+ const handleChange = (e) => {
76
+ const newValue = e.target.checked;
77
+
78
+ if (!isControlled) {
79
+ internalChecked.value = newValue;
80
+ }
81
+
82
+ // If controlled with a signal, update it
83
+ if (isControlled && checked && typeof checked.value !== 'undefined') {
84
+ checked.value = newValue;
85
+ }
86
+
87
+ if (onChange) onChange(newValue, e);
88
+ };
89
+
90
+ // Build DaisyUI toggle classes
91
+ const getToggleClass = () => {
92
+ const classes = ['toggle'];
93
+
94
+ if (size && size !== 'md') {
95
+ classes.push(`toggle-${size}`);
96
+ }
97
+
98
+ if (color) {
99
+ classes.push(`toggle-${color}`);
100
+ }
101
+
102
+ return classes.join(' ');
103
+ };
104
+
105
+ const toggleInput = input({
106
+ type: 'checkbox',
107
+ class: getToggleClass(),
108
+ checked: isControlled
109
+ ? (typeof checked === 'function' ? checked : () => getChecked())
110
+ : () => internalChecked.value,
111
+ disabled: typeof disabled === 'function' ? disabled : disabled,
112
+ name,
113
+ id: toggleId,
114
+ onchange: handleChange,
115
+ role: 'switch',
116
+ 'aria-checked': isControlled
117
+ ? (typeof checked === 'function' ? checked : () => getChecked())
118
+ : () => internalChecked.value,
119
+ ...rest
120
+ });
121
+
122
+ // If no label, return just the toggle
123
+ if (!labelText) {
124
+ // Check if we should use shadow DOM
125
+ let usesShadow = false;
126
+ if (LVX.shouldUseShadow) {
127
+ usesShadow = LVX.shouldUseShadow(useShadow);
128
+ } else {
129
+ usesShadow = useShadow === true;
130
+ }
131
+
132
+ if (usesShadow) {
133
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
134
+ // Use reactive theme signal if available, otherwise fallback to explicit 'theme' prop or default
135
+ const themeValue = theme || (LVX.themeSignal ? () => LVX.themeSignal.value : 'light');
136
+
137
+ return div({ class: 'content', style: 'display: inline-block' },
138
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
139
+ div({ 'data-theme': themeValue }, toggleInput)
140
+ )
141
+ );
142
+ }
143
+
144
+ return toggleInput;
145
+ }
146
+
147
+ // Build label content
148
+ const labelContent = div({ style: 'display: flex; flex-direction: column;' },
149
+ span({ class: 'label-text' }, labelText),
150
+ description ? span({ class: 'label-text-alt', style: 'opacity: 0.7;' }, description) : null
151
+ );
152
+
153
+ // Arrange based on label position
154
+ const labelChildren = labelPosition === 'right'
155
+ ? [toggleInput, labelContent]
156
+ : [labelContent, toggleInput];
157
+
158
+ const formControl = div({
159
+ class: `form-control ${className}`.trim()
160
+ },
161
+ label({ class: 'label cursor-pointer', style: 'justify-content: flex-start; gap: 0.75rem;' },
162
+ ...labelChildren
163
+ )
164
+ );
165
+
166
+ // Check if we should use shadow DOM
167
+ let usesShadow = false;
168
+ if (LVX.shouldUseShadow) {
169
+ usesShadow = LVX.shouldUseShadow(useShadow);
170
+ } else {
171
+ usesShadow = useShadow === true;
172
+ }
173
+
174
+ if (usesShadow) {
175
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
176
+
177
+ // Use reactive theme signal if available, otherwise fallback to explicit 'theme' prop or default
178
+ const themeValue = theme || (LVX.themeSignal ? () => LVX.themeSignal.value : 'light');
179
+
180
+ return span({ style: 'margin-right: 0.5rem' },
181
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
182
+ div({ 'data-theme': themeValue, style: 'display: inline-block' }, formControl)
183
+ )
184
+ );
185
+ }
186
+
187
+ return formControl;
188
+ };
189
+
190
+ // Auto-register
191
+ window.Lightview.tags.Toggle = Toggle;
192
+
193
+ // Register as Custom Element
194
+ if (window.LightviewX?.createCustomElement) {
195
+ const ToggleElement = window.LightviewX.createCustomElement(Toggle);
196
+ if (!customElements.get('lv-toggle')) {
197
+ customElements.define('lv-toggle', ToggleElement);
198
+ }
199
+ }
200
+
201
+ export default Toggle;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Lightview Components - DaisyUI Edition
3
+ * Main export file for all components
4
+ */
5
+
6
+ // DaisyUI utilities
7
+ export * from './daisyui.js';
8
+ export { default as DaisyUI } from './daisyui.js';
9
+
10
+ // Actions
11
+ export { default as Button } from './actions/button.js';
12
+ export { default as Dropdown } from './actions/dropdown.js';
13
+ export { default as Modal } from './actions/modal.js';
14
+ export { default as Swap } from './actions/swap.js';
15
+
16
+
17
+ // Data Display
18
+ export { default as Accordion } from './data-display/accordion.js';
19
+ export { default as Alert } from './data-display/alert.js';
20
+ export { default as Avatar } from './data-display/avatar.js';
21
+ export { default as Badge } from './data-display/badge.js';
22
+ export { default as Card } from './data-display/card.js';
23
+ export { default as Chart } from './data-display/chart.js';
24
+ export { default as Carousel } from './data-display/carousel.js';
25
+ export { default as Chat } from './data-display/chat.js';
26
+ export { default as Collapse } from './data-display/collapse.js';
27
+ export { default as Countdown } from './data-display/countdown.js';
28
+ export { default as Diff } from './data-display/diff.js';
29
+ export { default as Kbd } from './data-display/kbd.js';
30
+ export { default as Loading } from './data-display/loading.js';
31
+ export { default as Progress } from './data-display/progress.js';
32
+ export { default as RadialProgress } from './data-display/radial-progress.js';
33
+ export { default as Skeleton } from './data-display/skeleton.js';
34
+ export { default as Stats } from './data-display/stats.js';
35
+ export { default as Table } from './data-display/table.js';
36
+ export { default as Timeline } from './data-display/timeline.js';
37
+ export { default as Toast } from './data-display/toast.js';
38
+ export { default as Tooltip } from './data-display/tooltip.js';
39
+
40
+ // Data Input
41
+ export { default as Checkbox } from './data-input/checkbox.js';
42
+ export { default as FileInput } from './data-input/file-input.js';
43
+ export { default as Radio } from './data-input/radio.js';
44
+ export { default as Range } from './data-input/range.js';
45
+ export { default as Rating } from './data-input/rating.js';
46
+ export { default as Select } from './data-input/select.js';
47
+ export { default as Input } from './data-input/input.js';
48
+ export { default as Textarea } from './data-input/textarea.js';
49
+ export { default as Toggle } from './data-input/toggle.js';
50
+
51
+ // Layout
52
+ export { default as Divider } from './layout/divider.js';
53
+ export { default as Drawer } from './layout/drawer.js';
54
+ export { default as Footer } from './layout/footer.js';
55
+ export { default as Hero } from './layout/hero.js';
56
+ export { default as Indicator } from './layout/indicator.js';
57
+ export { default as Join } from './layout/join.js';
58
+ export { default as Navbar } from './layout/navbar.js';
59
+
60
+ // Navigation
61
+ export { default as Breadcrumbs } from './navigation/breadcrumbs.js';
62
+ export { default as Dock } from './navigation/dock.js';
63
+ export { default as Menu } from './navigation/menu.js';
64
+ export { default as Pagination } from './navigation/pagination.js';
65
+ export { default as Steps } from './navigation/steps.js';
66
+ export { default as Tabs } from './navigation/tabs.js';
67
+
68
+ /**
69
+ * Component manifest for documentation/tooling
70
+ */
71
+ export const componentManifest = {
72
+ actions: [
73
+ { name: 'Button', description: 'Versatile button with variants, colors, and loading states' },
74
+ { name: 'Dropdown', description: 'Dropdown menu that opens on click or hover' },
75
+ { name: 'Modal', description: 'Dialog overlay for important content' },
76
+ { name: 'Swap', description: 'Toggle between two elements with animation' },
77
+
78
+ ],
79
+ dataDisplay: [
80
+ { name: 'Accordion', description: 'Collapsible content sections' },
81
+ { name: 'Alert', description: 'Inline messages and notifications' },
82
+ { name: 'Avatar', description: 'User avatar with image/initials fallback' },
83
+ { name: 'Badge', description: 'Small label for status or counts' },
84
+ { name: 'Card', description: 'Container for grouping content' },
85
+ { name: 'Chart', description: 'Charts and graphs powered by charts.css' },
86
+ { name: 'Carousel', description: 'Scrollable image/content gallery' },
87
+ { name: 'Chat', description: 'Chat bubble messages' },
88
+ { name: 'Collapse', description: 'Show/hide content sections' },
89
+ { name: 'Countdown', description: 'Animated countdown number' },
90
+ { name: 'Diff', description: 'Side-by-side comparison' },
91
+ { name: 'Kbd', description: 'Keyboard shortcut indicator' },
92
+ { name: 'Loading', description: 'Loading spinner animations' },
93
+ { name: 'Progress', description: 'Linear progress bar' },
94
+ { name: 'RadialProgress', description: 'Circular progress indicator' },
95
+ { name: 'Skeleton', description: 'Loading placeholder' },
96
+ { name: 'Stats', description: 'Statistics display blocks' },
97
+ { name: 'Table', description: 'Data table with styling' },
98
+ { name: 'Timeline', description: 'Chronological event list' },
99
+ { name: 'Toast', description: 'Positioned notification container' },
100
+ { name: 'Tooltip', description: 'Hover tooltip' }
101
+ ],
102
+ dataInput: [
103
+ { name: 'Checkbox', description: 'Checkbox input' },
104
+ { name: 'FileInput', description: 'File upload input' },
105
+ { name: 'Radio', description: 'Radio button' },
106
+ { name: 'Range', description: 'Slider input' },
107
+ { name: 'Rating', description: 'Star rating input' },
108
+ { name: 'Select', description: 'Dropdown select' },
109
+ { name: 'Input', description: 'Text input field' },
110
+ { name: 'Textarea', description: 'Multi-line text input' },
111
+ { name: 'Toggle', description: 'Switch toggle' }
112
+ ],
113
+ layout: [
114
+ { name: 'Divider', description: 'Content separator' },
115
+ { name: 'Drawer', description: 'Slide-out sidebar' },
116
+ { name: 'Footer', description: 'Page footer' },
117
+ { name: 'Hero', description: 'Large banner section' },
118
+ { name: 'Indicator', description: 'Position badges on corners' },
119
+ { name: 'Join', description: 'Group items with shared borders' },
120
+ { name: 'Navbar', description: 'Navigation bar' }
121
+ ],
122
+ navigation: [
123
+ { name: 'Breadcrumbs', description: 'Navigation breadcrumb trail' },
124
+ { name: 'Dock', description: 'Bottom navigation bar' },
125
+ { name: 'Menu', description: 'Navigation menu list' },
126
+ { name: 'Pagination', description: 'Page navigation' },
127
+ { name: 'Steps', description: 'Step progress indicator' },
128
+ { name: 'Tabs', description: 'Tabbed navigation' }
129
+ ]
130
+ };
131
+
132
+ /**
133
+ * Initialize all components
134
+ */
135
+ export const initComponents = () => {
136
+ console.log('Lightview Components (DaisyUI) initialized');
137
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Lightview Divider Component (DaisyUI)
3
+ * @see https://daisyui.com/components/divider/
4
+ */
5
+
6
+ import '../daisyui.js';
7
+
8
+ /**
9
+ * Divider Component
10
+ * @param {Object} props
11
+ * @param {boolean} props.horizontal - Horizontal divider (default is vertical in flex row)
12
+ * @param {boolean} props.vertical - Vertical divider (explicit)
13
+ * @param {string} props.position - 'start' | 'end' for text position
14
+ * @param {string} props.color - 'neutral' | 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'info' | 'error'
15
+ * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
16
+ */
17
+ const Divider = (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
+ horizontal = false,
27
+ vertical = false,
28
+ position,
29
+ color,
30
+ useShadow,
31
+ class: className = '',
32
+ ...rest
33
+ } = props;
34
+
35
+ const classes = ['divider'];
36
+ if (horizontal) classes.push('divider-horizontal');
37
+ if (vertical) classes.push('divider-vertical');
38
+ if (position === 'start') classes.push('divider-start');
39
+ else if (position === 'end') classes.push('divider-end');
40
+ if (color) classes.push(`divider-${color}`);
41
+ if (className) classes.push(className);
42
+
43
+ const dividerEl = div({ class: classes.join(' '), ...rest }, ...children);
44
+
45
+ // Check if we should use shadow DOM
46
+ let usesShadow = false;
47
+ if (LVX.shouldUseShadow) {
48
+ usesShadow = LVX.shouldUseShadow(useShadow);
49
+ } else {
50
+ usesShadow = useShadow === true;
51
+ }
52
+
53
+ if (usesShadow) {
54
+ const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
55
+
56
+ const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
57
+
58
+ return div({ class: 'contents' },
59
+ shadowDOM({ mode: 'open', adoptedStyleSheets },
60
+ div({ 'data-theme': themeValue },
61
+ dividerEl
62
+ )
63
+ )
64
+ );
65
+ }
66
+
67
+ return dividerEl;
68
+ };
69
+
70
+ window.Lightview.tags.Divider = Divider;
71
+
72
+ export default Divider;