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,222 +1,109 @@
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 = [] as const;
3
6
 
4
7
  export interface TooltipOptions {
5
8
  text?: string;
6
9
  position?: 'top' | 'bottom' | 'left' | 'right';
7
- trigger?: 'hover' | 'click' | 'focus';
8
10
  style?: string;
9
11
  class?: string;
10
12
  }
11
13
 
12
14
  type TooltipState = {
13
15
  text: string;
14
- content?: string;
15
- position: 'top' | 'bottom' | 'left' | 'right';
16
- trigger: 'hover' | 'click' | 'focus';
16
+ position: string;
17
17
  style: string;
18
18
  class: string;
19
- isVisible: boolean;
20
19
  };
21
20
 
22
- export class Tooltip {
23
- state: TooltipState;
24
- container: HTMLElement | null = null;
25
- _id: string;
26
- id: string;
27
- private _tooltipElement: HTMLElement | null = null;
28
- private _targetElement: HTMLElement | null = null;
29
-
30
- // CRITICAL: Store bind/sync instructions for deferred wiring
31
- private _bindings: Array<{ event: string, handler: Function }> = [];
32
- private _syncBindings: Array<{
33
- property: string,
34
- stateObj: State<any>,
35
- toState?: Function,
36
- toComponent?: Function
37
- }> = [];
21
+ export class Tooltip extends BaseComponent<TooltipState> {
22
+ private _tooltip: HTMLElement | null = null;
23
+ private _target: HTMLElement | null = null;
38
24
 
39
25
  constructor(id: string, options: TooltipOptions = {}) {
40
- this._id = id;
41
- this.id = id;
42
-
43
- this.state = {
26
+ super(id, {
44
27
  text: options.text ?? '',
45
28
  position: options.position ?? 'top',
46
- trigger: options.trigger ?? 'hover',
47
29
  style: options.style ?? '',
48
- class: options.class ?? '',
49
- isVisible: false
50
- };
51
- }
52
-
53
- /* -------------------------
54
- * Fluent API
55
- * ------------------------- */
56
-
57
- text(value: string): this {
58
- this.state.text = value;
59
- if (this._tooltipElement) {
60
- this._tooltipElement.textContent = value;
61
- }
62
- return this;
63
- }
64
-
65
- position(value: 'top' | 'bottom' | 'left' | 'right'): this {
66
- this.state.position = value;
67
- return this;
30
+ class: options.class ?? ''
31
+ });
68
32
  }
69
33
 
70
- trigger(value: 'hover' | 'click' | 'focus'): this {
71
- this.state.trigger = value;
72
- return this;
34
+ protected getTriggerEvents(): readonly string[] {
35
+ return TRIGGER_EVENTS;
73
36
  }
74
37
 
75
- style(value: string): this {
76
- this.state.style = value;
77
- return this;
38
+ protected getCallbackEvents(): readonly string[] {
39
+ return CALLBACK_EVENTS;
78
40
  }
79
41
 
80
- class(value: string): this {
81
- this.state.class = value;
82
- return this;
83
- }
42
+ /* ═════════════════════════════════════════════════════════════════
43
+ * FLUENT API
44
+ * ═════════════════════════════════════════════════════════════════ */
84
45
 
85
- bind(event: string, handler: Function): this {
86
- this._bindings.push({ event, handler });
46
+ text(value: string): this {
47
+ this.state.text = value;
87
48
  return this;
88
49
  }
89
50
 
90
- sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
91
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
92
- throw new Error(`Tooltip.sync: Expected a State object for property "${property}"`);
93
- }
94
- this._syncBindings.push({ property, stateObj, toState, toComponent });
51
+ position(value: 'top' | 'bottom' | 'left' | 'right'): this {
52
+ this.state.position = value;
95
53
  return this;
96
54
  }
97
55
 
98
- /**
99
- * Attach tooltip to an element
100
- */
101
- attachTo(target: string | HTMLElement | any): this {
102
- let targetElement: HTMLElement | null = null;
103
-
104
- if (typeof target === 'string') {
105
- const el = document.querySelector(target);
106
- if (!el || !(el instanceof HTMLElement)) {
107
- throw new Error(`Tooltip: Target element "${target}" not found`);
108
- }
109
- targetElement = el;
110
- } else if (target instanceof HTMLElement) {
111
- targetElement = target;
112
- } else if (target && target.container) {
113
- targetElement = target.container;
114
- } else if (target && target._id) {
115
- const el = document.getElementById(target._id);
116
- if (!el) {
117
- throw new Error(`Tooltip: Target element with id "${target._id}" not found`);
118
- }
119
- targetElement = el;
120
- } else {
121
- throw new Error('Tooltip: Invalid target element');
122
- }
123
-
124
- this._targetElement = targetElement;
125
- this._createTooltip();
126
- this._attachEventListeners();
56
+ attachTo(target: HTMLElement | null): this {
57
+ if (!target) return this;
127
58
 
59
+ this._target = target;
60
+ this._setupTooltip();
128
61
  return this;
129
62
  }
130
63
 
131
- show(): void {
132
- if (!this._tooltipElement || !this._targetElement) return;
133
-
134
- this.state.isVisible = true;
135
- this._tooltipElement.classList.add('jux-tooltip-visible');
136
- this._positionTooltip();
137
- }
64
+ /* ═════════════════════════════════════════════════════════════════
65
+ * RENDER
66
+ * ═════════════════════════════════════════════════════════════════ */
138
67
 
139
- hide(): void {
140
- if (!this._tooltipElement) return;
141
-
142
- this.state.isVisible = false;
143
- this._tooltipElement.classList.remove('jux-tooltip-visible');
144
- }
68
+ private _setupTooltip(): void {
69
+ if (!this._target) return;
145
70
 
146
- /* -------------------------
147
- * Helpers
148
- * ------------------------- */
149
-
150
- private _createTooltip(): void {
151
71
  const { text, position, style, class: className } = this.state;
152
72
 
73
+ // Create tooltip element
153
74
  const tooltip = document.createElement('div');
154
75
  tooltip.className = `jux-tooltip jux-tooltip-${position}`;
155
- tooltip.id = `${this._id}-tooltip`;
76
+ tooltip.id = this._id;
77
+ if (className) tooltip.className += ` ${className}`;
78
+ if (style) tooltip.setAttribute('style', style);
156
79
  tooltip.textContent = text;
157
-
158
- if (className) {
159
- tooltip.className += ` ${className}`;
160
- }
161
-
162
- if (style) {
163
- tooltip.setAttribute('style', style);
164
- }
80
+ tooltip.style.display = 'none';
81
+ this._tooltip = tooltip;
165
82
 
166
83
  document.body.appendChild(tooltip);
167
- this._tooltipElement = tooltip;
168
-
169
- this._injectDefaultStyles();
170
- }
171
-
172
- private _attachEventListeners(): void {
173
- if (!this._targetElement) return;
174
-
175
- const { trigger } = this.state;
176
84
 
177
- if (trigger === 'hover') {
178
- this._targetElement.addEventListener('mouseenter', () => this.show());
179
- this._targetElement.addEventListener('mouseleave', () => this.hide());
180
- } else if (trigger === 'click') {
181
- this._targetElement.addEventListener('click', () => {
182
- if (this.state.isVisible) {
183
- this.hide();
184
- } else {
185
- this.show();
186
- }
187
- });
188
- } else if (trigger === 'focus') {
189
- this._targetElement.addEventListener('focus', () => this.show());
190
- this._targetElement.addEventListener('blur', () => this.hide());
191
- }
192
-
193
- // Wire up sync bindings
194
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
195
- if (property === 'text') {
196
- const transformToComponent = toComponent || ((v: any) => String(v));
85
+ // Show on hover
86
+ this._target.addEventListener('mouseenter', () => {
87
+ this._show();
88
+ });
197
89
 
198
- stateObj.subscribe((val: any) => {
199
- const transformed = transformToComponent(val);
200
- if (this._tooltipElement) {
201
- this._tooltipElement.textContent = transformed;
202
- }
203
- this.state.text = transformed;
204
- });
205
- }
90
+ this._target.addEventListener('mouseleave', () => {
91
+ this._hide();
206
92
  });
93
+
94
+ this._injectTooltipStyles();
207
95
  }
208
96
 
209
- private _positionTooltip(): void {
210
- if (!this._targetElement || !this._tooltipElement) return;
97
+ private _show(): void {
98
+ if (!this._tooltip || !this._target) return;
211
99
 
212
- const targetRect = this._targetElement.getBoundingClientRect();
213
- const tooltipRect = this._tooltipElement.getBoundingClientRect();
214
- const { position } = this.state;
100
+ const targetRect = this._target.getBoundingClientRect();
101
+ const tooltipRect = this._tooltip.getBoundingClientRect();
215
102
 
216
103
  let top = 0;
217
104
  let left = 0;
218
105
 
219
- switch (position) {
106
+ switch (this.state.position) {
220
107
  case 'top':
221
108
  top = targetRect.top - tooltipRect.height - 8;
222
109
  left = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
@@ -235,168 +122,50 @@ export class Tooltip {
235
122
  break;
236
123
  }
237
124
 
238
- this._tooltipElement.style.top = `${top + window.scrollY}px`;
239
- this._tooltipElement.style.left = `${left + window.scrollX}px`;
125
+ this._tooltip.style.top = `${top}px`;
126
+ this._tooltip.style.left = `${left}px`;
127
+ this._tooltip.style.display = 'block';
128
+ }
129
+
130
+ private _hide(): void {
131
+ if (this._tooltip) {
132
+ this._tooltip.style.display = 'none';
133
+ }
134
+ }
135
+
136
+ render(targetId?: string): this {
137
+ // Tooltips don't render to containers, they attach to targets
138
+ console.warn('Tooltip: Use .attachTo(element) instead of .render()');
139
+ return this;
240
140
  }
241
141
 
242
- private _injectDefaultStyles(): void {
142
+ private _injectTooltipStyles(): void {
243
143
  const styleId = 'jux-tooltip-styles';
244
144
  if (document.getElementById(styleId)) return;
245
145
 
246
146
  const style = document.createElement('style');
247
147
  style.id = styleId;
248
148
  style.textContent = `
249
- .jux-tooltip {
250
- position: absolute;
251
- background: #1f2937;
252
- color: white;
253
- padding: 8px 12px;
254
- border-radius: 6px;
255
- font-size: 13px;
256
- white-space: nowrap;
257
- z-index: 10000;
258
- pointer-events: none;
259
- opacity: 0;
260
- transition: opacity 0.2s;
261
- }
262
-
263
- .jux-tooltip-visible {
264
- opacity: 1;
265
- }
266
-
267
- .jux-tooltip-top::after {
268
- content: '';
269
- position: absolute;
270
- top: 100%;
271
- left: 50%;
272
- transform: translateX(-50%);
273
- border: 5px solid transparent;
274
- border-top-color: #1f2937;
275
- }
276
-
277
- .jux-tooltip-bottom::after {
278
- content: '';
279
- position: absolute;
280
- bottom: 100%;
281
- left: 50%;
282
- transform: translateX(-50%);
283
- border: 5px solid transparent;
284
- border-bottom-color: #1f2937;
285
- }
286
-
287
- .jux-tooltip-left::after {
288
- content: '';
289
- position: absolute;
290
- left: 100%;
291
- top: 50%;
292
- transform: translateY(-50%);
293
- border: 5px solid transparent;
294
- border-left-color: #1f2937;
295
- }
296
-
297
- .jux-tooltip-right::after {
298
- content: '';
299
- position: absolute;
300
- right: 100%;
301
- top: 50%;
302
- transform: translateY(-50%);
303
- border: 5px solid transparent;
304
- border-right-color: #1f2937;
305
- }
306
- `;
149
+ .jux-tooltip {
150
+ position: fixed;
151
+ background: #1f2937;
152
+ color: white;
153
+ padding: 6px 12px;
154
+ border-radius: 6px;
155
+ font-size: 12px;
156
+ white-space: nowrap;
157
+ z-index: 10000;
158
+ pointer-events: none;
159
+ animation: jux-tooltip-fade 0.2s;
160
+ }
161
+
162
+ @keyframes jux-tooltip-fade {
163
+ from { opacity: 0; }
164
+ to { opacity: 1; }
165
+ }
166
+ `;
307
167
  document.head.appendChild(style);
308
168
  }
309
-
310
- render(targetId?: string): this {
311
- // === 1. SETUP: Get or create container ===
312
- let container: HTMLElement;
313
- if (targetId) {
314
- const target = document.querySelector(targetId);
315
- if (!target || !(target instanceof HTMLElement)) {
316
- throw new Error(`Tooltip: Target "${targetId}" not found`);
317
- }
318
- container = target;
319
- } else {
320
- container = getOrCreateContainer(this._id);
321
- }
322
- this.container = container;
323
-
324
- // === 2. PREPARE: Destructure state ===
325
- const { text, content, position, trigger, style, class: className } = this.state;
326
-
327
- // === 3. BUILD: Create DOM elements ===
328
- const wrapper = document.createElement('div');
329
- wrapper.className = 'jux-tooltip-wrapper';
330
- wrapper.id = this._id;
331
- if (className) wrapper.className += ` ${className}`;
332
- if (style) wrapper.setAttribute('style', style);
333
-
334
- const contentEl = document.createElement('div');
335
- contentEl.className = 'jux-tooltip-content';
336
- contentEl.innerHTML = content;
337
- wrapper.appendChild(contentEl);
338
-
339
- const tooltip = document.createElement('div');
340
- tooltip.className = `jux-tooltip jux-tooltip-${position}`;
341
- tooltip.id = `${this._id}-tooltip`;
342
- tooltip.textContent = text;
343
- tooltip.style.display = 'none';
344
- wrapper.appendChild(tooltip);
345
-
346
- // === 4. WIRE: Attach event listeners and sync bindings ===
347
-
348
- // Tooltip trigger behavior
349
- if (trigger === 'hover') {
350
- wrapper.addEventListener('mouseenter', () => {
351
- tooltip.style.display = 'block';
352
- });
353
- wrapper.addEventListener('mouseleave', () => {
354
- tooltip.style.display = 'none';
355
- });
356
- } else if (trigger === 'click') {
357
- wrapper.addEventListener('click', () => {
358
- tooltip.style.display = tooltip.style.display === 'none' ? 'block' : 'none';
359
- });
360
- }
361
-
362
- // Wire custom bindings from .bind() calls
363
- this._bindings.forEach(({ event, handler }) => {
364
- wrapper.addEventListener(event, handler as EventListener);
365
- });
366
-
367
- // Wire sync bindings from .sync() calls
368
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
369
- if (property === 'text') {
370
- const transformToComponent = toComponent || ((v: any) => String(v));
371
-
372
- stateObj.subscribe((val: any) => {
373
- const transformed = transformToComponent(val);
374
- tooltip.textContent = transformed;
375
- this.state.text = transformed;
376
- });
377
- }
378
- else if (property === 'content') {
379
- const transformToComponent = toComponent || ((v: any) => String(v));
380
-
381
- stateObj.subscribe((val: any) => {
382
- const transformed = transformToComponent(val);
383
- contentEl.innerHTML = transformed;
384
- this.state.content = transformed;
385
- });
386
- }
387
- });
388
-
389
- // === 5. RENDER: Append to DOM and finalize ===
390
- container.appendChild(wrapper);
391
- return this;
392
- }
393
-
394
- renderTo(juxComponent: any): this {
395
- if (!juxComponent?._id) {
396
- throw new Error('Tooltip.renderTo: Invalid component');
397
- }
398
- return this.render(`#${juxComponent._id}`);
399
- }
400
169
  }
401
170
 
402
171
  export function tooltip(id: string, options: TooltipOptions = {}): Tooltip {