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,71 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { FormInput, FormInputState } from './base/FormInput.js';
|
|
2
|
+
import { renderIcon } from './icons.js';
|
|
3
|
+
|
|
4
|
+
// Event definitions
|
|
5
|
+
const TRIGGER_EVENTS = [] as const;
|
|
6
|
+
const CALLBACK_EVENTS = ['change'] as const;
|
|
3
7
|
|
|
4
8
|
export interface CheckboxOptions {
|
|
5
|
-
label?: string;
|
|
6
9
|
checked?: boolean;
|
|
10
|
+
label?: string;
|
|
11
|
+
required?: boolean;
|
|
7
12
|
disabled?: boolean;
|
|
8
13
|
name?: string;
|
|
9
14
|
value?: string;
|
|
10
|
-
onChange?: (checked: boolean) => void;
|
|
11
15
|
style?: string;
|
|
12
16
|
class?: string;
|
|
17
|
+
onValidate?: (checked: boolean) => boolean | string;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
label: string;
|
|
20
|
+
interface CheckboxState extends FormInputState {
|
|
17
21
|
checked: boolean;
|
|
18
|
-
disabled: boolean;
|
|
19
|
-
name: string;
|
|
20
22
|
value: string;
|
|
21
|
-
|
|
22
|
-
class: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export class Checkbox {
|
|
26
|
-
state: CheckboxState;
|
|
27
|
-
container: HTMLElement | null = null;
|
|
28
|
-
_id: string;
|
|
29
|
-
id: string;
|
|
30
|
-
private _onChange?: (checked: boolean) => void;
|
|
31
|
-
private _boundState?: State<boolean>;
|
|
23
|
+
}
|
|
32
24
|
|
|
25
|
+
export class Checkbox extends FormInput<CheckboxState> {
|
|
33
26
|
constructor(id: string, options: CheckboxOptions = {}) {
|
|
34
|
-
|
|
35
|
-
this.id = id;
|
|
36
|
-
this._onChange = options.onChange;
|
|
37
|
-
|
|
38
|
-
this.state = {
|
|
39
|
-
label: options.label ?? '',
|
|
27
|
+
super(id, {
|
|
40
28
|
checked: options.checked ?? false,
|
|
29
|
+
value: options.value ?? 'on',
|
|
30
|
+
label: options.label ?? '',
|
|
31
|
+
required: options.required ?? false,
|
|
41
32
|
disabled: options.disabled ?? false,
|
|
42
33
|
name: options.name ?? id,
|
|
43
|
-
value: options.value ?? 'on',
|
|
44
34
|
style: options.style ?? '',
|
|
45
|
-
class: options.class ?? ''
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
class: options.class ?? '',
|
|
36
|
+
errorMessage: undefined
|
|
37
|
+
});
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
if (options.onValidate) {
|
|
40
|
+
this._onValidate = options.onValidate;
|
|
41
|
+
}
|
|
52
42
|
}
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this._updateElement();
|
|
57
|
-
return this;
|
|
44
|
+
protected getTriggerEvents(): readonly string[] {
|
|
45
|
+
return TRIGGER_EVENTS;
|
|
58
46
|
}
|
|
59
47
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this._updateElement();
|
|
63
|
-
return this;
|
|
48
|
+
protected getCallbackEvents(): readonly string[] {
|
|
49
|
+
return CALLBACK_EVENTS;
|
|
64
50
|
}
|
|
65
51
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
53
|
+
* FLUENT API
|
|
54
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
55
|
+
|
|
56
|
+
// ✅ Inherited from FormInput/BaseComponent:
|
|
57
|
+
// - label(), required(), name(), onValidate()
|
|
58
|
+
// - validate(), isValid()
|
|
59
|
+
// - style(), class()
|
|
60
|
+
// - bind(), sync(), renderTo()
|
|
61
|
+
// - disabled(), enable(), disable()
|
|
62
|
+
|
|
63
|
+
checked(value: boolean): this {
|
|
64
|
+
return this.setValue(value);
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
value(value: string): this {
|
|
@@ -73,124 +69,265 @@ export class Checkbox {
|
|
|
73
69
|
return this;
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
72
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
73
|
+
* FORM INPUT IMPLEMENTATION
|
|
74
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
this.state.
|
|
83
|
-
return this;
|
|
76
|
+
getValue(): boolean {
|
|
77
|
+
return this.state.checked;
|
|
84
78
|
}
|
|
85
79
|
|
|
86
|
-
|
|
87
|
-
this.
|
|
80
|
+
setValue(value: boolean): this {
|
|
81
|
+
this.state.checked = value;
|
|
82
|
+
if (this._inputElement) {
|
|
83
|
+
(this._inputElement as HTMLInputElement).checked = value;
|
|
84
|
+
}
|
|
88
85
|
return this;
|
|
89
86
|
}
|
|
90
87
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
bind(stateObj: State<boolean>): this {
|
|
95
|
-
this._boundState = stateObj;
|
|
88
|
+
protected _validateValue(checked: boolean): boolean | string {
|
|
89
|
+
const { required } = this.state;
|
|
96
90
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this._updateElement();
|
|
101
|
-
});
|
|
91
|
+
if (required && !checked) {
|
|
92
|
+
return 'This field must be checked';
|
|
93
|
+
}
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (originalOnChange) {
|
|
108
|
-
originalOnChange(checked);
|
|
95
|
+
if (this._onValidate) {
|
|
96
|
+
const result = this._onValidate(checked);
|
|
97
|
+
if (result !== true) {
|
|
98
|
+
return result || 'Invalid value';
|
|
109
99
|
}
|
|
110
|
-
}
|
|
100
|
+
}
|
|
111
101
|
|
|
112
|
-
return
|
|
102
|
+
return true;
|
|
113
103
|
}
|
|
114
104
|
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
if (input) {
|
|
118
|
-
input.checked = this.state.checked;
|
|
119
|
-
input.disabled = this.state.disabled;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
105
|
+
protected _buildInputElement(): HTMLElement {
|
|
106
|
+
const { checked, value, required, disabled, name } = this.state;
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
|
|
108
|
+
const input = document.createElement('input');
|
|
109
|
+
input.type = 'checkbox';
|
|
110
|
+
input.className = 'jux-checkbox-input';
|
|
111
|
+
input.id = `${this._id}-input`;
|
|
112
|
+
input.name = name;
|
|
113
|
+
input.value = value;
|
|
114
|
+
input.checked = checked;
|
|
115
|
+
input.required = required;
|
|
116
|
+
input.disabled = disabled;
|
|
117
|
+
|
|
118
|
+
return input;
|
|
125
119
|
}
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
122
|
+
* RENDER
|
|
123
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
133
|
-
throw new Error(`Checkbox: Target element "${targetId}" not found`);
|
|
134
|
-
}
|
|
135
|
-
container = target;
|
|
136
|
-
} else {
|
|
137
|
-
container = getOrCreateContainer(this._id);
|
|
138
|
-
}
|
|
125
|
+
render(targetId?: string): this {
|
|
126
|
+
const container = this._setupContainer(targetId);
|
|
139
127
|
|
|
140
|
-
|
|
141
|
-
const { label, checked, disabled, name, value, style, class: className } = this.state;
|
|
128
|
+
const { style, class: className } = this.state;
|
|
142
129
|
|
|
130
|
+
// Build wrapper
|
|
143
131
|
const wrapper = document.createElement('div');
|
|
144
132
|
wrapper.className = 'jux-checkbox';
|
|
145
133
|
wrapper.id = this._id;
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
|
|
134
|
+
if (className) wrapper.className += ` ${className}`;
|
|
135
|
+
if (style) wrapper.setAttribute('style', style);
|
|
136
|
+
|
|
137
|
+
// Checkbox container
|
|
138
|
+
const checkboxContainer = document.createElement('label');
|
|
139
|
+
checkboxContainer.className = 'jux-checkbox-container';
|
|
140
|
+
checkboxContainer.htmlFor = `${this._id}-input`;
|
|
141
|
+
|
|
142
|
+
// Input element
|
|
143
|
+
const inputEl = this._buildInputElement() as HTMLInputElement;
|
|
144
|
+
this._inputElement = inputEl;
|
|
145
|
+
checkboxContainer.appendChild(inputEl);
|
|
146
|
+
|
|
147
|
+
// Checkmark (custom styled)
|
|
148
|
+
const checkmark = document.createElement('span');
|
|
149
|
+
checkmark.className = 'jux-checkbox-checkmark';
|
|
150
|
+
checkmark.appendChild(renderIcon('check'));
|
|
151
|
+
checkboxContainer.appendChild(checkmark);
|
|
152
|
+
|
|
153
|
+
// Label text
|
|
154
|
+
if (this.state.label) {
|
|
155
|
+
const labelText = document.createElement('span');
|
|
156
|
+
labelText.className = 'jux-checkbox-label-text';
|
|
157
|
+
labelText.textContent = this.state.label;
|
|
158
|
+
if (this.state.required) {
|
|
159
|
+
const requiredSpan = document.createElement('span');
|
|
160
|
+
requiredSpan.className = 'jux-input-required';
|
|
161
|
+
requiredSpan.textContent = ' *';
|
|
162
|
+
labelText.appendChild(requiredSpan);
|
|
163
|
+
}
|
|
164
|
+
checkboxContainer.appendChild(labelText);
|
|
149
165
|
}
|
|
150
166
|
|
|
151
|
-
|
|
152
|
-
wrapper.setAttribute('style', style);
|
|
153
|
-
}
|
|
167
|
+
wrapper.appendChild(checkboxContainer);
|
|
154
168
|
|
|
155
|
-
|
|
156
|
-
|
|
169
|
+
// Error element
|
|
170
|
+
wrapper.appendChild(this._renderError());
|
|
157
171
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
input.className = 'jux-checkbox-input';
|
|
161
|
-
input.id = `${this._id}-input`;
|
|
162
|
-
input.name = name;
|
|
163
|
-
input.value = value;
|
|
164
|
-
input.checked = checked;
|
|
165
|
-
input.disabled = disabled;
|
|
172
|
+
// Wire events
|
|
173
|
+
this._wireStandardEvents(wrapper);
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
// Wire checkbox-specific sync (maps 'checked' to 'value' property for consistency)
|
|
176
|
+
const valueSync = this._syncBindings.find(b => b.property === 'value' || b.property === 'checked');
|
|
177
|
+
|
|
178
|
+
if (valueSync) {
|
|
179
|
+
const { stateObj, toState, toComponent } = valueSync;
|
|
180
|
+
|
|
181
|
+
const transformToState = toState || ((v: boolean) => v);
|
|
182
|
+
const transformToComponent = toComponent || ((v: any) => Boolean(v));
|
|
183
|
+
|
|
184
|
+
let isUpdating = false;
|
|
174
185
|
|
|
175
|
-
|
|
186
|
+
// State → Component
|
|
187
|
+
stateObj.subscribe((val: any) => {
|
|
188
|
+
if (isUpdating) return;
|
|
189
|
+
const transformed = transformToComponent(val);
|
|
190
|
+
this.setValue(transformed);
|
|
191
|
+
});
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
193
|
+
// Component → State
|
|
194
|
+
inputEl.addEventListener('change', () => {
|
|
195
|
+
if (isUpdating) return;
|
|
196
|
+
isUpdating = true;
|
|
197
|
+
|
|
198
|
+
const checked = inputEl.checked;
|
|
199
|
+
this.state.checked = checked;
|
|
200
|
+
this._clearError();
|
|
201
|
+
|
|
202
|
+
const transformed = transformToState(checked);
|
|
203
|
+
stateObj.set(transformed);
|
|
204
|
+
|
|
205
|
+
// 🎯 Fire the change callback event
|
|
206
|
+
this._triggerCallback('change', checked);
|
|
207
|
+
|
|
208
|
+
setTimeout(() => { isUpdating = false; }, 0);
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
// Default behavior without sync
|
|
212
|
+
inputEl.addEventListener('change', () => {
|
|
213
|
+
this.state.checked = inputEl.checked;
|
|
214
|
+
this._clearError();
|
|
215
|
+
|
|
216
|
+
// 🎯 Fire the change callback event
|
|
217
|
+
this._triggerCallback('change', inputEl.checked);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Always add blur validation
|
|
222
|
+
inputEl.addEventListener('blur', () => {
|
|
223
|
+
this.validate();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Sync label changes
|
|
227
|
+
const labelSync = this._syncBindings.find(b => b.property === 'label');
|
|
228
|
+
if (labelSync) {
|
|
229
|
+
const transform = labelSync.toComponent || ((v: any) => String(v));
|
|
230
|
+
labelSync.stateObj.subscribe((val: any) => {
|
|
231
|
+
this.label(transform(val));
|
|
232
|
+
});
|
|
182
233
|
}
|
|
183
234
|
|
|
184
|
-
wrapper.appendChild(labelEl);
|
|
185
235
|
container.appendChild(wrapper);
|
|
236
|
+
this._injectCheckboxStyles();
|
|
237
|
+
|
|
238
|
+
requestAnimationFrame(() => {
|
|
239
|
+
if ((window as any).lucide) {
|
|
240
|
+
(window as any).lucide.createIcons();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
186
244
|
return this;
|
|
187
245
|
}
|
|
188
246
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
247
|
+
private _injectCheckboxStyles(): void {
|
|
248
|
+
const styleId = 'jux-checkbox-styles';
|
|
249
|
+
if (document.getElementById(styleId)) return;
|
|
250
|
+
|
|
251
|
+
const style = document.createElement('style');
|
|
252
|
+
style.id = styleId;
|
|
253
|
+
style.textContent = `
|
|
254
|
+
.jux-checkbox {
|
|
255
|
+
margin-bottom: 12px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.jux-checkbox-container {
|
|
259
|
+
display: inline-flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
user-select: none;
|
|
263
|
+
position: relative;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.jux-checkbox-input {
|
|
267
|
+
position: absolute;
|
|
268
|
+
opacity: 0;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
height: 0;
|
|
271
|
+
width: 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.jux-checkbox-checkmark {
|
|
275
|
+
position: relative;
|
|
276
|
+
height: 20px;
|
|
277
|
+
width: 20px;
|
|
278
|
+
border: 2px solid #d1d5db;
|
|
279
|
+
border-radius: 4px;
|
|
280
|
+
background-color: white;
|
|
281
|
+
transition: all 0.2s;
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
justify-content: center;
|
|
285
|
+
margin-right: 8px;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.jux-checkbox-checkmark svg {
|
|
289
|
+
width: 14px;
|
|
290
|
+
height: 14px;
|
|
291
|
+
stroke: white;
|
|
292
|
+
stroke-width: 3;
|
|
293
|
+
opacity: 0;
|
|
294
|
+
transition: opacity 0.2s;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.jux-checkbox-input:checked ~ .jux-checkbox-checkmark {
|
|
298
|
+
background-color: #3b82f6;
|
|
299
|
+
border-color: #3b82f6;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.jux-checkbox-input:checked ~ .jux-checkbox-checkmark svg {
|
|
303
|
+
opacity: 1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.jux-checkbox-input:disabled ~ .jux-checkbox-checkmark {
|
|
307
|
+
background-color: #f3f4f6;
|
|
308
|
+
border-color: #d1d5db;
|
|
309
|
+
cursor: not-allowed;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.jux-checkbox-input:disabled ~ .jux-checkbox-label-text {
|
|
313
|
+
color: #9ca3af;
|
|
314
|
+
cursor: not-allowed;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.jux-checkbox-input:focus ~ .jux-checkbox-checkmark {
|
|
318
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.jux-checkbox-input.jux-input-invalid ~ .jux-checkbox-checkmark {
|
|
322
|
+
border-color: #ef4444;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.jux-checkbox-label-text {
|
|
326
|
+
font-size: 14px;
|
|
327
|
+
color: #374151;
|
|
328
|
+
}
|
|
329
|
+
`;
|
|
330
|
+
document.head.appendChild(style);
|
|
194
331
|
}
|
|
195
332
|
}
|
|
196
333
|
|