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,188 +1,121 @@
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 = ['select'] as const;
2
6
 
3
- /**
4
- * Dropdown menu item
5
- */
6
7
  export interface DropdownItem {
7
- label: string;
8
- value?: string;
8
+ label?: string;
9
9
  icon?: string;
10
- click?: () => void;
11
- disabled?: boolean;
12
10
  divider?: boolean;
11
+ click?: () => void;
13
12
  }
14
13
 
15
- /**
16
- * Dropdown component options
17
- */
18
14
  export interface DropdownOptions {
19
- items?: DropdownItem[];
20
15
  trigger?: string;
16
+ items?: DropdownItem[];
21
17
  position?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
22
18
  style?: string;
23
19
  class?: string;
24
20
  }
25
21
 
26
- /**
27
- * Dropdown component state
28
- */
29
22
  type DropdownState = {
30
- items: DropdownItem[];
31
23
  trigger: string;
24
+ items: DropdownItem[];
32
25
  position: string;
26
+ open: boolean;
33
27
  style: string;
34
28
  class: string;
35
- isOpen: boolean;
36
29
  };
37
30
 
38
- /**
39
- * Dropdown Menu component - Context menus, action menus
40
- *
41
- * Usage:
42
- * jux.dropdown('actions', {
43
- * trigger: 'Actions ▾',
44
- * position: 'bottom-right',
45
- * items: [
46
- * { label: 'Edit', icon: '✏️', click: () => console.log('Edit') },
47
- * { label: 'Delete', icon: '🗑️', click: () => console.log('Delete') },
48
- * { divider: true },
49
- * { label: 'Archive', click: () => console.log('Archive') }
50
- * ]
51
- * }).render('#toolbar');
52
- *
53
- * // Control programmatically
54
- * const dd = jux.dropdown('my-dropdown').render();
55
- * dd.open();
56
- * dd.close();
57
- */
58
- export class Dropdown {
59
- state: DropdownState;
60
- container: HTMLElement | null = null;
61
- _id: string;
62
- id: string;
31
+ export class Dropdown extends BaseComponent<DropdownState> {
32
+ private _dropdown: HTMLElement | null = null;
33
+ private _menu: HTMLElement | null = null;
63
34
 
64
35
  constructor(id: string, options: DropdownOptions = {}) {
65
- this._id = id;
66
- this.id = id;
67
-
68
- this.state = {
69
- items: options.items ?? [],
36
+ super(id, {
70
37
  trigger: options.trigger ?? 'Menu',
38
+ items: options.items ?? [],
71
39
  position: options.position ?? 'bottom-left',
40
+ open: false,
72
41
  style: options.style ?? '',
73
- class: options.class ?? '',
74
- isOpen: false
75
- };
42
+ class: options.class ?? ''
43
+ });
76
44
  }
77
45
 
78
- /* -------------------------
79
- * Fluent API
80
- * ------------------------- */
81
-
82
- items(value: DropdownItem[]): this {
83
- this.state.items = value;
84
- return this;
46
+ protected getTriggerEvents(): readonly string[] {
47
+ return TRIGGER_EVENTS;
85
48
  }
86
49
 
87
- addItem(item: DropdownItem): this {
88
- this.state.items = [...this.state.items, item];
89
- return this;
50
+ protected getCallbackEvents(): readonly string[] {
51
+ return CALLBACK_EVENTS;
90
52
  }
91
53
 
54
+ /* ═════════════════════════════════════════════════════════════════
55
+ * FLUENT API
56
+ * ═════════════════════════════════════════════════════════════════ */
57
+
92
58
  trigger(value: string): this {
93
59
  this.state.trigger = value;
94
60
  return this;
95
61
  }
96
62
 
97
- position(value: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'): this {
98
- this.state.position = value;
99
- return this;
100
- }
101
-
102
- style(value: string): this {
103
- this.state.style = value;
63
+ items(value: DropdownItem[]): this {
64
+ this.state.items = value;
104
65
  return this;
105
66
  }
106
67
 
107
- class(value: string): this {
108
- this.state.class = value;
68
+ position(value: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'): this {
69
+ this.state.position = value;
109
70
  return this;
110
71
  }
111
72
 
112
- /* -------------------------
113
- * Methods
114
- * ------------------------- */
115
-
116
- open(): void {
117
- this.state.isOpen = true;
118
- const menu = document.getElementById(`${this._id}-menu`);
119
- if (menu) {
120
- menu.classList.add('jux-dropdown-menu-open');
73
+ open(): this {
74
+ this.state.open = true;
75
+ if (this._menu) {
76
+ this._menu.style.display = 'block';
121
77
  }
78
+ return this;
122
79
  }
123
80
 
124
- close(): void {
125
- this.state.isOpen = false;
126
- const menu = document.getElementById(`${this._id}-menu`);
127
- if (menu) {
128
- menu.classList.remove('jux-dropdown-menu-open');
81
+ close(): this {
82
+ this.state.open = false;
83
+ if (this._menu) {
84
+ this._menu.style.display = 'none';
129
85
  }
86
+ return this;
130
87
  }
131
88
 
132
- toggle(): void {
133
- if (this.state.isOpen) {
134
- this.close();
135
- } else {
136
- this.open();
137
- }
89
+ toggle(): this {
90
+ return this.state.open ? this.close() : this.open();
138
91
  }
139
92
 
140
- /* -------------------------
141
- * Render
142
- * ------------------------- */
93
+ /* ═════════════════════════════════════════════════════════════════
94
+ * RENDER
95
+ * ═════════════════════════════════════════════════════════════════ */
143
96
 
144
97
  render(targetId?: string): this {
145
- let container: HTMLElement;
146
-
147
- if (targetId) {
148
- const target = document.querySelector(targetId);
149
- if (!target || !(target instanceof HTMLElement)) {
150
- throw new Error(`Dropdown: Target element "${targetId}" not found`);
151
- }
152
- container = target;
153
- } else {
154
- container = getOrCreateContainer(this._id);
155
- }
98
+ const container = this._setupContainer(targetId);
156
99
 
157
- this.container = container;
158
- const { items, trigger, position, style, class: className } = this.state;
100
+ const { trigger, items, position, style, class: className } = this.state;
159
101
 
160
- // Wrapper
161
102
  const wrapper = document.createElement('div');
162
103
  wrapper.className = 'jux-dropdown';
163
104
  wrapper.id = this._id;
164
-
165
- if (className) {
166
- wrapper.className += ` ${className}`;
167
- }
168
-
169
- if (style) {
170
- wrapper.setAttribute('style', style);
171
- }
105
+ if (className) wrapper.className += ` ${className}`;
106
+ if (style) wrapper.setAttribute('style', style);
172
107
 
173
108
  // Trigger button
174
109
  const triggerBtn = document.createElement('button');
175
110
  triggerBtn.className = 'jux-dropdown-trigger';
176
111
  triggerBtn.textContent = trigger;
177
- triggerBtn.addEventListener('click', (e) => {
178
- e.stopPropagation();
179
- this.toggle();
180
- });
112
+ triggerBtn.type = 'button';
181
113
 
182
- // Menu
114
+ // Dropdown menu
183
115
  const menu = document.createElement('div');
184
- menu.className = `jux-dropdown-menu jux-dropdown-menu-${position}`;
185
- menu.id = `${this._id}-menu`;
116
+ menu.className = `jux-dropdown-menu jux-dropdown-${position}`;
117
+ menu.style.display = 'none';
118
+ this._menu = menu;
186
119
 
187
120
  items.forEach(item => {
188
121
  if (item.divider) {
@@ -192,24 +125,30 @@ export class Dropdown {
192
125
  } else {
193
126
  const menuItem = document.createElement('button');
194
127
  menuItem.className = 'jux-dropdown-item';
195
- menuItem.disabled = item.disabled ?? false;
128
+ menuItem.type = 'button';
196
129
 
197
130
  if (item.icon) {
198
131
  const icon = document.createElement('span');
199
- icon.className = 'jux-dropdown-item-icon';
132
+ icon.className = 'jux-dropdown-icon';
200
133
  icon.textContent = item.icon;
201
134
  menuItem.appendChild(icon);
202
135
  }
203
136
 
204
- const label = document.createElement('span');
205
- label.className = 'jux-dropdown-item-label';
206
- label.textContent = item.label;
207
- menuItem.appendChild(label);
137
+ if (item.label) {
138
+ const label = document.createElement('span');
139
+ label.textContent = item.label;
140
+ menuItem.appendChild(label);
141
+ }
208
142
 
209
143
  menuItem.addEventListener('click', () => {
144
+ // 🎯 Fire the select callback event
145
+ this._triggerCallback('select', item);
146
+
147
+ // Then fire item-specific click handler
210
148
  if (item.click) {
211
149
  item.click();
212
150
  }
151
+
213
152
  this.close();
214
153
  });
215
154
 
@@ -217,25 +156,128 @@ export class Dropdown {
217
156
  }
218
157
  });
219
158
 
220
- wrapper.appendChild(triggerBtn);
221
- wrapper.appendChild(menu);
222
- container.appendChild(wrapper);
159
+ // Toggle on trigger click
160
+ triggerBtn.addEventListener('click', (e) => {
161
+ e.stopPropagation();
162
+ this.toggle();
163
+ });
223
164
 
224
165
  // Close on outside click
225
166
  document.addEventListener('click', (e) => {
226
- if (!wrapper.contains(e.target as Node)) {
167
+ if (this._dropdown && !this._dropdown.contains(e.target as Node)) {
227
168
  this.close();
228
169
  }
229
170
  });
230
171
 
172
+ wrapper.appendChild(triggerBtn);
173
+ wrapper.appendChild(menu);
174
+
175
+ this._wireStandardEvents(wrapper);
176
+
177
+ container.appendChild(wrapper);
178
+ this._dropdown = wrapper;
179
+ this._injectDropdownStyles();
180
+
231
181
  return this;
232
182
  }
233
183
 
234
- renderTo(juxComponent: any): this {
235
- if (!juxComponent?._id) {
236
- throw new Error('Dropdown.renderTo: Invalid component');
237
- }
238
- return this.render(`#${juxComponent._id}`);
184
+ private _injectDropdownStyles(): void {
185
+ const styleId = 'jux-dropdown-styles';
186
+ if (document.getElementById(styleId)) return;
187
+
188
+ const style = document.createElement('style');
189
+ style.id = styleId;
190
+ style.textContent = `
191
+ .jux-dropdown {
192
+ position: relative;
193
+ display: inline-block;
194
+ }
195
+
196
+ .jux-dropdown-trigger {
197
+ padding: 8px 16px;
198
+ background: #3b82f6;
199
+ color: white;
200
+ border: none;
201
+ border-radius: 6px;
202
+ font-size: 14px;
203
+ cursor: pointer;
204
+ transition: background 0.2s;
205
+ }
206
+
207
+ .jux-dropdown-trigger:hover {
208
+ background: #2563eb;
209
+ }
210
+
211
+ .jux-dropdown-menu {
212
+ position: absolute;
213
+ background: white;
214
+ border: 1px solid #e5e7eb;
215
+ border-radius: 6px;
216
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
217
+ min-width: 200px;
218
+ z-index: 1000;
219
+ margin-top: 4px;
220
+ }
221
+
222
+ .jux-dropdown-bottom-left {
223
+ left: 0;
224
+ top: 100%;
225
+ }
226
+
227
+ .jux-dropdown-bottom-right {
228
+ right: 0;
229
+ top: 100%;
230
+ }
231
+
232
+ .jux-dropdown-top-left {
233
+ left: 0;
234
+ bottom: 100%;
235
+ margin-bottom: 4px;
236
+ }
237
+
238
+ .jux-dropdown-top-right {
239
+ right: 0;
240
+ bottom: 100%;
241
+ margin-bottom: 4px;
242
+ }
243
+
244
+ .jux-dropdown-item {
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 8px;
248
+ width: 100%;
249
+ padding: 10px 16px;
250
+ border: none;
251
+ background: none;
252
+ text-align: left;
253
+ cursor: pointer;
254
+ transition: background 0.2s;
255
+ font-size: 14px;
256
+ }
257
+
258
+ .jux-dropdown-item:hover {
259
+ background: #f3f4f6;
260
+ }
261
+
262
+ .jux-dropdown-item:first-child {
263
+ border-radius: 6px 6px 0 0;
264
+ }
265
+
266
+ .jux-dropdown-item:last-child {
267
+ border-radius: 0 0 6px 6px;
268
+ }
269
+
270
+ .jux-dropdown-divider {
271
+ height: 1px;
272
+ background: #e5e7eb;
273
+ margin: 4px 0;
274
+ }
275
+
276
+ .jux-dropdown-icon {
277
+ font-size: 16px;
278
+ }
279
+ `;
280
+ document.head.appendChild(style);
239
281
  }
240
282
  }
241
283