juxscript 1.0.7 → 1.0.9

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/README.md CHANGED
@@ -7,25 +7,33 @@
7
7
  + Stay tuned! For now, you will see our roadmap here!
8
8
 
9
9
  ```
10
+ - [X] Router
11
+ - [ ] Cross Page Store.
12
+ - [ ] Distributable Bundle (Static Sites)
13
+ - [ ] Tree Shake/Efficiencies.
14
+
10
15
  - [X] Layouts (100% done.)
16
+ - [ ] *Authoring Layout Pages* - `docs`
17
+ - [ ] *Authoring Application Pages* - `docs`
18
+ - [ ] *Authoring Custom Components* - `docs`
19
+
11
20
  - [ ] Render Dependency Tree
12
21
  > Idea here is, one element may be a predicate for another. Will need promises. **predicting problems with slow-loading components that depend on containers from other components. May to to separate concerns with container "building" vs. content addition OR use async processes (promises).
13
22
  - [X] Reactivity (90% done.)
14
23
  - [ ] Client Components (99% of what would be needed.)
15
- - [ ] Charts
24
+ - [X] Charts
16
25
  - [ ] Poor Intellisense support? Could be this issue.
17
26
  - [ ] Api Wrapper
18
27
  - [X] Params/Active State for Menu/Nav matching - built in.
19
28
  - [ ] CDN Bundle (import CDN/'state', 'jux' from cdn.)
20
29
  - [ ] Icon
21
- - [ ] Cross Page Store.
30
+
22
31
  - [ ] Quickstart Boilerplates (20% done,notion.jux)
23
32
  - [ ] Mobile Nav
24
33
  - [ ] `npx jux present notion|default` etc..
25
34
  - [ ] Server side components (api and database)
26
35
  - [ ] Quick deploy option
27
- - [ ] Distributable Bundle (Static Sites)
28
- - [ ] Tree Shake/Efficiencies.
36
+
29
37
 
30
38
  ## *JUX* Authoring UI's in pure javascript.
31
39
 
package/bin/cli.js CHANGED
@@ -183,33 +183,59 @@ async function buildProject(isServe = false) {
183
183
  // Create structure
184
184
  fs.mkdirSync(juxDir, { recursive: true });
185
185
 
186
- // Copy template file from lib/presets/index.juxt
187
- const templatePath = path.join(PATHS.packageRoot, 'lib', 'presets', 'index.juxt');
188
- const targetPath = path.join(juxDir, 'index.jux');
189
-
190
- if (fs.existsSync(templatePath)) {
191
- fs.copyFileSync(templatePath, targetPath);
192
- console.log('✅ Created jux/index.jux from template');
193
- } else {
194
- // Fallback if template doesn't exist
195
- console.warn('⚠️ Template not found, creating basic index.jux');
196
- const fallbackContent = `// Welcome to JUX!
197
- import { jux } from '/lib/jux.js';
186
+ // Create index.jux with proper imports
187
+ const indexContent = `// Welcome to JUX!
188
+ import { jux } from 'juxscript';
198
189
 
199
- jux.style('/lib/layouts/default.css');
200
- jux.theme('light');
190
+ // Apply a layout preset (optional)
191
+ // import 'juxscript/presets/notion.js';
201
192
 
202
- const header = jux.header('header').render("#app");
203
- const main = jux.main('main').render("#app");
204
- const footer = jux.footer('footer').render("#app");
193
+ // Create your app structure
194
+ jux.container('app-container')
195
+ .direction('column')
196
+ .gap(20)
197
+ .style('padding: 40px;')
198
+ .render('body');
205
199
 
206
- jux.hero('hero1', {
200
+ jux.hero('welcome-hero', {
207
201
  title: 'Welcome to JUX',
208
202
  subtitle: 'A JavaScript UX authorship platform'
209
- }).render('#main');
203
+ }).render('#app-container');
204
+
205
+ jux.divider({}).render('#app-container');
206
+
207
+ jux.write(\`
208
+ <h2>Getting Started</h2>
209
+ <p>Edit <code>jux/index.jux</code> to build your app.</p>
210
+ <ul>
211
+ <li>Run <code>npx jux build</code> to compile</li>
212
+ <li>Run <code>npx jux serve</code> for dev mode</li>
213
+ <li>Serve <code>jux-dist/</code> from your backend</li>
214
+ </ul>
215
+ \`).render('#app-container');
210
216
  `;
211
- fs.writeFileSync(targetPath, fallbackContent);
212
- console.log('✅ Created jux/index.jux');
217
+
218
+ const targetPath = path.join(juxDir, 'index.jux');
219
+ fs.writeFileSync(targetPath, indexContent);
220
+ console.log('✅ Created jux/index.jux');
221
+
222
+ // Create package.json if it doesn't exist
223
+ const pkgPath = path.join(PATHS.projectRoot, 'package.json');
224
+ if (!fs.existsSync(pkgPath)) {
225
+ const pkgContent = {
226
+ "name": "my-jux-project",
227
+ "version": "1.0.0",
228
+ "type": "module",
229
+ "scripts": {
230
+ "build": "jux build",
231
+ "serve": "jux serve"
232
+ },
233
+ "dependencies": {
234
+ "juxscript": "^1.0.8"
235
+ }
236
+ };
237
+ fs.writeFileSync(pkgPath, JSON.stringify(pkgContent, null, 2));
238
+ console.log('✅ Created package.json');
213
239
  }
214
240
 
215
241
  // Create .gitignore
@@ -226,9 +252,10 @@ node_modules/
226
252
 
227
253
  console.log('✅ Created jux/ directory\n');
228
254
  console.log('Next steps:');
229
- console.log(' 1. Edit jux/index.jux');
230
- console.log(' 2. Run: npx jux build');
231
- console.log(' 3. Serve jux-dist/ from your backend\n');
255
+ console.log(' 1. npm install # Install juxscript');
256
+ console.log(' 2. Edit jux/index.jux # Build your app');
257
+ console.log(' 3. npx jux build # Compile to jux-dist/');
258
+ console.log(' 4. Serve jux-dist/ from your backend\n');
232
259
 
233
260
  } else if (command === 'build') {
234
261
  await buildProject(false);
@@ -249,24 +276,25 @@ Usage:
249
276
  npx jux build Compile .jux files from ./jux/ to ./jux-dist/
250
277
  npx jux serve [port] Start dev server with hot reload (default: 3000)
251
278
 
252
- Project Structure (Convention):
279
+ Project Structure:
253
280
  my-project/
254
- ├── jux/ # Your .jux source files (REQUIRED)
255
- │ ├── index.jux
256
- ├── samples/
257
- │ │ └── mypage.ts # TypeScript files transpiled to .js
258
- │ └── pages/
259
- ├── jux-dist/ # Build output (generated, git-ignore this)
260
- │ ├── samples/
261
- │ │ └── mypage.js # Transpiled TypeScript
262
- │ └── ...
263
- ├── server/ # Your backend (untouched by jux)
281
+ ├── jux/ # Your .jux source files
282
+ │ ├── index.jux # Entry point (uses 'juxscript' imports)
283
+ └── pages/ # Additional pages
284
+ ├── jux-dist/ # Build output (git-ignore this)
285
+ ├── server/ # Your backend
264
286
  └── package.json
265
287
 
288
+ Import Style:
289
+ // In your project's .jux files
290
+ import { jux, state } from 'juxscript';
291
+ import 'juxscript/presets/notion.js';
292
+
266
293
  Getting Started:
267
- 1. npx jux init # Create jux/ directory
268
- 2. npx jux build # Build to jux-dist/
269
- 3. Serve jux-dist/ from your backend
294
+ 1. npx jux init # Create project structure
295
+ 2. npm install # Install dependencies
296
+ 3. npx jux build # Build to jux-dist/
297
+ 4. Serve jux-dist/ from your backend
270
298
 
271
299
  Examples:
272
300
  npx jux build Build for production
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Divider - Simple horizontal or vertical divider line
3
+ */
4
+
5
+ export interface DividerOptions {
6
+ orientation?: 'horizontal' | 'vertical';
7
+ thickness?: number;
8
+ color?: string;
9
+ margin?: string;
10
+ style?: string;
11
+ className?: string;
12
+ }
13
+
14
+ /**
15
+ * Divider component for visual separation
16
+ *
17
+ * Usage:
18
+ * // Simple horizontal divider
19
+ * jux.divider().render('#container');
20
+ *
21
+ * // Vertical divider
22
+ * jux.divider({ orientation: 'vertical' }).render('#container');
23
+ *
24
+ * // Custom styling
25
+ * jux.divider({
26
+ * thickness: 2,
27
+ * color: '#3b82f6',
28
+ * margin: '24px 0'
29
+ * }).render('#container');
30
+ *
31
+ * // With custom class
32
+ * jux.divider({ className: 'my-divider' }).render('#container');
33
+ */
34
+ export class Divider {
35
+ private options: Required<DividerOptions>;
36
+
37
+ constructor(options: DividerOptions = {}) {
38
+ this.options = {
39
+ orientation: 'horizontal',
40
+ thickness: 1,
41
+ color: '#e5e7eb',
42
+ margin: '16px 0',
43
+ style: '',
44
+ className: '',
45
+ ...options
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Set orientation
51
+ */
52
+ orientation(value: 'horizontal' | 'vertical'): this {
53
+ this.options.orientation = value;
54
+ return this;
55
+ }
56
+
57
+ /**
58
+ * Set thickness in pixels
59
+ */
60
+ thickness(value: number): this {
61
+ this.options.thickness = value;
62
+ return this;
63
+ }
64
+
65
+ /**
66
+ * Set color
67
+ */
68
+ color(value: string): this {
69
+ this.options.color = value;
70
+ return this;
71
+ }
72
+
73
+ /**
74
+ * Set margin
75
+ */
76
+ margin(value: string): this {
77
+ this.options.margin = value;
78
+ return this;
79
+ }
80
+
81
+ /**
82
+ * Set custom styles
83
+ */
84
+ style(value: string): this {
85
+ this.options.style = value;
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Set class name
91
+ */
92
+ className(value: string): this {
93
+ this.options.className = value;
94
+ return this;
95
+ }
96
+
97
+ /**
98
+ * Render divider to target element
99
+ */
100
+ render(targetSelector: string): this {
101
+ const target = document.querySelector(targetSelector);
102
+
103
+ if (!target || !(target instanceof HTMLElement)) {
104
+ console.warn(`Divider: Target element "${targetSelector}" not found`);
105
+ return this;
106
+ }
107
+
108
+ const divider = document.createElement('hr');
109
+ divider.className = `jux-divider ${this.options.className}`.trim();
110
+
111
+ const isVertical = this.options.orientation === 'vertical';
112
+
113
+ const baseStyles = `
114
+ border: none;
115
+ background-color: ${this.options.color};
116
+ margin: ${this.options.margin};
117
+ ${isVertical ? `
118
+ width: ${this.options.thickness}px;
119
+ height: 100%;
120
+ display: inline-block;
121
+ vertical-align: middle;
122
+ ` : `
123
+ width: 100%;
124
+ height: ${this.options.thickness}px;
125
+ `}
126
+ ${this.options.style}
127
+ `;
128
+
129
+ divider.setAttribute('style', baseStyles);
130
+
131
+ target.appendChild(divider);
132
+
133
+ return this;
134
+ }
135
+
136
+ /**
137
+ * Replace target content with divider
138
+ */
139
+ replace(targetSelector: string): this {
140
+ const target = document.querySelector(targetSelector);
141
+
142
+ if (!target || !(target instanceof HTMLElement)) {
143
+ console.warn(`Divider: Target element "${targetSelector}" not found`);
144
+ return this;
145
+ }
146
+
147
+ target.innerHTML = '';
148
+ return this.render(targetSelector);
149
+ }
150
+
151
+ /**
152
+ * Render before target element
153
+ */
154
+ before(targetSelector: string): this {
155
+ const target = document.querySelector(targetSelector);
156
+
157
+ if (!target || !(target instanceof HTMLElement)) {
158
+ console.warn(`Divider: Target element "${targetSelector}" not found`);
159
+ return this;
160
+ }
161
+
162
+ const divider = document.createElement('hr');
163
+ divider.className = `jux-divider ${this.options.className}`.trim();
164
+
165
+ const isVertical = this.options.orientation === 'vertical';
166
+
167
+ const baseStyles = `
168
+ border: none;
169
+ background-color: ${this.options.color};
170
+ margin: ${this.options.margin};
171
+ ${isVertical ? `
172
+ width: ${this.options.thickness}px;
173
+ height: 100%;
174
+ display: inline-block;
175
+ vertical-align: middle;
176
+ ` : `
177
+ width: 100%;
178
+ height: ${this.options.thickness}px;
179
+ `}
180
+ ${this.options.style}
181
+ `;
182
+
183
+ divider.setAttribute('style', baseStyles);
184
+ target.parentNode?.insertBefore(divider, target);
185
+
186
+ return this;
187
+ }
188
+
189
+ /**
190
+ * Render after target element
191
+ */
192
+ after(targetSelector: string): this {
193
+ const target = document.querySelector(targetSelector);
194
+
195
+ if (!target || !(target instanceof HTMLElement)) {
196
+ console.warn(`Divider: Target element "${targetSelector}" not found`);
197
+ return this;
198
+ }
199
+
200
+ const divider = document.createElement('hr');
201
+ divider.className = `jux-divider ${this.options.className}`.trim();
202
+
203
+ const isVertical = this.options.orientation === 'vertical';
204
+
205
+ const baseStyles = `
206
+ border: none;
207
+ background-color: ${this.options.color};
208
+ margin: ${this.options.margin};
209
+ ${isVertical ? `
210
+ width: ${this.options.thickness}px;
211
+ height: 100%;
212
+ display: inline-block;
213
+ vertical-align: middle;
214
+ ` : `
215
+ width: 100%;
216
+ height: ${this.options.thickness}px;
217
+ `}
218
+ ${this.options.style}
219
+ `;
220
+
221
+ divider.setAttribute('style', baseStyles);
222
+ target.parentNode?.insertBefore(divider, target.nextSibling);
223
+
224
+ return this;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Factory function for quick divider creation
230
+ */
231
+ export function divider(options: DividerOptions = {}): Divider {
232
+ return new Divider(options);
233
+ }
@@ -1553,6 +1553,75 @@
1553
1553
  ],
1554
1554
  "example": "jux.dialog('confirm-delete', {"
1555
1555
  },
1556
+ {
1557
+ "name": "Divider",
1558
+ "category": "UI Components",
1559
+ "description": "Divider - Simple horizontal or vertical divider line",
1560
+ "constructor": "jux.divider(options: DividerOptions = {})",
1561
+ "fluentMethods": [
1562
+ {
1563
+ "name": "orientation",
1564
+ "params": "(value)",
1565
+ "returns": "this",
1566
+ "description": "Set orientation"
1567
+ },
1568
+ {
1569
+ "name": "thickness",
1570
+ "params": "(value)",
1571
+ "returns": "this",
1572
+ "description": "Set thickness"
1573
+ },
1574
+ {
1575
+ "name": "color",
1576
+ "params": "(value)",
1577
+ "returns": "this",
1578
+ "description": "Set color"
1579
+ },
1580
+ {
1581
+ "name": "margin",
1582
+ "params": "(value)",
1583
+ "returns": "this",
1584
+ "description": "Set margin"
1585
+ },
1586
+ {
1587
+ "name": "style",
1588
+ "params": "(value)",
1589
+ "returns": "this",
1590
+ "description": "Set style"
1591
+ },
1592
+ {
1593
+ "name": "className",
1594
+ "params": "(value)",
1595
+ "returns": "this",
1596
+ "description": "Set className"
1597
+ },
1598
+ {
1599
+ "name": "render",
1600
+ "params": "(targetSelector)",
1601
+ "returns": "this",
1602
+ "description": "Set render"
1603
+ },
1604
+ {
1605
+ "name": "replace",
1606
+ "params": "(targetSelector)",
1607
+ "returns": "this",
1608
+ "description": "Set replace"
1609
+ },
1610
+ {
1611
+ "name": "before",
1612
+ "params": "(targetSelector)",
1613
+ "returns": "this",
1614
+ "description": "Set before"
1615
+ },
1616
+ {
1617
+ "name": "after",
1618
+ "params": "(targetSelector)",
1619
+ "returns": "this",
1620
+ "description": "Set after"
1621
+ }
1622
+ ],
1623
+ "example": "// Simple horizontal divider"
1624
+ },
1556
1625
  {
1557
1626
  "name": "Doughnutchart",
1558
1627
  "category": "UI Components",
@@ -2413,6 +2482,18 @@
2413
2482
  "returns": "this",
2414
2483
  "description": "Set animationDuration"
2415
2484
  },
2485
+ {
2486
+ "name": "borderRadius",
2487
+ "params": "(value)",
2488
+ "returns": "this",
2489
+ "description": "Set borderRadius"
2490
+ },
2491
+ {
2492
+ "name": "showAccentBar",
2493
+ "params": "(value)",
2494
+ "returns": "this",
2495
+ "description": "Set showAccentBar"
2496
+ },
2416
2497
  {
2417
2498
  "name": "class",
2418
2499
  "params": "(value)",
@@ -3431,5 +3512,5 @@
3431
3512
  }
3432
3513
  ],
3433
3514
  "version": "1.0.0",
3434
- "lastUpdated": "2026-01-21T04:25:36.636Z"
3515
+ "lastUpdated": "2026-01-21T20:25:28.145Z"
3435
3516
  }
@@ -0,0 +1,196 @@
1
+ import { getOrCreateContainer } from '../helpers.js';
2
+
3
+ /**
4
+ * Hero component options
5
+ */
6
+ export interface HeroOptions {
7
+ title?: string;
8
+ subtitle?: string;
9
+ cta?: string;
10
+ ctaLink?: string;
11
+ backgroundImage?: string;
12
+ variant?: 'default' | 'centered' | 'split';
13
+ style?: string;
14
+ class?: string;
15
+ }
16
+
17
+ /**
18
+ * Hero component state
19
+ */
20
+ type HeroState = {
21
+ title: string;
22
+ subtitle: string;
23
+ cta: string;
24
+ ctaLink: string;
25
+ backgroundImage: string;
26
+ variant: string;
27
+ style: string;
28
+ class: string;
29
+ };
30
+
31
+ /**
32
+ * Hero component
33
+ *
34
+ * Usage:
35
+ * const hero = jux.hero('myHero', {
36
+ * title: 'Welcome',
37
+ * subtitle: 'Get started today',
38
+ * cta: 'Learn More'
39
+ * });
40
+ * hero.render();
41
+ */
42
+ export class Hero1 {
43
+ state: HeroState;
44
+ container: HTMLElement | null = null;
45
+ _id: string;
46
+ id: string;
47
+
48
+ constructor(id: string, options: HeroOptions = {}) {
49
+ this._id = id;
50
+ this.id = id;
51
+
52
+ this.state = {
53
+ title: options.title ?? '',
54
+ subtitle: options.subtitle ?? '',
55
+ cta: options.cta ?? '',
56
+ ctaLink: options.ctaLink ?? '#',
57
+ backgroundImage: options.backgroundImage ?? '',
58
+ variant: options.variant ?? 'default',
59
+ style: options.style ?? '',
60
+ class: options.class ?? ''
61
+ };
62
+ }
63
+
64
+ /* -------------------------
65
+ * Fluent API
66
+ * ------------------------- */
67
+
68
+ title(value: string): this {
69
+ this.state.title = value;
70
+ return this;
71
+ }
72
+
73
+ subtitle(value: string): this {
74
+ this.state.subtitle = value;
75
+ return this;
76
+ }
77
+
78
+ cta(value: string): this {
79
+ this.state.cta = value;
80
+ return this;
81
+ }
82
+
83
+ ctaLink(value: string): this {
84
+ this.state.ctaLink = value;
85
+ return this;
86
+ }
87
+
88
+ backgroundImage(value: string): this {
89
+ this.state.backgroundImage = value;
90
+ return this;
91
+ }
92
+
93
+ variant(value: string): this {
94
+ this.state.variant = value;
95
+ return this;
96
+ }
97
+
98
+ style(value: string): this {
99
+ this.state.style = value;
100
+ return this;
101
+ }
102
+
103
+ class(value: string): this {
104
+ this.state.class = value;
105
+ return this;
106
+ }
107
+
108
+ /* -------------------------
109
+ * Render
110
+ * ------------------------- */
111
+
112
+ render(targetId?: string): this {
113
+ let container: HTMLElement;
114
+
115
+ if (targetId) {
116
+ const target = document.querySelector(targetId);
117
+ if (!target || !(target instanceof HTMLElement)) {
118
+ throw new Error(`Hero: Target element "${targetId}" not found`);
119
+ }
120
+ container = target;
121
+ } else {
122
+ container = getOrCreateContainer(this._id);
123
+ }
124
+
125
+ this.container = container;
126
+ const { title, subtitle, cta, ctaLink, backgroundImage, variant, style, class: className } = this.state;
127
+
128
+ const hero = document.createElement('div');
129
+ hero.className = `jux-hero jux-hero-${variant}`;
130
+ hero.id = this._id;
131
+
132
+ if (className) {
133
+ hero.className += ` ${className}`;
134
+ }
135
+
136
+ if (style) {
137
+ hero.setAttribute('style', style);
138
+ }
139
+
140
+ if (backgroundImage) {
141
+ hero.style.backgroundImage = `url(${backgroundImage})`;
142
+ }
143
+
144
+ const content = document.createElement('div');
145
+ content.className = 'jux-hero-content';
146
+
147
+ if (title) {
148
+ const titleEl = document.createElement('h1');
149
+ titleEl.className = 'jux-hero-title';
150
+ titleEl.textContent = title;
151
+ content.appendChild(titleEl);
152
+ }
153
+
154
+ if (subtitle) {
155
+ const subtitleEl = document.createElement('p');
156
+ subtitleEl.className = 'jux-hero-subtitle';
157
+ subtitleEl.textContent = subtitle;
158
+ content.appendChild(subtitleEl);
159
+ }
160
+
161
+ if (cta) {
162
+ const ctaEl = document.createElement('a');
163
+ ctaEl.className = 'jux-hero-cta jux-button jux-button-primary';
164
+ ctaEl.href = ctaLink;
165
+ ctaEl.textContent = cta;
166
+ content.appendChild(ctaEl);
167
+ }
168
+
169
+ hero.appendChild(content);
170
+ container.appendChild(hero);
171
+
172
+ return this;
173
+ }
174
+
175
+ /**
176
+ * Render to another Jux component's container
177
+ */
178
+ renderTo(juxComponent: any): this {
179
+ if (!juxComponent || typeof juxComponent !== 'object') {
180
+ throw new Error('Hero.renderTo: Invalid component - not an object');
181
+ }
182
+
183
+ if (!juxComponent._id || typeof juxComponent._id !== 'string') {
184
+ throw new Error('Hero.renderTo: Invalid component - missing _id (not a Jux component)');
185
+ }
186
+
187
+ return this.render(`#${juxComponent._id}`);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Factory helper
193
+ */
194
+ export function hero1(id: string, options: HeroOptions = {}): Hero1 {
195
+ return new Hero1(id, options);
196
+ }