lightview 2.0.9 → 2.1.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 (77) hide show
  1. package/build-bundles.mjs +109 -0
  2. package/cdom/helpers/array.js +70 -0
  3. package/cdom/helpers/compare.js +26 -0
  4. package/cdom/helpers/conditional.js +34 -0
  5. package/cdom/helpers/datetime.js +54 -0
  6. package/cdom/helpers/format.js +20 -0
  7. package/cdom/helpers/logic.js +24 -0
  8. package/cdom/helpers/lookup.js +25 -0
  9. package/cdom/helpers/math.js +34 -0
  10. package/cdom/helpers/network.js +41 -0
  11. package/cdom/helpers/state.js +77 -0
  12. package/cdom/helpers/stats.js +39 -0
  13. package/cdom/helpers/string.js +49 -0
  14. package/cdom/parser.js +602 -0
  15. package/components/actions/button.js +16 -3
  16. package/components/actions/swap.js +26 -3
  17. package/components/daisyui.js +1 -1
  18. package/components/data-display/alert.js +13 -3
  19. package/components/data-display/badge.js +11 -3
  20. package/components/data-display/kbd.js +9 -3
  21. package/components/data-display/loading.js +11 -3
  22. package/components/data-display/progress.js +11 -3
  23. package/components/data-display/radial-progress.js +12 -3
  24. package/components/data-display/tooltip.js +17 -0
  25. package/components/layout/divider.js +21 -1
  26. package/components/layout/indicator.js +14 -0
  27. package/components/navigation/tabs.js +291 -16
  28. package/docs/api/elements.html +125 -49
  29. package/docs/api/hypermedia.html +29 -2
  30. package/docs/api/index.html +6 -2
  31. package/docs/api/nav.html +18 -4
  32. package/docs/cdom-nav.html +29 -0
  33. package/docs/cdom.html +362 -0
  34. package/docs/components/alert.html +8 -8
  35. package/docs/components/badge.html +55 -0
  36. package/docs/components/button.html +78 -92
  37. package/docs/components/component-nav.html +1 -1
  38. package/docs/components/divider.html +65 -21
  39. package/docs/components/indicator.html +85 -31
  40. package/docs/components/kbd.html +64 -25
  41. package/docs/components/loading.html +55 -39
  42. package/docs/components/progress.html +44 -3
  43. package/docs/components/radial-progress.html +32 -12
  44. package/docs/components/swap.html +183 -100
  45. package/docs/components/tabs.html +146 -278
  46. package/docs/components/tooltip.html +71 -31
  47. package/docs/getting-started/index.html +7 -5
  48. package/docs/index.html +1 -1
  49. package/docs/syntax-nav.html +10 -0
  50. package/docs/syntax.html +8 -6
  51. package/index.html +2 -2
  52. package/lightview-all.js +1 -0
  53. package/lightview-cdom.js +1 -0
  54. package/lightview-x.js +1 -1608
  55. package/lightview.js +1 -766
  56. package/lightview.js.bak +1 -0
  57. package/package.json +6 -2
  58. package/src/lightview-all.js +10 -0
  59. package/src/lightview-cdom.js +305 -0
  60. package/src/lightview-x.js +1581 -0
  61. package/src/lightview.js +694 -0
  62. package/src/reactivity/signal.js +133 -0
  63. package/src/reactivity/state.js +217 -0
  64. package/test-text-tag.js +6 -0
  65. package/tests/cdom/fixtures/helpers.cdomc +62 -0
  66. package/tests/cdom/fixtures/user.cdom +14 -0
  67. package/tests/cdom/fixtures/user.cdomc +12 -0
  68. package/tests/cdom/fixtures/user.odom +18 -0
  69. package/tests/cdom/fixtures/user.vdom +11 -0
  70. package/tests/cdom/helpers.test.js +121 -0
  71. package/tests/cdom/loader.test.js +125 -0
  72. package/tests/cdom/parser.test.js +108 -0
  73. package/tests/cdom/reactivity.test.js +186 -0
  74. package/tests/text-tag.test.js +77 -0
  75. package/vite.config.mjs +52 -0
  76. package/components/data-display/skeleton.js +0 -66
  77. package/docs/components/skeleton.html +0 -447
@@ -0,0 +1,125 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import Lightview from '../../src/lightview.js';
6
+ import LightviewX from '../../src/lightview-x.js';
7
+ import LightviewCDOM from '../../src/lightview-cdom.js';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+
11
+ describe('cdom Full Loader Tests', () => {
12
+ let handleSrc;
13
+
14
+ beforeEach(() => {
15
+ globalThis.window = globalThis;
16
+ globalThis.__DEBUG__ = true;
17
+ globalThis.Lightview = Lightview;
18
+ globalThis.LightviewX = LightviewX;
19
+ globalThis.LightviewCDOM = LightviewCDOM;
20
+
21
+ if (typeof document !== 'undefined') {
22
+ document.body.innerHTML = '';
23
+ }
24
+
25
+ Lightview.registry.clear();
26
+ LightviewX.state({
27
+ user: {
28
+ name: 'Alice',
29
+ age: 30,
30
+ role: 'Admin',
31
+ status: 'Available',
32
+ score: 100,
33
+ level: 5,
34
+ points: [10, 20, 30],
35
+ isVip: true,
36
+ account: { type: 'Premium' },
37
+ details: { city: 'NYC', zip: '10001' },
38
+ discount: 10,
39
+ activity: {
40
+ purchases: [5, 10, 15]
41
+ }
42
+ }
43
+ }, 'app');
44
+
45
+ if (globalThis.LightviewX?.internals?.handleSrcAttribute) {
46
+ handleSrc = globalThis.LightviewX.internals.handleSrcAttribute;
47
+ }
48
+
49
+ globalThis.fetch = vi.fn().mockImplementation((url) => {
50
+ const fileName = url.toString().split('/').pop();
51
+ const filePath = path.join(__dirname, 'fixtures', fileName);
52
+ if (!fs.existsSync(filePath)) return Promise.resolve({ ok: false, status: 404 });
53
+ const content = fs.readFileSync(filePath, 'utf8');
54
+ return Promise.resolve({
55
+ ok: true,
56
+ text: () => Promise.resolve(content),
57
+ json: () => Promise.resolve(JSON.parse(content)),
58
+ url: url.toString()
59
+ });
60
+ });
61
+ });
62
+
63
+ const cleanHTML = (html) => html.replace(/<!--lv:[se]-->/g, '').replace(/\s+/g, ' ').trim();
64
+
65
+ it('should load and parse user.vdom with cdom expressions', async () => {
66
+ const container = Lightview.tags.div({ src: '/user.vdom' });
67
+ await handleSrc(container, '/user.vdom', 'div', {
68
+ element: Lightview.element,
69
+ setupChildren: Lightview.internals.setupChildren
70
+ });
71
+
72
+ const html = cleanHTML(container.domEl.innerHTML);
73
+ expect(html).toContain('Alice');
74
+ expect(html).toContain('Age: 30');
75
+ expect(html).toContain('Account: Premium');
76
+ expect(html).toContain('VIP Status: Yes');
77
+ expect(html).toContain('Location: NYC');
78
+ });
79
+
80
+ it('should load and parse user.odom with cdom expressions', async () => {
81
+ const container = Lightview.tags.div({ src: '/user.odom' });
82
+ await handleSrc(container, '/user.odom', 'div', {
83
+ element: Lightview.element,
84
+ setupChildren: Lightview.internals.setupChildren
85
+ });
86
+
87
+ const html = cleanHTML(container.domEl.innerHTML);
88
+ expect(html).toContain('Alice');
89
+ expect(html).toContain('Score: 100');
90
+ expect(html).toContain('Level: 5');
91
+ // Activity: purchases [5, 10, 15] -> sum = 30
92
+ expect(html).toContain('Activity Total: 30');
93
+ // Relative path: ../discount (10) * sum(purchases...) (30) = 300
94
+ expect(html).toContain('With Discount: 300');
95
+ });
96
+
97
+ it('should load and parse user.cdom (JSON ODOM file)', async () => {
98
+ const container = Lightview.tags.div({ src: '/user.cdom' });
99
+ await handleSrc(container, '/user.cdom', 'div', {
100
+ element: Lightview.element,
101
+ setupChildren: Lightview.internals.setupChildren
102
+ });
103
+
104
+ const html = cleanHTML(container.domEl.innerHTML);
105
+ expect(html).toContain('cdom-card');
106
+ expect(html).toContain('Welcome, Alice');
107
+ expect(html).toContain('(VIP)');
108
+ expect(html).toContain('Discount: 20%');
109
+ });
110
+
111
+ it('should load and parse user.cdomc (Unquoted Properties/Expressions)', async () => {
112
+ const container = Lightview.tags.div({ src: '/user.cdomc' });
113
+ await handleSrc(container, '/user.cdomc', 'div', {
114
+ element: Lightview.element,
115
+ setupChildren: Lightview.internals.setupChildren
116
+ });
117
+
118
+ const html = cleanHTML(container.domEl.innerHTML);
119
+ if (__DEBUG__) console.log('ACTUAL HTML:', html);
120
+ expect(html).toContain('profile-compiled');
121
+ expect(html).toContain('Alice');
122
+ expect(html).toContain('Available');
123
+ expect(html).toContain('Tier: Platinum');
124
+ });
125
+ });
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import Lightview from '../../src/lightview.js';
3
+ import LightviewX from '../../src/lightview-x.js';
4
+ import { resolvePath, parseExpression, registerHelper } from '../../cdom/parser.js';
5
+ import { registerMathHelpers } from '../../cdom/helpers/math.js';
6
+ import { registerLogicHelpers } from '../../cdom/helpers/logic.js';
7
+ import { registerStringHelpers } from '../../cdom/helpers/string.js';
8
+ import { registerArrayHelpers } from '../../cdom/helpers/array.js';
9
+ import { registerCompareHelpers } from '../../cdom/helpers/compare.js';
10
+ import { registerConditionalHelpers } from '../../cdom/helpers/conditional.js';
11
+ import { registerDateTimeHelpers } from '../../cdom/helpers/datetime.js';
12
+ import { registerFormatHelpers } from '../../cdom/helpers/format.js';
13
+ import { registerLookupHelpers } from '../../cdom/helpers/lookup.js';
14
+ import { registerStatsHelpers } from '../../cdom/helpers/stats.js';
15
+ import { registerStateHelpers } from '../../cdom/helpers/state.js';
16
+
17
+ describe('cdom Parser', () => {
18
+ beforeEach(() => {
19
+ // Clear registry before each test
20
+ Lightview.registry.clear();
21
+ // Register standard helpers
22
+ registerMathHelpers(registerHelper);
23
+ registerLogicHelpers(registerHelper);
24
+ registerStringHelpers(registerHelper);
25
+ registerArrayHelpers(registerHelper);
26
+ registerCompareHelpers(registerHelper);
27
+ registerConditionalHelpers(registerHelper);
28
+ registerDateTimeHelpers(registerHelper);
29
+ registerFormatHelpers(registerHelper);
30
+ registerLookupHelpers(registerHelper);
31
+ registerStatsHelpers(registerHelper);
32
+ registerStateHelpers((name, fn) => registerHelper(name, fn, { pathAware: true }));
33
+
34
+ // Attach to global for the parser to find it
35
+ globalThis.Lightview = Lightview;
36
+ });
37
+
38
+ describe('Path Resolution', () => {
39
+ it('resolves absolute global paths', () => {
40
+ Lightview.signal('Alice', 'userName');
41
+ expect(resolvePath('$/userName')).toBe('Alice');
42
+ });
43
+
44
+ it('resolves deep global paths in state', () => {
45
+ LightviewX.state({ profile: { name: 'Bob' } }, 'user');
46
+ expect(resolvePath('$/user/profile/name')).toBe('Bob');
47
+ });
48
+
49
+ it('resolves relative paths against context', () => {
50
+ const context = { age: 30, city: 'London' };
51
+ expect(resolvePath('./age', context)).toBe(30);
52
+ });
53
+
54
+ it('resolves array indices', () => {
55
+ const context = { items: ['apple', 'banana'] };
56
+ expect(resolvePath('./items/1', context)).toBe('banana');
57
+ });
58
+ });
59
+
60
+ describe('Expression Parsing', () => {
61
+ it('creates a computed signal for a path', () => {
62
+ const sig = Lightview.signal(10, 'count');
63
+ const expr = parseExpression('$/count');
64
+
65
+ expect(expr.value).toBe(10);
66
+
67
+ sig.value = 20;
68
+ expect(expr.value).toBe(20);
69
+ });
70
+
71
+ it('supports math helpers', () => {
72
+ Lightview.signal(5, 'a');
73
+ Lightview.signal(10, 'b');
74
+ const expr = parseExpression('$/+(a, b)');
75
+
76
+ expect(expr.value).toBe(15);
77
+ });
78
+
79
+ it('supports logical helpers', () => {
80
+ Lightview.signal(true, 'isVip');
81
+ const expr = parseExpression('$/if(isVip, "Gold", "Silver")');
82
+
83
+ expect(expr.value).toBe('Gold');
84
+
85
+ Lightview.get('isVip').value = false;
86
+ expect(expr.value).toBe('Silver');
87
+ });
88
+
89
+ it('supports the explosion operator (...)', () => {
90
+ LightviewX.state({ scores: [10, 20, 30] }, 'game');
91
+ const expr = parseExpression('$/sum(game/scores...)');
92
+
93
+ expect(expr.value).toBe(60);
94
+
95
+ const scores = Lightview.get('game').scores;
96
+ scores.push(40);
97
+ expect(expr.value).toBe(100);
98
+ });
99
+
100
+ it('handles nested navigation with helpers ($/user/upper(name))', () => {
101
+ LightviewX.state({ user: { name: 'charlie' } }, 'app');
102
+ // This tests: navigate to app/user, then call upper() on app/user.name
103
+ const expr = parseExpression('$/app/user/upper(name)');
104
+
105
+ expect(expr.value).toBe('CHARLIE');
106
+ });
107
+ });
108
+ });
@@ -0,0 +1,186 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import Lightview from '../../src/lightview.js';
3
+ import LightviewX from '../../src/lightview-x.js';
4
+ import LightviewCDOM from '../../src/lightview-cdom.js';
5
+
6
+ describe('cdom Reactivity', () => {
7
+ beforeEach(() => {
8
+ Lightview.registry.clear();
9
+ globalThis.Lightview = Lightview;
10
+ globalThis.LightviewCDOM = LightviewCDOM;
11
+ });
12
+
13
+ it('should update DOM-like values when signals change', () => {
14
+ const title = Lightview.signal('Hello', 'pageTitle');
15
+ const expr = LightviewCDOM.parseExpression('$/pageTitle');
16
+
17
+ expect(expr.value).toBe('Hello');
18
+
19
+ title.value = 'World';
20
+ expect(expr.value).toBe('World');
21
+ });
22
+
23
+ it('should handle deep state reactivity within a collection', () => {
24
+ const bills = LightviewX.state([
25
+ { id: 1, amount: 100 },
26
+ { id: 2, amount: 200 }
27
+ ], 'bills');
28
+
29
+ const totalExpr = LightviewCDOM.parseExpression('$/sum(bills/amount...)');
30
+ expect(totalExpr.value).toBe(300);
31
+
32
+ // Update an existing item
33
+ bills[0].amount = 150;
34
+ expect(totalExpr.value).toBe(350);
35
+
36
+ // Add a new item
37
+ bills.push({ id: 3, amount: 50 });
38
+ expect(totalExpr.value).toBe(400);
39
+
40
+ // Remove an item
41
+ bills.pop();
42
+ expect(totalExpr.value).toBe(350);
43
+ });
44
+
45
+ it('should handle conditional logic reactively', () => {
46
+ const user = LightviewX.state({ loggedIn: false, name: 'Guest' }, 'user');
47
+ const greeting = LightviewCDOM.parseExpression('$/user/if(loggedIn, concat("Welcome, ", ./name), "Please Login")');
48
+
49
+ expect(greeting.value).toBe('Please Login');
50
+
51
+ user.loggedIn = true;
52
+ user.name = 'Alice';
53
+ expect(greeting.value).toBe('Welcome, Alice');
54
+ });
55
+ it('should handle reactivity on multi-level nested objects', () => {
56
+ const settings = LightviewX.state({
57
+ theme: {
58
+ colors: {
59
+ primary: 'blue',
60
+ secondary: 'gray'
61
+ },
62
+ font: {
63
+ size: '16px'
64
+ }
65
+ },
66
+ notifications: {
67
+ email: true
68
+ }
69
+ }, 'settings');
70
+
71
+ const colorExpr = LightviewCDOM.parseExpression('$/settings/theme/colors/primary');
72
+ const sizeExpr = LightviewCDOM.parseExpression('$/settings/theme/font/size');
73
+ const deepExpr = LightviewCDOM.parseExpression('$/concat(settings/theme/colors/primary, "-", settings/theme/font/size)');
74
+
75
+ expect(colorExpr.value).toBe('blue');
76
+ expect(sizeExpr.value).toBe('16px');
77
+ expect(deepExpr.value).toBe('blue-16px');
78
+
79
+ // Update deep property
80
+ settings.theme.colors.primary = 'red';
81
+ expect(colorExpr.value).toBe('red');
82
+ expect(deepExpr.value).toBe('red-16px');
83
+
84
+ // Update another deep branch
85
+ settings.theme.font.size = '18px';
86
+ expect(sizeExpr.value).toBe('18px');
87
+ expect(deepExpr.value).toBe('red-18px');
88
+
89
+ // Update via swapping nested object
90
+ settings.theme.colors = { primary: 'green', secondary: 'white' };
91
+ expect(colorExpr.value).toBe('green');
92
+ expect(deepExpr.value).toBe('green-18px');
93
+ });
94
+
95
+ it('should handle reactivity in a multi-level cdomC structure', () => {
96
+ // 1. Setup Multi-level State
97
+ const profile = LightviewX.state({
98
+ user: {
99
+ details: {
100
+ name: 'Bob',
101
+ avatar: { src: 'img.png', alt: 'Profile' }
102
+ },
103
+ stats: {
104
+ posts: 10,
105
+ followers: 50
106
+ }
107
+ },
108
+ ui: {
109
+ theme: 'dark'
110
+ }
111
+ }, 'profile');
112
+
113
+ // 2. Define Multi-level cdomC Structure using expressions
114
+ // This simulates parsing a nested .cdomc file
115
+ const cdomcStructure = {
116
+ div: {
117
+ id: 'profile-card',
118
+ class: '$/profile/ui/theme', // Bound to ui.theme
119
+ children: [
120
+ {
121
+ div: {
122
+ class: 'header',
123
+ children: [
124
+ { h1: { children: ['$/profile/user/details/name'] } }, // Bound to user.details.name
125
+ { img: { src: '$/profile/user/details/avatar/src', alt: '$/profile/user/details/avatar/alt' } }
126
+ ]
127
+ }
128
+ },
129
+ {
130
+ div: {
131
+ class: 'stats',
132
+ children: [
133
+ { span: { children: ["Posts: ", '$/profile/user/stats/posts'] } }, // Bound to user.stats.posts
134
+ { span: { children: ["Followers: ", '$/profile/user/stats/followers'] } }
135
+ ]
136
+ }
137
+ },
138
+ // New section with reactive helpers
139
+ {
140
+ div: {
141
+ class: 'helpers-test',
142
+ children: [
143
+ // Logic + String helpers: "HELLO BOB" or "HELLO USER"
144
+ { p: { children: ['$/upper(if(profile/user/details/name, profile/user/details/name, "User"))'] } },
145
+ // Math + Stats helpers: Sum of posts and followers
146
+ { p: { children: ['Total interactions: ', '$/sum(profile/user/stats/posts, profile/user/stats/followers)'] } }
147
+ ]
148
+ }
149
+ }
150
+ ]
151
+ }
152
+ };
153
+
154
+ // 3. Hydrate the structure
155
+ const hydrated = LightviewCDOM.hydrate(cdomcStructure);
156
+
157
+ // 4. Inspect & Assert Initial State
158
+ const root = hydrated.div;
159
+ const header = root.children[0].div;
160
+ const stats = root.children[1].div;
161
+ const helpers = root.children[2].div;
162
+
163
+ expect(root.class.value).toBe('dark');
164
+ expect(header.children[0].h1.children[0].value).toBe('Bob');
165
+ expect(header.children[1].img.src.value).toBe('img.png');
166
+ expect(stats.children[0].span.children[1].value).toBe(10);
167
+
168
+ // Assert Helpers Initial State
169
+ expect(helpers.children[0].p.children[0].value).toBe('BOB'); // upper('Bob')
170
+ expect(helpers.children[1].p.children[1].value).toBe(60); // sum(10, 50)
171
+
172
+ // 5. Trigger Deep Updates
173
+ profile.user.details.name = 'Robert';
174
+ profile.ui.theme = 'light';
175
+ profile.user.stats.posts = 20;
176
+
177
+ // 6. Assert Updates Propagated to cdomC Structure
178
+ expect(root.class.value).toBe('light'); // Top-level attr
179
+ expect(header.children[0].h1.children[0].value).toBe('Robert'); // Deep nested text
180
+ expect(stats.children[0].span.children[1].value).toBe(20); // Deep nested sibling
181
+
182
+ // Assert Helpers Updated State
183
+ expect(helpers.children[0].p.children[0].value).toBe('ROBERT'); // upper('Robert')
184
+ expect(helpers.children[1].p.children[1].value).toBe(70); // sum(20, 50)
185
+ });
186
+ });
@@ -0,0 +1,77 @@
1
+
2
+ import { describe, it, expect, beforeEach } from 'vitest';
3
+ import { parseHTML } from 'linkedom';
4
+ import Lightview from '../src/lightview.js';
5
+
6
+ describe('text tag', () => {
7
+ let document;
8
+
9
+ beforeEach(() => {
10
+ const dom = parseHTML('<!DOCTYPE html><html><body></body></html>');
11
+ document = dom.document;
12
+ globalThis.document = document;
13
+ globalThis.Text = dom.Text;
14
+ globalThis.HTMLElement = dom.HTMLElement;
15
+ globalThis.ShadowRoot = dom.ShadowRoot;
16
+ globalThis.MutationObserver = class { observe() { } disconnect() { } };
17
+ globalThis.requestAnimationFrame = (fn) => setTimeout(fn, 0);
18
+ globalThis.CSSStyleSheet = class { };
19
+
20
+ Lightview.registry.clear();
21
+ });
22
+
23
+ it('should create a single text node with space-separated children', () => {
24
+ const el = Lightview.tags.text('Hello', 'World');
25
+ expect(el.domEl).toBeInstanceOf(globalThis.Text);
26
+ expect(el.domEl.textContent).toBe('Hello World');
27
+ });
28
+
29
+ it('should handle reactive children', async () => {
30
+ const name = Lightview.signal('Alice');
31
+ const el = Lightview.tags.text('Hello', () => name.value);
32
+
33
+ expect(el.domEl.textContent).toBe('Hello Alice');
34
+
35
+ name.value = 'Bob';
36
+ // Lightview effects run in a microtask or immediately depending on configuration
37
+ // Usually, Lightview signals in this project run synchronously if triggered?
38
+ // Let's check reactivity.
39
+
40
+ expect(el.domEl.textContent).toBe('Hello Bob');
41
+ });
42
+
43
+ it('should handle nested arrays and other types', () => {
44
+ const el = Lightview.tags.text('Count:', [1, 2, 3], true);
45
+ expect(el.domEl.textContent).toBe('Count: 1 2 3 true');
46
+ });
47
+
48
+ it('should ignore null and undefined values but keep the space', () => {
49
+ // Based on my implementation: .map(c => ... return val === null ? '' : String(val)).join(' ')
50
+ const el = Lightview.tags.text('a', null, 'b');
51
+ expect(el.domEl.textContent).toBe('a b');
52
+ });
53
+
54
+ it('should work with Lightview.tags.text(...) as a child of another element', () => {
55
+ const div = Lightview.tags.div(
56
+ Lightview.tags.text('Part', '1'),
57
+ ' ',
58
+ Lightview.tags.text('Part', '2')
59
+ );
60
+
61
+ expect(div.domEl.childNodes.length).toBe(3);
62
+ expect(div.domEl.childNodes[0]).toBeInstanceOf(globalThis.Text);
63
+ expect(div.domEl.childNodes[2]).toBeInstanceOf(globalThis.Text);
64
+ expect(div.domEl.textContent).toBe('Part 1 Part 2');
65
+ });
66
+
67
+ it('should use standard SVG text element when in SVG context', () => {
68
+ const svg = Lightview.element('svg', {}, [
69
+ { tag: 'text', children: ['Hello SVG'] }
70
+ ]);
71
+
72
+ const textElement = svg.domEl.firstChild;
73
+ expect(textElement.tagName.toLowerCase()).toBe('text');
74
+ expect(textElement.namespaceURI).toBe('http://www.w3.org/2000/svg');
75
+ expect(textElement).not.toBeInstanceOf(globalThis.Text);
76
+ });
77
+ });
@@ -0,0 +1,52 @@
1
+ import { defineConfig } from 'vite';
2
+ import { resolve } from 'path';
3
+
4
+ export default defineConfig({
5
+ build: {
6
+ lib: {
7
+ // Define multiple entry points
8
+ entry: {
9
+ 'lightview': resolve(__dirname, 'src/lightview.js'),
10
+ 'lightview-x': resolve(__dirname, 'src/lightview-x.js'),
11
+ 'lightview-cdom': resolve(__dirname, 'src/lightview-cdom.js'),
12
+ 'lightview-all': resolve(__dirname, 'src/lightview-all.js')
13
+ },
14
+ name: 'Lightview',
15
+ formats: ['iife', 'es'],
16
+ fileName: (format, entryName) => `${entryName}.${format === 'iife' ? 'js' : 'mjs'}`
17
+ },
18
+ outDir: 'build_tmp',
19
+ emptyOutDir: false,
20
+ rollupOptions: {
21
+ // Ensure components and docs are NOT part of the library bundle
22
+ external: (id) => id.includes('/components/') || id.includes('/docs/'),
23
+ output: {
24
+ // Handle global names for IIFE builds
25
+ globals: {
26
+ 'lightview': 'Lightview',
27
+ 'lightview-x': 'LightviewX',
28
+ 'lightview-cdom': 'LightviewCDOM'
29
+ },
30
+ extend: true
31
+ }
32
+ },
33
+ minify: 'terser',
34
+ terserOptions: {
35
+ compress: {
36
+ drop_console: false, // Keep console warnings for now
37
+ pure_funcs: ['console.debug']
38
+ }
39
+ }
40
+ },
41
+ test: {
42
+ environment: 'jsdom',
43
+ globals: true,
44
+ include: ['tests/**/*.test.js']
45
+ },
46
+ server: {
47
+ open: '/docs/index.html',
48
+ watch: {
49
+ usePolling: true
50
+ }
51
+ }
52
+ });
@@ -1,66 +0,0 @@
1
- /**
2
- * Lightview Skeleton Component (DaisyUI)
3
- * @see https://daisyui.com/components/skeleton/
4
- */
5
-
6
- import '../daisyui.js';
7
-
8
- /**
9
- * Skeleton Component - loading placeholder
10
- * @param {Object} props
11
- * @param {string} props.shape - 'circle' | 'rect'
12
- * @param {string} props.width - Width class or value
13
- * @param {string} props.height - Height class or value
14
- * @param {boolean} props.useShadow - Render in Shadow DOM with isolated DaisyUI styles
15
- */
16
- const Skeleton = (props = {}, ...children) => {
17
- const { tags } = globalThis.Lightview || {};
18
- const LVX = globalThis.LightviewX || {};
19
-
20
- if (!tags) return null;
21
-
22
- const { div, shadowDOM } = tags;
23
-
24
- const {
25
- shape,
26
- width = 'w-full',
27
- height = 'h-4',
28
- useShadow,
29
- class: className = '',
30
- ...rest
31
- } = props;
32
-
33
- const classes = ['skeleton', width, height];
34
- if (shape === 'circle') classes.push('rounded-full');
35
- if (className) classes.push(className);
36
-
37
- const skeletonEl = div({ class: classes.join(' '), ...rest }, ...children);
38
-
39
- // Check if we should use shadow DOM
40
- let usesShadow = false;
41
- if (LVX.shouldUseShadow) {
42
- usesShadow = LVX.shouldUseShadow(useShadow);
43
- } else {
44
- usesShadow = useShadow === true;
45
- }
46
-
47
- if (usesShadow) {
48
- const adoptedStyleSheets = LVX.getAdoptedStyleSheets ? LVX.getAdoptedStyleSheets() : [];
49
-
50
- const themeValue = LVX.themeSignal ? () => LVX.themeSignal.value : 'light';
51
-
52
- return div({ class: 'contents' },
53
- shadowDOM({ mode: 'open', adoptedStyleSheets },
54
- div({ 'data-theme': themeValue },
55
- skeletonEl
56
- )
57
- )
58
- );
59
- }
60
-
61
- return skeletonEl;
62
- };
63
-
64
- globalThis.Lightview.tags.Skeleton = Skeleton;
65
-
66
- export default Skeleton;