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
@@ -1,124 +1,258 @@
1
1
  import { ErrorHandler } from './error-handler.js';
2
2
 
3
+ /**
4
+ * App component options
5
+ */
6
+ export interface AppOptions {
7
+ title?: string;
8
+ theme?: 'light' | 'dark' | 'auto';
9
+ favicon?: string;
10
+ meta?: Record<string, string>;
11
+ stylesheets?: string[];
12
+ scripts?: string[];
13
+ layout?: {
14
+ header?: boolean | string;
15
+ subheader?: boolean | string;
16
+ sidebar?: boolean | string;
17
+ main?: boolean | string;
18
+ aside?: boolean | string;
19
+ footer?: boolean | string;
20
+ };
21
+ }
22
+
23
+ /**
24
+ * App component state
25
+ */
26
+ type AppState = {
27
+ title: string;
28
+ theme: string;
29
+ favicon: string;
30
+ meta: Map<string, string>;
31
+ stylesheets: string[];
32
+ scripts: string[];
33
+ layout: {
34
+ header: boolean | string;
35
+ subheader: boolean | string;
36
+ sidebar: boolean | string;
37
+ main: boolean | string;
38
+ aside: boolean | string;
39
+ footer: boolean | string;
40
+ };
41
+ };
42
+
3
43
  /**
4
44
  * App - Configure application-level settings
5
45
  * Manages document metadata, theme, and global configuration
46
+ *
47
+ * Usage:
48
+ * jux.app('myApp').setup({
49
+ * title: 'My App',
50
+ * theme: 'dark',
51
+ * favicon: '/favicon.ico',
52
+ * stylesheets: [
53
+ * '/lib/layouts/notion.css',
54
+ * '/lib/presets/light.css'
55
+ * ],
56
+ * layout: {
57
+ * header: true,
58
+ * sidebar: true,
59
+ * main: true,
60
+ * footer: true
61
+ * }
62
+ * });
63
+ *
64
+ * // Or fluent
65
+ * jux.app('myApp')
66
+ * .title('My App')
67
+ * .theme('dark')
68
+ * .addStylesheet('/presets/main.css')
69
+ * .render();
6
70
  */
7
71
  export class App {
8
- private _title: string = '';
9
- private _theme: 'light' | 'dark' | 'auto' = 'auto';
10
- private _favicon: string = '';
11
- private _meta: Map<string, string> = new Map();
12
-
13
- title(title: string): this {
14
- this._title = title;
15
- this.applyTitle();
72
+ state: AppState;
73
+ _componentId: string;
74
+ id: string;
75
+
76
+ constructor(id: string, options?: AppOptions) {
77
+ this._componentId = id;
78
+ this.id = id;
79
+
80
+ const opts = options || {};
81
+
82
+ this.state = {
83
+ title: opts.title ?? '',
84
+ theme: opts.theme ?? 'auto',
85
+ favicon: opts.favicon ?? '',
86
+ meta: new Map(Object.entries(opts.meta || {})),
87
+ stylesheets: opts.stylesheets || [],
88
+ scripts: opts.scripts || [],
89
+ layout: {
90
+ header: opts.layout?.header ?? false,
91
+ subheader: opts.layout?.subheader ?? false,
92
+ sidebar: opts.layout?.sidebar ?? false,
93
+ main: opts.layout?.main ?? true,
94
+ aside: opts.layout?.aside ?? false,
95
+ footer: opts.layout?.footer ?? false
96
+ }
97
+ };
98
+ }
99
+
100
+ /* -------------------------
101
+ * Fluent API
102
+ * ------------------------- */
103
+
104
+ title(value: string): this {
105
+ this.state.title = value;
16
106
  return this;
17
107
  }
18
108
 
19
- theme(theme: 'light' | 'dark' | 'auto'): this {
20
- this._theme = theme;
21
- this.applyTheme();
109
+ theme(value: 'light' | 'dark' | 'auto'): this {
110
+ this.state.theme = value;
22
111
  return this;
23
112
  }
24
113
 
25
- favicon(url: string): this {
26
- this._favicon = url;
27
- this.applyFavicon();
114
+ favicon(value: string): this {
115
+ this.state.favicon = value;
28
116
  return this;
29
117
  }
30
118
 
31
119
  meta(name: string, content: string): this {
32
- this._meta.set(name, content);
33
- this.applyMeta(name, content);
120
+ this.state.meta.set(name, content);
34
121
  return this;
35
122
  }
36
123
 
37
- private applyTitle(): void {
38
- if (typeof document === 'undefined') return;
39
-
40
- try {
41
- document.title = this._title;
42
- } catch (error: any) {
43
- ErrorHandler.captureError({
44
- component: 'App',
45
- method: 'applyTitle',
46
- message: error.message,
47
- stack: error.stack,
48
- timestamp: new Date(),
49
- context: { title: this._title }
50
- });
124
+ addStylesheet(url: string): this {
125
+ if (!this.state.stylesheets.includes(url)) {
126
+ this.state.stylesheets.push(url);
51
127
  }
128
+ return this;
52
129
  }
53
130
 
54
- private applyTheme(): void {
55
- if (typeof document === 'undefined') return;
56
-
57
- try {
58
- document.body.setAttribute('data-theme', this._theme);
59
-
60
- // Add theme class for styling
61
- document.body.classList.remove('theme-light', 'theme-dark', 'theme-auto');
62
- document.body.classList.add(`theme-${this._theme}`);
63
- } catch (error: any) {
64
- ErrorHandler.captureError({
65
- component: 'App',
66
- method: 'applyTheme',
67
- message: error.message,
68
- stack: error.stack,
69
- timestamp: new Date(),
70
- context: { theme: this._theme }
71
- });
131
+ addScript(url: string): this {
132
+ if (!this.state.scripts.includes(url)) {
133
+ this.state.scripts.push(url);
72
134
  }
135
+ return this;
73
136
  }
74
137
 
75
- private applyFavicon(): void {
76
- if (typeof document === 'undefined') return;
77
-
78
- try {
79
- let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
80
-
81
- if (!link) {
82
- link = document.createElement('link');
83
- link.rel = 'icon';
84
- document.head.appendChild(link);
85
- }
86
-
87
- link.href = this._favicon;
88
- } catch (error: any) {
89
- ErrorHandler.captureError({
90
- component: 'App',
91
- method: 'applyFavicon',
92
- message: error.message,
93
- stack: error.stack,
94
- timestamp: new Date(),
95
- context: { favicon: this._favicon }
138
+ /**
139
+ * Setup app with comprehensive configuration
140
+ */
141
+ setup(options: AppOptions): this {
142
+ if (options.title) this.state.title = options.title;
143
+ if (options.theme) this.state.theme = options.theme;
144
+ if (options.favicon) this.state.favicon = options.favicon;
145
+
146
+ if (options.meta) {
147
+ Object.entries(options.meta).forEach(([name, content]) => {
148
+ this.state.meta.set(name, content);
96
149
  });
97
150
  }
151
+
152
+ if (options.stylesheets) {
153
+ options.stylesheets.forEach(url => this.addStylesheet(url));
154
+ }
155
+
156
+ if (options.scripts) {
157
+ options.scripts.forEach(url => this.addScript(url));
158
+ }
159
+
160
+ if (options.layout) {
161
+ this.state.layout = { ...this.state.layout, ...options.layout };
162
+ }
163
+
164
+ return this;
98
165
  }
99
166
 
100
- private applyMeta(name: string, content: string): void {
101
- if (typeof document === 'undefined') return;
102
-
167
+ /* -------------------------
168
+ * Render
169
+ * ------------------------- */
170
+
171
+ render(): this {
172
+ if (typeof document === 'undefined') return this;
173
+
103
174
  try {
104
- let meta = document.querySelector<HTMLMetaElement>(`meta[name="${name}"]`);
105
-
106
- if (!meta) {
107
- meta = document.createElement('meta');
108
- meta.name = name;
109
- document.head.appendChild(meta);
175
+ const { title, theme, favicon, meta, stylesheets, scripts } = this.state;
176
+
177
+ // Apply title
178
+ if (title) {
179
+ document.title = title;
180
+ }
181
+
182
+ // Apply theme
183
+ if (theme) {
184
+ document.body.setAttribute('data-theme', theme);
185
+ document.body.classList.remove('theme-light', 'theme-dark', 'theme-auto');
186
+ document.body.classList.add(`theme-${theme}`);
110
187
  }
111
-
112
- meta.content = content;
188
+
189
+ // Apply favicon
190
+ if (favicon) {
191
+ let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
192
+
193
+ if (!link) {
194
+ link = document.createElement('link');
195
+ link.rel = 'icon';
196
+ document.head.appendChild(link);
197
+ }
198
+
199
+ link.href = favicon;
200
+ }
201
+
202
+ // Apply meta tags
203
+ meta.forEach((content, name) => {
204
+ let metaEl = document.querySelector<HTMLMetaElement>(`meta[name="${name}"]`);
205
+
206
+ if (!metaEl) {
207
+ metaEl = document.createElement('meta');
208
+ metaEl.name = name;
209
+ document.head.appendChild(metaEl);
210
+ }
211
+
212
+ metaEl.content = content;
213
+ });
214
+
215
+ // Load stylesheets
216
+ stylesheets.forEach(url => {
217
+ if (!document.querySelector(`link[href="${url}"]`)) {
218
+ const link = document.createElement('link');
219
+ link.rel = 'stylesheet';
220
+ link.href = url;
221
+ document.head.appendChild(link);
222
+ }
223
+ });
224
+
225
+ // Load scripts
226
+ scripts.forEach(url => {
227
+ if (!document.querySelector(`script[src="${url}"]`)) {
228
+ const script = document.createElement('script');
229
+ script.src = url;
230
+ document.head.appendChild(script);
231
+ }
232
+ });
233
+
113
234
  } catch (error: any) {
114
235
  ErrorHandler.captureError({
115
236
  component: 'App',
116
- method: 'applyMeta',
237
+ method: 'render',
117
238
  message: error.message,
118
239
  stack: error.stack,
119
240
  timestamp: new Date(),
120
- context: { name, content }
241
+ context: {
242
+ title: this.state.title,
243
+ theme: this.state.theme,
244
+ error: 'runtime_exception'
245
+ }
121
246
  });
122
247
  }
248
+
249
+ return this;
123
250
  }
251
+ }
252
+
253
+ /**
254
+ * Factory helper
255
+ */
256
+ export function app(id: string, options?: AppOptions): App {
257
+ return new App(id, options);
124
258
  }
@@ -0,0 +1,164 @@
1
+ import { getOrCreateContainer } from './helpers.js';
2
+
3
+ /**
4
+ * Badge component options
5
+ */
6
+ export interface BadgeOptions {
7
+ text?: string;
8
+ variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
9
+ size?: 'sm' | 'md' | 'lg';
10
+ pill?: boolean;
11
+ style?: string;
12
+ class?: string;
13
+ }
14
+
15
+ /**
16
+ * Badge component state
17
+ */
18
+ type BadgeState = {
19
+ text: string;
20
+ variant: string;
21
+ size: string;
22
+ pill: boolean;
23
+ style: string;
24
+ class: string;
25
+ };
26
+
27
+ /**
28
+ * Badge component - Status indicators, counts, labels
29
+ *
30
+ * Usage:
31
+ * jux.badge('status', {
32
+ * text: 'Active',
33
+ * variant: 'success',
34
+ * pill: true
35
+ * }).render('#card');
36
+ *
37
+ * jux.badge('count', { text: '5' }).render('#notifications');
38
+ */
39
+ export class Badge {
40
+ state: BadgeState;
41
+ container: HTMLElement | null = null;
42
+ _id: string;
43
+ id: string;
44
+
45
+ constructor(id: string, options: BadgeOptions = {}) {
46
+ this._id = id;
47
+ this.id = id;
48
+
49
+ this.state = {
50
+ text: options.text ?? '',
51
+ variant: options.variant ?? 'default',
52
+ size: options.size ?? 'md',
53
+ pill: options.pill ?? false,
54
+ style: options.style ?? '',
55
+ class: options.class ?? ''
56
+ };
57
+ }
58
+
59
+ /* -------------------------
60
+ * Fluent API
61
+ * ------------------------- */
62
+
63
+ text(value: string): this {
64
+ this.state.text = value;
65
+ this._updateElement();
66
+ return this;
67
+ }
68
+
69
+ variant(value: 'default' | 'success' | 'warning' | 'error' | 'info'): this {
70
+ this.state.variant = value;
71
+ this._updateElement();
72
+ return this;
73
+ }
74
+
75
+ size(value: 'sm' | 'md' | 'lg'): this {
76
+ this.state.size = value;
77
+ return this;
78
+ }
79
+
80
+ pill(value: boolean): this {
81
+ this.state.pill = value;
82
+ return this;
83
+ }
84
+
85
+ style(value: string): this {
86
+ this.state.style = value;
87
+ return this;
88
+ }
89
+
90
+ class(value: string): this {
91
+ this.state.class = value;
92
+ return this;
93
+ }
94
+
95
+ /* -------------------------
96
+ * Helpers
97
+ * ------------------------- */
98
+
99
+ private _updateElement(): void {
100
+ const element = document.getElementById(this._id);
101
+ if (element) {
102
+ element.textContent = this.state.text;
103
+ element.className = `jux-badge jux-badge-${this.state.variant} jux-badge-${this.state.size}`;
104
+ if (this.state.pill) {
105
+ element.classList.add('jux-badge-pill');
106
+ }
107
+ if (this.state.class) {
108
+ element.className += ` ${this.state.class}`;
109
+ }
110
+ }
111
+ }
112
+
113
+ /* -------------------------
114
+ * Render
115
+ * ------------------------- */
116
+
117
+ render(targetId?: string): this {
118
+ let container: HTMLElement;
119
+
120
+ if (targetId) {
121
+ const target = document.querySelector(targetId);
122
+ if (!target || !(target instanceof HTMLElement)) {
123
+ throw new Error(`Badge: Target element "${targetId}" not found`);
124
+ }
125
+ container = target;
126
+ } else {
127
+ container = getOrCreateContainer(this._id);
128
+ }
129
+
130
+ this.container = container;
131
+ const { text, variant, size, pill, style, class: className } = this.state;
132
+
133
+ const badge = document.createElement('span');
134
+ badge.className = `jux-badge jux-badge-${variant} jux-badge-${size}`;
135
+ badge.id = this._id;
136
+ badge.textContent = text;
137
+
138
+ if (pill) {
139
+ badge.classList.add('jux-badge-pill');
140
+ }
141
+
142
+ if (className) {
143
+ badge.className += ` ${className}`;
144
+ }
145
+
146
+ if (style) {
147
+ badge.setAttribute('style', style);
148
+ }
149
+
150
+ container.appendChild(badge);
151
+ return this;
152
+ }
153
+
154
+ renderTo(juxComponent: any): this {
155
+ if (!juxComponent?._id) {
156
+ throw new Error('Badge.renderTo: Invalid component');
157
+ }
158
+ return this.render(`#${juxComponent._id}`);
159
+ }
160
+ }
161
+
162
+ export function badge(id: string, options: BadgeOptions = {}): Badge {
163
+ return new Badge(id, options);
164
+ }