juxscript 1.0.19 → 1.0.21
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.
- package/bin/cli.js +121 -72
- package/lib/components/alert.ts +212 -165
- package/lib/components/badge.ts +93 -103
- package/lib/components/base/BaseComponent.ts +397 -0
- package/lib/components/base/FormInput.ts +322 -0
- package/lib/components/button.ts +63 -122
- package/lib/components/card.ts +109 -155
- package/lib/components/charts/areachart.ts +315 -0
- package/lib/components/charts/barchart.ts +421 -0
- package/lib/components/charts/doughnutchart.ts +263 -0
- package/lib/components/charts/lib/BaseChart.ts +402 -0
- package/lib/components/charts/lib/chart-types.ts +159 -0
- package/lib/components/charts/lib/chart-utils.ts +160 -0
- package/lib/components/charts/lib/chart.ts +707 -0
- package/lib/components/checkbox.ts +264 -127
- package/lib/components/code.ts +75 -108
- package/lib/components/container.ts +113 -130
- package/lib/components/data.ts +37 -5
- package/lib/components/datepicker.ts +195 -147
- package/lib/components/dialog.ts +187 -157
- package/lib/components/divider.ts +85 -191
- package/lib/components/docs-data.json +544 -2027
- package/lib/components/dropdown.ts +178 -136
- package/lib/components/element.ts +227 -171
- package/lib/components/fileupload.ts +285 -228
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +46 -69
- package/lib/components/helpers.ts +13 -6
- package/lib/components/hero.ts +107 -95
- package/lib/components/icon.ts +160 -0
- package/lib/components/icons.ts +175 -0
- package/lib/components/include.ts +153 -5
- package/lib/components/input.ts +174 -374
- package/lib/components/kpicard.ts +16 -16
- package/lib/components/list.ts +378 -240
- package/lib/components/loading.ts +142 -211
- package/lib/components/menu.ts +103 -97
- package/lib/components/modal.ts +138 -144
- package/lib/components/nav.ts +169 -90
- package/lib/components/paragraph.ts +49 -150
- package/lib/components/progress.ts +118 -200
- package/lib/components/radio.ts +297 -149
- package/lib/components/script.ts +19 -87
- package/lib/components/select.ts +184 -186
- package/lib/components/sidebar.ts +152 -140
- package/lib/components/style.ts +19 -82
- package/lib/components/switch.ts +258 -188
- package/lib/components/table.ts +1117 -170
- package/lib/components/tabs.ts +162 -145
- package/lib/components/theme-toggle.ts +108 -169
- package/lib/components/tooltip.ts +86 -157
- package/lib/components/write.ts +108 -127
- package/lib/jux.ts +86 -41
- package/machinery/build.js +466 -0
- package/machinery/compiler.js +354 -105
- package/machinery/server.js +23 -100
- package/machinery/watcher.js +153 -130
- package/package.json +1 -2
- package/presets/base.css +1166 -0
- package/presets/notion.css +2 -1975
- package/lib/adapters/base-adapter.js +0 -35
- package/lib/adapters/index.js +0 -33
- package/lib/adapters/mysql-adapter.js +0 -65
- package/lib/adapters/postgres-adapter.js +0 -70
- package/lib/adapters/sqlite-adapter.js +0 -56
- package/lib/components/areachart.ts +0 -1246
- package/lib/components/areachartsmooth.ts +0 -1380
- package/lib/components/barchart.ts +0 -1250
- package/lib/components/chart.ts +0 -127
- package/lib/components/doughnutchart.ts +0 -1191
- package/lib/components/footer.ts +0 -165
- package/lib/components/header.ts +0 -187
- package/lib/components/layout.ts +0 -239
- package/lib/components/main.ts +0 -137
- package/lib/layouts/default.jux +0 -8
- package/lib/layouts/figma.jux +0 -0
- /package/lib/{themes → components/charts/lib}/charts.js +0 -0
package/lib/components/footer.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Footer component options
|
|
5
|
-
*/
|
|
6
|
-
export interface FooterOptions {
|
|
7
|
-
content?: string;
|
|
8
|
-
copyright?: string;
|
|
9
|
-
links?: Array<{ label: string; href: string }>;
|
|
10
|
-
class?: string;
|
|
11
|
-
style?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Footer component state
|
|
16
|
-
*/
|
|
17
|
-
type FooterState = {
|
|
18
|
-
content: string;
|
|
19
|
-
copyright: string;
|
|
20
|
-
links: Array<{ label: string; href: string }>;
|
|
21
|
-
class: string;
|
|
22
|
-
style: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Footer component
|
|
27
|
-
*
|
|
28
|
-
* Usage:
|
|
29
|
-
* const footer = jux.footer('myFooter', {
|
|
30
|
-
* copyright: '© 2025 My Company',
|
|
31
|
-
* links: [
|
|
32
|
-
* { label: 'Privacy', href: '/privacy' },
|
|
33
|
-
* { label: 'Terms', href: '/terms' }
|
|
34
|
-
* ]
|
|
35
|
-
* });
|
|
36
|
-
* footer.render('#appfooter');
|
|
37
|
-
*/
|
|
38
|
-
export class Footer {
|
|
39
|
-
state: FooterState;
|
|
40
|
-
container: HTMLElement | null = null;
|
|
41
|
-
_id: string;
|
|
42
|
-
id: string;
|
|
43
|
-
|
|
44
|
-
constructor(id: string, options: FooterOptions = {}) {
|
|
45
|
-
this._id = id;
|
|
46
|
-
this.id = id;
|
|
47
|
-
|
|
48
|
-
this.state = {
|
|
49
|
-
content: options.content ?? '',
|
|
50
|
-
copyright: options.copyright ?? '',
|
|
51
|
-
links: options.links ?? [],
|
|
52
|
-
class: options.class ?? '',
|
|
53
|
-
style: options.style ?? ''
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* -------------------------
|
|
58
|
-
* Fluent API
|
|
59
|
-
* ------------------------- */
|
|
60
|
-
|
|
61
|
-
content(value: string): this {
|
|
62
|
-
this.state.content = value;
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
copyright(value: string): this {
|
|
67
|
-
this.state.copyright = value;
|
|
68
|
-
return this;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
links(value: Array<{ label: string; href: string }>): this {
|
|
72
|
-
this.state.links = value;
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
class(value: string): this {
|
|
77
|
-
this.state.class = value;
|
|
78
|
-
return this;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
style(value: string): this {
|
|
82
|
-
this.state.style = value;
|
|
83
|
-
return this;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* -------------------------
|
|
87
|
-
* Render
|
|
88
|
-
* ------------------------- */
|
|
89
|
-
|
|
90
|
-
render(targetId?: string): this {
|
|
91
|
-
let container: HTMLElement;
|
|
92
|
-
|
|
93
|
-
if (targetId) {
|
|
94
|
-
const target = document.querySelector(targetId);
|
|
95
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
96
|
-
throw new Error(`Footer: Target element "${targetId}" not found`);
|
|
97
|
-
}
|
|
98
|
-
container = target;
|
|
99
|
-
} else {
|
|
100
|
-
container = getOrCreateContainer(this._id);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
this.container = container;
|
|
104
|
-
const { content, copyright, links, class: className, style } = this.state;
|
|
105
|
-
|
|
106
|
-
const footer = document.createElement('footer');
|
|
107
|
-
footer.className = `jux-footer ${className}`.trim();
|
|
108
|
-
footer.style.cssText = style;
|
|
109
|
-
footer.id = this._id;
|
|
110
|
-
|
|
111
|
-
if (content) {
|
|
112
|
-
const contentEl = document.createElement('div');
|
|
113
|
-
contentEl.className = 'jux-footer-content';
|
|
114
|
-
contentEl.textContent = content;
|
|
115
|
-
footer.appendChild(contentEl);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (links.length > 0) {
|
|
119
|
-
const linksEl = document.createElement('div');
|
|
120
|
-
linksEl.className = 'jux-footer-links';
|
|
121
|
-
|
|
122
|
-
links.forEach(link => {
|
|
123
|
-
const linkEl = document.createElement('a');
|
|
124
|
-
linkEl.className = 'jux-footer-link';
|
|
125
|
-
linkEl.href = link.href;
|
|
126
|
-
linkEl.textContent = link.label;
|
|
127
|
-
linksEl.appendChild(linkEl);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
footer.appendChild(linksEl);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (copyright) {
|
|
134
|
-
const copyrightEl = document.createElement('div');
|
|
135
|
-
copyrightEl.className = 'jux-footer-copyright';
|
|
136
|
-
copyrightEl.textContent = copyright;
|
|
137
|
-
footer.appendChild(copyrightEl);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
container.appendChild(footer);
|
|
141
|
-
return this;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Render to another Jux component's container
|
|
146
|
-
*/
|
|
147
|
-
renderTo(juxComponent: any): this {
|
|
148
|
-
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
149
|
-
throw new Error('Footer.renderTo: Invalid component - not an object');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
153
|
-
throw new Error('Footer.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return this.render(`#${juxComponent._id}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Factory helper
|
|
162
|
-
*/
|
|
163
|
-
export function footer(id: string, options: FooterOptions = {}): Footer {
|
|
164
|
-
return new Footer(id, options);
|
|
165
|
-
}
|
package/lib/components/header.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Header component options
|
|
5
|
-
*/
|
|
6
|
-
export interface HeaderOptions {
|
|
7
|
-
title?: string;
|
|
8
|
-
logo?: string;
|
|
9
|
-
navigation?: Array<{ label: string; href: string }>;
|
|
10
|
-
sticky?: boolean;
|
|
11
|
-
style?: string;
|
|
12
|
-
class?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Header component state
|
|
17
|
-
*/
|
|
18
|
-
type HeaderState = {
|
|
19
|
-
title: string;
|
|
20
|
-
logo: string;
|
|
21
|
-
navigation: Array<{ label: string; href: string }>;
|
|
22
|
-
sticky: boolean;
|
|
23
|
-
style: string;
|
|
24
|
-
class: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Header component
|
|
29
|
-
*
|
|
30
|
-
* Usage:
|
|
31
|
-
* const header = jux.header('myHeader', {
|
|
32
|
-
* title: 'My App',
|
|
33
|
-
* navigation: [
|
|
34
|
-
* { label: 'Home', href: '/' },
|
|
35
|
-
* { label: 'About', href: '/about' }
|
|
36
|
-
* ]
|
|
37
|
-
* });
|
|
38
|
-
* header.render('#appheader');
|
|
39
|
-
*/
|
|
40
|
-
export class Header {
|
|
41
|
-
state: HeaderState;
|
|
42
|
-
container: HTMLElement | null = null;
|
|
43
|
-
_id: string;
|
|
44
|
-
id: string;
|
|
45
|
-
|
|
46
|
-
constructor(id: string, options: HeaderOptions = {}) {
|
|
47
|
-
this._id = id;
|
|
48
|
-
this.id = id;
|
|
49
|
-
|
|
50
|
-
this.state = {
|
|
51
|
-
title: options.title ?? '',
|
|
52
|
-
logo: options.logo ?? '',
|
|
53
|
-
navigation: options.navigation ?? [],
|
|
54
|
-
sticky: options.sticky ?? true,
|
|
55
|
-
style: options.style ?? '',
|
|
56
|
-
class: options.class ?? ''
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/* -------------------------
|
|
61
|
-
* Fluent API
|
|
62
|
-
* ------------------------- */
|
|
63
|
-
|
|
64
|
-
title(value: string): this {
|
|
65
|
-
this.state.title = value;
|
|
66
|
-
return this;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
logo(value: string): this {
|
|
70
|
-
this.state.logo = value;
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
navigation(value: Array<{ label: string; href: string }>): this {
|
|
75
|
-
this.state.navigation = value;
|
|
76
|
-
return this;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
sticky(value: boolean): this {
|
|
80
|
-
this.state.sticky = value;
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
class(value: string): this {
|
|
85
|
-
this.state.class = value;
|
|
86
|
-
return this;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
style(value: string): this {
|
|
90
|
-
this.state.style = value;
|
|
91
|
-
return this;
|
|
92
|
-
}
|
|
93
|
-
/* -------------------------
|
|
94
|
-
* Render
|
|
95
|
-
* ------------------------- */
|
|
96
|
-
|
|
97
|
-
render(targetId?: string): this {
|
|
98
|
-
let container: HTMLElement;
|
|
99
|
-
|
|
100
|
-
if (targetId) {
|
|
101
|
-
const target = document.querySelector(targetId);
|
|
102
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
103
|
-
throw new Error(`Header: Target element "${targetId}" not found`);
|
|
104
|
-
}
|
|
105
|
-
container = target;
|
|
106
|
-
} else {
|
|
107
|
-
container = getOrCreateContainer(this._id);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
this.container = container;
|
|
111
|
-
const { title, logo, navigation, sticky, style, class: className } = this.state;
|
|
112
|
-
|
|
113
|
-
const header = document.createElement('header');
|
|
114
|
-
header.className = 'jux-header';
|
|
115
|
-
|
|
116
|
-
header.id = this._id;
|
|
117
|
-
header.style.cssText = style;
|
|
118
|
-
header.className = `jux-header ${className}`.trim();
|
|
119
|
-
|
|
120
|
-
if (sticky) {
|
|
121
|
-
header.classList.add('jux-header-sticky');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Logo section
|
|
125
|
-
if (logo || title) {
|
|
126
|
-
const logoSection = document.createElement('div');
|
|
127
|
-
logoSection.className = 'jux-header-logo';
|
|
128
|
-
|
|
129
|
-
if (logo) {
|
|
130
|
-
const logoImg = document.createElement('img');
|
|
131
|
-
logoImg.src = logo;
|
|
132
|
-
logoImg.alt = title || 'Logo';
|
|
133
|
-
logoSection.appendChild(logoImg);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (title) {
|
|
137
|
-
const titleEl = document.createElement('span');
|
|
138
|
-
titleEl.className = 'jux-header-title';
|
|
139
|
-
titleEl.textContent = title;
|
|
140
|
-
logoSection.appendChild(titleEl);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
header.appendChild(logoSection);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Navigation
|
|
147
|
-
if (navigation.length > 0) {
|
|
148
|
-
const nav = document.createElement('nav');
|
|
149
|
-
nav.className = 'jux-header-nav';
|
|
150
|
-
|
|
151
|
-
navigation.forEach(item => {
|
|
152
|
-
const link = document.createElement('a');
|
|
153
|
-
link.className = 'jux-header-nav-item';
|
|
154
|
-
link.href = item.href;
|
|
155
|
-
link.textContent = item.label;
|
|
156
|
-
nav.appendChild(link);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
header.appendChild(nav);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
container.appendChild(header);
|
|
163
|
-
return this;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Render to another Jux component's container
|
|
168
|
-
*/
|
|
169
|
-
renderTo(juxComponent: any): this {
|
|
170
|
-
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
171
|
-
throw new Error('Header.renderTo: Invalid component - not an object');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
175
|
-
throw new Error('Header.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return this.render(`#${juxComponent._id}`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Factory helper
|
|
184
|
-
*/
|
|
185
|
-
export function header(id: string, options: HeaderOptions = {}): Header {
|
|
186
|
-
return new Header(id, options);
|
|
187
|
-
}
|
package/lib/components/layout.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { ErrorHandler } from './error-handler.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Layout component interface
|
|
5
|
-
* Represents any Jux component that can be rendered
|
|
6
|
-
*/
|
|
7
|
-
interface LayoutComponent {
|
|
8
|
-
render: (target?: string) => any;
|
|
9
|
-
_componentId?: string;
|
|
10
|
-
_id?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Layout - Compose and render multiple components
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* // As a composition function
|
|
18
|
-
* jux.layout(
|
|
19
|
-
* jux.include('/lib/presets/notion/notion.css'),
|
|
20
|
-
* jux.header('appheader').render('#app'),
|
|
21
|
-
* jux.sidebar('appsidebar').render('#app'),
|
|
22
|
-
* jux.main('appmain').render('#app')
|
|
23
|
-
* );
|
|
24
|
-
*
|
|
25
|
-
* // Or load from a .jux file
|
|
26
|
-
* jux.layout('/lib/presets/notion/main.jux');
|
|
27
|
-
*/
|
|
28
|
-
export class Layout {
|
|
29
|
-
private _juxFile: string;
|
|
30
|
-
private _loaded: boolean = false;
|
|
31
|
-
private _components: any[] = [];
|
|
32
|
-
|
|
33
|
-
constructor(...args: any[]) {
|
|
34
|
-
// If first arg is string, it's a file path
|
|
35
|
-
if (args.length === 1 && typeof args[0] === 'string' && args[0].endsWith('.jux')) {
|
|
36
|
-
this._juxFile = args[0];
|
|
37
|
-
this.load();
|
|
38
|
-
} else {
|
|
39
|
-
// Otherwise, it's a list of components to compose
|
|
40
|
-
this._juxFile = '';
|
|
41
|
-
this._components = args;
|
|
42
|
-
this._compose();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Set the JUX file to load
|
|
48
|
-
*/
|
|
49
|
-
file(juxFile: string): this {
|
|
50
|
-
this._juxFile = juxFile;
|
|
51
|
-
this._loaded = false;
|
|
52
|
-
this.load();
|
|
53
|
-
return this;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get the current JUX file path
|
|
58
|
-
*/
|
|
59
|
-
getFile(): string {
|
|
60
|
-
return this._juxFile;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Compose components immediately
|
|
65
|
-
* This handles the case where components are passed directly
|
|
66
|
-
*/
|
|
67
|
-
private _compose(): this {
|
|
68
|
-
try {
|
|
69
|
-
// Components are already rendered via chaining
|
|
70
|
-
// This just ensures they're tracked
|
|
71
|
-
this._components.forEach((component, index) => {
|
|
72
|
-
// Components that return themselves from .render() are already in DOM
|
|
73
|
-
// Just log for debugging
|
|
74
|
-
if (component && typeof component === 'object') {
|
|
75
|
-
const id = component._componentId || component._id || component.id || `component-${index}`;
|
|
76
|
-
console.log(`✓ Layout component composed: ${id}`);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
this._loaded = true;
|
|
81
|
-
} catch (error: any) {
|
|
82
|
-
ErrorHandler.captureError({
|
|
83
|
-
component: 'Layout',
|
|
84
|
-
method: '_compose',
|
|
85
|
-
message: `Failed to compose layout: ${error.message}`,
|
|
86
|
-
stack: error.stack,
|
|
87
|
-
timestamp: new Date(),
|
|
88
|
-
context: {
|
|
89
|
-
componentCount: this._components.length,
|
|
90
|
-
error: 'composition_failed'
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return this;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Normalize path to absolute URL from site root
|
|
100
|
-
*/
|
|
101
|
-
private normalizePath(path: string): string {
|
|
102
|
-
// If already a full URL, return as-is
|
|
103
|
-
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
104
|
-
return path;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Convert relative path to absolute
|
|
108
|
-
// Remove leading './' if present
|
|
109
|
-
let cleanPath = path.replace(/^\.\//, '');
|
|
110
|
-
|
|
111
|
-
// Ensure it starts with /
|
|
112
|
-
if (!cleanPath.startsWith('/')) {
|
|
113
|
-
cleanPath = '/' + cleanPath;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Return absolute URL with origin
|
|
117
|
-
return new URL(cleanPath, window.location.origin).href;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Load the layout from a .jux file
|
|
122
|
-
* This will dynamically import the compiled JS file
|
|
123
|
-
*/
|
|
124
|
-
async load(): Promise<this> {
|
|
125
|
-
if (typeof document === 'undefined') {
|
|
126
|
-
return this;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (!this._juxFile) {
|
|
130
|
-
console.warn('Layout: No file specified to load');
|
|
131
|
-
return this;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
// Convert .jux to .js for the compiled output
|
|
136
|
-
let jsFile = this._juxFile.replace(/\.jux$/, '.js');
|
|
137
|
-
|
|
138
|
-
// Normalize to absolute URL for browser import
|
|
139
|
-
jsFile = this.normalizePath(jsFile);
|
|
140
|
-
|
|
141
|
-
console.log(`Loading layout: ${jsFile}`);
|
|
142
|
-
|
|
143
|
-
// Dynamic import of the layout module
|
|
144
|
-
const layoutModule = await import(jsFile);
|
|
145
|
-
|
|
146
|
-
// If the module has an init or default export, call it
|
|
147
|
-
if (typeof layoutModule.default === 'function') {
|
|
148
|
-
await layoutModule.default();
|
|
149
|
-
} else if (typeof layoutModule.init === 'function') {
|
|
150
|
-
await layoutModule.init();
|
|
151
|
-
} else if (layoutModule.default && typeof layoutModule.default === 'object') {
|
|
152
|
-
// If default export is the layout object itself
|
|
153
|
-
console.log('✓ Layout module loaded (object export)');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
this._loaded = true;
|
|
157
|
-
console.log(`✓ Layout loaded: ${this._juxFile}`);
|
|
158
|
-
} catch (error: any) {
|
|
159
|
-
ErrorHandler.captureError({
|
|
160
|
-
component: 'Layout',
|
|
161
|
-
method: 'load',
|
|
162
|
-
message: `Failed to load layout: ${error.message}`,
|
|
163
|
-
stack: error.stack,
|
|
164
|
-
timestamp: new Date(),
|
|
165
|
-
context: {
|
|
166
|
-
juxFile: this._juxFile,
|
|
167
|
-
jsFile: this._juxFile.replace(/\.jux$/, '.js'),
|
|
168
|
-
errorCode: error.code
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return this;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Check if layout is loaded
|
|
178
|
-
*/
|
|
179
|
-
isLoaded(): boolean {
|
|
180
|
-
return this._loaded;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Get all composed components
|
|
185
|
-
*/
|
|
186
|
-
getComponents(): any[] {
|
|
187
|
-
return this._components;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Add a component to the layout
|
|
192
|
-
*/
|
|
193
|
-
add(component: any): this {
|
|
194
|
-
this._components.push(component);
|
|
195
|
-
return this;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Render all components
|
|
200
|
-
* Useful if components weren't already rendered
|
|
201
|
-
*/
|
|
202
|
-
render(target?: string): this {
|
|
203
|
-
this._components.forEach((component) => {
|
|
204
|
-
if (component && typeof component.render === 'function') {
|
|
205
|
-
try {
|
|
206
|
-
component.render(target);
|
|
207
|
-
} catch (error: any) {
|
|
208
|
-
ErrorHandler.captureError({
|
|
209
|
-
component: 'Layout',
|
|
210
|
-
method: 'render',
|
|
211
|
-
message: `Failed to render component: ${error.message}`,
|
|
212
|
-
stack: error.stack,
|
|
213
|
-
timestamp: new Date(),
|
|
214
|
-
context: {
|
|
215
|
-
componentId: component._componentId || component._id || 'unknown',
|
|
216
|
-
target
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
return this;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Factory helper
|
|
229
|
-
* Accepts either:
|
|
230
|
-
* - A file path: layout('/lib/presets/notion/main.jux')
|
|
231
|
-
* - A list of components: layout(header, sidebar, main)
|
|
232
|
-
*/
|
|
233
|
-
export function layout(...args: any[]): Layout {
|
|
234
|
-
return new Layout(...args);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/* -------------------------
|
|
238
|
-
* Module Exports
|
|
239
|
-
* ------------------------- */
|
package/lib/components/main.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Main component options
|
|
5
|
-
*/
|
|
6
|
-
export interface MainOptions {
|
|
7
|
-
content?: string;
|
|
8
|
-
padding?: string;
|
|
9
|
-
style?: string;
|
|
10
|
-
class?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Main component state
|
|
15
|
-
*/
|
|
16
|
-
type MainState = {
|
|
17
|
-
content: string;
|
|
18
|
-
padding: string;
|
|
19
|
-
style: string;
|
|
20
|
-
class: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Main component - represents the main content area
|
|
25
|
-
*
|
|
26
|
-
* Usage:
|
|
27
|
-
* const main = jux.main('myMain', {
|
|
28
|
-
* content: 'Main content here',
|
|
29
|
-
* padding: '2rem'
|
|
30
|
-
* });
|
|
31
|
-
* main.render('#appmain');
|
|
32
|
-
*/
|
|
33
|
-
export class Main {
|
|
34
|
-
state: MainState;
|
|
35
|
-
container: HTMLElement | null = null;
|
|
36
|
-
_id: string;
|
|
37
|
-
id: string;
|
|
38
|
-
|
|
39
|
-
constructor(id: string, options: MainOptions = {}) {
|
|
40
|
-
this._id = id;
|
|
41
|
-
this.id = id;
|
|
42
|
-
|
|
43
|
-
this.state = {
|
|
44
|
-
content: options.content ?? '',
|
|
45
|
-
padding: options.padding ?? 'var(--space-xl)',
|
|
46
|
-
style: options.style ?? '',
|
|
47
|
-
class: options.class ?? ''
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/* -------------------------
|
|
52
|
-
* Fluent API
|
|
53
|
-
* ------------------------- */
|
|
54
|
-
|
|
55
|
-
content(value: string): this {
|
|
56
|
-
this.state.content = value;
|
|
57
|
-
return this;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
padding(value: string): this {
|
|
61
|
-
this.state.padding = value;
|
|
62
|
-
return this;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
style(value: string): this {
|
|
66
|
-
this.state.style = value;
|
|
67
|
-
return this;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
class(value: string): this {
|
|
71
|
-
this.state.class = value;
|
|
72
|
-
return this;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* -------------------------
|
|
76
|
-
* Render
|
|
77
|
-
* ------------------------- */
|
|
78
|
-
|
|
79
|
-
render(targetId?: string): this {
|
|
80
|
-
let container: HTMLElement;
|
|
81
|
-
|
|
82
|
-
if (targetId) {
|
|
83
|
-
const target = document.querySelector(targetId);
|
|
84
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
85
|
-
throw new Error(`Main: Target element "${targetId}" not found`);
|
|
86
|
-
}
|
|
87
|
-
container = target;
|
|
88
|
-
} else {
|
|
89
|
-
container = getOrCreateContainer(this._id);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
this.container = container;
|
|
93
|
-
const { content, padding, style, class: className } = this.state;
|
|
94
|
-
|
|
95
|
-
const main = document.createElement('main');
|
|
96
|
-
main.className = 'jux-main';
|
|
97
|
-
main.id = this._id;
|
|
98
|
-
main.style.padding = padding;
|
|
99
|
-
|
|
100
|
-
if (style) {
|
|
101
|
-
main.setAttribute('style', style);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (className) {
|
|
105
|
-
main.className += ` ${className}`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (content) {
|
|
109
|
-
main.innerHTML = content;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
container.appendChild(main);
|
|
113
|
-
return this;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Render to another Jux component's container
|
|
118
|
-
*/
|
|
119
|
-
renderTo(juxComponent: any): this {
|
|
120
|
-
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
121
|
-
throw new Error('Main.renderTo: Invalid component - not an object');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
125
|
-
throw new Error('Main.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return this.render(`#${juxComponent._id}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Factory helper
|
|
134
|
-
*/
|
|
135
|
-
export function main(id: string, options: MainOptions = {}): Main {
|
|
136
|
-
return new Main(id, options);
|
|
137
|
-
}
|