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,156 +1,123 @@
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 = [] as const;
2
6
 
3
- /**
4
- * Code component options
5
- */
6
7
  export interface CodeOptions {
7
8
  code?: string;
8
9
  language?: string;
9
- lineNumbers?: boolean;
10
- highlight?: boolean;
11
10
  style?: string;
12
11
  class?: string;
13
12
  }
14
13
 
15
- /**
16
- * Code component state
17
- */
18
14
  type CodeState = {
19
15
  code: string;
20
16
  language: string;
21
- lineNumbers: boolean;
22
- highlight: boolean;
23
17
  style: string;
24
18
  class: string;
25
19
  };
26
20
 
27
- /**
28
- * Code component
29
- *
30
- * Usage:
31
- * const code = jux.code('myCode', 'console.log("hello")', 'javascript');
32
- * code.render();
33
- */
34
- export class Code {
35
- state: CodeState;
36
- container: HTMLElement | null = null;
37
- _id: string;
38
- id: string;
39
-
40
- constructor(
41
- id: string,
42
- content?: string,
43
- language?: string,
44
- options: CodeOptions = {}
45
- ) {
46
- this._id = id;
47
- this.id = id;
48
-
49
- this.state = {
50
- code: content ?? options.code ?? '',
51
- language: language ?? options.language ?? 'javascript',
52
- lineNumbers: options.lineNumbers ?? false,
53
- highlight: options.highlight ?? true,
21
+ export class Code extends BaseComponent<CodeState> {
22
+ constructor(id: string, options: CodeOptions = {}) {
23
+ super(id, {
24
+ code: options.code ?? '',
25
+ language: options.language ?? 'javascript',
54
26
  style: options.style ?? '',
55
27
  class: options.class ?? ''
56
- };
28
+ });
57
29
  }
58
30
 
59
- /* -------------------------
60
- * Fluent API
61
- * ------------------------- */
62
-
63
- code(value: string): this {
64
- this.state.code = value;
65
- return this;
31
+ protected getTriggerEvents(): readonly string[] {
32
+ return TRIGGER_EVENTS;
66
33
  }
67
34
 
68
- language(value: string): this {
69
- this.state.language = value;
70
- return this;
35
+ protected getCallbackEvents(): readonly string[] {
36
+ return CALLBACK_EVENTS;
71
37
  }
72
38
 
73
- lineNumbers(value: boolean): this {
74
- this.state.lineNumbers = value;
75
- return this;
76
- }
39
+ /* ═════════════════════════════════════════════════════════════════
40
+ * FLUENT API
41
+ * ═════════════════════════════════════════════════════════════════ */
77
42
 
78
- highlight(value: boolean): this {
79
- this.state.highlight = value;
80
- return this;
81
- }
43
+ // Inherited from BaseComponent
82
44
 
83
- style(value: string): this {
84
- this.state.style = value;
45
+ code(value: string): this {
46
+ this.state.code = value;
85
47
  return this;
86
48
  }
87
49
 
88
- class(value: string): this {
89
- this.state.class = value;
50
+ language(value: string): this {
51
+ this.state.language = value;
90
52
  return this;
91
53
  }
92
54
 
93
- /* -------------------------
94
- * Render
95
- * ------------------------- */
55
+ /* ═════════════════════════════════════════════════════════════════
56
+ * RENDER
57
+ * ═════════════════════════════════════════════════════════════════ */
96
58
 
97
59
  render(targetId?: string): this {
98
- let container: HTMLElement;
60
+ const container = this._setupContainer(targetId);
99
61
 
100
- if (targetId) {
101
- const target = document.querySelector(targetId);
102
- if (!target || !(target instanceof HTMLElement)) {
103
- throw new Error(`Code: Target element "${targetId}" not found`);
104
- }
105
- container = target;
106
- } else {
107
- container = getOrCreateContainer(this._id);
108
- }
62
+ const { code, language, style, class: className } = this.state;
109
63
 
110
- this.container = container;
111
- const { code, language, lineNumbers, highlight, style, class: className } = this.state;
64
+ const wrapper = document.createElement('div');
65
+ wrapper.className = 'jux-code';
66
+ wrapper.id = this._id;
67
+ if (className) wrapper.className += ` ${className}`;
68
+ if (style) wrapper.setAttribute('style', style);
112
69
 
113
70
  const pre = document.createElement('pre');
114
- pre.className = lineNumbers ? 'jux-code line-numbers' : 'jux-code';
115
- pre.id = this._id;
116
-
117
- if (className) {
118
- pre.className += ` ${className}`;
119
- }
120
-
121
- if (style) {
122
- pre.setAttribute('style', style);
123
- }
124
-
125
71
  const codeEl = document.createElement('code');
126
- codeEl.className = highlight ? `language-${language}` : '';
72
+ codeEl.className = `language-${language}`;
127
73
  codeEl.textContent = code;
128
-
129
74
  pre.appendChild(codeEl);
130
- container.appendChild(pre);
75
+ wrapper.appendChild(pre);
131
76
 
132
- return this;
133
- }
77
+ this._wireStandardEvents(wrapper);
134
78
 
135
- /**
136
- * Render to another Jux component's container
137
- */
138
- renderTo(juxComponent: any): this {
139
- if (!juxComponent || typeof juxComponent !== 'object') {
140
- throw new Error('Code.renderTo: Invalid component - not an object');
141
- }
79
+ // Wire sync bindings
80
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
81
+ if (property === 'code') {
82
+ const transform = toComponent || ((v: any) => String(v));
142
83
 
143
- if (!juxComponent._id || typeof juxComponent._id !== 'string') {
144
- throw new Error('Code.renderTo: Invalid component - missing _id (not a Jux component)');
145
- }
84
+ stateObj.subscribe((val: any) => {
85
+ const transformed = transform(val);
86
+ codeEl.textContent = transformed;
87
+ this.state.code = transformed;
146
88
 
147
- return this.render(`#${juxComponent._id}`);
89
+ if ((window as any).Prism) {
90
+ (window as any).Prism.highlightElement(codeEl);
91
+ }
92
+ });
93
+ }
94
+ else if (property === 'language') {
95
+ const transform = toComponent || ((v: any) => String(v));
96
+
97
+ stateObj.subscribe((val: any) => {
98
+ const transformed = transform(val);
99
+ codeEl.className = `language-${transformed}`;
100
+ this.state.language = transformed;
101
+
102
+ if ((window as any).Prism) {
103
+ (window as any).Prism.highlightElement(codeEl);
104
+ }
105
+ });
106
+ }
107
+ });
108
+
109
+ container.appendChild(wrapper);
110
+
111
+ requestAnimationFrame(() => {
112
+ if ((window as any).Prism) {
113
+ (window as any).Prism.highlightElement(codeEl);
114
+ }
115
+ });
116
+
117
+ return this;
148
118
  }
149
119
  }
150
120
 
151
- /**
152
- * Factory helper
153
- */
154
- export function code(id: string, content?: string, language?: string): Code {
155
- return new Code(id, content, language);
121
+ export function code(id: string, options: CodeOptions = {}): Code {
122
+ return new Code(id, options);
156
123
  }
@@ -1,178 +1,161 @@
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 = [] as const;
2
6
 
3
- /**
4
- * Container options
5
- */
6
7
  export interface ContainerOptions {
7
- class?: string;
8
- style?: string;
9
8
  direction?: 'row' | 'column';
10
- gap?: string | number;
9
+ gap?: number | string;
10
+ wrap?: boolean;
11
11
  align?: 'start' | 'center' | 'end' | 'stretch';
12
12
  justify?: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
13
+ padding?: string;
14
+ style?: string;
15
+ class?: string;
13
16
  }
14
17
 
15
- /**
16
- * Container state
17
- */
18
18
  type ContainerState = {
19
- class: string;
19
+ direction: string;
20
+ gap: number | string;
21
+ wrap: boolean;
22
+ align: string;
23
+ justify: string;
24
+ padding: string;
20
25
  style: string;
21
- direction?: 'row' | 'column';
22
- gap?: string | number;
23
- align?: string;
24
- justify?: string;
26
+ class: string;
25
27
  };
26
28
 
27
- /**
28
- * Container component - a simple div container for grouping elements
29
- *
30
- * Usage:
31
- * // Plain container
32
- * jux.container('wrapper').render('#app');
33
- *
34
- * // Flex container with layout
35
- * jux.container('toolbar')
36
- * .direction('row')
37
- * .gap(16)
38
- * .render('#app');
39
- */
40
- export class Container {
41
- state: ContainerState;
42
- container: HTMLElement | null = null;
43
- _id: string;
44
- id: string;
45
-
29
+ export class Container extends BaseComponent<ContainerState> {
46
30
  constructor(id: string, options: ContainerOptions = {}) {
47
- this._id = id;
48
- this.id = id;
49
-
50
- this.state = {
51
- class: options.class ?? '',
31
+ super(id, {
32
+ direction: options.direction ?? 'column',
33
+ gap: options.gap ?? 0,
34
+ wrap: options.wrap ?? false,
35
+ align: options.align ?? 'stretch',
36
+ justify: options.justify ?? 'start',
37
+ padding: options.padding ?? '0',
52
38
  style: options.style ?? '',
53
- direction: options.direction,
54
- gap: options.gap,
55
- align: options.align,
56
- justify: options.justify
57
- };
39
+ class: options.class ?? ''
40
+ });
58
41
  }
59
42
 
60
- /* -------------------------
61
- * Fluent API
62
- * ------------------------- */
63
-
64
- class(value: string): this {
65
- this.state.class = value;
66
- return this;
43
+ protected getTriggerEvents(): readonly string[] {
44
+ return TRIGGER_EVENTS;
67
45
  }
68
46
 
69
- style(value: string): this {
70
- this.state.style = value;
71
- return this;
47
+ protected getCallbackEvents(): readonly string[] {
48
+ return CALLBACK_EVENTS;
72
49
  }
73
50
 
74
- /**
75
- * Set flex direction (row = horizontal, column = vertical)
76
- * Automatically enables flexbox when called
77
- */
51
+ /* ═════════════════════════════════════════════════════════════════
52
+ * FLUENT API
53
+ * ═════════════════════════════════════════════════════════════════ */
54
+
55
+ // ✅ Inherited from BaseComponent:
56
+ // - style(), class()
57
+ // - addClass(), removeClass(), toggleClass()
58
+ // - visible(), show(), hide()
59
+ // - attr(), attrs(), removeAttr()
60
+ // - disabled(), enable(), disable()
61
+ // - bind(), sync(), renderTo()
62
+
78
63
  direction(value: 'row' | 'column'): this {
79
64
  this.state.direction = value;
80
65
  return this;
81
66
  }
82
67
 
83
- /**
84
- * Set gap between children
85
- * Automatically enables flexbox when called
86
- * @param value - Gap size (number = px, string = any CSS unit)
87
- */
88
- gap(value: string | number): this {
68
+ gap(value: number | string): this {
89
69
  this.state.gap = value;
90
70
  return this;
91
71
  }
92
72
 
93
- /**
94
- * Set alignment of children along cross axis
95
- * Automatically enables flexbox when called
96
- */
73
+ wrap(value: boolean): this {
74
+ this.state.wrap = value;
75
+ return this;
76
+ }
77
+
97
78
  align(value: 'start' | 'center' | 'end' | 'stretch'): this {
98
79
  this.state.align = value;
99
80
  return this;
100
81
  }
101
82
 
102
- /**
103
- * Set justification of children along main axis
104
- * Automatically enables flexbox when called
105
- */
106
83
  justify(value: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'): this {
107
84
  this.state.justify = value;
108
85
  return this;
109
86
  }
110
87
 
111
- /* -------------------------
112
- * Render
113
- * ------------------------- */
114
-
115
- render(targetId?: string | HTMLElement): this {
116
- let container: HTMLElement;
88
+ padding(value: string): this {
89
+ this.state.padding = value;
90
+ return this;
91
+ }
117
92
 
118
- if (targetId) {
119
- if (targetId instanceof HTMLElement) {
120
- container = targetId;
121
- } else {
122
- const target = document.querySelector(targetId);
123
- if (!target || !(target instanceof HTMLElement)) {
124
- throw new Error(`Container: Target element "${targetId}" not found`);
93
+ /* ═════════════════════════════════════════════════════════════════
94
+ * RENDER
95
+ * ═════════════════════════════════════════════════════════════════ */
96
+
97
+ render(targetId?: string): this {
98
+ const container = this._setupContainer(targetId);
99
+
100
+ const { direction, gap, wrap, align, justify, padding, style, class: className } = this.state;
101
+
102
+ const wrapper = document.createElement('div');
103
+ wrapper.className = 'jux-container';
104
+ wrapper.id = this._id;
105
+ if (className) wrapper.className += ` ${className}`;
106
+
107
+ // Build inline styles
108
+ const gapValue = typeof gap === 'number' ? `${gap}px` : gap;
109
+ const computedStyle = `
110
+ display: flex;
111
+ flex-direction: ${direction};
112
+ gap: ${gapValue};
113
+ flex-wrap: ${wrap ? 'wrap' : 'nowrap'};
114
+ align-items: ${align};
115
+ justify-content: ${justify};
116
+ padding: ${padding};
117
+ ${style}
118
+ `.trim();
119
+
120
+ wrapper.setAttribute('style', computedStyle);
121
+
122
+ this._wireStandardEvents(wrapper);
123
+
124
+ // Wire sync bindings
125
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
126
+ const transform = toComponent || ((v: any) => v);
127
+
128
+ stateObj.subscribe((val: any) => {
129
+ const transformed = transform(val);
130
+
131
+ if (property === 'direction') {
132
+ this.state.direction = String(transformed);
133
+ wrapper.style.flexDirection = this.state.direction;
134
+ } else if (property === 'gap') {
135
+ this.state.gap = transformed;
136
+ const gapVal = typeof transformed === 'number' ? `${transformed}px` : transformed;
137
+ wrapper.style.gap = gapVal;
138
+ } else if (property === 'wrap') {
139
+ this.state.wrap = Boolean(transformed);
140
+ wrapper.style.flexWrap = this.state.wrap ? 'wrap' : 'nowrap';
141
+ } else if (property === 'align') {
142
+ this.state.align = String(transformed);
143
+ wrapper.style.alignItems = this.state.align;
144
+ } else if (property === 'justify') {
145
+ this.state.justify = String(transformed);
146
+ wrapper.style.justifyContent = this.state.justify;
147
+ } else if (property === 'padding') {
148
+ this.state.padding = String(transformed);
149
+ wrapper.style.padding = this.state.padding;
125
150
  }
126
- container = target;
127
- }
128
- } else {
129
- container = getOrCreateContainer(this._id);
130
- }
131
-
132
- this.container = container;
133
- const { class: className, style, direction, gap, align, justify } = this.state;
134
-
135
- const div = document.createElement('div');
136
- div.id = this._id;
137
-
138
- // Always include jux-container class, append custom classes
139
- div.className = className ? `jux-container ${className}` : 'jux-container';
140
-
141
- // Only apply flex styles if any flex properties are set
142
- const usesFlexbox = direction || gap !== undefined || align || justify;
143
-
144
- let computedStyle = style;
145
-
146
- if (usesFlexbox) {
147
- const flexStyles: string[] = ['display: flex', 'width: max-content'];
148
-
149
- if (direction) flexStyles.push(`flex-direction: ${direction}`);
150
-
151
- if (gap !== undefined) {
152
- const gapValue = typeof gap === 'number' ? `${gap}px` : gap;
153
- flexStyles.push(`gap: ${gapValue}`);
154
- }
155
- // Only set align-items if explicitly specified
156
- if (align) flexStyles.push(`align-items: ${align}`);
157
- // Only set justify-content if explicitly specified
158
- if (justify) flexStyles.push(`justify-content: ${justify}`);
159
-
160
- computedStyle = `${flexStyles.join('; ')}; ${style}`.trim();
161
- }
162
-
163
- if (computedStyle) {
164
- div.setAttribute('style', computedStyle);
165
- }
166
-
167
- container.appendChild(div);
151
+ });
152
+ });
168
153
 
154
+ container.appendChild(wrapper);
169
155
  return this;
170
156
  }
171
157
  }
172
158
 
173
- /**
174
- * Factory helper
175
- */
176
159
  export function container(id: string, options: ContainerOptions = {}): Container {
177
160
  return new Container(id, options);
178
161
  }
@@ -93,11 +93,43 @@ export class Data {
93
93
  this._data = [];
94
94
  this._loading = false;
95
95
  }
96
+
97
+ // Remove id requirement, make it optional for caching
98
+ static async fetch<T = any>(url: string, options?: RequestInit): Promise<T> {
99
+ const response = await fetch(url, options);
100
+ if (!response.ok) {
101
+ throw new Error(`API error: ${response.statusText}`);
102
+ }
103
+ return response.json();
104
+ }
105
+
106
+ // ✅ Static methods - no ID needed
107
+ static async post<T = any>(url: string, body: any, options?: RequestInit): Promise<T> {
108
+ const response = await fetch(url, options);
109
+ if (!response.ok) {
110
+ throw new Error(`API error: ${response.statusText}`);
111
+ }
112
+ return response.json();
113
+ }
114
+
115
+ static async put<T = any>(url: string, body: any, options?: RequestInit): Promise<T> {
116
+ const response = await fetch(url, options);
117
+ if (!response.ok) {
118
+ throw new Error(`API error: ${response.statusText}`);
119
+ }
120
+ return response.json();
121
+ }
122
+
123
+ static async delete<T = any>(url: string, options?: RequestInit): Promise<T> {
124
+ const response = await fetch(url, options);
125
+ if (!response.ok) {
126
+ throw new Error(`API error: ${response.statusText}`);
127
+ }
128
+ return response.json();
129
+ }
96
130
  }
97
131
 
98
- /**
99
- * Factory helper
100
- */
101
- export function data(sql: string, params: any[] = [], apiUrl: string = '/api/query'): Data {
102
- return new Data(sql, params, apiUrl);
132
+ // ✅ No ID needed - pure data fetching
133
+ export function data<T = any>(url: string, options?: RequestInit): Promise<T> {
134
+ return Data.fetch<T>(url, options);
103
135
  }