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,9 +1,9 @@
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; // Headings are display-only, no events
3
6
 
4
- /**
5
- * Heading options
6
- */
7
7
  export interface HeadingOptions {
8
8
  level?: 1 | 2 | 3 | 4 | 5 | 6;
9
9
  text?: string;
@@ -11,9 +11,6 @@ export interface HeadingOptions {
11
11
  style?: string;
12
12
  }
13
13
 
14
- /**
15
- * Heading state
16
- */
17
14
  type HeadingState = {
18
15
  level: 1 | 2 | 3 | 4 | 5 | 6;
19
16
  text: string;
@@ -21,99 +18,52 @@ type HeadingState = {
21
18
  style: string;
22
19
  };
23
20
 
24
- /**
25
- * Heading component - semantic heading elements (h1-h6)
26
- *
27
- * Usage:
28
- * jux.heading('title', { level: 1, text: 'Welcome' }).render('#app');
29
- * jux.heading('subtitle').level(2).text('Getting Started').render('#app');
30
- */
31
- export class Heading {
32
- state: HeadingState;
33
- container: HTMLElement | null = null;
34
- _id: string;
35
- id: string;
36
-
37
- // CRITICAL: Store bind/sync instructions for deferred wiring
38
- private _bindings: Array<{ event: string, handler: Function }> = [];
39
- private _syncBindings: Array<{
40
- property: string,
41
- stateObj: State<any>,
42
- toState?: Function,
43
- toComponent?: Function
44
- }> = [];
45
-
21
+ export class Heading extends BaseComponent<HeadingState> {
46
22
  constructor(id: string, options: HeadingOptions = {}) {
47
- this._id = id;
48
- this.id = id;
49
-
50
- this.state = {
23
+ super(id, {
51
24
  level: options.level ?? 1,
52
25
  text: options.text ?? '',
53
26
  class: options.class ?? '',
54
27
  style: options.style ?? ''
55
- };
28
+ });
56
29
  }
57
30
 
58
- /* -------------------------
59
- * Fluent API
60
- * ------------------------- */
61
-
62
- level(value: 1 | 2 | 3 | 4 | 5 | 6): this {
63
- this.state.level = value;
64
- return this;
31
+ protected getTriggerEvents(): readonly string[] {
32
+ return TRIGGER_EVENTS;
65
33
  }
66
34
 
67
- text(value: string): this {
68
- this.state.text = value;
69
- return this;
35
+ protected getCallbackEvents(): readonly string[] {
36
+ return CALLBACK_EVENTS;
70
37
  }
71
38
 
72
- class(value: string): this {
73
- this.state.class = value;
74
- return this;
75
- }
39
+ /* ═════════════════════════════════════════════════════════════════
40
+ * FLUENT API
41
+ * ═════════════════════════════════════════════════════════════════ */
76
42
 
77
- style(value: string): this {
78
- this.state.style = value;
79
- return this;
80
- }
43
+ // Inherited from BaseComponent:
44
+ // - style(), class()
45
+ // - bind(), sync(), renderTo()
46
+ // - All other base methods
81
47
 
82
- bind(event: string, handler: Function): this {
83
- this._bindings.push({ event, handler });
48
+ level(value: 1 | 2 | 3 | 4 | 5 | 6): this {
49
+ this.state.level = value;
84
50
  return this;
85
51
  }
86
52
 
87
- sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
88
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
89
- throw new Error(`Heading.sync: Expected a State object for property "${property}"`);
90
- }
91
- this._syncBindings.push({ property, stateObj, toState, toComponent });
53
+ text(value: string): this {
54
+ this.state.text = value;
92
55
  return this;
93
56
  }
94
57
 
95
- /* -------------------------
96
- * Render
97
- * ------------------------- */
58
+ /* ═════════════════════════════════════════════════════════════════
59
+ * RENDER
60
+ * ═════════════════════════════════════════════════════════════════ */
98
61
 
99
62
  render(targetId?: string): this {
100
- // === 1. SETUP: Get or create container ===
101
- let container: HTMLElement;
102
- if (targetId) {
103
- const target = document.querySelector(targetId);
104
- if (!target || !(target instanceof HTMLElement)) {
105
- throw new Error(`Heading: Target "${targetId}" not found`);
106
- }
107
- container = target;
108
- } else {
109
- container = getOrCreateContainer(this._id);
110
- }
111
- this.container = container;
63
+ const container = this._setupContainer(targetId);
112
64
 
113
- // === 2. PREPARE: Destructure state ===
114
65
  const { text, level, style, class: className } = this.state;
115
66
 
116
- // === 3. BUILD: Create DOM elements ===
117
67
  const heading = document.createElement(`h${level}`) as HTMLHeadingElement;
118
68
  heading.className = `jux-heading jux-heading-${level}`;
119
69
  heading.id = this._id;
@@ -121,42 +71,26 @@ export class Heading {
121
71
  if (className) heading.className += ` ${className}`;
122
72
  if (style) heading.setAttribute('style', style);
123
73
 
124
- // === 4. WIRE: Attach event listeners and sync bindings ===
125
-
126
- // Wire custom bindings from .bind() calls
127
- this._bindings.forEach(({ event, handler }) => {
128
- heading.addEventListener(event, handler as EventListener);
129
- });
74
+ this._wireStandardEvents(heading);
130
75
 
131
- // Wire sync bindings from .sync() calls
76
+ // Wire sync bindings
132
77
  this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
133
78
  if (property === 'text') {
134
- const transformToComponent = toComponent || ((v: any) => String(v));
79
+ const transform = toComponent || ((v: any) => String(v));
135
80
 
136
81
  stateObj.subscribe((val: any) => {
137
- const transformed = transformToComponent(val);
82
+ const transformed = transform(val);
138
83
  heading.textContent = transformed;
139
84
  this.state.text = transformed;
140
85
  });
141
86
  }
142
87
  });
143
88
 
144
- // === 5. RENDER: Append to DOM and finalize ===
145
89
  container.appendChild(heading);
146
90
  return this;
147
91
  }
148
-
149
- renderTo(juxComponent: any): this {
150
- if (!juxComponent?._id) {
151
- throw new Error('Heading.renderTo: Invalid component');
152
- }
153
- return this.render(`#${juxComponent._id}`);
154
- }
155
92
  }
156
93
 
157
- /**
158
- * Factory helper
159
- */
160
94
  export function heading(id: string, options: HeadingOptions = {}): Heading {
161
95
  return new Heading(id, options);
162
96
  }
@@ -22,12 +22,19 @@ export function getOrCreateContainer(id: string): HTMLElement {
22
22
  container = document.createElement('div');
23
23
  container.id = id;
24
24
 
25
- // Find appropriate parent
26
- let parent: HTMLElement;
27
- // Page components go inside #appmain (or [data-jux-page] if no layout)
28
- const appmain = document.getElementById('appmain');
29
- const dataJuxPage = document.querySelector('[data-jux-page]');
30
- parent = (appmain || dataJuxPage || document.body) as HTMLElement;
25
+ // Find appropriate parent - [data-jux-page] takes precedence, then #app, then body
26
+ const dataJuxPage = document.querySelector('[data-jux-page]') as HTMLElement;
27
+ const app = document.getElementById('app');
28
+
29
+ const parent: HTMLElement = (dataJuxPage || app || document.body) as HTMLElement;
30
+
31
+ // Log warning if falling back to body
32
+ if (!dataJuxPage && !app) {
33
+ console.warn(
34
+ `[Jux] Preferred container targets "[data-jux-page]" or "#app" not found. Creating container "#${id}" in fallback parent: body`,
35
+ );
36
+ }
37
+
31
38
  parent.appendChild(container);
32
39
 
33
40
  return container;
@@ -1,9 +1,9 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.js';
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+
3
+ // Event definitions - Hero is display-only, but CTA button can trigger actions
4
+ const TRIGGER_EVENTS = [] as const;
5
+ const CALLBACK_EVENTS = ['ctaClick'] as const; // ✅ When CTA button is clicked
3
6
 
4
- /**
5
- * Hero component options
6
- */
7
7
  export interface HeroOptions {
8
8
  title?: string;
9
9
  subtitle?: string;
@@ -15,9 +15,6 @@ export interface HeroOptions {
15
15
  class?: string;
16
16
  }
17
17
 
18
- /**
19
- * Hero component state
20
- */
21
18
  type HeroState = {
22
19
  title: string;
23
20
  subtitle: string;
@@ -25,61 +22,43 @@ type HeroState = {
25
22
  ctaLink: string;
26
23
  backgroundImage: string;
27
24
  variant: string;
28
- style: string;
29
- class: string;
30
25
  content: string;
31
26
  backgroundOverlay: boolean;
32
27
  centered: boolean;
28
+ style: string;
29
+ class: string;
33
30
  };
34
31
 
35
- /**
36
- * Hero component
37
- *
38
- * Usage:
39
- * const hero = jux.hero('myHero', {
40
- * title: 'Welcome',
41
- * subtitle: 'Get started today',
42
- * cta: 'Learn More'
43
- * });
44
- * hero.render();
45
- */
46
- export class Hero {
47
- state: HeroState;
48
- container: HTMLElement | null = null;
49
- _id: string;
50
- id: string;
51
-
52
- // CRITICAL: Store bind/sync instructions for deferred wiring
53
- private _bindings: Array<{ event: string, handler: Function }> = [];
54
- private _syncBindings: Array<{
55
- property: string,
56
- stateObj: State<any>,
57
- toState?: Function,
58
- toComponent?: Function
59
- }> = [];
60
-
32
+ export class Hero extends BaseComponent<HeroState> {
61
33
  constructor(id: string, options: HeroOptions = {}) {
62
- this._id = id;
63
- this.id = id;
64
-
65
- this.state = {
34
+ super(id, {
66
35
  title: options.title ?? '',
67
36
  subtitle: options.subtitle ?? '',
68
37
  cta: options.cta ?? '',
69
38
  ctaLink: options.ctaLink ?? '#',
70
39
  backgroundImage: options.backgroundImage ?? '',
71
40
  variant: options.variant ?? 'default',
72
- style: options.style ?? '',
73
- class: options.class ?? '',
74
41
  content: '',
75
42
  backgroundOverlay: false,
76
- centered: false
77
- };
43
+ centered: false,
44
+ style: options.style ?? '',
45
+ class: options.class ?? ''
46
+ });
78
47
  }
79
48
 
80
- /* -------------------------
81
- * Fluent API
82
- * ------------------------- */
49
+ protected getTriggerEvents(): readonly string[] {
50
+ return TRIGGER_EVENTS;
51
+ }
52
+
53
+ protected getCallbackEvents(): readonly string[] {
54
+ return CALLBACK_EVENTS;
55
+ }
56
+
57
+ /* ═════════════════════════════════════════════════════════════════
58
+ * FLUENT API
59
+ * ═════════════════════════════════════════════════════════════════ */
60
+
61
+ // ✅ Inherited from BaseComponent
83
62
 
84
63
  title(value: string): this {
85
64
  this.state.title = value;
@@ -111,51 +90,15 @@ export class Hero {
111
90
  return this;
112
91
  }
113
92
 
114
- style(value: string): this {
115
- this.state.style = value;
116
- return this;
117
- }
118
-
119
- class(value: string): this {
120
- this.state.class = value;
121
- return this;
122
- }
123
-
124
- bind(event: string, handler: Function): this {
125
- this._bindings.push({ event, handler });
126
- return this;
127
- }
128
-
129
- sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
130
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
131
- throw new Error(`Hero.sync: Expected a State object for property "${property}"`);
132
- }
133
- this._syncBindings.push({ property, stateObj, toState, toComponent });
134
- return this;
135
- }
136
-
137
- /* -------------------------
138
- * Render
139
- * ------------------------- */
93
+ /* ═════════════════════════════════════════════════════════════════
94
+ * RENDER
95
+ * ═════════════════════════════════════════════════════════════════ */
140
96
 
141
97
  render(targetId?: string): this {
142
- // === 1. SETUP: Get or create container ===
143
- let container: HTMLElement;
144
- if (targetId) {
145
- const target = document.querySelector(targetId);
146
- if (!target || !(target instanceof HTMLElement)) {
147
- throw new Error(`Hero: Target "${targetId}" not found`);
148
- }
149
- container = target;
150
- } else {
151
- container = getOrCreateContainer(this._id);
152
- }
153
- this.container = container;
98
+ const container = this._setupContainer(targetId);
154
99
 
155
- // === 2. PREPARE: Destructure state ===
156
100
  const { title, subtitle, content, backgroundImage, backgroundOverlay, centered, style, class: className } = this.state;
157
101
 
158
- // === 3. BUILD: Create DOM elements ===
159
102
  const hero = document.createElement('section');
160
103
  hero.className = 'jux-hero';
161
104
  hero.id = this._id;
@@ -198,22 +141,30 @@ export class Hero {
198
141
  contentContainer.appendChild(contentEl);
199
142
  }
200
143
 
201
- hero.appendChild(contentContainer);
144
+ if (this.state.cta) {
145
+ const ctaButton = document.createElement('a');
146
+ ctaButton.className = 'jux-hero-cta';
147
+ ctaButton.href = this.state.ctaLink;
148
+ ctaButton.textContent = this.state.cta;
202
149
 
203
- // === 4. WIRE: Attach event listeners and sync bindings ===
150
+ // Fire callback when CTA is clicked
151
+ ctaButton.addEventListener('click', (e) => {
152
+ this._triggerCallback('ctaClick', e);
153
+ });
204
154
 
205
- // Wire custom bindings from .bind() calls
206
- this._bindings.forEach(({ event, handler }) => {
207
- hero.addEventListener(event, handler as EventListener);
208
- });
155
+ contentContainer.appendChild(ctaButton);
156
+ }
157
+
158
+ hero.appendChild(contentContainer);
159
+
160
+ this._wireStandardEvents(hero);
209
161
 
210
- // Wire sync bindings from .sync() calls
211
162
  this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
212
163
  if (property === 'title') {
213
- const transformToComponent = toComponent || ((v: any) => String(v));
164
+ const transform = toComponent || ((v: any) => String(v));
214
165
 
215
166
  stateObj.subscribe((val: any) => {
216
- const transformed = transformToComponent(val);
167
+ const transformed = transform(val);
217
168
  const titleEl = document.getElementById(`${this._id}-title`);
218
169
  if (titleEl) {
219
170
  titleEl.textContent = transformed;
@@ -222,10 +173,10 @@ export class Hero {
222
173
  });
223
174
  }
224
175
  else if (property === 'subtitle') {
225
- const transformToComponent = toComponent || ((v: any) => String(v));
176
+ const transform = toComponent || ((v: any) => String(v));
226
177
 
227
178
  stateObj.subscribe((val: any) => {
228
- const transformed = transformToComponent(val);
179
+ const transformed = transform(val);
229
180
  const subtitleEl = document.getElementById(`${this._id}-subtitle`);
230
181
  if (subtitleEl) {
231
182
  subtitleEl.textContent = transformed;
@@ -234,10 +185,10 @@ export class Hero {
234
185
  });
235
186
  }
236
187
  else if (property === 'content') {
237
- const transformToComponent = toComponent || ((v: any) => String(v));
188
+ const transform = toComponent || ((v: any) => String(v));
238
189
 
239
190
  stateObj.subscribe((val: any) => {
240
- const transformed = transformToComponent(val);
191
+ const transformed = transform(val);
241
192
  const contentEl = hero.querySelector('.jux-hero-body');
242
193
  if (contentEl) {
243
194
  contentEl.innerHTML = transformed;
@@ -247,25 +198,11 @@ export class Hero {
247
198
  }
248
199
  });
249
200
 
250
- // === 5. RENDER: Append to DOM and finalize ===
251
201
  container.appendChild(hero);
252
202
  return this;
253
203
  }
254
-
255
- /**
256
- * Render to another Jux component's container
257
- */
258
- renderTo(juxComponent: any): this {
259
- if (!juxComponent?._id) {
260
- throw new Error('Hero.renderTo: Invalid component');
261
- }
262
- return this.render(`#${juxComponent._id}`);
263
- }
264
204
  }
265
205
 
266
- /**
267
- * Factory helper
268
- */
269
206
  export function hero(id: string, options: HeroOptions = {}): Hero {
270
207
  return new Hero(id, options);
271
208
  }