juxscript 1.0.2 → 1.0.4

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 (71) hide show
  1. package/README.md +37 -92
  2. package/bin/cli.js +57 -56
  3. package/lib/components/alert.ts +240 -0
  4. package/lib/components/app.ts +216 -82
  5. package/lib/components/badge.ts +164 -0
  6. package/lib/components/button.ts +188 -53
  7. package/lib/components/card.ts +75 -61
  8. package/lib/components/chart.ts +17 -15
  9. package/lib/components/checkbox.ts +228 -0
  10. package/lib/components/code.ts +66 -152
  11. package/lib/components/container.ts +104 -208
  12. package/lib/components/data.ts +1 -3
  13. package/lib/components/datepicker.ts +226 -0
  14. package/lib/components/dialog.ts +258 -0
  15. package/lib/components/docs-data.json +1697 -388
  16. package/lib/components/dropdown.ts +244 -0
  17. package/lib/components/element.ts +271 -0
  18. package/lib/components/fileupload.ts +319 -0
  19. package/lib/components/footer.ts +37 -18
  20. package/lib/components/header.ts +53 -33
  21. package/lib/components/heading.ts +119 -0
  22. package/lib/components/helpers.ts +34 -0
  23. package/lib/components/hero.ts +57 -31
  24. package/lib/components/include.ts +292 -0
  25. package/lib/components/input.ts +166 -78
  26. package/lib/components/layout.ts +144 -18
  27. package/lib/components/list.ts +83 -74
  28. package/lib/components/loading.ts +263 -0
  29. package/lib/components/main.ts +43 -17
  30. package/lib/components/menu.ts +108 -24
  31. package/lib/components/modal.ts +50 -21
  32. package/lib/components/nav.ts +60 -18
  33. package/lib/components/paragraph.ts +111 -0
  34. package/lib/components/progress.ts +276 -0
  35. package/lib/components/radio.ts +236 -0
  36. package/lib/components/req.ts +300 -0
  37. package/lib/components/script.ts +33 -74
  38. package/lib/components/select.ts +247 -0
  39. package/lib/components/sidebar.ts +86 -36
  40. package/lib/components/style.ts +47 -70
  41. package/lib/components/switch.ts +261 -0
  42. package/lib/components/table.ts +47 -24
  43. package/lib/components/tabs.ts +105 -63
  44. package/lib/components/theme-toggle.ts +361 -0
  45. package/lib/components/token-calculator.ts +380 -0
  46. package/lib/components/tooltip.ts +244 -0
  47. package/lib/components/view.ts +36 -20
  48. package/lib/components/write.ts +284 -0
  49. package/lib/globals.d.ts +21 -0
  50. package/lib/jux.ts +172 -68
  51. package/lib/presets/notion.css +521 -0
  52. package/lib/presets/notion.jux +27 -0
  53. package/lib/reactivity/state.ts +364 -0
  54. package/machinery/compiler.js +126 -38
  55. package/machinery/generators/html.js +2 -3
  56. package/machinery/server.js +2 -2
  57. package/package.json +29 -3
  58. package/lib/components/import.ts +0 -430
  59. package/lib/components/node.ts +0 -200
  60. package/lib/components/reactivity.js +0 -104
  61. package/lib/components/theme.ts +0 -97
  62. package/lib/layouts/notion.css +0 -258
  63. package/lib/styles/base-theme.css +0 -186
  64. package/lib/styles/dark-theme.css +0 -144
  65. package/lib/styles/light-theme.css +0 -144
  66. package/lib/styles/tokens/dark.css +0 -86
  67. package/lib/styles/tokens/light.css +0 -86
  68. package/lib/templates/index.juxt +0 -33
  69. package/lib/themes/dark.css +0 -86
  70. package/lib/themes/light.css +0 -86
  71. /package/lib/{styles → presets}/global.css +0 -0
@@ -0,0 +1,228 @@
1
+ import { getOrCreateContainer } from './helpers.js';
2
+ import { State } from '../reactivity/state.js';
3
+
4
+ /**
5
+ * Checkbox component options
6
+ */
7
+ export interface CheckboxOptions {
8
+ label?: string;
9
+ checked?: boolean;
10
+ disabled?: boolean;
11
+ name?: string;
12
+ value?: string;
13
+ onChange?: (checked: boolean) => void;
14
+ style?: string;
15
+ class?: string;
16
+ }
17
+
18
+ /**
19
+ * Checkbox component state
20
+ */
21
+ type CheckboxState = {
22
+ label: string;
23
+ checked: boolean;
24
+ disabled: boolean;
25
+ name: string;
26
+ value: string;
27
+ style: string;
28
+ class: string;
29
+ };
30
+
31
+ /**
32
+ * Checkbox component - Boolean input with label
33
+ *
34
+ * Usage:
35
+ * jux.checkbox('agree', {
36
+ * label: 'I agree to the terms',
37
+ * checked: false,
38
+ * onChange: (checked) => console.log(checked)
39
+ * }).render('#form');
40
+ *
41
+ * // Two-way binding
42
+ * const agreedState = state(false);
43
+ * jux.checkbox('agree').label('I agree').bind(agreedState).render('#form');
44
+ */
45
+ export class Checkbox {
46
+ state: CheckboxState;
47
+ container: HTMLElement | null = null;
48
+ _id: string;
49
+ id: string;
50
+ private _onChange?: (checked: boolean) => void;
51
+ private _boundState?: State<boolean>;
52
+
53
+ constructor(id: string, options: CheckboxOptions = {}) {
54
+ this._id = id;
55
+ this.id = id;
56
+ this._onChange = options.onChange;
57
+
58
+ this.state = {
59
+ label: options.label ?? '',
60
+ checked: options.checked ?? false,
61
+ disabled: options.disabled ?? false,
62
+ name: options.name ?? id,
63
+ value: options.value ?? 'on',
64
+ style: options.style ?? '',
65
+ class: options.class ?? ''
66
+ };
67
+ }
68
+
69
+ /* -------------------------
70
+ * Fluent API
71
+ * ------------------------- */
72
+
73
+ label(value: string): this {
74
+ this.state.label = value;
75
+ return this;
76
+ }
77
+
78
+ checked(value: boolean): this {
79
+ this.state.checked = value;
80
+ this._updateElement();
81
+ return this;
82
+ }
83
+
84
+ disabled(value: boolean): this {
85
+ this.state.disabled = value;
86
+ this._updateElement();
87
+ return this;
88
+ }
89
+
90
+ name(value: string): this {
91
+ this.state.name = value;
92
+ return this;
93
+ }
94
+
95
+ value(value: string): this {
96
+ this.state.value = value;
97
+ return this;
98
+ }
99
+
100
+ style(value: string): this {
101
+ this.state.style = value;
102
+ return this;
103
+ }
104
+
105
+ class(value: string): this {
106
+ this.state.class = value;
107
+ return this;
108
+ }
109
+
110
+ onChange(handler: (checked: boolean) => void): this {
111
+ this._onChange = handler;
112
+ return this;
113
+ }
114
+
115
+ /**
116
+ * Two-way binding to state
117
+ */
118
+ bind(stateObj: State<boolean>): this {
119
+ this._boundState = stateObj;
120
+
121
+ // Update checkbox when state changes
122
+ stateObj.subscribe((val) => {
123
+ this.state.checked = val;
124
+ this._updateElement();
125
+ });
126
+
127
+ // Update state when checkbox changes
128
+ this.onChange((checked) => stateObj.set(checked));
129
+
130
+ return this;
131
+ }
132
+
133
+ /* -------------------------
134
+ * Helpers
135
+ * ------------------------- */
136
+
137
+ private _updateElement(): void {
138
+ const input = document.getElementById(`${this._id}-input`) as HTMLInputElement;
139
+ if (input) {
140
+ input.checked = this.state.checked;
141
+ input.disabled = this.state.disabled;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Get current checked state
147
+ */
148
+ isChecked(): boolean {
149
+ return this.state.checked;
150
+ }
151
+
152
+ /* -------------------------
153
+ * Render
154
+ * ------------------------- */
155
+
156
+ render(targetId?: string): this {
157
+ let container: HTMLElement;
158
+
159
+ if (targetId) {
160
+ const target = document.querySelector(targetId);
161
+ if (!target || !(target instanceof HTMLElement)) {
162
+ throw new Error(`Checkbox: Target element "${targetId}" not found`);
163
+ }
164
+ container = target;
165
+ } else {
166
+ container = getOrCreateContainer(this._id);
167
+ }
168
+
169
+ this.container = container;
170
+ const { label, checked, disabled, name, value, style, class: className } = this.state;
171
+
172
+ const wrapper = document.createElement('div');
173
+ wrapper.className = 'jux-checkbox';
174
+ wrapper.id = this._id;
175
+
176
+ if (className) {
177
+ wrapper.className += ` ${className}`;
178
+ }
179
+
180
+ if (style) {
181
+ wrapper.setAttribute('style', style);
182
+ }
183
+
184
+ const labelEl = document.createElement('label');
185
+ labelEl.className = 'jux-checkbox-label';
186
+
187
+ const input = document.createElement('input');
188
+ input.type = 'checkbox';
189
+ input.className = 'jux-checkbox-input';
190
+ input.id = `${this._id}-input`;
191
+ input.name = name;
192
+ input.value = value;
193
+ input.checked = checked;
194
+ input.disabled = disabled;
195
+
196
+ input.addEventListener('change', (e) => {
197
+ const target = e.target as HTMLInputElement;
198
+ this.state.checked = target.checked;
199
+ if (this._onChange) {
200
+ this._onChange(target.checked);
201
+ }
202
+ });
203
+
204
+ labelEl.appendChild(input);
205
+
206
+ if (label) {
207
+ const span = document.createElement('span');
208
+ span.className = 'jux-checkbox-text';
209
+ span.textContent = label;
210
+ labelEl.appendChild(span);
211
+ }
212
+
213
+ wrapper.appendChild(labelEl);
214
+ container.appendChild(wrapper);
215
+ return this;
216
+ }
217
+
218
+ renderTo(juxComponent: any): this {
219
+ if (!juxComponent?._id) {
220
+ throw new Error('Checkbox.renderTo: Invalid component');
221
+ }
222
+ return this.render(`#${juxComponent._id}`);
223
+ }
224
+ }
225
+
226
+ export function checkbox(id: string, options: CheckboxOptions = {}): Checkbox {
227
+ return new Checkbox(id, options);
228
+ }
@@ -1,4 +1,4 @@
1
- import { Reactive, getOrCreateContainer } from './reactivity.js';
1
+ import { getOrCreateContainer } from './helpers.js';
2
2
 
3
3
  /**
4
4
  * Code component options
@@ -6,11 +6,10 @@ import { Reactive, getOrCreateContainer } from './reactivity.js';
6
6
  export interface CodeOptions {
7
7
  code?: string;
8
8
  language?: string;
9
- title?: string | null;
10
- showLineNumbers?: boolean;
11
- highlightLines?: number[];
12
- maxHeight?: string | null;
13
- wrap?: boolean;
9
+ lineNumbers?: boolean;
10
+ highlight?: boolean;
11
+ style?: string;
12
+ class?: string;
14
13
  }
15
14
 
16
15
  /**
@@ -19,48 +18,42 @@ export interface CodeOptions {
19
18
  type CodeState = {
20
19
  code: string;
21
20
  language: string;
22
- title: string | null;
23
- showLineNumbers: boolean;
24
- highlightLines: number[];
25
- maxHeight: string | null;
26
- wrap: boolean;
21
+ lineNumbers: boolean;
22
+ highlight: boolean;
23
+ style: string;
24
+ class: string;
27
25
  };
28
26
 
29
27
  /**
30
- * Code component - displays formatted code blocks with syntax highlighting
28
+ * Code component
31
29
  *
32
30
  * Usage:
33
- * const codeBlock = jux.code('myCode', {
34
- * code: 'const x = 42;',
35
- * language: 'javascript',
36
- * title: 'Example Code',
37
- * showLineNumbers: true
38
- * });
39
- * codeBlock.render();
31
+ * const code = jux.code('myCode', 'console.log("hello")', 'javascript');
32
+ * code.render();
40
33
  */
41
- export class Code extends Reactive {
42
- state!: CodeState;
34
+ export class Code {
35
+ state: CodeState;
43
36
  container: HTMLElement | null = null;
44
-
45
- constructor(componentId: string, options: CodeOptions = {}) {
46
- super();
47
- this._setComponentId(componentId);
48
-
49
- // Map 'jux' language to 'javascript' for syntax highlighting
50
- let language = options.language || 'javascript';
51
- if (language === 'jux') {
52
- language = 'javascript';
53
- }
54
-
55
- this.state = this._createReactiveState({
56
- code: options.code ?? '',
57
- language: language,
58
- title: options.title ?? null,
59
- showLineNumbers: options.showLineNumbers ?? false,
60
- highlightLines: options.highlightLines ?? [],
61
- maxHeight: options.maxHeight ?? null,
62
- wrap: options.wrap ?? false
63
- }) as CodeState;
37
+ _id: string;
38
+ id: string;
39
+
40
+ constructor(
41
+ id: string,
42
+ content?: string,
43
+ language?: string,
44
+ options: CodeOptions = {}
45
+ ) {
46
+ this._id = id;
47
+ this.id = id;
48
+
49
+ this.state = {
50
+ code: content ?? options.code ?? '',
51
+ language: language ?? options.language ?? 'javascript',
52
+ lineNumbers: options.lineNumbers ?? false,
53
+ highlight: options.highlight ?? true,
54
+ style: options.style ?? '',
55
+ class: options.class ?? ''
56
+ };
64
57
  }
65
58
 
66
59
  /* -------------------------
@@ -73,76 +66,37 @@ export class Code extends Reactive {
73
66
  }
74
67
 
75
68
  language(value: string): this {
76
- this.state.language = value === 'jux' ? 'javascript' : value;
69
+ this.state.language = value;
77
70
  return this;
78
71
  }
79
72
 
80
- title(value: string): this {
81
- this.state.title = value;
73
+ lineNumbers(value: boolean): this {
74
+ this.state.lineNumbers = value;
82
75
  return this;
83
76
  }
84
77
 
85
- showLineNumbers(value: boolean): this {
86
- this.state.showLineNumbers = value;
78
+ highlight(value: boolean): this {
79
+ this.state.highlight = value;
87
80
  return this;
88
81
  }
89
82
 
90
- highlightLines(value: number[]): this {
91
- this.state.highlightLines = value;
83
+ style(value: string): this {
84
+ this.state.style = value;
92
85
  return this;
93
86
  }
94
87
 
95
- maxHeight(value: string): this {
96
- this.state.maxHeight = value;
88
+ class(value: string): this {
89
+ this.state.class = value;
97
90
  return this;
98
91
  }
99
92
 
100
- wrap(value: boolean): this {
101
- this.state.wrap = value;
102
- return this;
103
- }
104
-
105
- /* -------------------------
106
- * Helpers
107
- * ------------------------- */
108
-
109
- private _escapeHtml(text: string): string {
110
- const div = document.createElement('div');
111
- div.textContent = text;
112
- return div.innerHTML;
113
- }
114
-
115
- private _formatCode(): string {
116
- const { code, showLineNumbers, highlightLines } = this.state;
117
- const lines = code.split('\n');
118
-
119
- if (!showLineNumbers) {
120
- return `<code class="jux-code-block">${this._escapeHtml(code)}</code>`;
121
- }
122
-
123
- const formattedLines = lines
124
- .map((line, index) => {
125
- const lineNum = index + 1;
126
- const isHighlighted = highlightLines.includes(lineNum);
127
- const highlightClass = isHighlighted ? ' jux-code-line-highlighted' : '';
128
-
129
- return `<div class="jux-code-line${highlightClass}">
130
- <span class="jux-code-line-number">${lineNum}</span>
131
- <span class="jux-code-line-content">${this._escapeHtml(line) || ' '}</span>
132
- </div>`;
133
- })
134
- .join('');
135
-
136
- return `<code class="jux-code-block jux-code-numbered">${formattedLines}</code>`;
137
- }
138
-
139
93
  /* -------------------------
140
94
  * Render
141
95
  * ------------------------- */
142
96
 
143
97
  render(targetId?: string): this {
144
98
  let container: HTMLElement;
145
-
99
+
146
100
  if (targetId) {
147
101
  const target = document.querySelector(targetId);
148
102
  if (!target || !(target instanceof HTMLElement)) {
@@ -150,71 +104,31 @@ export class Code extends Reactive {
150
104
  }
151
105
  container = target;
152
106
  } else {
153
- container = getOrCreateContainer(this._componentId) as HTMLElement;
107
+ container = getOrCreateContainer(this._id);
154
108
  }
155
-
156
- this.container = container;
157
- const { language, title, maxHeight, wrap } = this.state;
158
-
159
- container.innerHTML = '';
160
- container.className = 'jux-code-container';
161
- container.id = this._componentId;
162
-
163
- // Title bar
164
- if (title) {
165
- const titleBar = document.createElement('div');
166
- titleBar.className = 'jux-code-title-bar';
167
109
 
168
- const titleEl = document.createElement('div');
169
- titleEl.className = 'jux-code-title';
170
- titleEl.textContent = title;
171
-
172
- const langBadge = document.createElement('div');
173
- langBadge.className = 'jux-code-language-badge';
174
- langBadge.textContent = language;
110
+ this.container = container;
111
+ const { code, language, lineNumbers, highlight, style, class: className } = this.state;
175
112
 
176
- titleBar.appendChild(titleEl);
177
- titleBar.appendChild(langBadge);
178
- container.appendChild(titleBar);
179
- }
113
+ const pre = document.createElement('pre');
114
+ pre.className = lineNumbers ? 'jux-code line-numbers' : 'jux-code';
115
+ pre.id = this._id;
180
116
 
181
- // Code block wrapper
182
- const codeWrapper = document.createElement('div');
183
- codeWrapper.className = 'jux-code-wrapper';
184
- if (maxHeight) {
185
- codeWrapper.style.maxHeight = maxHeight;
186
- codeWrapper.style.overflowY = 'auto';
117
+ if (className) {
118
+ pre.className += ` ${className}`;
187
119
  }
188
120
 
189
- // Pre element
190
- const preEl = document.createElement('pre');
191
- preEl.className = `jux-code-pre language-${language}`;
192
- if (wrap) {
193
- preEl.classList.add('jux-code-wrap');
121
+ if (style) {
122
+ pre.setAttribute('style', style);
194
123
  }
195
124
 
196
- preEl.innerHTML = this._formatCode();
197
-
198
- codeWrapper.appendChild(preEl);
199
- container.appendChild(codeWrapper);
125
+ const codeEl = document.createElement('code');
126
+ codeEl.className = highlight ? `language-${language}` : '';
127
+ codeEl.textContent = code;
200
128
 
201
- // Copy button
202
- const copyBtn = document.createElement('button');
203
- copyBtn.className = 'jux-code-copy-btn';
204
- copyBtn.textContent = 'Copy';
205
- container.appendChild(copyBtn);
129
+ pre.appendChild(codeEl);
130
+ container.appendChild(pre);
206
131
 
207
- // Event binding - copy functionality
208
- copyBtn.addEventListener('click', () => {
209
- navigator.clipboard.writeText(this.state.code).then(() => {
210
- copyBtn.textContent = 'Copied!';
211
- setTimeout(() => {
212
- copyBtn.textContent = 'Copy';
213
- }, 2000);
214
- });
215
- });
216
-
217
- this.emit('rendered');
218
132
  return this;
219
133
  }
220
134
 
@@ -225,18 +139,18 @@ export class Code extends Reactive {
225
139
  if (!juxComponent || typeof juxComponent !== 'object') {
226
140
  throw new Error('Code.renderTo: Invalid component - not an object');
227
141
  }
228
-
229
- if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
230
- throw new Error('Code.renderTo: Invalid component - missing _componentId (not a Jux component)');
142
+
143
+ if (!juxComponent._id || typeof juxComponent._id !== 'string') {
144
+ throw new Error('Code.renderTo: Invalid component - missing _id (not a Jux component)');
231
145
  }
232
-
233
- return this.render(`#${juxComponent._componentId}`);
146
+
147
+ return this.render(`#${juxComponent._id}`);
234
148
  }
235
149
  }
236
150
 
237
151
  /**
238
152
  * Factory helper
239
153
  */
240
- export function code(componentId: string, options: CodeOptions = {}): Code {
241
- return new Code(componentId, options);
154
+ export function code(id: string, content?: string, language?: string): Code {
155
+ return new Code(id, content, language);
242
156
  }