juxscript 1.0.20 → 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 (76) hide show
  1. package/bin/cli.js +121 -72
  2. package/lib/components/alert.ts +143 -92
  3. package/lib/components/badge.ts +93 -94
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +40 -131
  7. package/lib/components/card.ts +57 -79
  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/{chart-types.ts → charts/lib/chart-types.ts} +1 -1
  13. package/lib/components/{chart-utils.ts → charts/lib/chart-utils.ts} +1 -1
  14. package/lib/components/{chart.ts → charts/lib/chart.ts} +3 -3
  15. package/lib/components/checkbox.ts +255 -204
  16. package/lib/components/code.ts +31 -78
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +180 -147
  20. package/lib/components/dialog.ts +218 -221
  21. package/lib/components/divider.ts +63 -87
  22. package/lib/components/docs-data.json +498 -2404
  23. package/lib/components/dropdown.ts +191 -236
  24. package/lib/components/element.ts +196 -145
  25. package/lib/components/fileupload.ts +253 -167
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +31 -97
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +51 -114
  30. package/lib/components/icon.ts +33 -120
  31. package/lib/components/icons.ts +2 -1
  32. package/lib/components/include.ts +76 -3
  33. package/lib/components/input.ts +155 -407
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +358 -261
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +63 -152
  38. package/lib/components/modal.ts +42 -129
  39. package/lib/components/nav.ts +79 -101
  40. package/lib/components/paragraph.ts +38 -102
  41. package/lib/components/progress.ts +108 -166
  42. package/lib/components/radio.ts +283 -234
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +189 -199
  45. package/lib/components/sidebar.ts +110 -141
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +254 -183
  48. package/lib/components/table.ts +1078 -208
  49. package/lib/components/tabs.ts +42 -106
  50. package/lib/components/theme-toggle.ts +73 -165
  51. package/lib/components/tooltip.ts +85 -316
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +67 -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 -1
  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 -1128
  67. package/lib/components/areachartsmooth.ts +0 -1380
  68. package/lib/components/barchart.ts +0 -1322
  69. package/lib/components/doughnutchart.ts +0 -1259
  70. package/lib/components/footer.ts +0 -165
  71. package/lib/components/header.ts +0 -187
  72. package/lib/components/layout.ts +0 -239
  73. package/lib/components/main.ts +0 -137
  74. package/lib/layouts/default.jux +0 -8
  75. package/lib/layouts/figma.jux +0 -0
  76. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
@@ -1,5 +1,8 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.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;
3
6
 
4
7
  export interface SidebarOptions {
5
8
  position?: 'left' | 'right';
@@ -19,33 +22,40 @@ type SidebarState = {
19
22
  class: string;
20
23
  };
21
24
 
22
- export class Sidebar {
23
- state: SidebarState;
24
- container: HTMLElement | null = null;
25
- _id: string;
26
- id: string;
27
-
28
- private _bindings: Array<{ event: string, handler: Function }> = [];
29
- private _syncBindings: Array<{ property: string, stateObj: State<any>, transform?: Function }> = [];
25
+ export class Sidebar extends BaseComponent<SidebarState> {
26
+ private _sidebar: HTMLElement | null = null;
30
27
 
31
28
  constructor(id: string, options: SidebarOptions = {}) {
32
- this._id = id;
33
- this.id = id;
34
-
35
- this.state = {
29
+ super(id, {
36
30
  position: options.position ?? 'left',
37
- width: options.width ?? null,
31
+ width: options.width ?? '',
38
32
  collapsible: options.collapsible ?? true,
39
33
  collapsed: options.collapsed ?? false,
40
34
  style: options.style ?? '',
41
35
  class: options.class ?? ''
42
- };
36
+ });
37
+ }
38
+
39
+ protected getTriggerEvents(): readonly string[] {
40
+ return TRIGGER_EVENTS;
43
41
  }
44
42
 
45
- /* -------------------------
46
- * Fluent API
47
- * ------------------------- */
43
+ protected getCallbackEvents(): readonly string[] {
44
+ return CALLBACK_EVENTS;
45
+ }
46
+
47
+ /* ═════════════════════════════════════════════════════════════════
48
+ * FLUENT API
49
+ * ═════════════════════════════════════════════════════════════════ */
48
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()
49
59
 
50
60
  width(value: string): this {
51
61
  this.state.width = value;
@@ -68,59 +78,29 @@ export class Sidebar {
68
78
  return this;
69
79
  }
70
80
 
71
- style(value: string): this {
72
- this.state.style = value;
73
- return this;
74
- }
75
-
76
- class(value: string): this {
77
- this.state.class = value;
78
- return this;
79
- }
80
-
81
- bind(event: string, handler: Function): this {
82
- this._bindings.push({ event, handler });
83
- return this;
84
- }
85
-
86
- sync(property: string, stateObj: State<any>, transform?: Function): this {
87
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
88
- throw new Error(`Sidebar.sync: Expected a State object for property "${property}"`);
89
- }
90
- this._syncBindings.push({ property, stateObj, transform });
91
- return this;
92
- }
93
-
94
81
  toggle(): void {
95
82
  this.state.collapsed = !this.state.collapsed;
96
83
  this._updateCollapsedState();
84
+ // 🎯 Fire the toggle callback event
85
+ this._triggerCallback('toggle', this.state.collapsed);
97
86
  }
98
87
 
99
88
  private _updateCollapsedState(): void {
100
- const sidebar = document.getElementById(this._id);
101
- if (sidebar) {
102
- sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
89
+ if (this._sidebar) {
90
+ this._sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
103
91
  }
104
92
  }
105
93
 
94
+ /* ═════════════════════════════════════════════════════════════════
95
+ * RENDER
96
+ * ═════════════════════════════════════════════════════════════════ */
97
+
106
98
  render(targetId?: string): this {
107
- // === 1. SETUP: Get container ===
108
- let container: HTMLElement;
109
- if (targetId) {
110
- const target = document.querySelector(targetId);
111
- if (!target || !(target instanceof HTMLElement)) {
112
- throw new Error(`Sidebar: Target element "${targetId}" not found`);
113
- }
114
- container = target;
115
- } else {
116
- container = getOrCreateContainer(this._id);
117
- }
118
- this.container = container;
99
+ const container = this._setupContainer(targetId);
119
100
 
120
- // === 2. PREPARE: Destructure state ===
121
101
  const { position, width, collapsible, collapsed, style, class: className } = this.state;
122
102
 
123
- // === 3. BUILD: Create DOM elements ===
103
+ // Build sidebar element
124
104
  const sidebar = document.createElement('aside');
125
105
  sidebar.className = `jux-sidebar jux-sidebar-${position}`;
126
106
  sidebar.id = this._id;
@@ -144,14 +124,12 @@ export class Sidebar {
144
124
  toggleBtn.addEventListener('click', () => {
145
125
  this.toggle();
146
126
 
147
- // Update button icon and title
148
127
  const isCollapsed = this.state.collapsed;
149
128
  toggleBtn.innerHTML = isCollapsed
150
129
  ? (position === 'left' ? '▶' : '◀')
151
130
  : (position === 'left' ? '◀' : '▶');
152
131
  toggleBtn.setAttribute('title', isCollapsed ? 'Expand sidebar' : 'Collapse sidebar');
153
132
 
154
- // Animate width change
155
133
  if (width) {
156
134
  sidebar.style.width = isCollapsed ? '60px' : width;
157
135
  }
@@ -160,26 +138,25 @@ export class Sidebar {
160
138
  sidebar.appendChild(toggleBtn);
161
139
  }
162
140
 
163
- // === 4. WIRE: Add event listeners ===
141
+ // Wire events using inherited method
142
+ this._wireStandardEvents(sidebar);
164
143
 
165
- this._bindings.forEach(({ event, handler }) => {
166
- sidebar.addEventListener(event, handler as EventListener);
167
- });
168
-
169
- this._syncBindings.forEach(({ property, stateObj, transform }) => {
170
- stateObj.subscribe((val: any) => {
171
- const transformed = transform ? transform(val) : val;
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);
172
148
 
173
- if (property === 'collapsed') {
149
+ stateObj.subscribe((val: any) => {
150
+ const transformed = transform(val);
174
151
  const isCollapsed = Boolean(transformed);
175
152
  this.state.collapsed = isCollapsed;
176
153
  this._updateCollapsedState();
177
- }
178
- });
154
+ });
155
+ }
179
156
  });
180
157
 
181
- // === 5. RENDER: Append to DOM ===
182
158
  container.appendChild(sidebar);
159
+ this._sidebar = sidebar;
183
160
  this._injectDefaultStyles();
184
161
 
185
162
  return this;
@@ -192,75 +169,67 @@ export class Sidebar {
192
169
  const style = document.createElement('style');
193
170
  style.id = styleId;
194
171
  style.textContent = `
195
- .jux-sidebar {
196
- position: relative;
197
- height: 100vh;
198
- background: #f9fafb;
199
- border-right: 1px solid #e5e7eb;
200
- transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
201
- overflow: hidden;
202
- }
203
-
204
- .jux-sidebar-right {
205
- border-right: none;
206
- border-left: 1px solid #e5e7eb;
207
- }
208
-
209
- .jux-sidebar-toggle {
210
- position: absolute;
211
- bottom: 20px;
212
- right: 20px;
213
- width: 32px;
214
- height: 32px;
215
- border: none;
216
- background: #3b82f6;
217
- color: white;
218
- border-radius: 6px;
219
- cursor: pointer;
220
- font-size: 14px;
221
- display: flex;
222
- align-items: center;
223
- justify-content: center;
224
- transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
225
- z-index: 100;
226
- box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
227
- }
228
-
229
- .jux-sidebar-toggle:hover {
230
- background: #2563eb;
231
- transform: scale(1.05);
232
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
233
- }
234
-
235
- .jux-sidebar-toggle:active {
236
- transform: scale(0.95);
237
- }
238
-
239
- .jux-sidebar-collapsed {
240
- width: 60px !important;
241
- }
242
-
243
- /* Smooth fade for content */
244
- .jux-sidebar-collapsed > *:not(.jux-sidebar-toggle) {
245
- opacity: 0;
246
- pointer-events: none;
247
- transition: opacity 0.2s ease-out;
248
- }
249
-
250
- .jux-sidebar > *:not(.jux-sidebar-toggle) {
251
- opacity: 1;
252
- pointer-events: auto;
253
- transition: opacity 0.3s ease-in 0.1s;
254
- }
255
- `;
256
- document.head.appendChild(style);
257
- }
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
+ }
258
180
 
259
- renderTo(juxComponent: any): this {
260
- if (!juxComponent?._id) {
261
- throw new Error('Sidebar.renderTo: Invalid component');
262
- }
263
- return this.render(`#${juxComponent._id}`);
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
+ }
211
+
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);
264
233
  }
265
234
  }
266
235
 
@@ -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
  }