lightview 2.1.0 → 2.2.2

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 (71) hide show
  1. package/build-bundles.mjs +2 -6
  2. package/build.js +236 -46
  3. package/components/data-display/avatar.js +25 -1
  4. package/components/data-display/chart.js +22 -5
  5. package/components/data-display/countdown.js +3 -2
  6. package/components/data-input/checkbox.js +23 -1
  7. package/components/data-input/input.js +24 -1
  8. package/components/data-input/radio.js +37 -2
  9. package/components/data-input/select.js +24 -1
  10. package/components/data-input/toggle.js +21 -1
  11. package/components/navigation/breadcrumbs.js +42 -2
  12. package/docs/assets/js/examplify.js +1 -1
  13. package/docs/cdom-nav.html +32 -6
  14. package/docs/cdom.html +610 -180
  15. package/docs/components/avatar.html +24 -54
  16. package/docs/components/badge.html +14 -14
  17. package/docs/components/breadcrumbs.html +95 -29
  18. package/docs/components/chart-area.html +3 -3
  19. package/docs/components/chart-bar.html +4 -181
  20. package/docs/components/chart-column.html +4 -189
  21. package/docs/components/chart-line.html +3 -3
  22. package/docs/components/chart-pie.html +112 -166
  23. package/docs/components/chart.html +11 -13
  24. package/docs/components/checkbox.html +48 -28
  25. package/docs/components/collapse.html +6 -6
  26. package/docs/components/countdown.html +12 -12
  27. package/docs/components/dropdown.html +1 -1
  28. package/docs/components/file-input.html +4 -4
  29. package/docs/components/footer.html +11 -11
  30. package/docs/components/input.html +45 -29
  31. package/docs/components/join.html +4 -4
  32. package/docs/components/kbd.html +3 -3
  33. package/docs/components/loading.html +41 -53
  34. package/docs/components/pagination.html +4 -4
  35. package/docs/components/progress.html +6 -4
  36. package/docs/components/radio.html +42 -31
  37. package/docs/components/select.html +48 -59
  38. package/docs/components/toggle.html +44 -25
  39. package/docs/getting-started/index.html +4 -4
  40. package/jprx/LICENSE +21 -0
  41. package/jprx/README.md +130 -0
  42. package/{cdom → jprx}/helpers/array.js +9 -4
  43. package/{cdom → jprx}/helpers/state.js +6 -3
  44. package/jprx/index.js +69 -0
  45. package/jprx/package.json +24 -0
  46. package/jprx/parser.js +1517 -0
  47. package/lightview-all.js +3785 -1
  48. package/lightview-cdom.js +2128 -1
  49. package/lightview-router.js +179 -208
  50. package/lightview-x.js +1435 -1
  51. package/lightview.js +613 -1
  52. package/package.json +5 -2
  53. package/src/lightview-cdom.js +201 -49
  54. package/src/lightview-router.js +210 -0
  55. package/src/lightview-x.js +104 -55
  56. package/src/lightview.js +12 -1
  57. package/{watch.js → start-dev.js} +2 -1
  58. package/tests/cdom/parser.test.js +83 -12
  59. package/wrangler.toml +0 -3
  60. package/cdom/parser.js +0 -602
  61. package/test-text-tag.js +0 -6
  62. /package/{cdom → jprx}/helpers/compare.js +0 -0
  63. /package/{cdom → jprx}/helpers/conditional.js +0 -0
  64. /package/{cdom → jprx}/helpers/datetime.js +0 -0
  65. /package/{cdom → jprx}/helpers/format.js +0 -0
  66. /package/{cdom → jprx}/helpers/logic.js +0 -0
  67. /package/{cdom → jprx}/helpers/lookup.js +0 -0
  68. /package/{cdom → jprx}/helpers/math.js +0 -0
  69. /package/{cdom → jprx}/helpers/network.js +0 -0
  70. /package/{cdom → jprx}/helpers/stats.js +0 -0
  71. /package/{cdom → jprx}/helpers/string.js +0 -0
package/build-bundles.mjs CHANGED
@@ -10,6 +10,7 @@ const builds = [
10
10
  { entry: 'src/lightview.js', name: 'lightview', globalName: 'Lightview' },
11
11
  { entry: 'src/lightview-x.js', name: 'lightview-x', globalName: 'LightviewX' },
12
12
  { entry: 'src/lightview-cdom.js', name: 'lightview-cdom', globalName: 'LightviewCDOM' },
13
+ { entry: 'src/lightview-router.js', name: 'lightview-router', globalName: 'LightviewRouter' },
13
14
  { entry: 'src/lightview-all.js', name: 'lightview-all', globalName: 'LightviewAll' }
14
15
  ];
15
16
 
@@ -44,12 +45,7 @@ async function runBuilds() {
44
45
  rollupOptions: {
45
46
  external: (id) => id.includes('/components/') || id.includes('/docs/')
46
47
  },
47
- minify: 'terser',
48
- terserOptions: {
49
- compress: {
50
- drop_console: false
51
- }
52
- }
48
+ minify: false // No minification for easier debugging
53
49
  }
54
50
  });
55
51
  }
package/build.js CHANGED
@@ -1,70 +1,260 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { minify } = require('terser');
4
+
5
+ // Parse command line arguments
6
+ const args = process.argv.slice(2);
7
+ const envArg = args.find(arg => arg.startsWith('--env='));
8
+ const env = envArg ? envArg.split('=')[1] : 'prod';
9
+
10
+ // Validate env
11
+ const validEnvs = ['prod', 'test', 'dev'];
12
+ if (!validEnvs.includes(env)) {
13
+ console.error(`Invalid env "${env}". Allowed values: ${validEnvs.join(', ')}`);
14
+ process.exit(1);
15
+ }
16
+
17
+ const shouldMinify = env === 'prod';
3
18
 
4
19
  const rootDir = __dirname;
5
20
  const distDir = path.join(rootDir, 'dist');
6
21
  const docsDir = path.join(rootDir, 'docs');
7
22
  const componentsDir = path.join(rootDir, 'components');
8
23
  const middlewareDir = path.join(rootDir, 'middleware');
24
+ const jprxDir = path.join(rootDir, 'jprx');
9
25
 
10
26
  // Configuration
11
27
  // Files in root that should be copied
12
28
  const allowedExtensions = ['.html', '.js', '.css', '.txt', '.xml', '.ico', '.png', '.svg', '.jpg', '.jpeg', '.md'];
13
29
  const includeFiles = ['_headers']; // specific files to always include
14
- const excludeFiles = ['build.js', 'package.json', 'package-lock.json', 'wrangler.toml'];
30
+ const excludeFiles = ['build.js', 'build-bundles.mjs', 'package.json', 'package-lock.json', 'wrangler.toml'];
15
31
 
16
- console.log('Building for deployment...');
32
+ /**
33
+ * Minify a JavaScript file using terser
34
+ * @param {string} code - The JavaScript source code
35
+ * @returns {Promise<string>} - The minified code
36
+ */
37
+ async function minifyJS(code) {
38
+ if (!shouldMinify) {
39
+ return code; // Skip minification in dev mode
40
+ }
41
+ try {
42
+ const result = await minify(code, {
43
+ module: true, // Handle ES6 module syntax
44
+ compress: {
45
+ drop_console: false
46
+ },
47
+ mangle: {
48
+ reserved: ['examplify', 'examplifyIdCounter'] // Preserve global function names called from HTML
49
+ }
50
+ });
51
+ return result.code;
52
+ } catch (e) {
53
+ console.error('Minification error:', e);
54
+ return code; // Return original on error
55
+ }
56
+ }
17
57
 
18
- // 1. Clean/Create dist
19
- if (fs.existsSync(distDir)) {
20
- fs.rmSync(distDir, { recursive: true, force: true });
58
+ /**
59
+ * Copy a file, minifying if it's a JS file (and minification is enabled)
60
+ * @param {string} srcPath - Source file path
61
+ * @param {string} destPath - Destination file path
62
+ */
63
+ async function copyFile(srcPath, destPath) {
64
+ const ext = path.extname(srcPath).toLowerCase();
65
+
66
+ if (ext === '.js' && shouldMinify) {
67
+ // Read, minify, and write JS files
68
+ const code = fs.readFileSync(srcPath, 'utf8');
69
+ const minified = await minifyJS(code);
70
+ fs.writeFileSync(destPath, minified);
71
+ } else {
72
+ // Copy other files directly
73
+ fs.copyFileSync(srcPath, destPath);
74
+ }
21
75
  }
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
- }
76
+
77
+ /**
78
+ * Recursively copy a directory, minifying JS files if enabled
79
+ * @param {string} srcDir - Source directory
80
+ * @param {string} destDir - Destination directory
81
+ */
82
+ async function copyDirWithMinify(srcDir, destDir) {
83
+ if (!fs.existsSync(destDir)) {
84
+ fs.mkdirSync(destDir, { recursive: true });
43
85
  }
44
- });
45
86
 
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.');
87
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
88
+
89
+ for (const entry of entries) {
90
+ const srcPath = path.join(srcDir, entry.name);
91
+ const destPath = path.join(destDir, entry.name);
92
+
93
+ if (entry.isDirectory()) {
94
+ await copyDirWithMinify(srcPath, destPath);
95
+ } else {
96
+ await copyFile(srcPath, destPath);
97
+ }
98
+ }
52
99
  }
53
100
 
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.');
101
+ async function build() {
102
+ console.log(`Building for deployment (env: ${env}, minify: ${shouldMinify})...`);
103
+
104
+ // 1. Clean/Create dist - continue even if deletion fails (e.g., file locks on Windows)
105
+ if (fs.existsSync(distDir)) {
106
+ try {
107
+ fs.rmSync(distDir, { recursive: true, force: true });
108
+ console.log('Cleaned dist directory.');
109
+ } catch (e) {
110
+ console.warn('Warning: Could not delete dist directory (files may be locked). Overwriting files instead.');
111
+ }
112
+ }
113
+
114
+ if (!fs.existsSync(distDir)) {
115
+ fs.mkdirSync(distDir);
116
+ console.log('Created dist directory.');
117
+ }
118
+
119
+ // 2. Copy Root Files (with minification for JS if enabled)
120
+ const files = fs.readdirSync(rootDir);
121
+ for (const file of files) {
122
+ const srcPath = path.join(rootDir, file);
123
+ const stat = fs.statSync(srcPath);
124
+
125
+ if (stat.isFile()) {
126
+ const ext = path.extname(file).toLowerCase();
127
+
128
+ // Check if file should be copied
129
+ const isAllowedExt = allowedExtensions.includes(ext);
130
+ const isExplicitInclude = includeFiles.includes(file);
131
+ const isExcluded = excludeFiles.includes(file) || file.startsWith('.');
132
+
133
+ if ((isAllowedExt || isExplicitInclude) && !isExcluded) {
134
+ await copyFile(srcPath, path.join(distDir, file));
135
+ const suffix = ext === '.js' && shouldMinify ? ' (minified)' : '';
136
+ console.log(`Copied: ${file}${suffix}`);
137
+ }
138
+ }
139
+ }
140
+
141
+ // 3. Copy Docs Directory
142
+ if (fs.existsSync(docsDir)) {
143
+ await copyDirWithMinify(docsDir, path.join(distDir, 'docs'));
144
+ console.log(`Copied: docs directory${shouldMinify ? ' (JS files minified)' : ''}`);
145
+ } else {
146
+ console.warn('Warning: docs directory not found.');
147
+ }
148
+
149
+ // 4. Copy Components Directory
150
+ if (fs.existsSync(componentsDir)) {
151
+ await copyDirWithMinify(componentsDir, path.join(distDir, 'components'));
152
+ console.log(`Copied: components directory${shouldMinify ? ' (JS files minified)' : ''}`);
153
+ } else {
154
+ console.warn('Warning: components directory not found.');
155
+ }
156
+
157
+ // 5. Copy Middleware Directory
158
+ if (fs.existsSync(middlewareDir)) {
159
+ await copyDirWithMinify(middlewareDir, path.join(distDir, 'middleware'));
160
+ console.log(`Copied: middleware directory${shouldMinify ? ' (JS files minified)' : ''}`);
161
+ } else {
162
+ console.warn('Warning: middleware directory not found.');
163
+ }
164
+
165
+
166
+
167
+ // 7. Copy JPRX Directory
168
+ if (fs.existsSync(jprxDir)) {
169
+ await copyDirWithMinify(jprxDir, path.join(distDir, 'jprx'));
170
+ console.log(`Copied: jprx directory${shouldMinify ? ' (JS files minified)' : ''}`);
171
+ } else {
172
+ console.warn('Warning: jprx directory not found.');
173
+ }
174
+
175
+ console.log('Build complete! Assets are ready in ./dist');
60
176
  }
61
177
 
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');
178
+ // Check for --watch flag
179
+ const isWatch = args.includes('--watch');
180
+
181
+ if (isWatch) {
182
+ // Watch mode
183
+ const { watch } = require('fs');
184
+ const { execSync } = require('child_process');
185
+ let debounceTimer;
186
+ let building = false;
187
+ let queued = false;
188
+ let needsBundleRebuild = false;
189
+
190
+ const runBuildBundles = () => {
191
+ console.log('Rebuilding bundles...');
192
+ try {
193
+ execSync('node build-bundles.mjs', { cwd: rootDir, stdio: 'inherit' });
194
+ console.log('Bundles rebuilt successfully.');
195
+ } catch (e) {
196
+ console.error('Bundle rebuild failed:', e.message);
197
+ }
198
+ };
199
+
200
+ const runBuild = async () => {
201
+ if (building) {
202
+ queued = true;
203
+ return;
204
+ }
205
+ building = true;
206
+ try {
207
+ if (needsBundleRebuild) {
208
+ runBuildBundles();
209
+ needsBundleRebuild = false;
210
+ }
211
+ await build();
212
+ } catch (e) {
213
+ console.error('Build failed:', e);
214
+ }
215
+ building = false;
216
+ if (queued) {
217
+ queued = false;
218
+ runBuild();
219
+ }
220
+ };
221
+
222
+ const watchDir = (dir, name, triggersBundleRebuild = false) => {
223
+ if (fs.existsSync(dir)) {
224
+ watch(dir, { recursive: true }, (event, filename) => {
225
+ if (filename && !filename.includes('~') && !filename.includes('.git')) {
226
+ clearTimeout(debounceTimer);
227
+ if (triggersBundleRebuild) {
228
+ needsBundleRebuild = true;
229
+ }
230
+ debounceTimer = setTimeout(() => {
231
+ console.log(`\nChange detected in ${name}: ${filename}`);
232
+ runBuild();
233
+ }, 300);
234
+ }
235
+ });
236
+ console.log(`Watching: ${name}${triggersBundleRebuild ? ' (triggers bundle rebuild)' : ''}`);
237
+ }
238
+ };
239
+
240
+ console.log('Starting watch mode...\n');
241
+
242
+ // Initial build (bundles already built by npm script)
243
+ runBuild().then(() => {
244
+ // Watch all relevant directories
245
+ // src/ and jprx/ trigger bundle rebuilds
246
+ watchDir(path.join(rootDir, 'src'), 'src/', true);
247
+ watchDir(jprxDir, 'jprx/', true);
248
+ // Other directories just get copied
249
+ watchDir(docsDir, 'docs/');
250
+ watchDir(componentsDir, 'components/');
251
+ watchDir(middlewareDir, 'middleware/');
252
+ console.log('\nWaiting for changes...');
253
+ });
66
254
  } else {
67
- console.warn('Warning: middleware directory not found.');
255
+ // Single build
256
+ build().catch(e => {
257
+ console.error('Build failed:', e);
258
+ process.exit(1);
259
+ });
68
260
  }
69
-
70
- console.log('Build complete! Assets are ready in ./dist');
@@ -158,7 +158,31 @@ tags.Avatar = Avatar;
158
158
  tags['Avatar.Group'] = Avatar.Group;
159
159
 
160
160
  // Register as Custom Elements
161
- if (globalThis.LightviewX?.createCustomElement) {
161
+ if (globalThis.LightviewX?.customElementWrapper) {
162
+ if (!customElements.get('lv-avatar')) {
163
+ customElements.define('lv-avatar', globalThis.LightviewX.customElementWrapper(Avatar, {
164
+ attributeMap: {
165
+ src: String,
166
+ alt: String,
167
+ placeholder: String,
168
+ size: String,
169
+ shape: String,
170
+ ring: Boolean,
171
+ ringColor: String,
172
+ online: Boolean,
173
+ offline: Boolean,
174
+ class: String
175
+ }
176
+ }));
177
+ }
178
+ if (!customElements.get('lv-avatar-group')) {
179
+ customElements.define('lv-avatar-group', globalThis.LightviewX.customElementWrapper(Avatar.Group, {
180
+ attributeMap: {
181
+ class: String
182
+ }
183
+ }));
184
+ }
185
+ } else if (globalThis.LightviewX?.createCustomElement) {
162
186
  if (!customElements.get('lv-avatar')) {
163
187
  customElements.define('lv-avatar', globalThis.LightviewX.createCustomElement(Avatar));
164
188
  }
@@ -24,9 +24,22 @@ import '../daisyui.js';
24
24
  */
25
25
  const CHARTS_CSS_URL = 'https://cdn.jsdelivr.net/npm/charts.css/dist/charts.min.css';
26
26
 
27
+ // Register stylesheet for Shadow DOM usage (Adopted StyleSheets)
28
+ // Using top-level await in module to ensure it's loaded before any component renders
29
+ if (globalThis.LightviewX?.registerStyleSheet) {
30
+ await LightviewX.registerStyleSheet(CHARTS_CSS_URL);
31
+ }
32
+
27
33
  // Auto-load charts.css for Global/Light DOM usage
28
34
  if (typeof document !== 'undefined') {
29
- if (!document.querySelector(`link[href^="https://cdn.jsdelivr.net/npm/charts.css"]`)) {
35
+ if (!document.querySelector(`link[href^="${CHARTS_CSS_URL}"]`)) {
36
+ // Preload for better performance
37
+ const preload = document.createElement('link');
38
+ preload.rel = 'preload';
39
+ preload.as = 'style';
40
+ preload.href = CHARTS_CSS_URL;
41
+ document.head.appendChild(preload);
42
+
30
43
  const link = document.createElement('link');
31
44
  link.rel = 'stylesheet';
32
45
  link.href = CHARTS_CSS_URL;
@@ -48,8 +61,8 @@ const Chart = (props = {}, ...children) => {
48
61
  labels = false,
49
62
  dataOnHover = false,
50
63
  primaryAxis = false,
64
+ secondaryAxesCount,
51
65
  secondaryAxis = false,
52
- spacing,
53
66
  reverse = false,
54
67
  multiple = false,
55
68
  stacked = false,
@@ -65,10 +78,14 @@ const Chart = (props = {}, ...children) => {
65
78
  if (heading) classes.push('show-heading');
66
79
  if (primaryAxis) classes.push('show-primary-axis');
67
80
 
68
- if (secondaryAxis === true) classes.push('show-10-secondary-axes');
69
- else if (typeof secondaryAxis === 'string') classes.push(secondaryAxis);
81
+ if (secondaryAxesCount) {
82
+ classes.push(`show-${secondaryAxesCount}-secondary-axes`);
83
+ } else if (secondaryAxis === true) {
84
+ classes.push('show-10-secondary-axes');
85
+ } else if (typeof secondaryAxis === 'string') {
86
+ classes.push(secondaryAxis);
87
+ }
70
88
 
71
- if (spacing) classes.push(`data-spacing-${spacing}`);
72
89
  if (reverse) classes.push('reverse');
73
90
  if (multiple) classes.push('multiple');
74
91
  if (stacked) classes.push('stacked');
@@ -30,12 +30,13 @@ const Countdown = (props = {}, ...children) => {
30
30
 
31
31
  const countdownEl = span({
32
32
  class: `countdown ${className}`.trim(),
33
+ style: 'line-height: 1.2em; height: 1.2em; vertical-align: middle;',
33
34
  ...rest
34
35
  },
35
36
  span({
36
37
  style: typeof value === 'function'
37
- ? () => `--value:${getValue()};`
38
- : `--value:${value};`
38
+ ? () => `--value:${getValue()}; height: 1.2em;`
39
+ : `--value:${value}; height: 1.2em;`
39
40
  })
40
41
  );
41
42
 
@@ -242,7 +242,29 @@ const Checkbox = (props = {}) => {
242
242
  globalThis.Lightview.tags.Checkbox = Checkbox;
243
243
 
244
244
  // Register as Custom Element
245
- if (globalThis.LightviewX?.createCustomElement) {
245
+ if (globalThis.LightviewX?.customElementWrapper) {
246
+ const CheckboxElement = globalThis.LightviewX.customElementWrapper(Checkbox, {
247
+ attributeMap: {
248
+ checked: Boolean,
249
+ defaultChecked: Boolean,
250
+ indeterminate: Boolean,
251
+ size: String,
252
+ color: String,
253
+ disabled: Boolean,
254
+ required: Boolean,
255
+ label: String,
256
+ description: String,
257
+ error: String,
258
+ name: String,
259
+ id: String,
260
+ value: String,
261
+ class: String
262
+ }
263
+ });
264
+ if (!customElements.get('lv-checkbox')) {
265
+ customElements.define('lv-checkbox', CheckboxElement);
266
+ }
267
+ } else if (globalThis.LightviewX?.createCustomElement) {
246
268
  const CheckboxElement = globalThis.LightviewX.createCustomElement(Checkbox);
247
269
  if (!customElements.get('lv-checkbox')) {
248
270
  customElements.define('lv-checkbox', CheckboxElement);
@@ -254,7 +254,30 @@ const Input = (props = {}) => {
254
254
  globalThis.Lightview.tags.Input = Input;
255
255
 
256
256
  // Register as Custom Element
257
- if (globalThis.LightviewX?.createCustomElement) {
257
+ if (globalThis.LightviewX?.customElementWrapper) {
258
+ const InputElement = globalThis.LightviewX.customElementWrapper(Input, {
259
+ attributeMap: {
260
+ type: String,
261
+ value: String,
262
+ defaultValue: String,
263
+ placeholder: String,
264
+ size: String,
265
+ color: String,
266
+ ghost: Boolean,
267
+ disabled: Boolean,
268
+ required: Boolean,
269
+ label: String,
270
+ helper: String,
271
+ error: String,
272
+ name: String,
273
+ id: String,
274
+ class: String
275
+ }
276
+ });
277
+ if (!customElements.get('lv-input')) {
278
+ customElements.define('lv-input', InputElement);
279
+ }
280
+ } else if (globalThis.LightviewX?.createCustomElement) {
258
281
  const InputElement = globalThis.LightviewX.createCustomElement(Input);
259
282
  if (!customElements.get('lv-input')) {
260
283
  customElements.define('lv-input', InputElement);
@@ -168,7 +168,7 @@ const RadioGroup = (props = {}) => {
168
168
  } = props;
169
169
 
170
170
  // Normalize options
171
- const normalizedOptions = options.map(opt =>
171
+ const normalizedOptions = (Array.isArray(options) ? options : []).map(opt =>
172
172
  typeof opt === 'string' ? { value: opt, label: opt } : opt
173
173
  );
174
174
 
@@ -323,7 +323,42 @@ globalThis.Lightview.tags.Radio = Radio;
323
323
  globalThis.Lightview.tags.RadioGroup = RadioGroup;
324
324
 
325
325
  // Register as Custom Elements
326
- if (globalThis.LightviewX?.createCustomElement) {
326
+ if (globalThis.LightviewX?.customElementWrapper) {
327
+ if (!customElements.get('lv-radio')) {
328
+ customElements.define('lv-radio', globalThis.LightviewX.customElementWrapper(Radio, {
329
+ attributeMap: {
330
+ name: String,
331
+ value: String,
332
+ checked: Boolean,
333
+ size: String,
334
+ color: String,
335
+ disabled: Boolean,
336
+ label: String,
337
+ id: String,
338
+ class: String
339
+ }
340
+ }));
341
+ }
342
+ if (!customElements.get('lv-radio-group')) {
343
+ customElements.define('lv-radio-group', globalThis.LightviewX.customElementWrapper(RadioGroup, {
344
+ attributeMap: {
345
+ options: Array,
346
+ value: String,
347
+ defaultValue: String,
348
+ name: String,
349
+ label: String,
350
+ helper: String,
351
+ error: String,
352
+ color: String,
353
+ size: String,
354
+ horizontal: Boolean,
355
+ disabled: Boolean,
356
+ required: Boolean,
357
+ class: String
358
+ }
359
+ }));
360
+ }
361
+ } else if (globalThis.LightviewX?.createCustomElement) {
327
362
  const RadioElement = globalThis.LightviewX.createCustomElement(Radio);
328
363
  if (!customElements.get('lv-radio')) {
329
364
  customElements.define('lv-radio', RadioElement);
@@ -277,7 +277,30 @@ const Select = (props = {}) => {
277
277
  globalThis.Lightview.tags.Select = Select;
278
278
 
279
279
  // Register as Custom Element
280
- if (globalThis.LightviewX?.createCustomElement) {
280
+ if (globalThis.LightviewX?.customElementWrapper) {
281
+ const SelectElement = globalThis.LightviewX.customElementWrapper(Select, {
282
+ attributeMap: {
283
+ options: Array,
284
+ value: String,
285
+ defaultValue: String,
286
+ placeholder: String,
287
+ size: String,
288
+ color: String,
289
+ ghost: Boolean,
290
+ disabled: Boolean,
291
+ required: Boolean,
292
+ label: String,
293
+ helper: String,
294
+ error: String,
295
+ name: String,
296
+ id: String,
297
+ class: String
298
+ }
299
+ });
300
+ if (!customElements.get('lv-select')) {
301
+ customElements.define('lv-select', SelectElement);
302
+ }
303
+ } else if (globalThis.LightviewX?.createCustomElement) {
281
304
  const SelectElement = globalThis.LightviewX.createCustomElement(Select);
282
305
  if (!customElements.get('lv-select')) {
283
306
  customElements.define('lv-select', SelectElement);
@@ -191,7 +191,27 @@ const Toggle = (props = {}) => {
191
191
  globalThis.Lightview.tags.Toggle = Toggle;
192
192
 
193
193
  // Register as Custom Element
194
- if (globalThis.LightviewX?.createCustomElement) {
194
+ if (globalThis.LightviewX?.customElementWrapper) {
195
+ const ToggleElement = globalThis.LightviewX.customElementWrapper(Toggle, {
196
+ attributeMap: {
197
+ checked: Boolean,
198
+ defaultChecked: Boolean,
199
+ size: String,
200
+ color: String,
201
+ disabled: Boolean,
202
+ label: String,
203
+ labelPosition: String,
204
+ description: String,
205
+ name: String,
206
+ id: String,
207
+ class: String,
208
+ theme: String
209
+ }
210
+ });
211
+ if (!customElements.get('lv-toggle')) {
212
+ customElements.define('lv-toggle', ToggleElement);
213
+ }
214
+ } else if (globalThis.LightviewX?.createCustomElement) {
195
215
  const ToggleElement = globalThis.LightviewX.createCustomElement(Toggle);
196
216
  if (!customElements.get('lv-toggle')) {
197
217
  customElements.define('lv-toggle', ToggleElement);
@@ -74,18 +74,58 @@ Breadcrumbs.Item = (props = {}, ...children) => {
74
74
  if (!tags) return null;
75
75
 
76
76
  const { href, class: className = '', ...rest } = props;
77
+ const allChildren = children.flat(Infinity);
77
78
 
78
79
  if (href) {
79
80
  return tags.li({ class: className, ...rest },
80
- tags.a({ href }, ...children)
81
+ tags.a({ href }, ...allChildren)
81
82
  );
82
83
  }
83
84
 
84
- return tags.li({ class: className, ...rest }, ...children);
85
+ return tags.li({ class: className, ...rest }, ...allChildren);
85
86
  };
86
87
 
87
88
 
88
89
  const tags = globalThis.Lightview.tags;
89
90
  tags.Breadcrumbs = Breadcrumbs;
90
91
  tags['Breadcrumbs.Item'] = Breadcrumbs.Item;
92
+
93
+ // Register as custom elements
94
+ if (globalThis.LightviewX?.customElementWrapper) {
95
+ const BreadcrumbsElement = globalThis.LightviewX.customElementWrapper(Breadcrumbs, {
96
+ attributeMap: {
97
+ items: Array,
98
+ useShadow: Boolean,
99
+ class: String
100
+ },
101
+ childElements: {
102
+ 'lv-breadcrumbs-item': {
103
+ component: Breadcrumbs.Item,
104
+ attributeMap: {
105
+ href: String,
106
+ class: String
107
+ }
108
+ }
109
+ }
110
+ });
111
+
112
+ if (!customElements.get('lv-breadcrumbs')) {
113
+ customElements.define('lv-breadcrumbs', BreadcrumbsElement);
114
+ }
115
+
116
+ const BreadcrumbsItemElement = globalThis.LightviewX.customElementWrapper(Breadcrumbs.Item, {
117
+ attributeMap: {
118
+ href: String,
119
+ class: String
120
+ }
121
+ });
122
+
123
+ if (!customElements.get('lv-breadcrumbs-item')) {
124
+ customElements.define('lv-breadcrumbs-item', BreadcrumbsItemElement);
125
+ }
126
+ } else if (globalThis.LightviewX?.createCustomElement) {
127
+ globalThis.LightviewX.createCustomElement(Breadcrumbs, { tagName: 'lv-breadcrumbs' });
128
+ globalThis.LightviewX.createCustomElement(Breadcrumbs.Item, { tagName: 'lv-breadcrumbs-item' });
129
+ }
130
+
91
131
  export default Breadcrumbs;