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,92 +1,73 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.js';
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
- /**
5
- * DatePicker component options
6
- */
7
8
  export interface DatePickerOptions {
8
9
  value?: string;
9
- min?: string;
10
- max?: string;
10
+ label?: string;
11
11
  placeholder?: string;
12
+ required?: boolean;
12
13
  disabled?: boolean;
13
14
  name?: string;
14
- onChange?: (value: string) => void;
15
+ min?: string;
16
+ max?: string;
15
17
  style?: string;
16
18
  class?: string;
19
+ onValidate?: (value: string) => boolean | string;
17
20
  }
18
21
 
19
- /**
20
- * DatePicker component state
21
- */
22
- type DatePickerState = {
22
+ interface DatePickerState extends FormInputState {
23
23
  value: string;
24
- min: string;
25
- max: string;
26
24
  placeholder: string;
27
- disabled: boolean;
28
- name: string;
29
- style: string;
30
- class: string;
31
- };
32
-
33
- /**
34
- * DatePicker component - Calendar input
35
- *
36
- * Usage:
37
- * jux.datepicker('start-date', {
38
- * placeholder: 'Select date',
39
- * value: '2024-01-15',
40
- * onChange: (date) => console.log(date)
41
- * }).render('#form');
42
- *
43
- * // Two-way binding
44
- * const dateState = state('2024-01-15');
45
- * jux.datepicker('date').bind(dateState).render('#form');
46
- */
47
- export class DatePicker {
48
- state: DatePickerState;
49
- container: HTMLElement | null = null;
50
- _id: string;
51
- id: string;
52
- private _onChange?: (value: string) => void;
53
- private _boundState?: State<string>;
25
+ min?: string;
26
+ max?: string;
27
+ }
54
28
 
29
+ export class DatePicker extends FormInput<DatePickerState> {
55
30
  constructor(id: string, options: DatePickerOptions = {}) {
56
- this._id = id;
57
- this.id = id;
58
- this._onChange = options.onChange;
59
-
60
- this.state = {
31
+ super(id, {
61
32
  value: options.value ?? '',
62
- min: options.min ?? '',
63
- max: options.max ?? '',
64
- placeholder: options.placeholder ?? 'Select date',
33
+ placeholder: options.placeholder ?? 'Select a date',
34
+ label: options.label ?? '',
35
+ required: options.required ?? false,
65
36
  disabled: options.disabled ?? false,
66
37
  name: options.name ?? id,
38
+ min: options.min,
39
+ max: options.max,
67
40
  style: options.style ?? '',
68
- class: options.class ?? ''
69
- };
70
- }
41
+ class: options.class ?? '',
42
+ errorMessage: undefined
43
+ });
71
44
 
72
- /* -------------------------
73
- * Fluent API
74
- * ------------------------- */
45
+ if (options.onValidate) {
46
+ this._onValidate = options.onValidate;
47
+ }
48
+ }
75
49
 
76
- value(value: string): this {
77
- this.state.value = value;
78
- this._updateElement();
79
- return this;
50
+ protected getTriggerEvents(): readonly string[] {
51
+ return TRIGGER_EVENTS;
80
52
  }
81
53
 
82
- min(value: string): this {
83
- this.state.min = value;
84
- return this;
54
+ protected getCallbackEvents(): readonly string[] {
55
+ return CALLBACK_EVENTS;
85
56
  }
86
57
 
87
- max(value: string): this {
88
- this.state.max = value;
89
- return this;
58
+ /* ═════════════════════════════════════════════════════════════════
59
+ * FLUENT API
60
+ * ═════════════════════════════════════════════════════════════════ */
61
+
62
+ // ✅ Inherited from FormInput/BaseComponent:
63
+ // - label(), required(), name(), onValidate()
64
+ // - validate(), isValid()
65
+ // - style(), class()
66
+ // - bind(), sync(), renderTo()
67
+ // - disabled(), enable(), disable()
68
+
69
+ value(value: string): this {
70
+ return this.setValue(value);
90
71
  }
91
72
 
92
73
  placeholder(value: string): this {
@@ -94,130 +75,197 @@ export class DatePicker {
94
75
  return this;
95
76
  }
96
77
 
97
- disabled(value: boolean): this {
98
- this.state.disabled = value;
99
- this._updateElement();
78
+ min(value: string): this {
79
+ this.state.min = value;
100
80
  return this;
101
81
  }
102
82
 
103
- name(value: string): this {
104
- this.state.name = value;
83
+ max(value: string): this {
84
+ this.state.max = value;
105
85
  return this;
106
86
  }
107
87
 
108
- style(value: string): this {
109
- this.state.style = value;
110
- return this;
111
- }
88
+ /* ═════════════════════════════════════════════════════════════════
89
+ * FORM INPUT IMPLEMENTATION
90
+ * ═════════════════════════════════════════════════════════════════ */
112
91
 
113
- class(value: string): this {
114
- this.state.class = value;
115
- return this;
92
+ getValue(): string {
93
+ return this.state.value;
116
94
  }
117
95
 
118
- onChange(handler: (value: string) => void): this {
119
- this._onChange = handler;
96
+ setValue(value: string): this {
97
+ this.state.value = value;
98
+ if (this._inputElement) {
99
+ (this._inputElement as HTMLInputElement).value = value;
100
+ }
120
101
  return this;
121
102
  }
122
103
 
123
- /**
124
- * Two-way binding to state
125
- */
126
- bind(stateObj: State<string>): this {
127
- this._boundState = stateObj;
128
-
129
- stateObj.subscribe((val) => {
130
- this.state.value = val;
131
- this._updateElement();
132
- });
133
-
134
- this.onChange((value) => stateObj.set(value));
135
-
136
- return this;
104
+ getDate(): Date | null {
105
+ if (!this.state.value) return null;
106
+ const date = new Date(this.state.value);
107
+ return isNaN(date.getTime()) ? null : date;
137
108
  }
138
109
 
139
- /* -------------------------
140
- * Helpers
141
- * ------------------------- */
110
+ protected _validateValue(value: string): boolean | string {
111
+ const { required, min, max } = this.state;
142
112
 
143
- private _updateElement(): void {
144
- const input = document.getElementById(`${this._id}-input`) as HTMLInputElement;
145
- if (input) {
146
- input.value = this.state.value;
147
- input.disabled = this.state.disabled;
113
+ if (required && !value) {
114
+ return 'Please select a date';
148
115
  }
149
- }
150
-
151
- getValue(): string {
152
- return this.state.value;
153
- }
154
116
 
155
- getDate(): Date | null {
156
- return this.state.value ? new Date(this.state.value) : null;
157
- }
117
+ if (value) {
118
+ const date = new Date(value);
158
119
 
159
- /* -------------------------
160
- * Render
161
- * ------------------------- */
120
+ if (isNaN(date.getTime())) {
121
+ return 'Invalid date';
122
+ }
162
123
 
163
- render(targetId?: string): this {
164
- let container: HTMLElement;
124
+ if (min) {
125
+ const minDate = new Date(min);
126
+ if (date < minDate) {
127
+ return `Date must be on or after ${min}`;
128
+ }
129
+ }
165
130
 
166
- if (targetId) {
167
- const target = document.querySelector(targetId);
168
- if (!target || !(target instanceof HTMLElement)) {
169
- throw new Error(`DatePicker: Target element "${targetId}" not found`);
131
+ if (max) {
132
+ const maxDate = new Date(max);
133
+ if (date > maxDate) {
134
+ return `Date must be on or before ${max}`;
135
+ }
170
136
  }
171
- container = target;
172
- } else {
173
- container = getOrCreateContainer(this._id);
174
137
  }
175
138
 
176
- this.container = container;
177
- const { value, min, max, placeholder, disabled, name, style, class: className } = this.state;
178
-
179
- const wrapper = document.createElement('div');
180
- wrapper.className = 'jux-datepicker';
181
- wrapper.id = this._id;
182
-
183
- if (className) {
184
- wrapper.className += ` ${className}`;
139
+ if (this._onValidate) {
140
+ const result = this._onValidate(value);
141
+ if (result !== true) {
142
+ return result || 'Invalid date';
143
+ }
185
144
  }
186
145
 
187
- if (style) {
188
- wrapper.setAttribute('style', style);
189
- }
146
+ return true;
147
+ }
148
+
149
+ protected _buildInputElement(): HTMLElement {
150
+ const { value, placeholder, required, disabled, name, min, max } = this.state;
190
151
 
191
152
  const input = document.createElement('input');
192
153
  input.type = 'date';
193
- input.className = 'jux-datepicker-input';
154
+ input.className = 'jux-input-element jux-datepicker-input';
194
155
  input.id = `${this._id}-input`;
195
156
  input.name = name;
196
157
  input.value = value;
158
+ input.placeholder = placeholder;
159
+ input.required = required;
197
160
  input.disabled = disabled;
198
161
 
199
162
  if (min) input.min = min;
200
163
  if (max) input.max = max;
201
- if (placeholder) input.setAttribute('placeholder', placeholder);
202
164
 
203
- input.addEventListener('change', (e) => {
204
- const target = e.target as HTMLInputElement;
205
- this.state.value = target.value;
206
- if (this._onChange) {
207
- this._onChange(target.value);
208
- }
165
+ return input;
166
+ }
167
+
168
+ /* ═════════════════════════════════════════════════════════════════
169
+ * RENDER
170
+ * ═════════════════════════════════════════════════════════════════ */
171
+
172
+ render(targetId?: string): this {
173
+ const container = this._setupContainer(targetId);
174
+
175
+ const { style, class: className } = this.state;
176
+
177
+ // Build wrapper
178
+ const wrapper = document.createElement('div');
179
+ wrapper.className = 'jux-input jux-datepicker';
180
+ wrapper.id = this._id;
181
+ if (className) wrapper.className += ` ${className}`;
182
+ if (style) wrapper.setAttribute('style', style);
183
+
184
+ // Label
185
+ if (this.state.label) {
186
+ wrapper.appendChild(this._renderLabel());
187
+ }
188
+
189
+ // Input container
190
+ const inputContainer = document.createElement('div');
191
+ inputContainer.className = 'jux-input-container jux-datepicker-container';
192
+
193
+ // Calendar icon
194
+ const iconEl = document.createElement('span');
195
+ iconEl.className = 'jux-input-icon';
196
+ iconEl.appendChild(renderIcon('calendar'));
197
+ inputContainer.appendChild(iconEl);
198
+
199
+ // Date input
200
+ const inputEl = this._buildInputElement();
201
+ this._inputElement = inputEl;
202
+ inputContainer.appendChild(inputEl);
203
+ wrapper.appendChild(inputContainer);
204
+
205
+ // Error element
206
+ wrapper.appendChild(this._renderError());
207
+
208
+ // Wire events
209
+ this._wireStandardEvents(wrapper);
210
+ this._wireFormSync(inputEl, 'change');
211
+
212
+ // 🎯 Fire change event when date changes
213
+ inputEl.addEventListener('change', () => {
214
+ const input = inputEl as HTMLInputElement;
215
+ this._triggerCallback('change', input.value);
209
216
  });
210
217
 
211
- wrapper.appendChild(input);
218
+ // Sync label changes
219
+ const labelSync = this._syncBindings.find(b => b.property === 'label');
220
+ if (labelSync) {
221
+ const transform = labelSync.toComponent || ((v: any) => String(v));
222
+ labelSync.stateObj.subscribe((val: any) => {
223
+ this.label(transform(val));
224
+ });
225
+ }
226
+
212
227
  container.appendChild(wrapper);
228
+ this._injectDatePickerStyles();
229
+ this._injectFormStyles();
230
+
231
+ requestAnimationFrame(() => {
232
+ if ((window as any).lucide) {
233
+ (window as any).lucide.createIcons();
234
+ }
235
+ });
236
+
213
237
  return this;
214
238
  }
215
239
 
216
- renderTo(juxComponent: any): this {
217
- if (!juxComponent?._id) {
218
- throw new Error('DatePicker.renderTo: Invalid component');
219
- }
220
- return this.render(`#${juxComponent._id}`);
240
+ private _injectDatePickerStyles(): void {
241
+ const styleId = 'jux-datepicker-styles';
242
+ if (document.getElementById(styleId)) return;
243
+
244
+ const style = document.createElement('style');
245
+ style.id = styleId;
246
+ style.textContent = `
247
+ .jux-datepicker-container {
248
+ position: relative;
249
+ }
250
+
251
+ .jux-datepicker-container .jux-input-icon {
252
+ pointer-events: none;
253
+ }
254
+
255
+ .jux-datepicker-input {
256
+ padding-left: 40px;
257
+ }
258
+
259
+ .jux-datepicker-input::-webkit-calendar-picker-indicator {
260
+ cursor: pointer;
261
+ opacity: 0;
262
+ position: absolute;
263
+ right: 0;
264
+ width: 100%;
265
+ height: 100%;
266
+ }
267
+ `;
268
+ document.head.appendChild(style);
221
269
  }
222
270
  }
223
271