juxscript 1.0.64 → 1.0.65

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.
@@ -0,0 +1,34 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="textGrad" x1="100" y1="0" x2="412" y2="0" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0%" stop-color="#22D3EE"/>
5
+ <stop offset="50%" stop-color="#A855F7"/>
6
+ <stop offset="100%" stop-color="#F472B6"/>
7
+ </linearGradient>
8
+ </defs>
9
+
10
+ <!-- Background -->
11
+ <rect width="512" height="512" rx="64" fill="#0F172A"/>
12
+
13
+ <!-- Left Curly Brace { -->
14
+ <path d="M80 130
15
+ Q80 200, 55 230
16
+ Q30 256, 55 282
17
+ Q80 312, 80 382"
18
+ stroke="#94A3B8" stroke-width="14" stroke-linecap="round" fill="none"/>
19
+
20
+ <!-- Right Curly Brace } -->
21
+ <path d="M432 130
22
+ Q432 200, 457 230
23
+ Q482 256, 457 282
24
+ Q432 312, 432 382"
25
+ stroke="#94A3B8" stroke-width="14" stroke-linecap="round" fill="none"/>
26
+
27
+ <!-- JUX Text -->
28
+ <text x="256" y="285"
29
+ font-family="system-ui, -apple-system, sans-serif"
30
+ font-size="130"
31
+ font-weight="900"
32
+ text-anchor="middle"
33
+ fill="url(#textGrad)">JUX</text>
34
+ </svg>
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Base
3
+ */
4
+
5
+ :root {
6
+ /* Typography */
7
+ --font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif;
8
+ --font-size-sm: 0.875rem;
9
+ --font-size-base: 1rem;
10
+ --font-size-lg: 1.125rem;
11
+ --line-height-normal: 1.6;
12
+
13
+ /* Spacing Scale */
14
+ --space-xs: 0.25rem;
15
+ --space-sm: 0.5rem;
16
+ --space-md: 1rem;
17
+ --space-lg: 1.5rem;
18
+ --space-xl: 2rem;
19
+
20
+ /* Layout */
21
+ --radius-sm: 6px;
22
+ --radius-md: 10px;
23
+ --border-width: 1px;
24
+
25
+ /* Animation */
26
+ --transition-fast: 150ms ease;
27
+ --transition-normal: 300ms ease;
28
+
29
+ /* Color Palette - RxTrail Brand Colors */
30
+ --color-brand: #e57373;
31
+ --color-brand-hover: #ef5350;
32
+ --color-brand-subtle: rgba(229, 115, 115, 0.08);
33
+
34
+ --color-text-primary: #2c2f3e;
35
+ --color-text-secondary: #6b7280;
36
+ --color-text-inverse: #ffffff;
37
+
38
+ --color-background: #fafbfc;
39
+ --color-surface-base: #ffffff;
40
+ --color-surface-hover: #fef5f5;
41
+ --color-surface-active: #fce8e8;
42
+
43
+ --color-border: #e5e7eb;
44
+ --color-border-hover: #d1d5db;
45
+
46
+ /* Accent colors for clinical data */
47
+ --color-success: #00a878;
48
+ --color-warning: #f39c12;
49
+ --color-danger: #e74c3c;
50
+ }
51
+
52
+ /* Dark Mode Overrides */
53
+ [data-theme="dark"] {
54
+ --color-text-primary: #e8edf3;
55
+ --color-text-secondary: #9aa6b5;
56
+ --color-background: #0f1419;
57
+ --color-surface-base: #1a2332;
58
+ --color-surface-hover: #243447;
59
+ --color-border: #384456;
60
+ }
@@ -0,0 +1,54 @@
1
+
2
+ /*gemini*/
3
+ :root {
4
+ /* Typography - Larger, more comfortable */
5
+ --font-family-base: 'Google Sans', system-ui, -apple-system, sans-serif;
6
+ --font-size-sm: 0.9375rem; /* 15px */
7
+ --font-size-base: 1.0625rem; /* 17px */
8
+ --font-size-lg: 1.25rem; /* 20px */
9
+ --line-height-normal: 1.6;
10
+
11
+ /* Spacing Scale - More generous */
12
+ --space-xs: 0.5rem; /* 8px */
13
+ --space-sm: 0.75rem; /* 12px */
14
+ --space-md: 1.25rem; /* 20px */
15
+ --space-lg: 2rem; /* 32px */
16
+ --space-xl: 3rem; /* 48px */
17
+
18
+ /* Layout - Softer, rounder */
19
+ --radius-sm: 8px;
20
+ --radius-md: 16px;
21
+ --radius-lg: 24px;
22
+ --border-width: 1px;
23
+
24
+ /* Animation - Smoother */
25
+ --transition-fast: 200ms cubic-bezier(0.4, 0, 0.2, 1);
26
+ --transition-normal: 350ms cubic-bezier(0.4, 0, 0.2, 1);
27
+
28
+ /* Color Palette (Semantic) - Gemini-inspired */
29
+ --color-brand: #1a73e8;
30
+ --color-brand-hover: #1557b0;
31
+ --color-brand-subtle: rgba(26, 115, 232, 0.08);
32
+
33
+ --color-text-primary: #202124;
34
+ --color-text-secondary: #5f6368;
35
+ --color-text-inverse: #ffffff;
36
+
37
+ --color-background: #ffffff;
38
+ --color-surface-base: #f8f9fa;
39
+ --color-surface-hover: #f1f3f4;
40
+ --color-surface-active: #e8eaed;
41
+
42
+ --color-border: #dadce0;
43
+ --color-border-hover: #bdc1c6;
44
+ }
45
+
46
+ /* Dark Mode Overrides */
47
+ [data-theme="dark"] {
48
+ --color-text-primary: #e8eaed;
49
+ --color-text-secondary: #9aa0a6;
50
+ --color-background: #202124;
51
+ --color-surface-base: #292a2d;
52
+ --color-surface-hover: #35363a;
53
+ --color-border: #5f6368;
54
+ }
@@ -0,0 +1,16 @@
1
+ import { juxV2 } from '../../lib/componentsv2';
2
+
3
+
4
+ juxV2.Grid('base-layout')
5
+ .rows([
6
+ { size: '60px', class: 'header-row' }, // Header
7
+ { size: '1fr', class: 'content-row' }, // Content
8
+ { size: '40px', class: 'footer-row' } // Footer
9
+ ])
10
+ .columns([
11
+ { size: '200px', class: 'sidebar-col' }, // Sidebar
12
+ { size: '1fr', class: 'main-col' } // Main Content
13
+ ])
14
+ .gap('12px')
15
+ .gridder(true) // Enable gridder to visualize the layout
16
+ .render('app');
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.64",
3
+ "version": "1.0.65",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",
@@ -16,21 +16,14 @@
16
16
  "import": "./lib/jux.js",
17
17
  "default": "./lib/jux.js"
18
18
  },
19
- "./reactivity": {
20
- "types": "./lib/reactivity/index.d.ts",
21
- "import": "./lib/reactivity/index.js",
22
- "default": "./lib/reactivity/index.js"
23
- },
24
- "./components/*": "./lib/components/*/index.js",
25
- "./presets/*": "./presets/*.*",
26
19
  "./package.json": "./package.json"
27
20
  },
28
21
  "files": [
29
- "lib",
30
22
  "bin",
31
- "docs",
23
+ "create",
24
+ "lib",
32
25
  "machinery",
33
- "presets",
26
+ "tests",
34
27
  "types",
35
28
  "juxconfig.example.js",
36
29
  "README.md",
@@ -0,0 +1,25 @@
1
+ import { dropdown } from '../lib/components/dropdown.js';
2
+
3
+
4
+ const dd = dropdown('test-dropdown', {
5
+ items: [
6
+ { value: 'option1', label: 'Option 1' },
7
+ { value: 'option2', label: 'Option 2' },
8
+ { value: 'option3', label: 'Option 3' }
9
+ ],
10
+ placeholder: 'Select an option',
11
+ searchable: true,
12
+ multiSelect: false
13
+ });
14
+ //dd.open();
15
+
16
+ console.log('Current Props', dd.props)
17
+
18
+
19
+
20
+ dd.select('option3');
21
+ dd.open();
22
+
23
+ console.log('Dropdown opened:', dd.props);
24
+ //dd.selectItem('option2');
25
+ //console.log('Selected item:', dd.getSelectedItems());
@@ -0,0 +1,8 @@
1
+ import { jux } from 'juxscript';
2
+
3
+ // This is valid
4
+ jux.hero('syntax-test', { title: 'Syntax Test' }).render('#app');
5
+
6
+ // This is INVALID syntax and should fail the compiler
7
+
8
+ const brokenVariable = ;
@@ -0,0 +1,10 @@
1
+ import { jux } from 'juxscript';
2
+
3
+ // ❌ This dependency does not exist in node_modules
4
+ // The compiler will warn, but might proceed.
5
+ // The Verifier should CATCH this because 'phantom-lib' won't be in the import map.
6
+ import { magic } from 'phantom-lib';
7
+
8
+ jux.hero('dep-test', { title: 'Ghost Dependency' }).render('#app');
9
+
10
+ console.log(magic);
@@ -0,0 +1,191 @@
1
+ import { ListEngine, ListItem } from '../lib/componentsv2/list/engine.js';
2
+ import { ServerSQLitePlugin } from '../lib/componentsv2/plugins/ServerSQLitePlugin.js';
3
+ import Database from 'better-sqlite3';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ const DB_PATH = path.resolve('temp/jux_test.db');
8
+ const PRESERVE_DB = process.argv.includes('--preserve-db');
9
+
10
+ // --- Integrity System ---
11
+ const SNAPSHOTS: Record<string, any[]> = {};
12
+ const EXPECTATIONS: Record<string, any[]> = {};
13
+
14
+ function capture(phase: string, items: ListItem[]) {
15
+ // Deep copy and simplify to core data shape
16
+ SNAPSHOTS[phase] = items.map(i => ({
17
+ id: i.id,
18
+ text: i.text,
19
+ classes: i.classes,
20
+ details: i.details
21
+ }));
22
+ }
23
+
24
+ function expectShape(phase: string, shape: any[]) {
25
+ EXPECTATIONS[phase] = shape;
26
+ }
27
+
28
+ /**
29
+ * Generate a precision checksum for data integrity.
30
+ */
31
+ function getChecksum(data: any): string {
32
+ const str = JSON.stringify(data);
33
+ let hash = 5381;
34
+ for (let i = 0; i < str.length; i++) {
35
+ hash = ((hash << 5) + hash) + str.charCodeAt(i);
36
+ }
37
+ return (hash >>> 0).toString(16).toUpperCase().padStart(8, '0');
38
+ }
39
+
40
+ async function runTest() {
41
+ console.log('--- STARTING RIGOROUS SERVER-SIDE PLUGIN TEST ---');
42
+ if (PRESERVE_DB) console.log('ℹ️ MODE: Preserving Database File');
43
+
44
+ // Ensure temp directory exists
45
+ const tempDir = path.dirname(DB_PATH);
46
+ if (!fs.existsSync(tempDir)) {
47
+ fs.mkdirSync(tempDir, { recursive: true });
48
+ }
49
+
50
+ // Cleanup previous runs
51
+ if (fs.existsSync(DB_PATH)) fs.unlinkSync(DB_PATH);
52
+
53
+ // 1. Initialize DB
54
+ const db = new Database(DB_PATH);
55
+
56
+ // 🔍 PROOF OF LIFE
57
+ const sqliteVersion = db.prepare('select sqlite_version()').pluck().get();
58
+ const TEST_TIMESTAMP = new Date().toISOString();
59
+ const EPHEMERAL_TAG = ` [v${sqliteVersion}::${TEST_TIMESTAMP}]`;
60
+ console.log(`🔌 CONNECTION ACTIVE: SQLite ${sqliteVersion} | Run ID: ${TEST_TIMESTAMP}`);
61
+
62
+ const engine = new ListEngine('server-list-01');
63
+
64
+ // --- DATA CONTRACTS ---
65
+ const SHAPE_1_EMPTY: any[] = [];
66
+
67
+ const SHAPE_2_HYDRATED = [
68
+ { id: '1', text: `Alice${EPHEMERAL_TAG}`, classes: ['admin'], details: { active: true } },
69
+ { id: '2', text: `Bob${EPHEMERAL_TAG}`, classes: ['user'], details: { active: true } },
70
+ { id: '3', text: `Charlie${EPHEMERAL_TAG}`, classes: ['guest'], details: { active: false } }
71
+ ];
72
+
73
+ const SHAPE_3_MODIFIED = [
74
+ { id: '1', text: `Alice${EPHEMERAL_TAG}`, classes: ['superadmin'], details: { active: true } },
75
+ { id: '2', text: `Bob${EPHEMERAL_TAG}`, classes: ['user'], details: { active: true } },
76
+ { id: '4', text: `Dave${EPHEMERAL_TAG}`, classes: ['user'], details: {} }
77
+ ];
78
+
79
+ // Register Contracts
80
+ expectShape('PHASE 1: Empty', SHAPE_1_EMPTY);
81
+ expectShape('PHASE 2: Hydrated', SHAPE_2_HYDRATED);
82
+ expectShape('PHASE 3: Modified', SHAPE_3_MODIFIED);
83
+
84
+ // === EXECUTION ===
85
+
86
+ // Phase 1
87
+ console.log('\n▶ PHASE 1: Initialization');
88
+ capture('PHASE 1: Empty', engine.state.items);
89
+
90
+ // Phase 2
91
+ console.log('\n▶ PHASE 2: Schema & Hydration');
92
+ db.exec(`CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, role TEXT DEFAULT 'user', meta TEXT);`);
93
+ const insert = db.prepare('INSERT INTO users (username, role, meta) VALUES (?, ?, ?)');
94
+ insert.run('Alice', 'admin', JSON.stringify({ active: true }));
95
+ insert.run('Bob', 'user', JSON.stringify({ active: true }));
96
+ insert.run('Charlie', 'guest', JSON.stringify({ active: false }));
97
+
98
+ // Inject Decoupled Plugin
99
+ const plugin = ServerSQLitePlugin({
100
+ database: db,
101
+ query: `SELECT id, username || '${EPHEMERAL_TAG}' as username, role, meta FROM users`,
102
+ bindTo: 'items', // ✅ Bind to ListEngine's 'items' property
103
+ mapRow: (row: any) => ({
104
+ id: String(row.id),
105
+ text: row.username,
106
+ classes: row.role ? [row.role] : [],
107
+ details: row.meta ? JSON.parse(row.meta) : {}
108
+ }),
109
+ autoLoad: true
110
+ });
111
+ engine.addPlugin(plugin);
112
+
113
+ capture('PHASE 2: Hydrated', engine.state.items);
114
+
115
+ // Phase 3
116
+ console.log('\n▶ PHASE 3: External Access');
117
+ db.prepare("UPDATE users SET role = 'superadmin' WHERE username = 'Alice'").run();
118
+ db.prepare("DELETE FROM users WHERE username = 'Charlie'").run();
119
+ db.prepare("INSERT INTO users (username, role, meta) VALUES (?, ?, ?)").run('Dave', 'user', '{}');
120
+
121
+ // @ts-ignore
122
+ engine['emit']('db:reload', {});
123
+
124
+ capture('PHASE 3: Modified', engine.state.items);
125
+
126
+ // === VERIFICATION REPORT ===
127
+ console.log('\n=============================================================');
128
+ console.log(' 🛡️ INTEGRITY CHECKSUM REPORT ');
129
+ console.log('=============================================================');
130
+ console.log(` CONNECTION ACTIVE: SQLite ${sqliteVersion} | Run ID: ${TEST_TIMESTAMP}`);
131
+ console.log('-------------------------------------------------------------');
132
+ console.log(' PHASES | CHECKSUM | EXPECTED | STATUS ');
133
+ console.log('-------------------------|----------|----------|-------------');
134
+
135
+ let allPassed = true;
136
+ Object.keys(EXPECTATIONS).forEach(phase => {
137
+ const actual = SNAPSHOTS[phase] || [];
138
+ const expected = EXPECTATIONS[phase];
139
+
140
+ const actHash = getChecksum(actual);
141
+ const expHash = getChecksum(expected);
142
+ const match = actHash === expHash;
143
+
144
+ if (!match) allPassed = false;
145
+
146
+ const status = match ? '✅ PASS' : '❌ FAIL';
147
+ console.log(` ${phase.padEnd(23)} | ${actHash} | ${expHash} | ${status}`);
148
+ });
149
+
150
+ console.log('-------------------------------------------------------------');
151
+
152
+ // === HISTORY DUMP ===
153
+ console.log('\n==================================================');
154
+ console.log(' 📜 STATE LEDGER ');
155
+ console.log('==================================================');
156
+ engine.stateHistory.forEach((snap, i) => {
157
+ const s = JSON.parse(snap);
158
+ console.log(`${i.toString().padStart(2, '0')} | Items: ${s.items.length} | Loading: ${String(s.loading).padEnd(5)} | Msg: ${s.noItemsMessage || 'None'}`);
159
+ });
160
+
161
+ console.log('\n==================================================');
162
+ console.log(' 📡 EVENT TOPOLOGY ');
163
+ console.log('==================================================');
164
+ engine.emitHistory.forEach((entryStr, i) => {
165
+ try {
166
+ const entry = JSON.parse(entryStr);
167
+ const time = entry.timestamp.split('T')[1].slice(0, 8);
168
+ const dataSummary = JSON.stringify(entry.data).substring(0, 60);
169
+ console.log(`#${i.toString().padStart(2, '0')} [${time}] ${entry.event.padEnd(15)} -> ${dataSummary}`);
170
+ } catch (e) { }
171
+ });
172
+
173
+ engine.dispose();
174
+ db.close();
175
+
176
+ // Cleanup
177
+ if (PRESERVE_DB) {
178
+ console.log(`\n💾 DATABASE PRESERVED: ${DB_PATH}`);
179
+ } else {
180
+ if (fs.existsSync(DB_PATH)) fs.unlinkSync(DB_PATH);
181
+ }
182
+
183
+ if (allPassed) {
184
+ console.log('\n🌟🌟🌟 RESOUNDING ALL TESTS PASSED 🌟🌟🌟\n');
185
+ } else {
186
+ console.error('\n💥 VERIFICATION FAILED: Checksum Mismatch detected.\n');
187
+ process.exit(1);
188
+ }
189
+ }
190
+
191
+ runTest();
@@ -1,244 +0,0 @@
1
- # JUX Deprecation System
2
-
3
- This document explains how deprecation warnings are implemented in JUX components to help users migrate to newer patterns while maintaining backward compatibility.
4
-
5
- ## Overview
6
-
7
- JUX uses a **console-based deprecation warning system** that:
8
- - Shows warnings **once per feature per page load** (no spam)
9
- - Includes **removal date and version** information
10
- - Provides **migration guidance** via documentation links
11
- - Warns on **constructor options, fluent methods, and sync bindings**
12
-
13
- ## Implementation Pattern
14
-
15
- ### 1. Define the Deprecation Message
16
-
17
- At the top of any component file:
18
-
19
- ```typescript
20
- // filepath: /path/to/component.ts
21
-
22
- // Deprecation warning template
23
- const DEPRECATION_WARNING = (feature: string) =>
24
- `[JUX Deprecation Warning] ComponentName.${feature} will be removed in v1.0.30+ (January 30, 2026). ` +
25
- `Please use CSS or the .style() method instead. See: [docs placeholder]`;
26
-
27
- // Track which warnings have been shown (prevents spam)
28
- const _shownWarnings = new Set<string>();
29
-
30
- function warnOnce(feature: string): void {
31
- if (!_shownWarnings.has(feature)) {
32
- console.warn(DEPRECATION_WARNING(feature));
33
- _shownWarnings.add(feature);
34
- }
35
- }
36
- ```
37
-
38
- ### 2. Warn in Constructor (Options)
39
-
40
- ```typescript
41
- export class Container extends BaseComponent<ContainerState> {
42
- constructor(id: string, options: ContainerOptions = {}) {
43
- // Check for deprecated options and warn
44
- if (options.direction !== undefined) warnOnce('direction (option)');
45
- if (options.gap !== undefined) warnOnce('gap (option)');
46
- if (options.padding !== undefined) warnOnce('padding (option)');
47
-
48
- super(id, {
49
- direction: options.direction ?? 'column',
50
- gap: options.gap ?? 0,
51
- padding: options.padding ?? '0'
52
- });
53
- }
54
- }
55
- ```
56
-
57
- ### 3. Warn in Fluent Methods
58
-
59
- ```typescript
60
- export class Container extends BaseComponent<ContainerState> {
61
-
62
- direction(value: 'row' | 'column'): this {
63
- warnOnce('direction()');
64
- this.state.direction = value;
65
- return this;
66
- }
67
-
68
- gap(value: number | string): this {
69
- warnOnce('gap()');
70
- this.state.gap = value;
71
- return this;
72
- }
73
-
74
- padding(value: string): this {
75
- warnOnce('padding()');
76
- this.state.padding = value;
77
- return this;
78
- }
79
- }
80
- ```
81
-
82
- ### 4. Warn in Sync Bindings (Optional)
83
-
84
- If a deprecated property can be synced with state:
85
-
86
- ```typescript
87
- render(targetId?: string): this {
88
- // ...existing render code...
89
-
90
- this._syncBindings.forEach(({ property, stateObj, toComponent }) => {
91
- const transform = toComponent || ((v: any) => v);
92
-
93
- if (property === 'direction') {
94
- warnOnce('direction (sync)');
95
- stateObj.subscribe((val: any) => {
96
- this.state.direction = String(transform(val));
97
- // ...apply changes to DOM...
98
- });
99
- }
100
- // ...existing code...
101
- });
102
-
103
- return this;
104
- }
105
- ```
106
-
107
- ## User Experience
108
-
109
- When a user writes deprecated code:
110
-
111
- ```javascript
112
- // This will show a console warning
113
- jux.container('my-container')
114
- .direction('row') // ⚠️ Warning: Container.direction() will be removed...
115
- .gap(20) // ⚠️ Warning: Container.gap() will be removed...
116
- .render('#app');
117
-
118
- // Subsequent calls DON'T show warnings (no spam)
119
- jux.container('another')
120
- .gap(10) // Silent (already warned once)
121
- .render('#app');
122
- ```
123
-
124
- **Console output:**
125
- ```
126
- [JUX Deprecation Warning] Container.direction() will be removed in v1.0.30+
127
- (January 30, 2026). Please use CSS or the .style() method instead.
128
- See: [docs placeholder]
129
-
130
- [JUX Deprecation Warning] Container.gap() will be removed in v1.0.30+
131
- (January 30, 2026). Please use CSS or the .style() method instead.
132
- See: [docs placeholder]
133
- ```
134
-
135
- ## Migration Path
136
-
137
- Users should migrate from deprecated properties to CSS:
138
-
139
- ```javascript
140
- // ❌ Old (deprecated)
141
- jux.container('box')
142
- .direction('row')
143
- .gap(20)
144
- .padding('1rem')
145
- .render('#app');
146
-
147
- // ✅ New (recommended)
148
- jux.container('box')
149
- .style('display: flex; flex-direction: row; gap: 20px; padding: 1rem;')
150
- .render('#app');
151
-
152
- // ✅ Or use a CSS class
153
- jux.container('box')
154
- .class('flex-row gap-20 p-4')
155
- .render('#app');
156
- ```
157
-
158
- ## Why This Approach?
159
-
160
- 1. **Non-breaking**: Old code continues to work during deprecation period
161
- 2. **Gradual migration**: Developers can update at their own pace
162
- 3. **Clear timeline**: Version and date make planning easy
163
- 4. **No console spam**: Each warning shown only once per page load
164
- 5. **Clear guidance**: Links to migration documentation
165
-
166
- ## Adding New Deprecations
167
-
168
- To deprecate a feature in any component:
169
-
170
- ### Step 1: Add Warning Infrastructure
171
-
172
- ```typescript
173
- const DEPRECATION_WARNING = (feature: string) =>
174
- `[JUX Deprecation Warning] YourComponent.${feature} will be removed in vX.X.X+ (Date). ` +
175
- `Migration guide: [docs placeholder]`;
176
-
177
- const _shownWarnings = new Set<string>();
178
-
179
- function warnOnce(feature: string): void {
180
- if (!_shownWarnings.has(feature)) {
181
- console.warn(DEPRECATION_WARNING(feature));
182
- _shownWarnings.add(feature);
183
- }
184
- }
185
- ```
186
-
187
- ### Step 2: Call warnOnce() Everywhere the Feature is Used
188
-
189
- - In constructor for options
190
- - In fluent methods
191
- - In sync binding handlers (if applicable)
192
-
193
- ### Step 3: Update Documentation
194
-
195
- - Add migration examples to docs
196
- - Update changelog
197
- - Set removal date (typically 6-12 months out)
198
-
199
- ### Step 4: Add to Current Deprecations Table Below
200
-
201
- ## Current Deprecations
202
-
203
- | Component | Feature | Replacement | Removal Version | Removal Date |
204
- |-----------|---------|-------------|-----------------|--------------|
205
- | Container | `.direction()` | `.style('flex-direction: ...')` | v1.0.30+ | Jan 30, 2026 |
206
- | Container | `.gap()` | `.style('gap: ...')` | v1.0.30+ | Jan 30, 2026 |
207
- | Container | `.wrap()` | `.style('flex-wrap: ...')` | v1.0.30+ | Jan 30, 2026 |
208
- | Container | `.align()` | `.style('align-items: ...')` | v1.0.30+ | Jan 30, 2026 |
209
- | Container | `.justify()` | `.style('justify-content: ...')` | v1.0.30+ | Jan 30, 2026 |
210
- | Container | `.padding()` | `.style('padding: ...')` | v1.0.30+ | Jan 30, 2026 |
211
-
212
- ## Testing Deprecation Warnings
213
-
214
- You can verify deprecation warnings are working:
215
-
216
- ```javascript
217
- // Open browser console and run:
218
- jux.container('test')
219
- .direction('row') // Should show warning
220
- .gap(20) // Should show warning
221
- .render('#app');
222
-
223
- jux.container('test2')
224
- .direction('row') // Should NOT show warning (already shown)
225
- .render('#app');
226
- ```
227
-
228
- ## Best Practices
229
-
230
- 1. **Give users time**: At least 6 months before removal
231
- 2. **Be specific**: Show exact feature name in warning
232
- 3. **Provide alternatives**: Always suggest what to use instead
233
- 4. **Document migrations**: Create clear before/after examples
234
- 5. **Version appropriately**: Use semver correctly (major bump for removals)
235
-
236
- ## Questions?
237
-
238
- - Migration guides: [docs placeholder - coming soon]
239
- - Breaking changes: [docs placeholder - coming soon]
240
- - Changelog: [GitHub releases placeholder]
241
-
242
- ---
243
-
244
- **Last updated**: January 28, 2026