juxscript 1.0.18 → 1.0.20
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/lib/components/alert.ts +124 -128
- package/lib/components/areachart.ts +169 -287
- package/lib/components/areachartsmooth.ts +2 -2
- package/lib/components/badge.ts +63 -72
- package/lib/components/barchart.ts +120 -48
- package/lib/components/button.ts +99 -101
- package/lib/components/card.ts +97 -121
- package/lib/components/chart-types.ts +159 -0
- package/lib/components/chart-utils.ts +160 -0
- package/lib/components/chart.ts +628 -48
- package/lib/components/checkbox.ts +137 -51
- package/lib/components/code.ts +89 -75
- package/lib/components/container.ts +1 -1
- package/lib/components/datepicker.ts +93 -78
- package/lib/components/dialog.ts +163 -130
- package/lib/components/divider.ts +111 -193
- package/lib/components/docs-data.json +711 -264
- package/lib/components/doughnutchart.ts +125 -57
- package/lib/components/dropdown.ts +172 -85
- package/lib/components/element.ts +66 -61
- package/lib/components/fileupload.ts +142 -171
- package/lib/components/heading.ts +64 -21
- package/lib/components/hero.ts +109 -34
- package/lib/components/icon.ts +247 -0
- package/lib/components/icons.ts +174 -0
- package/lib/components/include.ts +77 -2
- package/lib/components/input.ts +174 -125
- package/lib/components/list.ts +120 -79
- package/lib/components/menu.ts +97 -2
- package/lib/components/modal.ts +144 -63
- package/lib/components/nav.ts +153 -52
- package/lib/components/paragraph.ts +78 -28
- package/lib/components/progress.ts +83 -107
- package/lib/components/radio.ts +151 -52
- package/lib/components/select.ts +110 -102
- package/lib/components/sidebar.ts +148 -105
- package/lib/components/switch.ts +124 -125
- package/lib/components/table.ts +214 -137
- package/lib/components/tabs.ts +194 -113
- package/lib/components/theme-toggle.ts +38 -7
- package/lib/components/tooltip.ts +207 -47
- package/lib/jux.ts +24 -5
- package/lib/reactivity/state.ts +13 -299
- package/package.json +1 -2
|
@@ -1,57 +1,41 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
import { State } from '../reactivity/state.js';
|
|
2
3
|
|
|
3
|
-
/**
|
|
4
|
-
* Sidebar component options
|
|
5
|
-
*/
|
|
6
4
|
export interface SidebarOptions {
|
|
7
|
-
title?: string;
|
|
8
|
-
width?: string;
|
|
9
5
|
position?: 'left' | 'right';
|
|
6
|
+
width?: string;
|
|
10
7
|
collapsible?: boolean;
|
|
11
8
|
collapsed?: boolean;
|
|
12
9
|
style?: string;
|
|
13
10
|
class?: string;
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
/**
|
|
17
|
-
* Sidebar component state
|
|
18
|
-
*/
|
|
19
13
|
type SidebarState = {
|
|
20
|
-
|
|
21
|
-
width: string
|
|
22
|
-
position: string;
|
|
14
|
+
position: 'left' | 'right';
|
|
15
|
+
width: string;
|
|
23
16
|
collapsible: boolean;
|
|
24
17
|
collapsed: boolean;
|
|
25
18
|
style: string;
|
|
26
19
|
class: string;
|
|
27
20
|
};
|
|
28
21
|
|
|
29
|
-
/**
|
|
30
|
-
* Sidebar component
|
|
31
|
-
*
|
|
32
|
-
* Usage:
|
|
33
|
-
* const sidebar = jux.sidebar('mySidebar', {
|
|
34
|
-
* title: 'Navigation',
|
|
35
|
-
* width: '250px',
|
|
36
|
-
* position: 'left'
|
|
37
|
-
* });
|
|
38
|
-
* sidebar.render('#appsidebar');
|
|
39
|
-
*/
|
|
40
22
|
export class Sidebar {
|
|
41
23
|
state: SidebarState;
|
|
42
24
|
container: HTMLElement | null = null;
|
|
43
25
|
_id: string;
|
|
44
26
|
id: string;
|
|
45
27
|
|
|
28
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
29
|
+
private _syncBindings: Array<{ property: string, stateObj: State<any>, transform?: Function }> = [];
|
|
30
|
+
|
|
46
31
|
constructor(id: string, options: SidebarOptions = {}) {
|
|
47
32
|
this._id = id;
|
|
48
33
|
this.id = id;
|
|
49
34
|
|
|
50
35
|
this.state = {
|
|
51
|
-
title: options.title ?? '',
|
|
52
|
-
width: options.width ?? null, // No default width - let CSS handle it
|
|
53
36
|
position: options.position ?? 'left',
|
|
54
|
-
|
|
37
|
+
width: options.width ?? null,
|
|
38
|
+
collapsible: options.collapsible ?? true,
|
|
55
39
|
collapsed: options.collapsed ?? false,
|
|
56
40
|
style: options.style ?? '',
|
|
57
41
|
class: options.class ?? ''
|
|
@@ -62,10 +46,6 @@ export class Sidebar {
|
|
|
62
46
|
* Fluent API
|
|
63
47
|
* ------------------------- */
|
|
64
48
|
|
|
65
|
-
title(value: string): this {
|
|
66
|
-
this.state.title = value;
|
|
67
|
-
return this;
|
|
68
|
-
}
|
|
69
49
|
|
|
70
50
|
width(value: string): this {
|
|
71
51
|
this.state.width = value;
|
|
@@ -84,6 +64,7 @@ export class Sidebar {
|
|
|
84
64
|
|
|
85
65
|
collapsed(value: boolean): this {
|
|
86
66
|
this.state.collapsed = value;
|
|
67
|
+
this._updateCollapsedState();
|
|
87
68
|
return this;
|
|
88
69
|
}
|
|
89
70
|
|
|
@@ -97,49 +78,34 @@ export class Sidebar {
|
|
|
97
78
|
return this;
|
|
98
79
|
}
|
|
99
80
|
|
|
100
|
-
|
|
101
|
-
this.
|
|
102
|
-
this._updateDOM();
|
|
81
|
+
bind(event: string, handler: Function): this {
|
|
82
|
+
this._bindings.push({ event, handler });
|
|
103
83
|
return this;
|
|
104
84
|
}
|
|
105
85
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
private _updateDOM(): void {
|
|
111
|
-
if (!this.container) return;
|
|
112
|
-
|
|
113
|
-
const sidebar = this.container.querySelector(`#${this._id}`);
|
|
114
|
-
if (!sidebar || !(sidebar instanceof HTMLElement)) return;
|
|
115
|
-
|
|
116
|
-
const { width, collapsed } = this.state;
|
|
117
|
-
|
|
118
|
-
if (collapsed) {
|
|
119
|
-
sidebar.classList.add('jux-sidebar-collapsed');
|
|
120
|
-
if (width) {
|
|
121
|
-
sidebar.style.width = '0';
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
sidebar.classList.remove('jux-sidebar-collapsed');
|
|
125
|
-
if (width) {
|
|
126
|
-
sidebar.style.width = width;
|
|
127
|
-
}
|
|
86
|
+
sync(property: string, stateObj: State<any>, transform?: Function): this {
|
|
87
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
88
|
+
throw new Error(`Sidebar.sync: Expected a State object for property "${property}"`);
|
|
128
89
|
}
|
|
90
|
+
this._syncBindings.push({ property, stateObj, transform });
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
129
93
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
94
|
+
toggle(): void {
|
|
95
|
+
this.state.collapsed = !this.state.collapsed;
|
|
96
|
+
this._updateCollapsedState();
|
|
134
97
|
}
|
|
135
98
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
99
|
+
private _updateCollapsedState(): void {
|
|
100
|
+
const sidebar = document.getElementById(this._id);
|
|
101
|
+
if (sidebar) {
|
|
102
|
+
sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
139
105
|
|
|
140
106
|
render(targetId?: string): this {
|
|
107
|
+
// === 1. SETUP: Get container ===
|
|
141
108
|
let container: HTMLElement;
|
|
142
|
-
|
|
143
109
|
if (targetId) {
|
|
144
110
|
const target = document.querySelector(targetId);
|
|
145
111
|
if (!target || !(target instanceof HTMLElement)) {
|
|
@@ -149,78 +115,155 @@ export class Sidebar {
|
|
|
149
115
|
} else {
|
|
150
116
|
container = getOrCreateContainer(this._id);
|
|
151
117
|
}
|
|
152
|
-
|
|
153
118
|
this.container = container;
|
|
154
|
-
const { title, width, position, collapsible, collapsed, style, class: className } = this.state;
|
|
155
119
|
|
|
120
|
+
// === 2. PREPARE: Destructure state ===
|
|
121
|
+
const { position, width, collapsible, collapsed, style, class: className } = this.state;
|
|
122
|
+
|
|
123
|
+
// === 3. BUILD: Create DOM elements ===
|
|
156
124
|
const sidebar = document.createElement('aside');
|
|
157
125
|
sidebar.className = `jux-sidebar jux-sidebar-${position}`;
|
|
158
126
|
sidebar.id = this._id;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (width) {
|
|
162
|
-
sidebar.style.width = collapsed ? '0' : width;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (className) {
|
|
166
|
-
sidebar.className += ` ${className}`;
|
|
167
|
-
}
|
|
127
|
+
if (className) sidebar.className += ` ${className}`;
|
|
128
|
+
if (collapsed) sidebar.classList.add('jux-sidebar-collapsed');
|
|
168
129
|
|
|
169
|
-
|
|
170
|
-
|
|
130
|
+
const sidebarStyle = `${width ? `width: ${collapsed ? '60px' : width};` : ''} ${style}`;
|
|
131
|
+
if (sidebarStyle.trim()) {
|
|
132
|
+
sidebar.setAttribute('style', sidebarStyle);
|
|
171
133
|
}
|
|
172
134
|
|
|
173
|
-
if (collapsed) {
|
|
174
|
-
sidebar.classList.add('jux-sidebar-collapsed');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (title) {
|
|
178
|
-
const titleEl = document.createElement('div');
|
|
179
|
-
titleEl.className = 'jux-sidebar-title';
|
|
180
|
-
titleEl.textContent = title;
|
|
181
|
-
sidebar.appendChild(titleEl);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const content = document.createElement('div');
|
|
185
|
-
content.className = 'jux-sidebar-content';
|
|
186
|
-
sidebar.appendChild(content);
|
|
187
|
-
|
|
188
|
-
// Event binding - toggle button
|
|
189
135
|
if (collapsible) {
|
|
190
136
|
const toggleBtn = document.createElement('button');
|
|
191
137
|
toggleBtn.className = 'jux-sidebar-toggle';
|
|
192
|
-
toggleBtn.
|
|
138
|
+
toggleBtn.innerHTML = collapsed
|
|
139
|
+
? (position === 'left' ? '▶' : '◀')
|
|
140
|
+
: (position === 'left' ? '◀' : '▶');
|
|
141
|
+
toggleBtn.setAttribute('aria-label', 'Toggle sidebar');
|
|
142
|
+
toggleBtn.setAttribute('title', collapsed ? 'Expand sidebar' : 'Collapse sidebar');
|
|
193
143
|
|
|
194
144
|
toggleBtn.addEventListener('click', () => {
|
|
195
145
|
this.toggle();
|
|
146
|
+
|
|
147
|
+
// Update button icon and title
|
|
148
|
+
const isCollapsed = this.state.collapsed;
|
|
149
|
+
toggleBtn.innerHTML = isCollapsed
|
|
150
|
+
? (position === 'left' ? '▶' : '◀')
|
|
151
|
+
: (position === 'left' ? '◀' : '▶');
|
|
152
|
+
toggleBtn.setAttribute('title', isCollapsed ? 'Expand sidebar' : 'Collapse sidebar');
|
|
153
|
+
|
|
154
|
+
// Animate width change
|
|
155
|
+
if (width) {
|
|
156
|
+
sidebar.style.width = isCollapsed ? '60px' : width;
|
|
157
|
+
}
|
|
196
158
|
});
|
|
197
159
|
|
|
198
160
|
sidebar.appendChild(toggleBtn);
|
|
199
161
|
}
|
|
200
162
|
|
|
163
|
+
// === 4. WIRE: Add event listeners ===
|
|
164
|
+
|
|
165
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
166
|
+
sidebar.addEventListener(event, handler as EventListener);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
this._syncBindings.forEach(({ property, stateObj, transform }) => {
|
|
170
|
+
stateObj.subscribe((val: any) => {
|
|
171
|
+
const transformed = transform ? transform(val) : val;
|
|
172
|
+
|
|
173
|
+
if (property === 'collapsed') {
|
|
174
|
+
const isCollapsed = Boolean(transformed);
|
|
175
|
+
this.state.collapsed = isCollapsed;
|
|
176
|
+
this._updateCollapsedState();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// === 5. RENDER: Append to DOM ===
|
|
201
182
|
container.appendChild(sidebar);
|
|
183
|
+
this._injectDefaultStyles();
|
|
184
|
+
|
|
202
185
|
return this;
|
|
203
186
|
}
|
|
204
187
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
188
|
+
private _injectDefaultStyles(): void {
|
|
189
|
+
const styleId = 'jux-sidebar-styles';
|
|
190
|
+
if (document.getElementById(styleId)) return;
|
|
191
|
+
|
|
192
|
+
const style = document.createElement('style');
|
|
193
|
+
style.id = styleId;
|
|
194
|
+
style.textContent = `
|
|
195
|
+
.jux-sidebar {
|
|
196
|
+
position: relative;
|
|
197
|
+
height: 100vh;
|
|
198
|
+
background: #f9fafb;
|
|
199
|
+
border-right: 1px solid #e5e7eb;
|
|
200
|
+
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
201
|
+
overflow: hidden;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.jux-sidebar-right {
|
|
205
|
+
border-right: none;
|
|
206
|
+
border-left: 1px solid #e5e7eb;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.jux-sidebar-toggle {
|
|
210
|
+
position: absolute;
|
|
211
|
+
bottom: 20px;
|
|
212
|
+
right: 20px;
|
|
213
|
+
width: 32px;
|
|
214
|
+
height: 32px;
|
|
215
|
+
border: none;
|
|
216
|
+
background: #3b82f6;
|
|
217
|
+
color: white;
|
|
218
|
+
border-radius: 6px;
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
font-size: 14px;
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
justify-content: center;
|
|
224
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
225
|
+
z-index: 100;
|
|
226
|
+
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.jux-sidebar-toggle:hover {
|
|
230
|
+
background: #2563eb;
|
|
231
|
+
transform: scale(1.05);
|
|
232
|
+
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.jux-sidebar-toggle:active {
|
|
236
|
+
transform: scale(0.95);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.jux-sidebar-collapsed {
|
|
240
|
+
width: 60px !important;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Smooth fade for content */
|
|
244
|
+
.jux-sidebar-collapsed > *:not(.jux-sidebar-toggle) {
|
|
245
|
+
opacity: 0;
|
|
246
|
+
pointer-events: none;
|
|
247
|
+
transition: opacity 0.2s ease-out;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.jux-sidebar > *:not(.jux-sidebar-toggle) {
|
|
251
|
+
opacity: 1;
|
|
252
|
+
pointer-events: auto;
|
|
253
|
+
transition: opacity 0.3s ease-in 0.1s;
|
|
254
|
+
}
|
|
255
|
+
`;
|
|
256
|
+
document.head.appendChild(style);
|
|
257
|
+
}
|
|
212
258
|
|
|
213
|
-
|
|
214
|
-
|
|
259
|
+
renderTo(juxComponent: any): this {
|
|
260
|
+
if (!juxComponent?._id) {
|
|
261
|
+
throw new Error('Sidebar.renderTo: Invalid component');
|
|
215
262
|
}
|
|
216
|
-
|
|
217
263
|
return this.render(`#${juxComponent._id}`);
|
|
218
264
|
}
|
|
219
265
|
}
|
|
220
266
|
|
|
221
|
-
/**
|
|
222
|
-
* Factory helper
|
|
223
|
-
*/
|
|
224
267
|
export function sidebar(id: string, options: SidebarOptions = {}): Sidebar {
|
|
225
268
|
return new Sidebar(id, options);
|
|
226
269
|
}
|