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,70 +1,56 @@
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 = ['tabChange'] as const;
2
6
 
3
- /**
4
- * Tab configuration
5
- */
6
7
  export interface Tab {
7
8
  id: string;
8
9
  label: string;
9
- content: string | (() => string);
10
+ content: string | HTMLElement;
11
+ icon?: string;
10
12
  }
11
13
 
12
- /**
13
- * Tabs component options
14
- */
15
14
  export interface TabsOptions {
16
15
  tabs?: Tab[];
17
16
  activeTab?: string;
17
+ variant?: 'default' | 'pills' | 'underline';
18
18
  style?: string;
19
19
  class?: string;
20
20
  }
21
21
 
22
- /**
23
- * Tabs component state
24
- */
25
22
  type TabsState = {
26
23
  tabs: Tab[];
27
24
  activeTab: string;
25
+ variant: string;
28
26
  style: string;
29
27
  class: string;
30
28
  };
31
29
 
32
- /**
33
- * Tabs component
34
- *
35
- * Usage:
36
- * const tabs = jux.tabs('myTabs', {
37
- * tabs: [
38
- * { id: 'tab1', label: 'Tab 1', content: 'Content 1' },
39
- * { id: 'tab2', label: 'Tab 2', content: 'Content 2' }
40
- * ]
41
- * });
42
- * tabs.render();
43
- */
44
- export class Tabs {
45
- state: TabsState;
46
- container: HTMLElement | null = null;
47
- _id: string;
48
- id: string;
49
-
30
+ export class Tabs extends BaseComponent<TabsState> {
50
31
  constructor(id: string, options: TabsOptions = {}) {
51
- this._id = id;
52
- this.id = id;
53
-
54
- const tabs = options.tabs ?? [];
55
- const activeTab = options.activeTab ?? (tabs.length > 0 ? tabs[0].id : '');
56
-
57
- this.state = {
58
- tabs,
59
- activeTab,
32
+ super(id, {
33
+ tabs: options.tabs ?? [],
34
+ activeTab: options.activeTab ?? (options.tabs?.[0]?.id ?? ''),
35
+ variant: options.variant ?? 'default',
60
36
  style: options.style ?? '',
61
37
  class: options.class ?? ''
62
- };
38
+ });
39
+ }
40
+
41
+ protected getTriggerEvents(): readonly string[] {
42
+ return TRIGGER_EVENTS;
43
+ }
44
+
45
+ protected getCallbackEvents(): readonly string[] {
46
+ return CALLBACK_EVENTS;
63
47
  }
64
48
 
65
- /* -------------------------
66
- * Fluent API
67
- * ------------------------- */
49
+ /* ═════════════════════════════════════════════════════════════════
50
+ * FLUENT API
51
+ * ═════════════════════════════════════════════════════════════════ */
52
+
53
+ // ✅ Inherited from BaseComponent
68
54
 
69
55
  tabs(value: Tab[]): this {
70
56
  this.state.tabs = value;
@@ -78,156 +64,187 @@ export class Tabs {
78
64
 
79
65
  activeTab(value: string): this {
80
66
  this.state.activeTab = value;
67
+ this._updateActiveTab();
81
68
  return this;
82
69
  }
83
70
 
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;
71
+ variant(value: 'default' | 'pills' | 'underline'): this {
72
+ this.state.variant = value;
91
73
  return this;
92
74
  }
93
75
 
94
- /* -------------------------
95
- * Helpers
96
- * ------------------------- */
97
-
98
- private _updateActiveTab(tabId: string): void {
76
+ private _updateActiveTab(): void {
99
77
  if (!this.container) return;
100
-
101
78
  const wrapper = this.container.querySelector(`#${this._id}`);
102
79
  if (!wrapper) return;
103
80
 
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
- }
81
+ wrapper.querySelectorAll('.jux-tabs-button').forEach(btn => {
82
+ btn.classList.toggle('jux-tabs-button-active', btn.getAttribute('data-tab') === this.state.activeTab);
114
83
  });
115
84
 
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
- }
85
+ wrapper.querySelectorAll('.jux-tabs-panel').forEach(panel => {
86
+ const isActive = panel.getAttribute('data-tab') === this.state.activeTab;
87
+ panel.classList.toggle('jux-tabs-panel-active', isActive);
88
+ (panel as HTMLElement).style.display = isActive ? 'block' : 'none';
125
89
  });
126
90
  }
127
91
 
128
- /* -------------------------
129
- * Render
130
- * ------------------------- */
92
+ /* ═════════════════════════════════════════════════════════════════
93
+ * RENDER
94
+ * ═════════════════════════════════════════════════════════════════ */
131
95
 
132
96
  render(targetId?: string): this {
133
- let container: HTMLElement;
97
+ const container = this._setupContainer(targetId);
134
98
 
135
- if (targetId) {
136
- const target = document.querySelector(targetId);
137
- if (!target || !(target instanceof HTMLElement)) {
138
- throw new Error(`Tabs: Target element "${targetId}" not found`);
139
- }
140
- container = target;
141
- } else {
142
- container = getOrCreateContainer(this._id);
143
- }
144
-
145
- this.container = container;
146
- const { tabs, activeTab, style, class: className } = this.state;
99
+ const { tabs, activeTab, variant, style, class: className } = this.state;
100
+ const hasActiveTabSync = this._syncBindings.some(b => b.property === 'activeTab');
147
101
 
148
102
  const wrapper = document.createElement('div');
149
- wrapper.className = 'jux-tabs';
103
+ wrapper.className = `jux-tabs jux-tabs-${variant}`;
150
104
  wrapper.id = this._id;
105
+ if (className) wrapper.className += ` ${className}`;
106
+ if (style) wrapper.setAttribute('style', style);
151
107
 
152
- if (className) {
153
- wrapper.className += ` ${className}`;
154
- }
155
-
156
- if (style) {
157
- wrapper.setAttribute('style', style);
158
- }
108
+ const tabList = document.createElement('div');
109
+ tabList.className = 'jux-tabs-list';
159
110
 
160
- // Tab headers
161
- const tabHeaders = document.createElement('div');
162
- tabHeaders.className = 'jux-tabs-header';
111
+ const tabPanels = document.createElement('div');
112
+ tabPanels.className = 'jux-tabs-panels';
163
113
 
164
114
  tabs.forEach(tab => {
165
- const tabBtn = document.createElement('button');
166
- tabBtn.className = 'jux-tab-button';
167
- tabBtn.textContent = tab.label;
168
-
115
+ const tabButton = document.createElement('button');
116
+ tabButton.className = 'jux-tabs-button';
117
+ tabButton.setAttribute('data-tab', tab.id);
118
+ if (tab.id === activeTab) tabButton.classList.add('jux-tabs-button-active');
119
+ tabButton.textContent = tab.label;
120
+ tabList.appendChild(tabButton);
121
+
122
+ const tabPanel = document.createElement('div');
123
+ tabPanel.className = 'jux-tabs-panel';
124
+ tabPanel.setAttribute('data-tab', tab.id);
169
125
  if (tab.id === activeTab) {
170
- tabBtn.classList.add('jux-tab-button-active');
126
+ tabPanel.classList.add('jux-tabs-panel-active');
127
+ } else {
128
+ tabPanel.style.display = 'none';
171
129
  }
130
+ if (typeof tab.content === 'string') {
131
+ tabPanel.innerHTML = tab.content;
132
+ } else {
133
+ tabPanel.appendChild(tab.content);
134
+ }
135
+ tabPanels.appendChild(tabPanel);
136
+ });
172
137
 
173
- tabHeaders.appendChild(tabBtn);
138
+ wrapper.appendChild(tabList);
139
+ wrapper.appendChild(tabPanels);
174
140
 
175
- // Event binding - tab click
176
- tabBtn.addEventListener('click', () => {
177
- this._updateActiveTab(tab.id);
141
+ if (!hasActiveTabSync) {
142
+ tabList.querySelectorAll('.jux-tabs-button').forEach(button => {
143
+ button.addEventListener('click', () => {
144
+ const tabId = button.getAttribute('data-tab')!;
145
+ this.state.activeTab = tabId;
146
+ this._switchTab(tabId, wrapper);
147
+ });
178
148
  });
179
- });
149
+ }
180
150
 
181
- wrapper.appendChild(tabHeaders);
151
+ this._wireStandardEvents(wrapper);
182
152
 
183
- // Tab panels
184
- const tabPanels = document.createElement('div');
185
- tabPanels.className = 'jux-tabs-panels';
153
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
154
+ if (property === 'activeTab') {
155
+ const transformToState = toState || ((v: any) => String(v));
156
+ const transformToComponent = toComponent || ((v: any) => String(v));
186
157
 
187
- tabs.forEach(tab => {
188
- const panel = document.createElement('div');
189
- panel.className = 'jux-tab-panel';
190
- panel.setAttribute('data-tab-id', tab.id);
158
+ let isUpdating = false;
191
159
 
192
- if (tab.id === activeTab) {
193
- panel.classList.add('jux-tab-panel-active');
194
- }
160
+ stateObj.subscribe((val: any) => {
161
+ if (isUpdating) return;
162
+ const transformed = transformToComponent(val);
163
+ this.state.activeTab = transformed;
164
+ this._switchTab(transformed, wrapper);
165
+ });
195
166
 
196
- // Render content
197
- if (typeof tab.content === 'function') {
198
- panel.innerHTML = tab.content();
199
- } else {
200
- panel.innerHTML = tab.content;
201
- }
167
+ tabList.querySelectorAll('.jux-tabs-button').forEach(button => {
168
+ button.addEventListener('click', () => {
169
+ if (isUpdating) return;
170
+ isUpdating = true;
171
+
172
+ const tabId = button.getAttribute('data-tab')!;
173
+ this.state.activeTab = tabId;
174
+ this._switchTab(tabId, wrapper);
202
175
 
203
- tabPanels.appendChild(panel);
176
+ const transformed = transformToState(tabId);
177
+ stateObj.set(transformed);
178
+
179
+ setTimeout(() => { isUpdating = false; }, 0);
180
+ });
181
+ });
182
+ }
183
+ else if (property === 'tabs') {
184
+ const transform = toComponent || ((v: any) => v);
185
+
186
+ stateObj.subscribe((val: any) => {
187
+ const transformed = transform(val);
188
+ this.state.tabs = transformed;
189
+
190
+ tabList.innerHTML = '';
191
+ tabPanels.innerHTML = '';
192
+
193
+ transformed.forEach((tab: any) => {
194
+ const tabButton = document.createElement('button');
195
+ tabButton.className = 'jux-tabs-button';
196
+ tabButton.setAttribute('data-tab', tab.id);
197
+ if (tab.id === this.state.activeTab) tabButton.classList.add('jux-tabs-button-active');
198
+ tabButton.textContent = tab.label;
199
+ tabList.appendChild(tabButton);
200
+
201
+ const tabPanel = document.createElement('div');
202
+ tabPanel.className = 'jux-tabs-panel';
203
+ tabPanel.setAttribute('data-tab', tab.id);
204
+ if (tab.id === this.state.activeTab) {
205
+ tabPanel.classList.add('jux-tabs-panel-active');
206
+ } else {
207
+ tabPanel.style.display = 'none';
208
+ }
209
+ if (typeof tab.content === 'string') {
210
+ tabPanel.innerHTML = tab.content;
211
+ } else {
212
+ tabPanel.appendChild(tab.content);
213
+ }
214
+ tabPanels.appendChild(tabPanel);
215
+ });
216
+
217
+ tabList.querySelectorAll('.jux-tabs-button').forEach(button => {
218
+ button.addEventListener('click', () => {
219
+ const tabId = button.getAttribute('data-tab')!;
220
+ this.state.activeTab = tabId;
221
+ this._switchTab(tabId, wrapper);
222
+ });
223
+ });
224
+ });
225
+ }
204
226
  });
205
227
 
206
- wrapper.appendChild(tabPanels);
207
228
  container.appendChild(wrapper);
208
-
209
229
  return this;
210
230
  }
211
231
 
212
- /**
213
- * Render to another Jux component's container
214
- */
215
- renderTo(juxComponent: any): this {
216
- if (!juxComponent || typeof juxComponent !== 'object') {
217
- throw new Error('Tabs.renderTo: Invalid component - not an object');
218
- }
232
+ private _switchTab(tabId: string, wrapper: HTMLElement): void {
233
+ wrapper.querySelectorAll('.jux-tabs-button').forEach(btn => {
234
+ btn.classList.toggle('jux-tabs-button-active', btn.getAttribute('data-tab') === tabId);
235
+ });
219
236
 
220
- if (!juxComponent._id || typeof juxComponent._id !== 'string') {
221
- throw new Error('Tabs.renderTo: Invalid component - missing _id (not a Jux component)');
222
- }
237
+ wrapper.querySelectorAll('.jux-tabs-panel').forEach(panel => {
238
+ const isActive = panel.getAttribute('data-tab') === tabId;
239
+ panel.classList.toggle('jux-tabs-panel-active', isActive);
240
+ (panel as HTMLElement).style.display = isActive ? 'block' : 'none';
241
+ });
223
242
 
224
- return this.render(`#${juxComponent._id}`);
243
+ // 🎯 Fire the tabChange callback event
244
+ this._triggerCallback('tabChange', tabId);
225
245
  }
226
246
  }
227
247
 
228
- /**
229
- * Factory helper
230
- */
231
248
  export function tabs(id: string, options: TabsOptions = {}): Tabs {
232
249
  return new Tabs(id, options);
233
250
  }