juxscript 1.1.4 → 1.1.6
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/index.d.ts +10 -10
- package/index.d.ts.map +1 -0
- package/lib/components/alert.d.ts +32 -0
- package/lib/components/alert.d.ts.map +1 -0
- package/lib/components/alert.js +153 -0
- package/lib/components/alert.ts +200 -0
- package/lib/components/app.d.ts +89 -0
- package/lib/components/app.d.ts.map +1 -0
- package/lib/components/app.js +175 -0
- package/lib/components/app.ts +247 -0
- package/lib/components/badge.d.ts +27 -0
- package/lib/components/badge.d.ts.map +1 -0
- package/lib/components/badge.js +70 -0
- package/lib/components/badge.ts +101 -0
- package/lib/components/base/BaseComponent.d.ts +142 -0
- package/lib/components/base/BaseComponent.d.ts.map +1 -0
- package/lib/components/base/BaseComponent.js +363 -0
- package/lib/components/base/BaseComponent.ts +421 -0
- package/lib/components/base/FormInput.d.ts +73 -0
- package/lib/components/base/FormInput.d.ts.map +1 -0
- package/lib/components/base/FormInput.js +163 -0
- package/lib/components/base/FormInput.ts +227 -0
- package/lib/components/button.d.ts +48 -0
- package/lib/components/button.d.ts.map +1 -0
- package/lib/components/button.js +121 -0
- package/lib/components/button.ts +178 -0
- package/lib/components/card.d.ts +34 -0
- package/lib/components/card.d.ts.map +1 -0
- package/lib/components/card.js +127 -0
- package/lib/components/card.ts +173 -0
- package/lib/components/chart.d.ts +45 -0
- package/lib/components/chart.d.ts.map +1 -0
- package/lib/components/chart.js +186 -0
- package/lib/components/chart.ts +231 -0
- package/lib/components/checkbox.d.ts +31 -0
- package/lib/components/checkbox.d.ts.map +1 -0
- package/lib/components/checkbox.js +185 -0
- package/lib/components/checkbox.ts +242 -0
- package/lib/components/code.d.ts +24 -0
- package/lib/components/code.d.ts.map +1 -0
- package/lib/components/code.js +88 -0
- package/lib/components/code.ts +123 -0
- package/lib/components/container.d.ts +42 -0
- package/lib/components/container.d.ts.map +1 -0
- package/lib/components/container.js +93 -0
- package/lib/components/container.ts +140 -0
- package/lib/components/data.d.ts +36 -0
- package/lib/components/data.d.ts.map +1 -0
- package/lib/components/data.js +110 -0
- package/lib/components/data.ts +135 -0
- package/lib/components/datepicker.d.ts +38 -0
- package/lib/components/datepicker.d.ts.map +1 -0
- package/lib/components/datepicker.js +177 -0
- package/lib/components/datepicker.ts +234 -0
- package/lib/components/dialog.d.ts +38 -0
- package/lib/components/dialog.d.ts.map +1 -0
- package/lib/components/dialog.js +126 -0
- package/lib/components/dialog.ts +172 -0
- package/lib/components/divider.d.ts +30 -0
- package/lib/components/divider.d.ts.map +1 -0
- package/lib/components/divider.js +69 -0
- package/lib/components/divider.ts +100 -0
- package/lib/components/dropdown.d.ts +39 -0
- package/lib/components/dropdown.d.ts.map +1 -0
- package/lib/components/dropdown.js +133 -0
- package/lib/components/dropdown.ts +186 -0
- package/lib/components/element.d.ts +50 -0
- package/lib/components/element.d.ts.map +1 -0
- package/lib/components/element.js +206 -0
- package/lib/components/element.ts +267 -0
- package/lib/components/fileupload.d.ts +40 -0
- package/lib/components/fileupload.d.ts.map +1 -0
- package/lib/components/fileupload.js +241 -0
- package/lib/components/fileupload.ts +309 -0
- package/lib/components/grid.d.ts +87 -0
- package/lib/components/grid.d.ts.map +1 -0
- package/lib/components/grid.js +205 -0
- package/lib/components/grid.ts +291 -0
- package/lib/components/guard.d.ts +41 -0
- package/lib/components/guard.d.ts.map +1 -0
- package/lib/components/guard.js +56 -0
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.d.ts +24 -0
- package/lib/components/heading.d.ts.map +1 -0
- package/lib/components/heading.js +67 -0
- package/lib/components/heading.ts +96 -0
- package/lib/components/helpers.d.ts +9 -0
- package/lib/components/helpers.d.ts.map +1 -0
- package/lib/components/helpers.js +30 -0
- package/lib/components/helpers.ts +41 -0
- package/lib/components/hero.d.ts +45 -0
- package/lib/components/hero.d.ts.map +1 -0
- package/lib/components/hero.js +165 -0
- package/lib/components/hero.ts +224 -0
- package/lib/components/icon.d.ts +35 -0
- package/lib/components/icon.d.ts.map +1 -0
- package/lib/components/icon.js +132 -0
- package/lib/components/icon.ts +178 -0
- package/lib/components/icons.d.ts +25 -0
- package/lib/components/icons.d.ts.map +1 -0
- package/lib/components/icons.js +440 -0
- package/lib/components/icons.ts +464 -0
- package/lib/components/include.d.ts +120 -0
- package/lib/components/include.d.ts.map +1 -0
- package/lib/components/include.js +350 -0
- package/lib/components/include.ts +410 -0
- package/lib/components/input.d.ts +83 -0
- package/lib/components/input.d.ts.map +1 -0
- package/lib/components/input.js +348 -0
- package/lib/components/input.ts +457 -0
- package/lib/components/list.d.ts +82 -0
- package/lib/components/list.d.ts.map +1 -0
- package/lib/components/list.js +311 -0
- package/lib/components/list.ts +419 -0
- package/lib/components/loading.d.ts +24 -0
- package/lib/components/loading.d.ts.map +1 -0
- package/lib/components/loading.js +73 -0
- package/lib/components/loading.ts +100 -0
- package/lib/components/menu.d.ts +37 -0
- package/lib/components/menu.d.ts.map +1 -0
- package/lib/components/menu.js +202 -0
- package/lib/components/menu.ts +275 -0
- package/lib/components/modal.d.ts +51 -0
- package/lib/components/modal.d.ts.map +1 -0
- package/lib/components/modal.js +227 -0
- package/lib/components/modal.ts +284 -0
- package/lib/components/nav.d.ts +45 -0
- package/lib/components/nav.d.ts.map +1 -0
- package/lib/components/nav.js +190 -0
- package/lib/components/nav.ts +257 -0
- package/lib/components/paragraph.d.ts +21 -0
- package/lib/components/paragraph.d.ts.map +1 -0
- package/lib/components/paragraph.js +70 -0
- package/lib/components/paragraph.ts +97 -0
- package/lib/components/progress.d.ts +39 -0
- package/lib/components/progress.d.ts.map +1 -0
- package/lib/components/progress.js +113 -0
- package/lib/components/progress.ts +159 -0
- package/lib/components/radio.d.ts +41 -0
- package/lib/components/radio.d.ts.map +1 -0
- package/lib/components/radio.js +203 -0
- package/lib/components/radio.ts +278 -0
- package/lib/components/req.d.ts +155 -0
- package/lib/components/req.d.ts.map +1 -0
- package/lib/components/req.js +253 -0
- package/lib/components/req.ts +303 -0
- package/lib/components/script.d.ts +14 -0
- package/lib/components/script.d.ts.map +1 -0
- package/lib/components/script.js +33 -0
- package/lib/components/script.ts +41 -0
- package/lib/components/select.d.ts +40 -0
- package/lib/components/select.d.ts.map +1 -0
- package/lib/components/select.js +183 -0
- package/lib/components/select.ts +252 -0
- package/lib/components/sidebar.d.ts +48 -0
- package/lib/components/sidebar.d.ts.map +1 -0
- package/lib/components/sidebar.js +207 -0
- package/lib/components/sidebar.ts +275 -0
- package/lib/components/style.d.ts +14 -0
- package/lib/components/style.d.ts.map +1 -0
- package/lib/components/style.js +33 -0
- package/lib/components/style.ts +41 -0
- package/lib/components/switch.d.ts +32 -0
- package/lib/components/switch.d.ts.map +1 -0
- package/lib/components/switch.js +186 -0
- package/lib/components/switch.ts +246 -0
- package/lib/components/table.d.ts +137 -0
- package/lib/components/table.d.ts.map +1 -0
- package/lib/components/table.js +1045 -0
- package/lib/components/table.ts +1249 -0
- package/lib/components/tabs.d.ts +36 -0
- package/lib/components/tabs.d.ts.map +1 -0
- package/lib/components/tabs.js +198 -0
- package/lib/components/tabs.ts +250 -0
- package/lib/components/theme-toggle.d.ts +44 -0
- package/lib/components/theme-toggle.d.ts.map +1 -0
- package/lib/components/theme-toggle.js +215 -0
- package/lib/components/theme-toggle.ts +293 -0
- package/lib/components/tooltip.d.ts +30 -0
- package/lib/components/tooltip.d.ts.map +1 -0
- package/lib/components/tooltip.js +109 -0
- package/lib/components/tooltip.ts +144 -0
- package/lib/components/view.d.ts +48 -0
- package/lib/components/view.d.ts.map +1 -0
- package/lib/components/view.js +149 -0
- package/lib/components/view.ts +190 -0
- package/lib/components/write.d.ts +107 -0
- package/lib/components/write.d.ts.map +1 -0
- package/lib/components/write.js +222 -0
- package/lib/components/write.ts +272 -0
- package/lib/layouts/default.css +260 -0
- package/lib/layouts/figma.css +334 -0
- package/lib/reactivity/state.d.ts +36 -0
- package/lib/reactivity/state.d.ts.map +1 -0
- package/lib/reactivity/state.js +67 -0
- package/lib/reactivity/state.ts +78 -0
- package/lib/utils/fetch.d.ts +176 -0
- package/lib/utils/fetch.d.ts.map +1 -0
- package/lib/utils/fetch.js +427 -0
- package/lib/utils/fetch.ts +553 -0
- package/machinery/compiler3.js +78 -0
- package/machinery/doc-generator.js +136 -0
- package/machinery/imports.js +155 -0
- package/machinery/ts-shim.js +46 -0
- package/package.json +9 -15
|
@@ -0,0 +1,309 @@
|
|
|
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', 'filesSelected', 'clear'] as const;
|
|
7
|
+
|
|
8
|
+
export interface FileUploadOptions {
|
|
9
|
+
label?: string;
|
|
10
|
+
accept?: string;
|
|
11
|
+
multiple?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
name?: string;
|
|
14
|
+
icon?: string;
|
|
15
|
+
required?: boolean;
|
|
16
|
+
style?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
onValidate?: (files: File[]) => boolean | string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface FileUploadState extends FormInputState {
|
|
22
|
+
files: File[];
|
|
23
|
+
accept: string;
|
|
24
|
+
multiple: boolean;
|
|
25
|
+
icon: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class FileUpload extends FormInput<FileUploadState> {
|
|
29
|
+
private _fileListElement: HTMLElement | null = null;
|
|
30
|
+
|
|
31
|
+
constructor(id: string, options: FileUploadOptions = {}) {
|
|
32
|
+
super(id, {
|
|
33
|
+
files: [],
|
|
34
|
+
accept: options.accept ?? '',
|
|
35
|
+
multiple: options.multiple ?? false,
|
|
36
|
+
icon: options.icon ?? 'upload',
|
|
37
|
+
label: options.label ?? '',
|
|
38
|
+
required: options.required ?? false,
|
|
39
|
+
disabled: options.disabled ?? false,
|
|
40
|
+
name: options.name ?? id,
|
|
41
|
+
style: options.style ?? '',
|
|
42
|
+
class: options.class ?? '',
|
|
43
|
+
errorMessage: undefined
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (options.onValidate) {
|
|
47
|
+
this._onValidate = options.onValidate;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected getTriggerEvents(): readonly string[] {
|
|
52
|
+
return TRIGGER_EVENTS;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected getCallbackEvents(): readonly string[] {
|
|
56
|
+
return CALLBACK_EVENTS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
60
|
+
* FLUENT API
|
|
61
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
62
|
+
|
|
63
|
+
// ✅ Inherited from FormInput/BaseComponent:
|
|
64
|
+
// - label(), required(), name(), onValidate()
|
|
65
|
+
// - validate(), isValid()
|
|
66
|
+
// - style(), class()
|
|
67
|
+
// - bind(), sync(), renderTo()
|
|
68
|
+
// - disabled(), enable(), disable()
|
|
69
|
+
|
|
70
|
+
accept(value: string): this {
|
|
71
|
+
this.state.accept = value;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
multiple(value: boolean): this {
|
|
76
|
+
this.state.multiple = value;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
icon(value: string): this {
|
|
81
|
+
this.state.icon = value;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
clear(): this {
|
|
86
|
+
this.state.files = [];
|
|
87
|
+
if (this._inputElement) {
|
|
88
|
+
(this._inputElement as HTMLInputElement).value = '';
|
|
89
|
+
}
|
|
90
|
+
if (this._fileListElement) {
|
|
91
|
+
this._updateFileList([]);
|
|
92
|
+
}
|
|
93
|
+
// 🎯 Fire the clear callback event
|
|
94
|
+
this._triggerCallback('clear');
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
99
|
+
* FORM INPUT IMPLEMENTATION
|
|
100
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
101
|
+
|
|
102
|
+
getValue(): File[] {
|
|
103
|
+
return this.state.files;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setValue(files: File[]): this {
|
|
107
|
+
this.state.files = files;
|
|
108
|
+
if (this._fileListElement) {
|
|
109
|
+
this._updateFileList(files);
|
|
110
|
+
}
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getFiles(): File[] {
|
|
115
|
+
return this.getValue();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
protected _validateValue(files: File[]): boolean | string {
|
|
119
|
+
const { required } = this.state;
|
|
120
|
+
|
|
121
|
+
if (required && files.length === 0) {
|
|
122
|
+
return 'Please select at least one file';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (this._onValidate) {
|
|
126
|
+
const result = this._onValidate(files);
|
|
127
|
+
if (result !== true) {
|
|
128
|
+
return result || 'Invalid files';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
protected _buildInputElement(): HTMLElement {
|
|
136
|
+
const { accept, multiple, required, disabled, name } = this.state;
|
|
137
|
+
|
|
138
|
+
const input = document.createElement('input');
|
|
139
|
+
input.type = 'file';
|
|
140
|
+
input.className = 'jux-fileupload-input';
|
|
141
|
+
input.id = `${this._id}-input`;
|
|
142
|
+
input.name = name;
|
|
143
|
+
input.required = required;
|
|
144
|
+
input.disabled = disabled;
|
|
145
|
+
|
|
146
|
+
if (accept) input.accept = accept;
|
|
147
|
+
if (multiple) input.multiple = multiple;
|
|
148
|
+
|
|
149
|
+
return input;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
153
|
+
* RENDER
|
|
154
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
155
|
+
|
|
156
|
+
render(targetId?: string): this {
|
|
157
|
+
const container = this._setupContainer(targetId);
|
|
158
|
+
|
|
159
|
+
const { icon, style, class: className } = this.state;
|
|
160
|
+
|
|
161
|
+
// Build wrapper
|
|
162
|
+
const wrapper = document.createElement('div');
|
|
163
|
+
wrapper.className = 'jux-input jux-fileupload';
|
|
164
|
+
wrapper.id = this._id;
|
|
165
|
+
if (className) wrapper.className += ` ${className}`;
|
|
166
|
+
if (style) wrapper.setAttribute('style', style);
|
|
167
|
+
|
|
168
|
+
// Label
|
|
169
|
+
if (this.state.label) {
|
|
170
|
+
wrapper.appendChild(this._renderLabel());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Hidden file input
|
|
174
|
+
const inputEl = this._buildInputElement() as HTMLInputElement;
|
|
175
|
+
this._inputElement = inputEl;
|
|
176
|
+
wrapper.appendChild(inputEl);
|
|
177
|
+
|
|
178
|
+
// Button container
|
|
179
|
+
const buttonContainer = document.createElement('div');
|
|
180
|
+
buttonContainer.className = 'jux-fileupload-button-container';
|
|
181
|
+
|
|
182
|
+
if (icon) {
|
|
183
|
+
const iconEl = document.createElement('span');
|
|
184
|
+
iconEl.className = 'jux-fileupload-icon';
|
|
185
|
+
iconEl.appendChild(renderIcon(icon));
|
|
186
|
+
buttonContainer.appendChild(iconEl);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const button = document.createElement('button');
|
|
190
|
+
button.type = 'button';
|
|
191
|
+
button.className = 'jux-fileupload-button';
|
|
192
|
+
button.textContent = 'Choose File(s)';
|
|
193
|
+
button.disabled = this.state.disabled;
|
|
194
|
+
|
|
195
|
+
buttonContainer.appendChild(button);
|
|
196
|
+
wrapper.appendChild(buttonContainer);
|
|
197
|
+
|
|
198
|
+
// File list
|
|
199
|
+
const fileList = document.createElement('div');
|
|
200
|
+
fileList.className = 'jux-fileupload-list';
|
|
201
|
+
this._fileListElement = fileList;
|
|
202
|
+
wrapper.appendChild(fileList);
|
|
203
|
+
|
|
204
|
+
// Error element
|
|
205
|
+
wrapper.appendChild(this._renderError());
|
|
206
|
+
|
|
207
|
+
// Button click triggers file input
|
|
208
|
+
button.addEventListener('click', () => inputEl.click());
|
|
209
|
+
|
|
210
|
+
// Wire events
|
|
211
|
+
this._wireStandardEvents(wrapper);
|
|
212
|
+
|
|
213
|
+
// Wire file-specific sync
|
|
214
|
+
const filesSync = this._syncBindings.find(b => b.property === 'files' || b.property === 'value');
|
|
215
|
+
|
|
216
|
+
if (filesSync) {
|
|
217
|
+
const { stateObj, toState, toComponent } = filesSync;
|
|
218
|
+
|
|
219
|
+
const transformToState = toState || ((v: File[]) => v);
|
|
220
|
+
const transformToComponent = toComponent || ((v: any) => v);
|
|
221
|
+
|
|
222
|
+
let isUpdating = false;
|
|
223
|
+
|
|
224
|
+
// State → Component
|
|
225
|
+
stateObj.subscribe((val: any) => {
|
|
226
|
+
if (isUpdating) return;
|
|
227
|
+
const transformed = transformToComponent(val);
|
|
228
|
+
this.setValue(transformed);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Component → State
|
|
232
|
+
inputEl.addEventListener('change', () => {
|
|
233
|
+
if (isUpdating) return;
|
|
234
|
+
isUpdating = true;
|
|
235
|
+
|
|
236
|
+
const files = Array.from(inputEl.files || []);
|
|
237
|
+
this.state.files = files;
|
|
238
|
+
this._updateFileList(files);
|
|
239
|
+
this._clearError();
|
|
240
|
+
|
|
241
|
+
const transformed = transformToState(files);
|
|
242
|
+
stateObj.set(transformed);
|
|
243
|
+
|
|
244
|
+
// 🎯 Fire the callback events
|
|
245
|
+
this._triggerCallback('change', files);
|
|
246
|
+
this._triggerCallback('filesSelected', files);
|
|
247
|
+
|
|
248
|
+
setTimeout(() => { isUpdating = false; }, 0);
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
// Default behavior without sync
|
|
252
|
+
inputEl.addEventListener('change', () => {
|
|
253
|
+
const files = Array.from(inputEl.files || []);
|
|
254
|
+
this.state.files = files;
|
|
255
|
+
this._updateFileList(files);
|
|
256
|
+
this._clearError();
|
|
257
|
+
|
|
258
|
+
// 🎯 Fire the callback events
|
|
259
|
+
this._triggerCallback('change', files);
|
|
260
|
+
this._triggerCallback('filesSelected', files);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Always add blur validation
|
|
265
|
+
inputEl.addEventListener('blur', () => {
|
|
266
|
+
this.validate();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Sync label changes
|
|
270
|
+
const labelSync = this._syncBindings.find(b => b.property === 'label');
|
|
271
|
+
if (labelSync) {
|
|
272
|
+
const transform = labelSync.toComponent || ((v: any) => String(v));
|
|
273
|
+
labelSync.stateObj.subscribe((val: any) => {
|
|
274
|
+
this.label(transform(val));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
container.appendChild(wrapper);
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private _updateFileList(files: File[]): void {
|
|
283
|
+
if (!this._fileListElement) return;
|
|
284
|
+
|
|
285
|
+
this._fileListElement.innerHTML = '';
|
|
286
|
+
|
|
287
|
+
if (files.length === 0) {
|
|
288
|
+
this._fileListElement.textContent = 'No files selected';
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
files.forEach(file => {
|
|
293
|
+
const fileItem = document.createElement('div');
|
|
294
|
+
fileItem.className = 'jux-fileupload-item';
|
|
295
|
+
fileItem.textContent = `${file.name} (${this._formatFileSize(file.size)})`;
|
|
296
|
+
this._fileListElement!.appendChild(fileItem);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private _formatFileSize(bytes: number): string {
|
|
301
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
302
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
303
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function fileupload(id: string, options: FileUploadOptions = {}): FileUpload {
|
|
308
|
+
return new FileUpload(id, options);
|
|
309
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
+
export interface GridRowConfig {
|
|
3
|
+
height?: string;
|
|
4
|
+
style?: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface GridColumnConfig {
|
|
8
|
+
width?: string;
|
|
9
|
+
style?: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GridItemConfig {
|
|
13
|
+
target: string;
|
|
14
|
+
text?: string;
|
|
15
|
+
html?: string;
|
|
16
|
+
component?: any;
|
|
17
|
+
}
|
|
18
|
+
export interface GridCreateConfig {
|
|
19
|
+
columns?: GridColumnConfig[] | number;
|
|
20
|
+
rows?: GridRowConfig[] | number;
|
|
21
|
+
items?: GridItemConfig[];
|
|
22
|
+
}
|
|
23
|
+
export interface GridOptions {
|
|
24
|
+
columns?: GridColumnConfig[] | number;
|
|
25
|
+
rows?: GridRowConfig[] | number;
|
|
26
|
+
width?: string;
|
|
27
|
+
height?: string;
|
|
28
|
+
gap?: string;
|
|
29
|
+
style?: string;
|
|
30
|
+
class?: string;
|
|
31
|
+
}
|
|
32
|
+
type GridState = {
|
|
33
|
+
rows: GridRowConfig[];
|
|
34
|
+
columns: GridColumnConfig[];
|
|
35
|
+
boundary: {
|
|
36
|
+
width: string;
|
|
37
|
+
height: string;
|
|
38
|
+
};
|
|
39
|
+
gap: string;
|
|
40
|
+
cells: Map<string, HTMLElement>;
|
|
41
|
+
style: string;
|
|
42
|
+
class: string;
|
|
43
|
+
gridder: boolean;
|
|
44
|
+
};
|
|
45
|
+
export declare class Grid extends BaseComponent<GridState> {
|
|
46
|
+
constructor(id: string, options?: GridOptions);
|
|
47
|
+
protected getTriggerEvents(): readonly string[];
|
|
48
|
+
protected getCallbackEvents(): readonly string[];
|
|
49
|
+
width(value: string): this;
|
|
50
|
+
height(value: string): this;
|
|
51
|
+
gap(value: string): this;
|
|
52
|
+
/**
|
|
53
|
+
* Define rows (SETTER)
|
|
54
|
+
* @param config - Number (creates N equal rows) or Array of row configs
|
|
55
|
+
*/
|
|
56
|
+
rows(config: GridRowConfig[] | number): this;
|
|
57
|
+
/**
|
|
58
|
+
* Define columns (SETTER)
|
|
59
|
+
* @param config - Number (creates N equal columns) or Array of column configs
|
|
60
|
+
*/
|
|
61
|
+
columns(config: GridColumnConfig[] | number): this;
|
|
62
|
+
/**
|
|
63
|
+
* Get the cell ID for a specific coordinate
|
|
64
|
+
*/
|
|
65
|
+
getCellId(row: number, col: number): string;
|
|
66
|
+
/**
|
|
67
|
+
* Get the DOM element for a specific cell
|
|
68
|
+
*/
|
|
69
|
+
getCell(row: number, col: number): HTMLElement | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* ✅ Enable blueprint/wireframe visualization
|
|
72
|
+
* Shows grid structure, cell coordinates, and geometric borders
|
|
73
|
+
*/
|
|
74
|
+
gridder(value?: boolean): this;
|
|
75
|
+
/**
|
|
76
|
+
* Pattern B: The Static Orchestration
|
|
77
|
+
* Creates the grid and populates it with items in one operation
|
|
78
|
+
*/
|
|
79
|
+
create(config: GridCreateConfig): this;
|
|
80
|
+
render(targetId?: string): this;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Factory function
|
|
84
|
+
*/
|
|
85
|
+
export declare function grid(id: string, options?: GridOptions): Grid;
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["grid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
+
// Event definitions - Grid is layout-only, no interactive events
|
|
3
|
+
const TRIGGER_EVENTS = [];
|
|
4
|
+
const CALLBACK_EVENTS = [];
|
|
5
|
+
export class Grid extends BaseComponent {
|
|
6
|
+
constructor(id, options = {}) {
|
|
7
|
+
// Normalize columns/rows to config arrays
|
|
8
|
+
const columnsConfig = normalizeTrackConfig(options.columns || 1);
|
|
9
|
+
const rowsConfig = normalizeTrackConfig(options.rows || 1);
|
|
10
|
+
super(id, {
|
|
11
|
+
rows: rowsConfig,
|
|
12
|
+
columns: columnsConfig,
|
|
13
|
+
boundary: {
|
|
14
|
+
width: options.width ?? '', // ✅ CHANGE: Empty = inherit parent
|
|
15
|
+
height: options.height ?? '' // ✅ CHANGE: Empty = inherit parent
|
|
16
|
+
},
|
|
17
|
+
gap: options.gap ?? '0',
|
|
18
|
+
cells: new Map(),
|
|
19
|
+
style: options.style ?? '',
|
|
20
|
+
class: options.class ?? '',
|
|
21
|
+
gridder: false
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
getTriggerEvents() {
|
|
25
|
+
return TRIGGER_EVENTS;
|
|
26
|
+
}
|
|
27
|
+
getCallbackEvents() {
|
|
28
|
+
return CALLBACK_EVENTS;
|
|
29
|
+
}
|
|
30
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
31
|
+
* PROPS ACCESSOR - Inherited from BaseComponent
|
|
32
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
33
|
+
// ✅ Already available via BaseComponent.props getter
|
|
34
|
+
// Access: grid.props.rows, grid.props.columns, grid.props.gap, etc.
|
|
35
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
36
|
+
* FLUENT API - The Blueprint (Setters)
|
|
37
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
38
|
+
width(value) {
|
|
39
|
+
this.state.boundary.width = value;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
height(value) {
|
|
43
|
+
this.state.boundary.height = value;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
gap(value) {
|
|
47
|
+
this.state.gap = value;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Define rows (SETTER)
|
|
52
|
+
* @param config - Number (creates N equal rows) or Array of row configs
|
|
53
|
+
*/
|
|
54
|
+
rows(config) {
|
|
55
|
+
this.state.rows = normalizeTrackConfig(config);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Define columns (SETTER)
|
|
60
|
+
* @param config - Number (creates N equal columns) or Array of column configs
|
|
61
|
+
*/
|
|
62
|
+
columns(config) {
|
|
63
|
+
this.state.columns = normalizeTrackConfig(config);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the cell ID for a specific coordinate
|
|
68
|
+
*/
|
|
69
|
+
getCellId(row, col) {
|
|
70
|
+
return `${this._id}-${row}-${col}`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the DOM element for a specific cell
|
|
74
|
+
*/
|
|
75
|
+
getCell(row, col) {
|
|
76
|
+
const cellId = this.getCellId(row, col);
|
|
77
|
+
return this.state.cells.get(cellId);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* ✅ Enable blueprint/wireframe visualization
|
|
81
|
+
* Shows grid structure, cell coordinates, and geometric borders
|
|
82
|
+
*/
|
|
83
|
+
gridder(value = true) {
|
|
84
|
+
this.state.gridder = value;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
88
|
+
* ORCHESTRATION - The Execution
|
|
89
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
90
|
+
/**
|
|
91
|
+
* Pattern B: The Static Orchestration
|
|
92
|
+
* Creates the grid and populates it with items in one operation
|
|
93
|
+
*/
|
|
94
|
+
create(config) {
|
|
95
|
+
// Apply configuration
|
|
96
|
+
if (config.columns) {
|
|
97
|
+
this.columns(config.columns);
|
|
98
|
+
}
|
|
99
|
+
if (config.rows) {
|
|
100
|
+
this.rows(config.rows);
|
|
101
|
+
}
|
|
102
|
+
// Render the grid structure
|
|
103
|
+
this.render();
|
|
104
|
+
// Populate items
|
|
105
|
+
if (config.items) {
|
|
106
|
+
config.items.forEach(item => {
|
|
107
|
+
const targetId = item.target.replace(/^#/, '');
|
|
108
|
+
const cell = document.getElementById(targetId);
|
|
109
|
+
if (!cell) {
|
|
110
|
+
console.warn(`[Grid] Cell not found: ${item.target}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (item.component) {
|
|
114
|
+
// Render Jux component
|
|
115
|
+
item.component.render(`#${targetId}`);
|
|
116
|
+
}
|
|
117
|
+
else if (item.html) {
|
|
118
|
+
cell.innerHTML = item.html;
|
|
119
|
+
}
|
|
120
|
+
else if (item.text) {
|
|
121
|
+
cell.textContent = item.text;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
128
|
+
* RENDER
|
|
129
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
130
|
+
render(targetId) {
|
|
131
|
+
const container = this._setupContainer(targetId);
|
|
132
|
+
const { rows, columns, boundary, gap, style, class: className, gridder } = this.state;
|
|
133
|
+
// Build grid container
|
|
134
|
+
const grid = document.createElement('div');
|
|
135
|
+
grid.className = 'jux-grid';
|
|
136
|
+
grid.id = this._id;
|
|
137
|
+
if (gridder)
|
|
138
|
+
grid.classList.add('jux-grid-gridder');
|
|
139
|
+
if (className)
|
|
140
|
+
grid.className += ` ${className}`;
|
|
141
|
+
// Apply grid styles
|
|
142
|
+
const gridTemplateRows = rows.map(r => r.height || '1fr').join(' ');
|
|
143
|
+
const gridTemplateColumns = columns.map(c => c.width || '1fr').join(' ');
|
|
144
|
+
const gridStyle = `
|
|
145
|
+
display: grid;
|
|
146
|
+
grid-template-rows: ${gridTemplateRows};
|
|
147
|
+
grid-template-columns: ${gridTemplateColumns};
|
|
148
|
+
gap: ${gap};
|
|
149
|
+
${boundary.width ? `width: ${boundary.width};` : ''} /* ✅ CHANGE: Only apply if set */
|
|
150
|
+
${boundary.height ? `height: ${boundary.height};` : ''} /* ✅ CHANGE: Only apply if set */
|
|
151
|
+
${style}
|
|
152
|
+
`;
|
|
153
|
+
grid.setAttribute('style', gridStyle.trim());
|
|
154
|
+
// Generate cells with coordinate-based IDs
|
|
155
|
+
this.state.cells.clear();
|
|
156
|
+
rows.forEach((row, rowIndex) => {
|
|
157
|
+
columns.forEach((col, colIndex) => {
|
|
158
|
+
const cell = document.createElement('div');
|
|
159
|
+
const cellId = this.getCellId(rowIndex, colIndex);
|
|
160
|
+
cell.id = cellId;
|
|
161
|
+
cell.className = 'jux-grid-cell';
|
|
162
|
+
cell.setAttribute('data-row', String(rowIndex));
|
|
163
|
+
cell.setAttribute('data-col', String(colIndex));
|
|
164
|
+
// ✅ CHANGE: Use title attribute instead of DOM element
|
|
165
|
+
if (gridder) {
|
|
166
|
+
cell.setAttribute('title', `Cell [${rowIndex},${colIndex}] — #${cellId}`);
|
|
167
|
+
}
|
|
168
|
+
// Apply row-specific styles
|
|
169
|
+
if (row.style) {
|
|
170
|
+
cell.setAttribute('style', (cell.getAttribute('style') || '') + row.style);
|
|
171
|
+
}
|
|
172
|
+
if (row.class) {
|
|
173
|
+
cell.className += ` ${row.class}`;
|
|
174
|
+
}
|
|
175
|
+
// Apply column-specific styles
|
|
176
|
+
if (col.style) {
|
|
177
|
+
cell.setAttribute('style', (cell.getAttribute('style') || '') + col.style);
|
|
178
|
+
}
|
|
179
|
+
if (col.class) {
|
|
180
|
+
cell.className += ` ${col.class}`;
|
|
181
|
+
}
|
|
182
|
+
this.state.cells.set(cellId, cell);
|
|
183
|
+
grid.appendChild(cell);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
this._wireStandardEvents(grid);
|
|
187
|
+
container.appendChild(grid);
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Normalize track configuration to array format
|
|
193
|
+
*/
|
|
194
|
+
function normalizeTrackConfig(config) {
|
|
195
|
+
if (typeof config === 'number') {
|
|
196
|
+
return Array(config).fill({});
|
|
197
|
+
}
|
|
198
|
+
return config;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Factory function
|
|
202
|
+
*/
|
|
203
|
+
export function grid(id, options = {}) {
|
|
204
|
+
return new Grid(id, options);
|
|
205
|
+
}
|