juxscript 1.0.3 → 1.0.4

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 (71) hide show
  1. package/README.md +37 -92
  2. package/bin/cli.js +57 -56
  3. package/lib/components/alert.ts +240 -0
  4. package/lib/components/app.ts +216 -82
  5. package/lib/components/badge.ts +164 -0
  6. package/lib/components/button.ts +188 -53
  7. package/lib/components/card.ts +75 -61
  8. package/lib/components/chart.ts +17 -15
  9. package/lib/components/checkbox.ts +228 -0
  10. package/lib/components/code.ts +66 -152
  11. package/lib/components/container.ts +104 -208
  12. package/lib/components/data.ts +1 -3
  13. package/lib/components/datepicker.ts +226 -0
  14. package/lib/components/dialog.ts +258 -0
  15. package/lib/components/docs-data.json +1697 -388
  16. package/lib/components/dropdown.ts +244 -0
  17. package/lib/components/element.ts +271 -0
  18. package/lib/components/fileupload.ts +319 -0
  19. package/lib/components/footer.ts +37 -18
  20. package/lib/components/header.ts +53 -33
  21. package/lib/components/heading.ts +119 -0
  22. package/lib/components/helpers.ts +34 -0
  23. package/lib/components/hero.ts +57 -31
  24. package/lib/components/include.ts +292 -0
  25. package/lib/components/input.ts +166 -78
  26. package/lib/components/layout.ts +144 -18
  27. package/lib/components/list.ts +83 -74
  28. package/lib/components/loading.ts +263 -0
  29. package/lib/components/main.ts +43 -17
  30. package/lib/components/menu.ts +108 -24
  31. package/lib/components/modal.ts +50 -21
  32. package/lib/components/nav.ts +60 -18
  33. package/lib/components/paragraph.ts +111 -0
  34. package/lib/components/progress.ts +276 -0
  35. package/lib/components/radio.ts +236 -0
  36. package/lib/components/req.ts +300 -0
  37. package/lib/components/script.ts +33 -74
  38. package/lib/components/select.ts +247 -0
  39. package/lib/components/sidebar.ts +86 -36
  40. package/lib/components/style.ts +47 -70
  41. package/lib/components/switch.ts +261 -0
  42. package/lib/components/table.ts +47 -24
  43. package/lib/components/tabs.ts +105 -63
  44. package/lib/components/theme-toggle.ts +361 -0
  45. package/lib/components/token-calculator.ts +380 -0
  46. package/lib/components/tooltip.ts +244 -0
  47. package/lib/components/view.ts +36 -20
  48. package/lib/components/write.ts +284 -0
  49. package/lib/globals.d.ts +21 -0
  50. package/lib/jux.ts +172 -68
  51. package/lib/presets/notion.css +521 -0
  52. package/lib/presets/notion.jux +27 -0
  53. package/lib/reactivity/state.ts +364 -0
  54. package/machinery/compiler.js +126 -38
  55. package/machinery/generators/html.js +2 -3
  56. package/machinery/server.js +2 -2
  57. package/package.json +29 -3
  58. package/lib/components/import.ts +0 -430
  59. package/lib/components/node.ts +0 -200
  60. package/lib/components/reactivity.js +0 -104
  61. package/lib/components/theme.ts +0 -97
  62. package/lib/layouts/notion.css +0 -258
  63. package/lib/styles/base-theme.css +0 -186
  64. package/lib/styles/dark-theme.css +0 -144
  65. package/lib/styles/light-theme.css +0 -144
  66. package/lib/styles/tokens/dark.css +0 -86
  67. package/lib/styles/tokens/light.css +0 -86
  68. package/lib/templates/index.juxt +0 -33
  69. package/lib/themes/dark.css +0 -86
  70. package/lib/themes/light.css +0 -86
  71. /package/lib/{styles → presets}/global.css +0 -0
@@ -1,4 +1,4 @@
1
- import { Reactive, getOrCreateContainer } from './reactivity.js';
1
+ import { getOrCreateContainer } from './helpers.js';
2
2
 
3
3
  /**
4
4
  * Table column configuration
@@ -20,7 +20,9 @@ export interface TableOptions {
20
20
  striped?: boolean;
21
21
  hoverable?: boolean;
22
22
  bordered?: boolean;
23
- allowHtml?: boolean; // Enable HTML rendering in cells
23
+ allowHtml?: boolean;
24
+ style?: string;
25
+ class?: string;
24
26
  }
25
27
 
26
28
  /**
@@ -33,6 +35,8 @@ type TableState = {
33
35
  hoverable: boolean;
34
36
  bordered: boolean;
35
37
  allowHtml: boolean;
38
+ style: string;
39
+ class: string;
36
40
  };
37
41
 
38
42
  /**
@@ -46,7 +50,7 @@ type TableState = {
46
50
  * { name: 'Bob', age: 25 }
47
51
  * ],
48
52
  * striped: true,
49
- * allowHtml: true // Enable HTML rendering
53
+ * allowHtml: true
50
54
  * });
51
55
  * table.render();
52
56
  *
@@ -63,22 +67,26 @@ type TableState = {
63
67
  * });
64
68
  * table.render();
65
69
  */
66
- export class Table extends Reactive {
67
- state!: TableState;
70
+ export class Table {
71
+ state: TableState;
68
72
  container: HTMLElement | null = null;
73
+ _id: string;
74
+ id: string;
69
75
 
70
- constructor(componentId: string, options: TableOptions = {}) {
71
- super();
72
- this._setComponentId(componentId);
76
+ constructor(id: string, options: TableOptions = {}) {
77
+ this._id = id;
78
+ this.id = id;
73
79
 
74
- this.state = this._createReactiveState({
80
+ this.state = {
75
81
  columns: options.columns ?? [],
76
82
  data: options.data ?? [],
77
83
  striped: options.striped ?? false,
78
84
  hoverable: options.hoverable ?? true,
79
85
  bordered: options.bordered ?? false,
80
- allowHtml: options.allowHtml ?? true // Default to true for flexibility
81
- }) as TableState;
86
+ allowHtml: options.allowHtml ?? true,
87
+ style: options.style ?? '',
88
+ class: options.class ?? ''
89
+ };
82
90
  }
83
91
 
84
92
  /* -------------------------
@@ -115,6 +123,16 @@ export class Table extends Reactive {
115
123
  return this;
116
124
  }
117
125
 
126
+ style(value: string): this {
127
+ this.state.style = value;
128
+ return this;
129
+ }
130
+
131
+ class(value: string): this {
132
+ this.state.class = value;
133
+ return this;
134
+ }
135
+
118
136
  /* -------------------------
119
137
  * Helpers
120
138
  * ------------------------- */
@@ -144,14 +162,12 @@ export class Table extends Reactive {
144
162
  * 'user_id' -> 'User Id'
145
163
  */
146
164
  private _formatLabel(key: string): string {
147
- // Split on camelCase or snake_case
148
165
  const words = key
149
- .replace(/([A-Z])/g, ' $1') // camelCase -> camel Case
150
- .replace(/_/g, ' ') // snake_case -> snake case
166
+ .replace(/([A-Z])/g, ' $1')
167
+ .replace(/_/g, ' ')
151
168
  .trim()
152
169
  .split(/\s+/);
153
170
 
154
- // Capitalize first letter of each word
155
171
  return words
156
172
  .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
157
173
  .join(' ');
@@ -171,11 +187,11 @@ export class Table extends Reactive {
171
187
  }
172
188
  container = target;
173
189
  } else {
174
- container = getOrCreateContainer(this._componentId) as HTMLElement;
190
+ container = getOrCreateContainer(this._id);
175
191
  }
176
192
 
177
193
  this.container = container;
178
- let { columns, data, striped, hoverable, bordered, allowHtml } = this.state;
194
+ let { columns, data, striped, hoverable, bordered, allowHtml, style, class: className } = this.state;
179
195
 
180
196
  // Auto-generate columns if not provided
181
197
  if (columns.length === 0 && data.length > 0) {
@@ -184,7 +200,15 @@ export class Table extends Reactive {
184
200
 
185
201
  const wrapper = document.createElement('div');
186
202
  wrapper.className = 'jux-table-wrapper';
187
- wrapper.id = this._componentId;
203
+ wrapper.id = this._id;
204
+
205
+ if (className) {
206
+ wrapper.className += ` ${className}`;
207
+ }
208
+
209
+ if (style) {
210
+ wrapper.setAttribute('style', style);
211
+ }
188
212
 
189
213
  const table = document.createElement('table');
190
214
  table.className = 'jux-table';
@@ -218,7 +242,6 @@ export class Table extends Reactive {
218
242
  const td = document.createElement('td');
219
243
  if (col.align) td.style.textAlign = col.align;
220
244
 
221
- // Get cell value
222
245
  const cellValue = row[col.key] ?? '';
223
246
 
224
247
  // Custom render function takes precedence
@@ -263,17 +286,17 @@ export class Table extends Reactive {
263
286
  throw new Error('Table.renderTo: Invalid component - not an object');
264
287
  }
265
288
 
266
- if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
267
- throw new Error('Table.renderTo: Invalid component - missing _componentId (not a Jux component)');
289
+ if (!juxComponent._id || typeof juxComponent._id !== 'string') {
290
+ throw new Error('Table.renderTo: Invalid component - missing _id (not a Jux component)');
268
291
  }
269
292
 
270
- return this.render(`#${juxComponent._componentId}`);
293
+ return this.render(`#${juxComponent._id}`);
271
294
  }
272
295
  }
273
296
 
274
297
  /**
275
298
  * Factory helper
276
299
  */
277
- export function table(componentId: string, options: TableOptions = {}): Table {
278
- return new Table(componentId, options);
300
+ export function table(id: string, options: TableOptions = {}): Table {
301
+ return new Table(id, options);
279
302
  }
@@ -1,4 +1,4 @@
1
- import { Reactive, getOrCreateContainer } from './reactivity.js';
1
+ import { getOrCreateContainer } from './helpers.js';
2
2
 
3
3
  /**
4
4
  * Tab configuration
@@ -15,6 +15,8 @@ export interface Tab {
15
15
  export interface TabsOptions {
16
16
  tabs?: Tab[];
17
17
  activeTab?: string;
18
+ style?: string;
19
+ class?: string;
18
20
  }
19
21
 
20
22
  /**
@@ -23,6 +25,8 @@ export interface TabsOptions {
23
25
  type TabsState = {
24
26
  tabs: Tab[];
25
27
  activeTab: string;
28
+ style: string;
29
+ class: string;
26
30
  };
27
31
 
28
32
  /**
@@ -35,23 +39,27 @@ type TabsState = {
35
39
  * { id: 'tab2', label: 'Tab 2', content: 'Content 2' }
36
40
  * ]
37
41
  * });
38
- * await tabs.render();
42
+ * tabs.render();
39
43
  */
40
- export class Tabs extends Reactive {
41
- state!: TabsState;
44
+ export class Tabs {
45
+ state: TabsState;
42
46
  container: HTMLElement | null = null;
43
-
44
- constructor(componentId: string, options: TabsOptions = {}) {
45
- super();
46
- this._setComponentId(componentId);
47
-
47
+ _id: string;
48
+ id: string;
49
+
50
+ constructor(id: string, options: TabsOptions = {}) {
51
+ this._id = id;
52
+ this.id = id;
53
+
48
54
  const tabs = options.tabs ?? [];
49
55
  const activeTab = options.activeTab ?? (tabs.length > 0 ? tabs[0].id : '');
50
-
51
- this.state = this._createReactiveState({
56
+
57
+ this.state = {
52
58
  tabs,
53
- activeTab
54
- }) as TabsState;
59
+ activeTab,
60
+ style: options.style ?? '',
61
+ class: options.class ?? ''
62
+ };
55
63
  }
56
64
 
57
65
  /* -------------------------
@@ -64,7 +72,7 @@ export class Tabs extends Reactive {
64
72
  }
65
73
 
66
74
  addTab(tab: Tab): this {
67
- this.state.tabs.push(tab);
75
+ this.state.tabs = [...this.state.tabs, tab];
68
76
  return this;
69
77
  }
70
78
 
@@ -73,13 +81,57 @@ export class Tabs extends Reactive {
73
81
  return this;
74
82
  }
75
83
 
84
+ style(value: string): this {
85
+ this.state.style = value;
86
+ return this;
87
+ }
88
+
89
+ class(value: string): this {
90
+ this.state.class = value;
91
+ return this;
92
+ }
93
+
94
+ /* -------------------------
95
+ * Helpers
96
+ * ------------------------- */
97
+
98
+ private _updateActiveTab(tabId: string): void {
99
+ if (!this.container) return;
100
+
101
+ const wrapper = this.container.querySelector(`#${this._id}`);
102
+ if (!wrapper) return;
103
+
104
+ this.state.activeTab = tabId;
105
+
106
+ // Update tab buttons
107
+ const tabButtons = wrapper.querySelectorAll('.jux-tab-button');
108
+ tabButtons.forEach((btn, index) => {
109
+ if (this.state.tabs[index]?.id === tabId) {
110
+ btn.classList.add('jux-tab-button-active');
111
+ } else {
112
+ btn.classList.remove('jux-tab-button-active');
113
+ }
114
+ });
115
+
116
+ // Update tab panels
117
+ const tabPanels = wrapper.querySelectorAll('.jux-tab-panel');
118
+ tabPanels.forEach(panel => {
119
+ const panelId = panel.getAttribute('data-tab-id');
120
+ if (panelId === tabId) {
121
+ panel.classList.add('jux-tab-panel-active');
122
+ } else {
123
+ panel.classList.remove('jux-tab-panel-active');
124
+ }
125
+ });
126
+ }
127
+
76
128
  /* -------------------------
77
129
  * Render
78
130
  * ------------------------- */
79
131
 
80
- async render(targetId?: string): Promise<this> {
132
+ render(targetId?: string): this {
81
133
  let container: HTMLElement;
82
-
134
+
83
135
  if (targetId) {
84
136
  const target = document.querySelector(targetId);
85
137
  if (!target || !(target instanceof HTMLElement)) {
@@ -87,105 +139,95 @@ export class Tabs extends Reactive {
87
139
  }
88
140
  container = target;
89
141
  } else {
90
- container = getOrCreateContainer(this._componentId) as HTMLElement;
142
+ container = getOrCreateContainer(this._id);
91
143
  }
92
-
144
+
93
145
  this.container = container;
94
- const { tabs, activeTab } = this.state;
95
-
146
+ const { tabs, activeTab, style, class: className } = this.state;
147
+
96
148
  const wrapper = document.createElement('div');
97
149
  wrapper.className = 'jux-tabs';
98
- wrapper.id = this._componentId;
99
-
150
+ wrapper.id = this._id;
151
+
152
+ if (className) {
153
+ wrapper.className += ` ${className}`;
154
+ }
155
+
156
+ if (style) {
157
+ wrapper.setAttribute('style', style);
158
+ }
159
+
100
160
  // Tab headers
101
161
  const tabHeaders = document.createElement('div');
102
162
  tabHeaders.className = 'jux-tabs-header';
103
-
163
+
104
164
  tabs.forEach(tab => {
105
165
  const tabBtn = document.createElement('button');
106
166
  tabBtn.className = 'jux-tab-button';
107
167
  tabBtn.textContent = tab.label;
108
-
168
+
109
169
  if (tab.id === activeTab) {
110
170
  tabBtn.classList.add('jux-tab-button-active');
111
171
  }
112
-
172
+
113
173
  tabHeaders.appendChild(tabBtn);
114
-
174
+
115
175
  // Event binding - tab click
116
176
  tabBtn.addEventListener('click', () => {
117
- this.state.activeTab = tab.id;
118
-
119
- // Update active states
120
- tabHeaders.querySelectorAll('.jux-tab-button').forEach(btn => {
121
- btn.classList.remove('jux-tab-button-active');
122
- });
123
- tabBtn.classList.add('jux-tab-button-active');
124
-
125
- // Update content
126
- tabPanels.querySelectorAll('.jux-tab-panel').forEach(panel => {
127
- panel.classList.remove('jux-tab-panel-active');
128
- });
129
- const activePanel = tabPanels.querySelector(`[data-tab-id="${tab.id}"]`);
130
- if (activePanel) {
131
- activePanel.classList.add('jux-tab-panel-active');
132
- }
133
-
134
- this.emit('tabChange', { tabId: tab.id });
177
+ this._updateActiveTab(tab.id);
135
178
  });
136
179
  });
137
-
180
+
138
181
  wrapper.appendChild(tabHeaders);
139
-
182
+
140
183
  // Tab panels
141
184
  const tabPanels = document.createElement('div');
142
185
  tabPanels.className = 'jux-tabs-panels';
143
-
144
- for (const tab of tabs) {
186
+
187
+ tabs.forEach(tab => {
145
188
  const panel = document.createElement('div');
146
189
  panel.className = 'jux-tab-panel';
147
190
  panel.setAttribute('data-tab-id', tab.id);
148
-
191
+
149
192
  if (tab.id === activeTab) {
150
193
  panel.classList.add('jux-tab-panel-active');
151
194
  }
152
-
195
+
153
196
  // Render content
154
197
  if (typeof tab.content === 'function') {
155
198
  panel.innerHTML = tab.content();
156
199
  } else {
157
200
  panel.innerHTML = tab.content;
158
201
  }
159
-
202
+
160
203
  tabPanels.appendChild(panel);
161
- }
162
-
204
+ });
205
+
163
206
  wrapper.appendChild(tabPanels);
164
207
  container.appendChild(wrapper);
165
-
166
- this.emit('rendered');
208
+
167
209
  return this;
168
210
  }
169
211
 
170
212
  /**
171
213
  * Render to another Jux component's container
172
214
  */
173
- async renderTo(juxComponent: any): Promise<this> {
215
+ renderTo(juxComponent: any): this {
174
216
  if (!juxComponent || typeof juxComponent !== 'object') {
175
217
  throw new Error('Tabs.renderTo: Invalid component - not an object');
176
218
  }
177
-
178
- if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
179
- throw new Error('Tabs.renderTo: Invalid component - missing _componentId (not a Jux component)');
219
+
220
+ if (!juxComponent._id || typeof juxComponent._id !== 'string') {
221
+ throw new Error('Tabs.renderTo: Invalid component - missing _id (not a Jux component)');
180
222
  }
181
-
182
- return this.render(`#${juxComponent._componentId}`);
223
+
224
+ return this.render(`#${juxComponent._id}`);
183
225
  }
184
226
  }
185
227
 
186
228
  /**
187
229
  * Factory helper
188
230
  */
189
- export function tabs(componentId: string, options: TabsOptions = {}): Tabs {
190
- return new Tabs(componentId, options);
231
+ export function tabs(id: string, options: TabsOptions = {}): Tabs {
232
+ return new Tabs(id, options);
191
233
  }