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
package/lib/components/select.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { FormInput, FormInputState } from './base/FormInput.js';
|
|
2
|
+
|
|
3
|
+
// Event definitions
|
|
4
|
+
const TRIGGER_EVENTS = [] as const;
|
|
5
|
+
const CALLBACK_EVENTS = ['change'] as const;
|
|
3
6
|
|
|
4
7
|
export interface SelectOption {
|
|
5
8
|
label: string;
|
|
@@ -10,269 +13,264 @@ export interface SelectOption {
|
|
|
10
13
|
export interface SelectOptions {
|
|
11
14
|
options?: SelectOption[];
|
|
12
15
|
value?: string;
|
|
13
|
-
placeholder?: string;
|
|
14
16
|
label?: string;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
required?: boolean;
|
|
15
19
|
disabled?: boolean;
|
|
16
20
|
name?: string;
|
|
17
|
-
onChange?: (value: string) => void;
|
|
18
21
|
style?: string;
|
|
19
22
|
class?: string;
|
|
23
|
+
onValidate?: (value: string) => boolean | string;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
interface SelectState extends FormInputState {
|
|
23
27
|
options: SelectOption[];
|
|
24
28
|
value: string;
|
|
25
29
|
placeholder: string;
|
|
26
|
-
|
|
27
|
-
disabled: boolean;
|
|
28
|
-
name: string;
|
|
29
|
-
style: string;
|
|
30
|
-
class: string;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export class Select {
|
|
34
|
-
state: SelectState;
|
|
35
|
-
container: HTMLElement | null = null;
|
|
36
|
-
_id: string;
|
|
37
|
-
id: string;
|
|
38
|
-
private _onChange?: (value: string) => void;
|
|
39
|
-
private _boundState?: State<string>;
|
|
30
|
+
}
|
|
40
31
|
|
|
32
|
+
export class Select extends FormInput<SelectState> {
|
|
41
33
|
constructor(id: string, options: SelectOptions = {}) {
|
|
42
|
-
|
|
43
|
-
this.id = id;
|
|
44
|
-
this._onChange = options.onChange;
|
|
45
|
-
|
|
46
|
-
this.state = {
|
|
34
|
+
super(id, {
|
|
47
35
|
options: options.options ?? [],
|
|
48
36
|
value: options.value ?? '',
|
|
49
|
-
placeholder: options.placeholder ?? 'Select
|
|
37
|
+
placeholder: options.placeholder ?? 'Select an option',
|
|
50
38
|
label: options.label ?? '',
|
|
39
|
+
required: options.required ?? false,
|
|
51
40
|
disabled: options.disabled ?? false,
|
|
52
41
|
name: options.name ?? id,
|
|
53
42
|
style: options.style ?? '',
|
|
54
|
-
class: options.class ?? ''
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
options(value: SelectOption[]): this {
|
|
59
|
-
this.state.options = value;
|
|
60
|
-
return this;
|
|
61
|
-
}
|
|
43
|
+
class: options.class ?? '',
|
|
44
|
+
errorMessage: undefined
|
|
45
|
+
});
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
if (options.onValidate) {
|
|
48
|
+
this._onValidate = options.onValidate;
|
|
49
|
+
}
|
|
66
50
|
}
|
|
67
51
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this._updateElement();
|
|
71
|
-
return this;
|
|
52
|
+
protected getTriggerEvents(): readonly string[] {
|
|
53
|
+
return TRIGGER_EVENTS;
|
|
72
54
|
}
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return this;
|
|
56
|
+
protected getCallbackEvents(): readonly string[] {
|
|
57
|
+
return CALLBACK_EVENTS;
|
|
77
58
|
}
|
|
78
59
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
60
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
61
|
+
* FLUENT API
|
|
62
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
83
63
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
64
|
+
// ✅ Inherited from FormInput/BaseComponent:
|
|
65
|
+
// - label(), required(), name(), onValidate()
|
|
66
|
+
// - validate(), isValid()
|
|
67
|
+
// - style(), class()
|
|
68
|
+
// - bind(), sync(), renderTo()
|
|
69
|
+
// - disabled(), enable(), disable()
|
|
89
70
|
|
|
90
|
-
|
|
91
|
-
this.state.
|
|
71
|
+
options(value: SelectOption[]): this {
|
|
72
|
+
this.state.options = value;
|
|
92
73
|
return this;
|
|
93
74
|
}
|
|
94
75
|
|
|
95
|
-
|
|
96
|
-
this.
|
|
97
|
-
return this;
|
|
76
|
+
value(value: string): this {
|
|
77
|
+
return this.setValue(value);
|
|
98
78
|
}
|
|
99
79
|
|
|
100
|
-
|
|
101
|
-
this.state.
|
|
80
|
+
placeholder(value: string): this {
|
|
81
|
+
this.state.placeholder = value;
|
|
102
82
|
return this;
|
|
103
83
|
}
|
|
104
84
|
|
|
105
|
-
|
|
106
|
-
this.
|
|
85
|
+
addOption(option: SelectOption): this {
|
|
86
|
+
this.state.options = [...this.state.options, option];
|
|
107
87
|
return this;
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
|
|
111
|
-
*
|
|
112
|
-
*/
|
|
113
|
-
bind(stateObj: State<string>): this {
|
|
114
|
-
this._boundState = stateObj;
|
|
115
|
-
|
|
116
|
-
// Update select when state changes
|
|
117
|
-
stateObj.subscribe((val) => {
|
|
118
|
-
this.state.value = val;
|
|
119
|
-
this._updateElement();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Update state when select changes
|
|
123
|
-
const originalOnChange = this._onChange;
|
|
124
|
-
this._onChange = (value) => {
|
|
125
|
-
stateObj.set(value);
|
|
126
|
-
if (originalOnChange) {
|
|
127
|
-
originalOnChange(value);
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
return this;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private _updateElement(): void {
|
|
135
|
-
const select = document.getElementById(`${this._id}-select`) as HTMLSelectElement;
|
|
136
|
-
if (select) {
|
|
137
|
-
select.value = this.state.value;
|
|
138
|
-
select.disabled = this.state.disabled;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
90
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
91
|
+
* FORM INPUT IMPLEMENTATION
|
|
92
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
141
93
|
|
|
142
94
|
getValue(): string {
|
|
143
95
|
return this.state.value;
|
|
144
96
|
}
|
|
145
97
|
|
|
146
|
-
|
|
147
|
-
|
|
98
|
+
setValue(value: string): this {
|
|
99
|
+
this.state.value = value;
|
|
148
100
|
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
152
|
-
throw new Error(`Select: Target element "${targetId}" not found`);
|
|
153
|
-
}
|
|
154
|
-
container = target;
|
|
155
|
-
} else {
|
|
156
|
-
container = getOrCreateContainer(this._id);
|
|
101
|
+
if (this._inputElement) {
|
|
102
|
+
(this._inputElement as HTMLSelectElement).value = value;
|
|
157
103
|
}
|
|
158
104
|
|
|
159
|
-
this
|
|
160
|
-
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
161
107
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
wrapper.id = this._id;
|
|
108
|
+
protected _validateValue(value: string): boolean | string {
|
|
109
|
+
const { required, options } = this.state;
|
|
165
110
|
|
|
166
|
-
if (
|
|
167
|
-
|
|
111
|
+
if (required && !value) {
|
|
112
|
+
return 'Please select an option';
|
|
168
113
|
}
|
|
169
114
|
|
|
170
|
-
|
|
171
|
-
|
|
115
|
+
// Validate that value is one of the options
|
|
116
|
+
if (value && !options.some(opt => opt.value === value)) {
|
|
117
|
+
return 'Invalid option selected';
|
|
172
118
|
}
|
|
173
119
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
labelEl.textContent = label;
|
|
180
|
-
wrapper.appendChild(labelEl);
|
|
120
|
+
if (this._onValidate) {
|
|
121
|
+
const result = this._onValidate(value);
|
|
122
|
+
if (result !== true) {
|
|
123
|
+
return result || 'Invalid value';
|
|
124
|
+
}
|
|
181
125
|
}
|
|
182
126
|
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
protected _buildInputElement(): HTMLElement {
|
|
131
|
+
const { options, value, placeholder, required, disabled, name } = this.state;
|
|
132
|
+
|
|
183
133
|
const select = document.createElement('select');
|
|
184
|
-
select.className = 'jux-select-element';
|
|
185
|
-
select.id = `${this._id}-
|
|
134
|
+
select.className = 'jux-input-element jux-select-element';
|
|
135
|
+
select.id = `${this._id}-input`;
|
|
186
136
|
select.name = name;
|
|
137
|
+
select.required = required;
|
|
187
138
|
select.disabled = disabled;
|
|
188
139
|
|
|
189
140
|
// Placeholder option
|
|
190
141
|
if (placeholder) {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
select.appendChild(
|
|
142
|
+
const placeholderOption = document.createElement('option');
|
|
143
|
+
placeholderOption.value = '';
|
|
144
|
+
placeholderOption.textContent = placeholder;
|
|
145
|
+
placeholderOption.disabled = true;
|
|
146
|
+
placeholderOption.selected = !value;
|
|
147
|
+
select.appendChild(placeholderOption);
|
|
197
148
|
}
|
|
198
149
|
|
|
199
150
|
// Options
|
|
200
|
-
options.forEach(
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
select.appendChild(
|
|
151
|
+
options.forEach(option => {
|
|
152
|
+
const optionEl = document.createElement('option');
|
|
153
|
+
optionEl.value = option.value;
|
|
154
|
+
optionEl.textContent = option.label;
|
|
155
|
+
optionEl.disabled = option.disabled || false;
|
|
156
|
+
optionEl.selected = option.value === value;
|
|
157
|
+
select.appendChild(optionEl);
|
|
207
158
|
});
|
|
208
159
|
|
|
209
|
-
select
|
|
210
|
-
|
|
211
|
-
this.state.value = target.value;
|
|
212
|
-
if (this._onChange) {
|
|
213
|
-
this._onChange(target.value);
|
|
214
|
-
}
|
|
215
|
-
});
|
|
160
|
+
return select;
|
|
161
|
+
}
|
|
216
162
|
|
|
217
|
-
|
|
218
|
-
|
|
163
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
164
|
+
* RENDER
|
|
165
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
219
166
|
|
|
220
|
-
|
|
221
|
-
this.
|
|
167
|
+
render(targetId?: string): this {
|
|
168
|
+
const container = this._setupContainer(targetId);
|
|
169
|
+
|
|
170
|
+
const { style, class: className } = this.state;
|
|
171
|
+
|
|
172
|
+
// Build wrapper
|
|
173
|
+
const wrapper = document.createElement('div');
|
|
174
|
+
wrapper.className = 'jux-input jux-select';
|
|
175
|
+
wrapper.id = this._id;
|
|
176
|
+
if (className) wrapper.className += ` ${className}`;
|
|
177
|
+
if (style) wrapper.setAttribute('style', style);
|
|
178
|
+
|
|
179
|
+
// Label
|
|
180
|
+
if (this.state.label) {
|
|
181
|
+
wrapper.appendChild(this._renderLabel());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Select container
|
|
185
|
+
const selectContainer = document.createElement('div');
|
|
186
|
+
selectContainer.className = 'jux-input-container jux-select-container';
|
|
187
|
+
|
|
188
|
+
// Select element
|
|
189
|
+
const selectEl = this._buildInputElement() as HTMLSelectElement;
|
|
190
|
+
this._inputElement = selectEl;
|
|
191
|
+
selectContainer.appendChild(selectEl);
|
|
192
|
+
wrapper.appendChild(selectContainer);
|
|
193
|
+
|
|
194
|
+
// Error element
|
|
195
|
+
wrapper.appendChild(this._renderError());
|
|
196
|
+
|
|
197
|
+
// Wire events
|
|
198
|
+
this._wireStandardEvents(wrapper);
|
|
199
|
+
this._wireFormSync(selectEl, 'change');
|
|
200
|
+
|
|
201
|
+
// Sync label changes
|
|
202
|
+
const labelSync = this._syncBindings.find(b => b.property === 'label');
|
|
203
|
+
if (labelSync) {
|
|
204
|
+
const transform = labelSync.toComponent || ((v: any) => String(v));
|
|
205
|
+
labelSync.stateObj.subscribe((val: any) => {
|
|
206
|
+
this.label(transform(val));
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Sync options changes
|
|
211
|
+
const optionsSync = this._syncBindings.find(b => b.property === 'options');
|
|
212
|
+
if (optionsSync) {
|
|
213
|
+
const transform = optionsSync.toComponent || ((v: any) => v);
|
|
214
|
+
optionsSync.stateObj.subscribe((val: any) => {
|
|
215
|
+
const transformed = transform(val);
|
|
216
|
+
this.state.options = transformed;
|
|
217
|
+
|
|
218
|
+
// Rebuild select options
|
|
219
|
+
selectEl.innerHTML = '';
|
|
220
|
+
|
|
221
|
+
if (this.state.placeholder) {
|
|
222
|
+
const placeholderOption = document.createElement('option');
|
|
223
|
+
placeholderOption.value = '';
|
|
224
|
+
placeholderOption.textContent = this.state.placeholder;
|
|
225
|
+
placeholderOption.disabled = true;
|
|
226
|
+
placeholderOption.selected = !this.state.value;
|
|
227
|
+
selectEl.appendChild(placeholderOption);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
transformed.forEach((option: SelectOption) => {
|
|
231
|
+
const optionEl = document.createElement('option');
|
|
232
|
+
optionEl.value = option.value;
|
|
233
|
+
optionEl.textContent = option.label;
|
|
234
|
+
optionEl.disabled = option.disabled || false;
|
|
235
|
+
optionEl.selected = option.value === this.state.value;
|
|
236
|
+
selectEl.appendChild(optionEl);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
container.appendChild(wrapper);
|
|
242
|
+
this._injectSelectStyles();
|
|
243
|
+
this._injectFormStyles();
|
|
222
244
|
|
|
223
245
|
return this;
|
|
224
246
|
}
|
|
225
247
|
|
|
226
|
-
private
|
|
248
|
+
private _injectSelectStyles(): void {
|
|
227
249
|
const styleId = 'jux-select-styles';
|
|
228
250
|
if (document.getElementById(styleId)) return;
|
|
229
251
|
|
|
230
252
|
const style = document.createElement('style');
|
|
231
253
|
style.id = styleId;
|
|
232
254
|
style.textContent = `
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
background: white;
|
|
251
|
-
cursor: pointer;
|
|
252
|
-
transition: border-color 0.2s;
|
|
253
|
-
box-sizing: border-box;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.jux-select-element:focus {
|
|
257
|
-
outline: none;
|
|
258
|
-
border-color: #3b82f6;
|
|
259
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.jux-select-element:disabled {
|
|
263
|
-
background-color: #f3f4f6;
|
|
264
|
-
cursor: not-allowed;
|
|
265
|
-
}
|
|
266
|
-
`;
|
|
255
|
+
.jux-select-container {
|
|
256
|
+
position: relative;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.jux-select-element {
|
|
260
|
+
appearance: none;
|
|
261
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7280' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
|
|
262
|
+
background-repeat: no-repeat;
|
|
263
|
+
background-position: right 12px center;
|
|
264
|
+
padding-right: 36px;
|
|
265
|
+
cursor: pointer;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.jux-select-element:disabled {
|
|
269
|
+
cursor: not-allowed;
|
|
270
|
+
}
|
|
271
|
+
`;
|
|
267
272
|
document.head.appendChild(style);
|
|
268
273
|
}
|
|
269
|
-
|
|
270
|
-
renderTo(juxComponent: any): this {
|
|
271
|
-
if (!juxComponent?._id) {
|
|
272
|
-
throw new Error('Select.renderTo: Invalid component');
|
|
273
|
-
}
|
|
274
|
-
return this.render(`#${juxComponent._id}`);
|
|
275
|
-
}
|
|
276
274
|
}
|
|
277
275
|
|
|
278
276
|
export function select(id: string, options: SelectOptions = {}): Select {
|