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.
Files changed (77) hide show
  1. package/bin/cli.js +121 -72
  2. package/lib/components/alert.ts +212 -165
  3. package/lib/components/badge.ts +93 -103
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +63 -122
  7. package/lib/components/card.ts +109 -155
  8. package/lib/components/charts/areachart.ts +315 -0
  9. package/lib/components/charts/barchart.ts +421 -0
  10. package/lib/components/charts/doughnutchart.ts +263 -0
  11. package/lib/components/charts/lib/BaseChart.ts +402 -0
  12. package/lib/components/charts/lib/chart-types.ts +159 -0
  13. package/lib/components/charts/lib/chart-utils.ts +160 -0
  14. package/lib/components/charts/lib/chart.ts +707 -0
  15. package/lib/components/checkbox.ts +264 -127
  16. package/lib/components/code.ts +75 -108
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +195 -147
  20. package/lib/components/dialog.ts +187 -157
  21. package/lib/components/divider.ts +85 -191
  22. package/lib/components/docs-data.json +544 -2027
  23. package/lib/components/dropdown.ts +178 -136
  24. package/lib/components/element.ts +227 -171
  25. package/lib/components/fileupload.ts +285 -228
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +46 -69
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +107 -95
  30. package/lib/components/icon.ts +160 -0
  31. package/lib/components/icons.ts +175 -0
  32. package/lib/components/include.ts +153 -5
  33. package/lib/components/input.ts +174 -374
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +378 -240
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +103 -97
  38. package/lib/components/modal.ts +138 -144
  39. package/lib/components/nav.ts +169 -90
  40. package/lib/components/paragraph.ts +49 -150
  41. package/lib/components/progress.ts +118 -200
  42. package/lib/components/radio.ts +297 -149
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +184 -186
  45. package/lib/components/sidebar.ts +152 -140
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +258 -188
  48. package/lib/components/table.ts +1117 -170
  49. package/lib/components/tabs.ts +162 -145
  50. package/lib/components/theme-toggle.ts +108 -169
  51. package/lib/components/tooltip.ts +86 -157
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +86 -41
  54. package/machinery/build.js +466 -0
  55. package/machinery/compiler.js +354 -105
  56. package/machinery/server.js +23 -100
  57. package/machinery/watcher.js +153 -130
  58. package/package.json +1 -2
  59. package/presets/base.css +1166 -0
  60. package/presets/notion.css +2 -1975
  61. package/lib/adapters/base-adapter.js +0 -35
  62. package/lib/adapters/index.js +0 -33
  63. package/lib/adapters/mysql-adapter.js +0 -65
  64. package/lib/adapters/postgres-adapter.js +0 -70
  65. package/lib/adapters/sqlite-adapter.js +0 -56
  66. package/lib/components/areachart.ts +0 -1246
  67. package/lib/components/areachartsmooth.ts +0 -1380
  68. package/lib/components/barchart.ts +0 -1250
  69. package/lib/components/chart.ts +0 -127
  70. package/lib/components/doughnutchart.ts +0 -1191
  71. package/lib/components/footer.ts +0 -165
  72. package/lib/components/header.ts +0 -187
  73. package/lib/components/layout.ts +0 -239
  74. package/lib/components/main.ts +0 -137
  75. package/lib/layouts/default.jux +0 -8
  76. package/lib/layouts/figma.jux +0 -0
  77. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
@@ -1,72 +1,62 @@
1
- import { getOrCreateContainer } from './helpers.js';
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+
3
+ // Event definitions
4
+ const TRIGGER_EVENTS = [] as const;
5
+ const CALLBACK_EVENTS = ['toggle'] as const;
2
6
 
3
- /**
4
- * Sidebar component options
5
- */
6
7
  export interface SidebarOptions {
7
- title?: string;
8
- width?: string;
9
8
  position?: 'left' | 'right';
9
+ width?: string;
10
10
  collapsible?: boolean;
11
11
  collapsed?: boolean;
12
12
  style?: string;
13
13
  class?: string;
14
14
  }
15
15
 
16
- /**
17
- * Sidebar component state
18
- */
19
16
  type SidebarState = {
20
- title: string;
21
- width: string | null;
22
- position: string;
17
+ position: 'left' | 'right';
18
+ width: string;
23
19
  collapsible: boolean;
24
20
  collapsed: boolean;
25
21
  style: string;
26
22
  class: string;
27
23
  };
28
24
 
29
- /**
30
- * Sidebar component
31
- *
32
- * Usage:
33
- * const sidebar = jux.sidebar('mySidebar', {
34
- * title: 'Navigation',
35
- * width: '250px',
36
- * position: 'left'
37
- * });
38
- * sidebar.render('#appsidebar');
39
- */
40
- export class Sidebar {
41
- state: SidebarState;
42
- container: HTMLElement | null = null;
43
- _id: string;
44
- id: string;
25
+ export class Sidebar extends BaseComponent<SidebarState> {
26
+ private _sidebar: HTMLElement | null = null;
45
27
 
46
28
  constructor(id: string, options: SidebarOptions = {}) {
47
- this._id = id;
48
- this.id = id;
49
-
50
- this.state = {
51
- title: options.title ?? '',
52
- width: options.width ?? null, // No default width - let CSS handle it
29
+ super(id, {
53
30
  position: options.position ?? 'left',
54
- collapsible: options.collapsible ?? false,
31
+ width: options.width ?? '',
32
+ collapsible: options.collapsible ?? true,
55
33
  collapsed: options.collapsed ?? false,
56
34
  style: options.style ?? '',
57
35
  class: options.class ?? ''
58
- };
36
+ });
59
37
  }
60
38
 
61
- /* -------------------------
62
- * Fluent API
63
- * ------------------------- */
39
+ protected getTriggerEvents(): readonly string[] {
40
+ return TRIGGER_EVENTS;
41
+ }
64
42
 
65
- title(value: string): this {
66
- this.state.title = value;
67
- return this;
43
+ protected getCallbackEvents(): readonly string[] {
44
+ return CALLBACK_EVENTS;
68
45
  }
69
46
 
47
+ /* ═════════════════════════════════════════════════════════════════
48
+ * FLUENT API
49
+ * ═════════════════════════════════════════════════════════════════ */
50
+
51
+ // ✅ Inherited from BaseComponent:
52
+ // - style(), class()
53
+ // - bind(), sync(), renderTo()
54
+ // - addClass(), removeClass(), toggleClass()
55
+ // - visible(), show(), hide()
56
+ // - attr(), attrs(), removeAttr()
57
+ // - disabled(), enable(), disable()
58
+ // - loading(), focus(), blur(), remove()
59
+
70
60
  width(value: string): this {
71
61
  this.state.width = value;
72
62
  return this;
@@ -84,143 +74,165 @@ export class Sidebar {
84
74
 
85
75
  collapsed(value: boolean): this {
86
76
  this.state.collapsed = value;
77
+ this._updateCollapsedState();
87
78
  return this;
88
79
  }
89
80
 
90
- style(value: string): this {
91
- this.state.style = value;
92
- return this;
93
- }
94
-
95
- class(value: string): this {
96
- this.state.class = value;
97
- return this;
98
- }
99
-
100
- toggle(): this {
81
+ toggle(): void {
101
82
  this.state.collapsed = !this.state.collapsed;
102
- this._updateDOM();
103
- return this;
83
+ this._updateCollapsedState();
84
+ // 🎯 Fire the toggle callback event
85
+ this._triggerCallback('toggle', this.state.collapsed);
104
86
  }
105
87
 
106
- /* -------------------------
107
- * Helpers
108
- * ------------------------- */
109
-
110
- private _updateDOM(): void {
111
- if (!this.container) return;
112
-
113
- const sidebar = this.container.querySelector(`#${this._id}`);
114
- if (!sidebar || !(sidebar instanceof HTMLElement)) return;
115
-
116
- const { width, collapsed } = this.state;
117
-
118
- if (collapsed) {
119
- sidebar.classList.add('jux-sidebar-collapsed');
120
- if (width) {
121
- sidebar.style.width = '0';
122
- }
123
- } else {
124
- sidebar.classList.remove('jux-sidebar-collapsed');
125
- if (width) {
126
- sidebar.style.width = width;
127
- }
128
- }
129
-
130
- const toggleBtn = sidebar.querySelector('.jux-sidebar-toggle');
131
- if (toggleBtn) {
132
- toggleBtn.textContent = collapsed ? '>' : '<';
88
+ private _updateCollapsedState(): void {
89
+ if (this._sidebar) {
90
+ this._sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
133
91
  }
134
92
  }
135
93
 
136
- /* -------------------------
137
- * Render
138
- * ------------------------- */
94
+ /* ═════════════════════════════════════════════════════════════════
95
+ * RENDER
96
+ * ═════════════════════════════════════════════════════════════════ */
139
97
 
140
98
  render(targetId?: string): this {
141
- let container: HTMLElement;
99
+ const container = this._setupContainer(targetId);
142
100
 
143
- if (targetId) {
144
- const target = document.querySelector(targetId);
145
- if (!target || !(target instanceof HTMLElement)) {
146
- throw new Error(`Sidebar: Target element "${targetId}" not found`);
147
- }
148
- container = target;
149
- } else {
150
- container = getOrCreateContainer(this._id);
151
- }
152
-
153
- this.container = container;
154
- const { title, width, position, collapsible, collapsed, style, class: className } = this.state;
101
+ const { position, width, collapsible, collapsed, style, class: className } = this.state;
155
102
 
103
+ // Build sidebar element
156
104
  const sidebar = document.createElement('aside');
157
105
  sidebar.className = `jux-sidebar jux-sidebar-${position}`;
158
106
  sidebar.id = this._id;
159
-
160
- // Only set width if explicitly provided
161
- if (width) {
162
- sidebar.style.width = collapsed ? '0' : width;
163
- }
164
-
165
- if (className) {
166
- sidebar.className += ` ${className}`;
167
- }
168
-
169
- if (style) {
170
- sidebar.setAttribute('style', sidebar.getAttribute('style') + '; ' + style);
171
- }
107
+ if (className) sidebar.className += ` ${className}`;
108
+ if (collapsed) sidebar.classList.add('jux-sidebar-collapsed');
172
109
 
173
- if (collapsed) {
174
- sidebar.classList.add('jux-sidebar-collapsed');
110
+ const sidebarStyle = `${width ? `width: ${collapsed ? '60px' : width};` : ''} ${style}`;
111
+ if (sidebarStyle.trim()) {
112
+ sidebar.setAttribute('style', sidebarStyle);
175
113
  }
176
114
 
177
- if (title) {
178
- const titleEl = document.createElement('div');
179
- titleEl.className = 'jux-sidebar-title';
180
- titleEl.textContent = title;
181
- sidebar.appendChild(titleEl);
182
- }
183
-
184
- const content = document.createElement('div');
185
- content.className = 'jux-sidebar-content';
186
- sidebar.appendChild(content);
187
-
188
- // Event binding - toggle button
189
115
  if (collapsible) {
190
116
  const toggleBtn = document.createElement('button');
191
117
  toggleBtn.className = 'jux-sidebar-toggle';
192
- toggleBtn.textContent = collapsed ? '>' : '<';
118
+ toggleBtn.innerHTML = collapsed
119
+ ? (position === 'left' ? '▶' : '◀')
120
+ : (position === 'left' ? '◀' : '▶');
121
+ toggleBtn.setAttribute('aria-label', 'Toggle sidebar');
122
+ toggleBtn.setAttribute('title', collapsed ? 'Expand sidebar' : 'Collapse sidebar');
193
123
 
194
124
  toggleBtn.addEventListener('click', () => {
195
125
  this.toggle();
126
+
127
+ const isCollapsed = this.state.collapsed;
128
+ toggleBtn.innerHTML = isCollapsed
129
+ ? (position === 'left' ? '▶' : '◀')
130
+ : (position === 'left' ? '◀' : '▶');
131
+ toggleBtn.setAttribute('title', isCollapsed ? 'Expand sidebar' : 'Collapse sidebar');
132
+
133
+ if (width) {
134
+ sidebar.style.width = isCollapsed ? '60px' : width;
135
+ }
196
136
  });
197
137
 
198
138
  sidebar.appendChild(toggleBtn);
199
139
  }
200
140
 
141
+ // Wire events using inherited method
142
+ this._wireStandardEvents(sidebar);
143
+
144
+ // Wire sync bindings for 'collapsed' property
145
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
146
+ if (property === 'collapsed') {
147
+ const transform = toComponent || ((v: any) => v);
148
+
149
+ stateObj.subscribe((val: any) => {
150
+ const transformed = transform(val);
151
+ const isCollapsed = Boolean(transformed);
152
+ this.state.collapsed = isCollapsed;
153
+ this._updateCollapsedState();
154
+ });
155
+ }
156
+ });
157
+
201
158
  container.appendChild(sidebar);
159
+ this._sidebar = sidebar;
160
+ this._injectDefaultStyles();
161
+
202
162
  return this;
203
163
  }
204
164
 
205
- /**
206
- * Render to another Jux component's container
207
- */
208
- renderTo(juxComponent: any): this {
209
- if (!juxComponent || typeof juxComponent !== 'object') {
210
- throw new Error('Sidebar.renderTo: Invalid component - not an object');
211
- }
165
+ private _injectDefaultStyles(): void {
166
+ const styleId = 'jux-sidebar-styles';
167
+ if (document.getElementById(styleId)) return;
168
+
169
+ const style = document.createElement('style');
170
+ style.id = styleId;
171
+ style.textContent = `
172
+ .jux-sidebar {
173
+ position: relative;
174
+ height: 100vh;
175
+ background: #f9fafb;
176
+ border-right: 1px solid #e5e7eb;
177
+ transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
178
+ overflow: hidden;
179
+ }
212
180
 
213
- if (!juxComponent._id || typeof juxComponent._id !== 'string') {
214
- throw new Error('Sidebar.renderTo: Invalid component - missing _id (not a Jux component)');
215
- }
181
+ .jux-sidebar-right {
182
+ border-right: none;
183
+ border-left: 1px solid #e5e7eb;
184
+ }
185
+
186
+ .jux-sidebar-toggle {
187
+ position: absolute;
188
+ bottom: 20px;
189
+ right: 20px;
190
+ width: 32px;
191
+ height: 32px;
192
+ border: none;
193
+ background: #3b82f6;
194
+ color: white;
195
+ border-radius: 6px;
196
+ cursor: pointer;
197
+ font-size: 14px;
198
+ display: flex;
199
+ align-items: center;
200
+ justify-content: center;
201
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
202
+ z-index: 100;
203
+ box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
204
+ }
205
+
206
+ .jux-sidebar-toggle:hover {
207
+ background: #2563eb;
208
+ transform: scale(1.05);
209
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
210
+ }
216
211
 
217
- return this.render(`#${juxComponent._id}`);
212
+ .jux-sidebar-toggle:active {
213
+ transform: scale(0.95);
214
+ }
215
+
216
+ .jux-sidebar-collapsed {
217
+ width: 60px !important;
218
+ }
219
+
220
+ .jux-sidebar-collapsed > *:not(.jux-sidebar-toggle) {
221
+ opacity: 0;
222
+ pointer-events: none;
223
+ transition: opacity 0.2s ease-out;
224
+ }
225
+
226
+ .jux-sidebar > *:not(.jux-sidebar-toggle) {
227
+ opacity: 1;
228
+ pointer-events: auto;
229
+ transition: opacity 0.3s ease-in 0.1s;
230
+ }
231
+ `;
232
+ document.head.appendChild(style);
218
233
  }
219
234
  }
220
235
 
221
- /**
222
- * Factory helper
223
- */
224
236
  export function sidebar(id: string, options: SidebarOptions = {}): Sidebar {
225
237
  return new Sidebar(id, options);
226
238
  }
@@ -6,101 +6,38 @@ import { ErrorHandler } from './error-handler.js';
6
6
  * Auto-renders when created or modified
7
7
  */
8
8
  export class Style {
9
- private _content: string = '';
10
- private _element: HTMLStyleElement | null = null;
9
+ private _id: string;
10
+ private css: string;
11
11
 
12
- constructor(content: string = '') {
13
- this._content = content;
14
-
15
- // Auto-render if content provided
16
- if (content) {
17
- this.render();
18
- }
19
- }
20
-
21
- /**
22
- * Set inline CSS content
23
- */
24
- content(css: string): this {
25
- this._content = css;
26
- this.render();
27
- return this;
12
+ constructor(css: string, id?: string) {
13
+ // ID only for deduplication, auto-generate if not provided
14
+ this._id = id || `jux-style-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
15
+ this.css = css;
28
16
  }
29
17
 
30
- /**
31
- * Append CSS to existing content
32
- */
33
- append(css: string): this {
34
- this._content += '\n' + css;
35
- this.render();
36
- return this;
37
- }
38
-
39
- /**
40
- * Prepend CSS to existing content
41
- */
42
- prepend(css: string): this {
43
- this._content = css + '\n' + this._content;
44
- this.render();
45
- return this;
46
- }
47
-
48
- /**
49
- * Render the inline style element
50
- */
51
18
  render(): this {
52
- if (typeof document === 'undefined') {
19
+ // Check if style with this ID already exists
20
+ if (document.getElementById(this._id)) {
53
21
  return this;
54
22
  }
55
23
 
56
- try {
57
- // Remove existing element if it exists
58
- this.remove();
59
-
60
- // Create <style> element for inline CSS
61
- const style = document.createElement('style');
62
- style.textContent = this._content;
63
- document.head.appendChild(style);
64
- this._element = style;
65
-
66
- console.log('✓ Inline styles rendered');
67
- } catch (error: any) {
68
- ErrorHandler.captureError({
69
- component: 'Style',
70
- method: 'render',
71
- message: error.message,
72
- stack: error.stack,
73
- timestamp: new Date(),
74
- context: {
75
- type: 'inline',
76
- error: 'runtime_exception'
77
- }
78
- });
79
- }
24
+ const style = document.createElement('style');
25
+ style.id = this._id;
26
+ style.textContent = this.css;
27
+ document.head.appendChild(style);
80
28
 
81
29
  return this;
82
30
  }
83
31
 
84
- /**
85
- * Remove the style from the document
86
- */
87
- remove(): this {
88
- if (this._element && this._element.parentNode) {
89
- this._element.parentNode.removeChild(this._element);
90
- this._element = null;
32
+ remove(): void {
33
+ const existing = document.getElementById(this._id);
34
+ if (existing) {
35
+ existing.remove();
91
36
  }
92
- return this;
93
37
  }
94
38
  }
95
39
 
96
- /**
97
- * Factory function
98
- *
99
- * Usage:
100
- * jux.style('body { margin: 0; }');
101
- * jux.style().content('.highlight { color: red; }');
102
- * jux.style('h1 { font-size: 2rem; }').append('h2 { font-size: 1.5rem; }');
103
- */
104
- export function style(content: string = ''): Style {
105
- return new Style(content);
40
+ // ✅ ID is optional
41
+ export function style(css: string, id?: string): Style {
42
+ return new Style(css, id);
106
43
  }