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.
- package/bin/cli.js +121 -72
- package/lib/components/alert.ts +212 -165
- package/lib/components/badge.ts +93 -103
- package/lib/components/base/BaseComponent.ts +397 -0
- package/lib/components/base/FormInput.ts +322 -0
- package/lib/components/button.ts +63 -122
- package/lib/components/card.ts +109 -155
- package/lib/components/charts/areachart.ts +315 -0
- package/lib/components/charts/barchart.ts +421 -0
- package/lib/components/charts/doughnutchart.ts +263 -0
- package/lib/components/charts/lib/BaseChart.ts +402 -0
- package/lib/components/charts/lib/chart-types.ts +159 -0
- package/lib/components/charts/lib/chart-utils.ts +160 -0
- package/lib/components/charts/lib/chart.ts +707 -0
- package/lib/components/checkbox.ts +264 -127
- package/lib/components/code.ts +75 -108
- package/lib/components/container.ts +113 -130
- package/lib/components/data.ts +37 -5
- package/lib/components/datepicker.ts +195 -147
- package/lib/components/dialog.ts +187 -157
- package/lib/components/divider.ts +85 -191
- package/lib/components/docs-data.json +544 -2027
- package/lib/components/dropdown.ts +178 -136
- package/lib/components/element.ts +227 -171
- package/lib/components/fileupload.ts +285 -228
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +46 -69
- package/lib/components/helpers.ts +13 -6
- package/lib/components/hero.ts +107 -95
- package/lib/components/icon.ts +160 -0
- package/lib/components/icons.ts +175 -0
- package/lib/components/include.ts +153 -5
- package/lib/components/input.ts +174 -374
- package/lib/components/kpicard.ts +16 -16
- package/lib/components/list.ts +378 -240
- package/lib/components/loading.ts +142 -211
- package/lib/components/menu.ts +103 -97
- package/lib/components/modal.ts +138 -144
- package/lib/components/nav.ts +169 -90
- package/lib/components/paragraph.ts +49 -150
- package/lib/components/progress.ts +118 -200
- package/lib/components/radio.ts +297 -149
- package/lib/components/script.ts +19 -87
- package/lib/components/select.ts +184 -186
- package/lib/components/sidebar.ts +152 -140
- package/lib/components/style.ts +19 -82
- package/lib/components/switch.ts +258 -188
- package/lib/components/table.ts +1117 -170
- package/lib/components/tabs.ts +162 -145
- package/lib/components/theme-toggle.ts +108 -169
- package/lib/components/tooltip.ts +86 -157
- package/lib/components/write.ts +108 -127
- package/lib/jux.ts +86 -41
- package/machinery/build.js +466 -0
- package/machinery/compiler.js +354 -105
- package/machinery/server.js +23 -100
- package/machinery/watcher.js +153 -130
- package/package.json +1 -2
- package/presets/base.css +1166 -0
- package/presets/notion.css +2 -1975
- package/lib/adapters/base-adapter.js +0 -35
- package/lib/adapters/index.js +0 -33
- package/lib/adapters/mysql-adapter.js +0 -65
- package/lib/adapters/postgres-adapter.js +0 -70
- package/lib/adapters/sqlite-adapter.js +0 -56
- package/lib/components/areachart.ts +0 -1246
- package/lib/components/areachartsmooth.ts +0 -1380
- package/lib/components/barchart.ts +0 -1250
- package/lib/components/chart.ts +0 -127
- package/lib/components/doughnutchart.ts +0 -1191
- package/lib/components/footer.ts +0 -165
- package/lib/components/header.ts +0 -187
- package/lib/components/layout.ts +0 -239
- package/lib/components/main.ts +0 -137
- package/lib/layouts/default.jux +0 -8
- package/lib/layouts/figma.jux +0 -0
- /package/lib/{themes → components/charts/lib}/charts.js +0 -0
|
@@ -1,188 +1,121 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
75
|
-
};
|
|
42
|
+
class: options.class ?? ''
|
|
43
|
+
});
|
|
76
44
|
}
|
|
77
45
|
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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
|
-
|
|
98
|
-
this.state.
|
|
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
|
-
|
|
108
|
-
this.state.
|
|
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
|
-
|
|
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():
|
|
125
|
-
this.state.
|
|
126
|
-
|
|
127
|
-
|
|
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():
|
|
133
|
-
|
|
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
|
-
*
|
|
142
|
-
*
|
|
93
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
94
|
+
* RENDER
|
|
95
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
143
96
|
|
|
144
97
|
render(targetId?: string): this {
|
|
145
|
-
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
178
|
-
e.stopPropagation();
|
|
179
|
-
this.toggle();
|
|
180
|
-
});
|
|
112
|
+
triggerBtn.type = 'button';
|
|
181
113
|
|
|
182
|
-
//
|
|
114
|
+
// Dropdown menu
|
|
183
115
|
const menu = document.createElement('div');
|
|
184
|
-
menu.className = `jux-dropdown-menu jux-dropdown
|
|
185
|
-
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.
|
|
128
|
+
menuItem.type = 'button';
|
|
196
129
|
|
|
197
130
|
if (item.icon) {
|
|
198
131
|
const icon = document.createElement('span');
|
|
199
|
-
icon.className = 'jux-dropdown-
|
|
132
|
+
icon.className = 'jux-dropdown-icon';
|
|
200
133
|
icon.textContent = item.icon;
|
|
201
134
|
menuItem.appendChild(icon);
|
|
202
135
|
}
|
|
203
136
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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 (!
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|