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,94 +1,72 @@
1
- import { getOrCreateContainer } from './helpers.js';
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+ import { renderIcon } from './icons.js';
3
+
4
+ // Event definitions
5
+ const TRIGGER_EVENTS = [] as const;
6
+ const CALLBACK_EVENTS = ['dismiss'] as const;
2
7
 
3
- /**
4
- * Alert component options
5
- */
6
8
  export interface AlertOptions {
7
9
  message?: string;
8
- variant?: 'info' | 'success' | 'warning' | 'error';
9
- title?: string;
10
+ type?: 'info' | 'success' | 'warning' | 'error';
10
11
  dismissible?: boolean;
11
- onDismiss?: () => void;
12
12
  icon?: string;
13
13
  style?: string;
14
14
  class?: string;
15
15
  }
16
16
 
17
- /**
18
- * Alert component state
19
- */
20
17
  type AlertState = {
21
18
  message: string;
22
- variant: string;
23
- title: string;
19
+ type: string;
24
20
  dismissible: boolean;
25
21
  icon: string;
22
+ visible: boolean;
26
23
  style: string;
27
24
  class: string;
28
25
  };
29
26
 
30
- /**
31
- * Alert component - Status messages and notifications
32
- *
33
- * Usage:
34
- * jux.alert('my-alert', {
35
- * variant: 'success',
36
- * title: 'Success!',
37
- * message: 'Your changes have been saved.',
38
- * dismissible: true
39
- * }).render('#app');
40
- *
41
- * Variants: info, success, warning, error
42
- */
43
- export class Alert {
44
- state: AlertState;
45
- container: HTMLElement | null = null;
46
- _id: string;
47
- id: string;
48
- private _onDismiss?: () => void;
27
+ export class Alert extends BaseComponent<AlertState> {
28
+ private _alert: HTMLElement | null = null;
49
29
 
50
30
  constructor(id: string, options: AlertOptions = {}) {
51
- this._id = id;
52
- this.id = id;
53
- this._onDismiss = options.onDismiss;
54
-
55
- // Default icons per variant
56
- const defaultIcons = {
57
- info: 'ℹ️',
58
- success: '✅',
59
- warning: '⚠️',
60
- error: '❌'
61
- };
62
-
63
- const variant = options.variant ?? 'info';
64
-
65
- this.state = {
31
+ super(id, {
66
32
  message: options.message ?? '',
67
- variant,
68
- title: options.title ?? '',
69
- dismissible: options.dismissible ?? false,
70
- icon: options.icon ?? defaultIcons[variant as keyof typeof defaultIcons],
33
+ type: options.type ?? 'info',
34
+ dismissible: options.dismissible ?? true,
35
+ icon: options.icon ?? '',
36
+ visible: true,
71
37
  style: options.style ?? '',
72
38
  class: options.class ?? ''
73
- };
39
+ });
74
40
  }
75
41
 
76
- /* -------------------------
77
- * Fluent API
78
- * ------------------------- */
42
+ protected getTriggerEvents(): readonly string[] {
43
+ return TRIGGER_EVENTS;
44
+ }
79
45
 
80
- message(value: string): this {
81
- this.state.message = value;
82
- return this;
46
+ protected getCallbackEvents(): readonly string[] {
47
+ return CALLBACK_EVENTS;
83
48
  }
84
49
 
85
- variant(value: 'info' | 'success' | 'warning' | 'error'): this {
86
- this.state.variant = value;
50
+ /* ═════════════════════════════════════════════════════════════════
51
+ * FLUENT API
52
+ * ═════════════════════════════════════════════════════════════════ */
53
+
54
+ // ✅ Inherited from BaseComponent:
55
+ // - style(), class()
56
+ // - bind(), sync(), renderTo()
57
+ // - addClass(), removeClass(), toggleClass()
58
+ // - visible(), show(), hide(), toggleVisibility()
59
+ // - attr(), attrs(), removeAttr()
60
+ // - disabled(), enable(), disable()
61
+ // - loading(), focus(), blur(), remove()
62
+
63
+ message(value: string): this {
64
+ this.state.message = value;
87
65
  return this;
88
66
  }
89
67
 
90
- title(value: string): this {
91
- this.state.title = value;
68
+ type(value: 'info' | 'success' | 'warning' | 'error'): this {
69
+ this.state.type = value;
92
70
  return this;
93
71
  }
94
72
 
@@ -102,139 +80,208 @@ export class Alert {
102
80
  return this;
103
81
  }
104
82
 
105
- style(value: string): this {
106
- this.state.style = value;
107
- return this;
108
- }
109
-
110
- class(value: string): this {
111
- this.state.class = value;
112
- return this;
113
- }
114
-
115
- /* -------------------------
116
- * Methods
117
- * ------------------------- */
118
-
119
- /**
120
- * Dismiss/remove the alert
121
- */
122
- dismiss(): void {
123
- const element = document.getElementById(this._id);
124
- if (element) {
125
- element.style.opacity = '0';
126
- element.style.transform = 'translateY(-10px)';
127
- setTimeout(() => {
128
- element.remove();
129
- if (this._onDismiss) {
130
- this._onDismiss();
131
- }
132
- }, 200);
133
- }
134
- }
135
-
136
- /**
137
- * Auto-dismiss after delay
138
- */
139
- autoDismiss(delay: number = 5000): this {
140
- setTimeout(() => this.dismiss(), delay);
141
- return this;
142
- }
143
-
144
- /* -------------------------
145
- * Render
146
- * ------------------------- */
83
+ /* ═════════════════════════════════════════════════════════════════
84
+ * RENDER
85
+ * ═════════════════════════════════════════════════════════════════ */
147
86
 
148
87
  render(targetId?: string): this {
149
- let container: HTMLElement;
150
-
151
- if (targetId) {
152
- const target = document.querySelector(targetId);
153
- if (!target || !(target instanceof HTMLElement)) {
154
- throw new Error(`Alert: Target element "${targetId}" not found`);
155
- }
156
- container = target;
157
- } else {
158
- container = getOrCreateContainer(this._id);
159
- }
88
+ const container = this._setupContainer(targetId);
160
89
 
161
- this.container = container;
162
- const { message, variant, title, dismissible, icon, style, class: className } = this.state;
90
+ const { message, type, dismissible, icon, style, class: className } = this.state;
91
+ const hasVisibleSync = this._syncBindings.some(b => b.property === 'visible');
163
92
 
93
+ // Build alert element
164
94
  const alert = document.createElement('div');
165
- alert.className = `jux-alert jux-alert-${variant}`;
95
+ alert.className = `jux-alert jux-alert-${type}`;
166
96
  alert.id = this._id;
167
- alert.setAttribute('role', 'alert');
168
-
169
- if (className) {
170
- alert.className += ` ${className}`;
171
- }
172
-
173
- if (style) {
174
- alert.setAttribute('style', style);
175
- }
97
+ if (className) alert.className += ` ${className}`;
98
+ if (style) alert.setAttribute('style', style);
176
99
 
177
- // Icon
178
100
  if (icon) {
179
101
  const iconEl = document.createElement('span');
180
102
  iconEl.className = 'jux-alert-icon';
181
- iconEl.textContent = icon;
103
+ iconEl.appendChild(renderIcon(icon));
182
104
  alert.appendChild(iconEl);
183
105
  }
184
106
 
185
- // Content
186
107
  const content = document.createElement('div');
187
108
  content.className = 'jux-alert-content';
109
+ content.textContent = message;
110
+ alert.appendChild(content);
188
111
 
189
- if (title) {
190
- const titleEl = document.createElement('div');
191
- titleEl.className = 'jux-alert-title';
192
- titleEl.textContent = title;
193
- content.appendChild(titleEl);
112
+ if (dismissible) {
113
+ const closeBtn = document.createElement('button');
114
+ closeBtn.className = 'jux-alert-close';
115
+ closeBtn.innerHTML = '×';
116
+ alert.appendChild(closeBtn);
194
117
  }
195
118
 
196
- if (message) {
197
- const messageEl = document.createElement('div');
198
- messageEl.className = 'jux-alert-message';
199
- messageEl.textContent = message;
200
- content.appendChild(messageEl);
119
+ // Default dismiss behavior (only if NOT using sync)
120
+ if (!hasVisibleSync && dismissible) {
121
+ const closeBtn = alert.querySelector('.jux-alert-close');
122
+ closeBtn?.addEventListener('click', () => {
123
+ // 🎯 Fire the dismiss callback event
124
+ this._triggerCallback('dismiss');
125
+ alert.remove();
126
+ });
201
127
  }
202
128
 
203
- alert.appendChild(content);
129
+ // Wire events using inherited method
130
+ this._wireStandardEvents(alert);
204
131
 
205
- // Dismiss button
206
- if (dismissible) {
207
- const dismissBtn = document.createElement('button');
208
- dismissBtn.className = 'jux-alert-dismiss';
209
- dismissBtn.innerHTML = '×';
210
- dismissBtn.setAttribute('aria-label', 'Dismiss alert');
211
- dismissBtn.addEventListener('click', () => this.dismiss());
212
- alert.appendChild(dismissBtn);
213
- }
132
+ // Wire sync bindings
133
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
134
+ if (property === 'message') {
135
+ const transform = toComponent || ((v: any) => String(v));
136
+
137
+ stateObj.subscribe((val: any) => {
138
+ const transformed = transform(val);
139
+ content.textContent = transformed;
140
+ this.state.message = transformed;
141
+ });
142
+ }
143
+ else if (property === 'type') {
144
+ const transform = toComponent || ((v: any) => String(v));
145
+
146
+ stateObj.subscribe((val: any) => {
147
+ const transformed = transform(val);
148
+ alert.className = `jux-alert jux-alert-${transformed}`;
149
+ if (className) alert.className += ` ${className}`;
150
+ this.state.type = transformed;
151
+ });
152
+ }
153
+ else if (property === 'visible') {
154
+ const transformToState = toState || ((v: any) => Boolean(v));
155
+ const transform = toComponent || ((v: any) => Boolean(v));
156
+
157
+ let isUpdating = false;
158
+
159
+ // State → Component
160
+ stateObj.subscribe((val: any) => {
161
+ if (isUpdating) return;
162
+ const transformed = transform(val);
163
+ alert.style.display = transformed ? 'flex' : 'none';
164
+ });
165
+
166
+ // Component → State (close button)
167
+ if (dismissible) {
168
+ const closeBtn = alert.querySelector('.jux-alert-close');
169
+ closeBtn?.addEventListener('click', () => {
170
+ if (isUpdating) return;
171
+ isUpdating = true;
172
+
173
+ alert.style.display = 'none';
174
+ stateObj.set(transformToState(false));
175
+
176
+ // 🎯 Fire the dismiss callback event
177
+ this._triggerCallback('dismiss');
178
+
179
+ setTimeout(() => { isUpdating = false; }, 0);
180
+ });
181
+ }
182
+ }
183
+ });
214
184
 
215
185
  container.appendChild(alert);
216
- return this;
217
- }
186
+ this._alert = alert;
218
187
 
219
- /**
220
- * Render to another Jux component's container
221
- */
222
- renderTo(juxComponent: any): this {
223
- if (!juxComponent || typeof juxComponent !== 'object') {
224
- throw new Error('Alert.renderTo: Invalid component - not an object');
225
- }
188
+ this._injectAlertStyles();
226
189
 
227
- if (!juxComponent._id || typeof juxComponent._id !== 'string') {
228
- throw new Error('Alert.renderTo: Invalid component - missing _id (not a Jux component)');
229
- }
190
+ requestAnimationFrame(() => {
191
+ if ((window as any).lucide) {
192
+ (window as any).lucide.createIcons();
193
+ }
194
+ });
230
195
 
231
- return this.render(`#${juxComponent._id}`);
196
+ return this;
197
+ }
198
+
199
+ private _injectAlertStyles(): void {
200
+ const styleId = 'jux-alert-styles';
201
+ if (document.getElementById(styleId)) return;
202
+
203
+ const style = document.createElement('style');
204
+ style.id = styleId;
205
+ style.textContent = `
206
+ .jux-alert {
207
+ display: flex;
208
+ align-items: center;
209
+ gap: 12px;
210
+ padding: 16px;
211
+ border-radius: 8px;
212
+ margin-bottom: 12px;
213
+ position: relative;
214
+ animation: slideIn 0.3s ease-out;
215
+ }
216
+
217
+ @keyframes slideIn {
218
+ from {
219
+ opacity: 0;
220
+ transform: translateX(-20px);
221
+ }
222
+ to {
223
+ opacity: 1;
224
+ transform: translateX(0);
225
+ }
226
+ }
227
+
228
+ .jux-alert-info {
229
+ background: #dbeafe;
230
+ border-left: 4px solid #3b82f6;
231
+ color: #1e40af;
232
+ }
233
+
234
+ .jux-alert-success {
235
+ background: #d1fae5;
236
+ border-left: 4px solid #10b981;
237
+ color: #065f46;
238
+ }
239
+
240
+ .jux-alert-warning {
241
+ background: #fef3c7;
242
+ border-left: 4px solid #f59e0b;
243
+ color: #92400e;
244
+ }
245
+
246
+ .jux-alert-error {
247
+ background: #fee2e2;
248
+ border-left: 4px solid #ef4444;
249
+ color: #991b1b;
250
+ }
251
+
252
+ .jux-alert-icon {
253
+ flex-shrink: 0;
254
+ }
255
+
256
+ .jux-alert-content {
257
+ flex: 1;
258
+ font-size: 14px;
259
+ }
260
+
261
+ .jux-alert-close {
262
+ flex-shrink: 0;
263
+ background: transparent;
264
+ border: none;
265
+ font-size: 24px;
266
+ cursor: pointer;
267
+ opacity: 0.6;
268
+ transition: opacity 0.2s;
269
+ padding: 0;
270
+ width: 24px;
271
+ height: 24px;
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ }
276
+
277
+ .jux-alert-close:hover {
278
+ opacity: 1;
279
+ }
280
+ `;
281
+ document.head.appendChild(style);
232
282
  }
233
283
  }
234
284
 
235
- /**
236
- * Factory helper
237
- */
238
285
  export function alert(id: string, options: AlertOptions = {}): Alert {
239
286
  return new Alert(id, options);
240
287
  }