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
package/lib/components/list.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
import { State } from '../reactivity/state.js';
|
|
3
|
+
import { renderIcon } from './icons.js';
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* List item interface
|
|
5
|
-
*/
|
|
6
5
|
export interface ListItem {
|
|
7
6
|
icon?: string;
|
|
8
7
|
title?: string;
|
|
@@ -11,9 +10,6 @@ export interface ListItem {
|
|
|
11
10
|
metadata?: string;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
/**
|
|
15
|
-
* List component options
|
|
16
|
-
*/
|
|
17
13
|
export interface ListOptions {
|
|
18
14
|
items?: ListItem[];
|
|
19
15
|
header?: string;
|
|
@@ -27,9 +23,6 @@ export interface ListOptions {
|
|
|
27
23
|
class?: string;
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
/**
|
|
31
|
-
* List component state
|
|
32
|
-
*/
|
|
33
26
|
type ListState = {
|
|
34
27
|
items: ListItem[];
|
|
35
28
|
header: string;
|
|
@@ -37,42 +30,28 @@ type ListState = {
|
|
|
37
30
|
direction: string;
|
|
38
31
|
selectable: boolean;
|
|
39
32
|
selectedIndex: number | null;
|
|
33
|
+
ordered?: boolean;
|
|
40
34
|
style: string;
|
|
41
35
|
class: string;
|
|
42
36
|
};
|
|
43
37
|
|
|
44
|
-
/**
|
|
45
|
-
* List component - renders a list of items with optional header
|
|
46
|
-
*
|
|
47
|
-
* Usage:
|
|
48
|
-
* const myList = jux.list('myList', {
|
|
49
|
-
* header: '✓ Accomplishments',
|
|
50
|
-
* items: [
|
|
51
|
-
* { icon: '✓', title: 'Task 1', body: 'Description', type: 'success' },
|
|
52
|
-
* { icon: '⚠️', title: 'Task 2', body: 'Description', type: 'warning' }
|
|
53
|
-
* ],
|
|
54
|
-
* gap: '0.75rem',
|
|
55
|
-
* selectable: true,
|
|
56
|
-
* onItemClick: (item, index) => console.log('Clicked:', item, index)
|
|
57
|
-
* });
|
|
58
|
-
* myList.render();
|
|
59
|
-
*
|
|
60
|
-
* // Add item
|
|
61
|
-
* myList.add({ icon: '🎉', title: 'New Task', body: 'Done!', type: 'success' });
|
|
62
|
-
*
|
|
63
|
-
* // Remove item by index
|
|
64
|
-
* myList.remove(1);
|
|
65
|
-
*
|
|
66
|
-
* // Move item from index 0 to index 2
|
|
67
|
-
* myList.move(0, 2);
|
|
68
|
-
*/
|
|
69
38
|
export class List {
|
|
70
39
|
state: ListState;
|
|
71
40
|
container: HTMLElement | null = null;
|
|
72
41
|
_id: string;
|
|
73
42
|
id: string;
|
|
74
|
-
|
|
75
|
-
|
|
43
|
+
|
|
44
|
+
// CRITICAL: Store bind/sync instructions for deferred wiring
|
|
45
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
46
|
+
private _syncBindings: Array<{
|
|
47
|
+
property: string,
|
|
48
|
+
stateObj: State<any>,
|
|
49
|
+
toState?: Function,
|
|
50
|
+
toComponent?: Function
|
|
51
|
+
}> = [];
|
|
52
|
+
|
|
53
|
+
private _onItemClick: ((item: ListItem, index: number, e: Event) => void) | null = null;
|
|
54
|
+
private _onItemDoubleClick: ((item: ListItem, index: number, e: Event) => void) | null = null;
|
|
76
55
|
|
|
77
56
|
constructor(id: string, options: ListOptions = {}) {
|
|
78
57
|
this._id = id;
|
|
@@ -94,47 +73,50 @@ export class List {
|
|
|
94
73
|
}
|
|
95
74
|
|
|
96
75
|
/* -------------------------
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
* Fluent API
|
|
77
|
+
* ------------------------- */
|
|
99
78
|
|
|
100
79
|
items(value: ListItem[]): this {
|
|
101
80
|
this.state.items = value;
|
|
102
81
|
return this;
|
|
103
82
|
}
|
|
104
83
|
|
|
105
|
-
|
|
106
|
-
this.state.
|
|
84
|
+
addItem(value: ListItem): this {
|
|
85
|
+
this.state.items = [...this.state.items, value];
|
|
107
86
|
return this;
|
|
108
87
|
}
|
|
109
88
|
|
|
110
|
-
|
|
111
|
-
this.state.
|
|
89
|
+
ordered(value: boolean): this {
|
|
90
|
+
this.state.ordered = value;
|
|
112
91
|
return this;
|
|
113
92
|
}
|
|
114
93
|
|
|
115
|
-
|
|
116
|
-
this.state.
|
|
94
|
+
style(value: string): this {
|
|
95
|
+
this.state.style = value;
|
|
117
96
|
return this;
|
|
118
97
|
}
|
|
119
98
|
|
|
120
|
-
|
|
121
|
-
this.state.
|
|
99
|
+
class(value: string): this {
|
|
100
|
+
this.state.class = value;
|
|
122
101
|
return this;
|
|
123
102
|
}
|
|
124
103
|
|
|
125
|
-
|
|
126
|
-
this.
|
|
104
|
+
bind(event: string, handler: Function): this {
|
|
105
|
+
this._bindings.push({ event, handler });
|
|
127
106
|
return this;
|
|
128
107
|
}
|
|
129
108
|
|
|
130
|
-
|
|
131
|
-
|
|
109
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
110
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
111
|
+
throw new Error(`List.sync: Expected a State object for property "${property}"`);
|
|
112
|
+
}
|
|
113
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
132
114
|
return this;
|
|
133
115
|
}
|
|
134
116
|
|
|
135
117
|
/* -------------------------
|
|
136
|
-
|
|
137
|
-
|
|
118
|
+
* List operations
|
|
119
|
+
* ------------------------- */
|
|
138
120
|
|
|
139
121
|
add(item: ListItem, index?: number): this {
|
|
140
122
|
const items = [...this.state.items];
|
|
@@ -242,18 +224,21 @@ export class List {
|
|
|
242
224
|
}
|
|
243
225
|
|
|
244
226
|
/* -------------------------
|
|
245
|
-
|
|
246
|
-
|
|
227
|
+
* Helpers
|
|
228
|
+
* ------------------------- */
|
|
247
229
|
|
|
248
230
|
private _updateDOM(): void {
|
|
249
231
|
if (!this.container) return;
|
|
250
232
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
233
|
+
const existingWrapper = this.container.querySelector(`#${this._id}`);
|
|
234
|
+
if (existingWrapper) {
|
|
235
|
+
existingWrapper.remove();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this._buildAndAppendList();
|
|
254
239
|
}
|
|
255
240
|
|
|
256
|
-
private
|
|
241
|
+
private _buildAndAppendList(): void {
|
|
257
242
|
if (!this.container) return;
|
|
258
243
|
|
|
259
244
|
const { items, header, gap, direction, selectable, selectedIndex, style, class: className } = this.state;
|
|
@@ -296,7 +281,11 @@ export class List {
|
|
|
296
281
|
if (item.icon) {
|
|
297
282
|
const iconEl = document.createElement('span');
|
|
298
283
|
iconEl.className = 'jux-list-item-icon';
|
|
299
|
-
iconEl.
|
|
284
|
+
iconEl.style.display = 'flex';
|
|
285
|
+
iconEl.style.alignItems = 'center';
|
|
286
|
+
iconEl.style.justifyContent = 'center';
|
|
287
|
+
const iconElement = renderIcon(item.icon);
|
|
288
|
+
iconEl.appendChild(iconElement);
|
|
300
289
|
itemEl.appendChild(iconEl);
|
|
301
290
|
}
|
|
302
291
|
|
|
@@ -353,49 +342,101 @@ export class List {
|
|
|
353
342
|
}
|
|
354
343
|
|
|
355
344
|
/* -------------------------
|
|
356
|
-
|
|
357
|
-
|
|
345
|
+
* Render
|
|
346
|
+
* ------------------------- */
|
|
358
347
|
|
|
359
348
|
render(targetId?: string): this {
|
|
349
|
+
// === 1. SETUP: Get or create container ===
|
|
360
350
|
let container: HTMLElement;
|
|
361
|
-
|
|
362
351
|
if (targetId) {
|
|
363
352
|
const target = document.querySelector(targetId);
|
|
364
353
|
if (!target || !(target instanceof HTMLElement)) {
|
|
365
|
-
throw new Error(`List: Target
|
|
354
|
+
throw new Error(`List: Target "${targetId}" not found`);
|
|
366
355
|
}
|
|
367
356
|
container = target;
|
|
368
357
|
} else {
|
|
369
358
|
container = getOrCreateContainer(this._id);
|
|
370
359
|
}
|
|
371
|
-
|
|
372
360
|
this.container = container;
|
|
373
|
-
this.container.innerHTML = '';
|
|
374
361
|
|
|
375
|
-
|
|
362
|
+
// === 2. PREPARE: Destructure state ===
|
|
363
|
+
const { items, ordered, style, class: className } = this.state;
|
|
364
|
+
|
|
365
|
+
// === 3. BUILD: Create DOM elements ===
|
|
366
|
+
const list = document.createElement(ordered ? 'ol' : 'ul') as HTMLOListElement | HTMLUListElement;
|
|
367
|
+
list.className = `jux-list ${ordered ? 'jux-list-ordered' : 'jux-list-unordered'}`;
|
|
368
|
+
list.id = this._id;
|
|
369
|
+
if (className) list.className += ` ${className}`;
|
|
370
|
+
if (style) list.setAttribute('style', style);
|
|
371
|
+
|
|
372
|
+
items.forEach(item => {
|
|
373
|
+
const li = document.createElement('li');
|
|
374
|
+
li.className = 'jux-list-item';
|
|
375
|
+
|
|
376
|
+
// Handle ListItem object
|
|
377
|
+
if (item.title) {
|
|
378
|
+
li.textContent = item.title;
|
|
379
|
+
}
|
|
380
|
+
if (item.body) {
|
|
381
|
+
const bodyEl = document.createElement('div');
|
|
382
|
+
bodyEl.textContent = item.body;
|
|
383
|
+
li.appendChild(bodyEl);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
list.appendChild(li);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// === 4. WIRE: Attach event listeners and sync bindings ===
|
|
390
|
+
|
|
391
|
+
// Wire custom bindings from .bind() calls
|
|
392
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
393
|
+
list.addEventListener(event, handler as EventListener);
|
|
394
|
+
});
|
|
376
395
|
|
|
396
|
+
// Wire sync bindings from .sync() calls
|
|
397
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
398
|
+
if (property === 'items') {
|
|
399
|
+
const transformToComponent = toComponent || ((v: any) => v);
|
|
400
|
+
|
|
401
|
+
stateObj.subscribe((val: any) => {
|
|
402
|
+
const transformed = transformToComponent(val);
|
|
403
|
+
this.state.items = transformed;
|
|
404
|
+
|
|
405
|
+
// Re-render items
|
|
406
|
+
list.innerHTML = '';
|
|
407
|
+
transformed.forEach((item: ListItem) => {
|
|
408
|
+
const li = document.createElement('li');
|
|
409
|
+
li.className = 'jux-list-item';
|
|
410
|
+
|
|
411
|
+
// Handle ListItem object
|
|
412
|
+
if (item.title) {
|
|
413
|
+
li.textContent = item.title;
|
|
414
|
+
}
|
|
415
|
+
if (item.body) {
|
|
416
|
+
const bodyEl = document.createElement('div');
|
|
417
|
+
bodyEl.textContent = item.body;
|
|
418
|
+
li.appendChild(bodyEl);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
list.appendChild(li);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
428
|
+
container.appendChild(list);
|
|
377
429
|
return this;
|
|
378
430
|
}
|
|
379
431
|
|
|
380
|
-
/**
|
|
381
|
-
* Render to another Jux component's container
|
|
382
|
-
*/
|
|
383
432
|
renderTo(juxComponent: any): this {
|
|
384
|
-
if (!juxComponent
|
|
385
|
-
throw new Error('List.renderTo: Invalid component
|
|
433
|
+
if (!juxComponent?._id) {
|
|
434
|
+
throw new Error('List.renderTo: Invalid component');
|
|
386
435
|
}
|
|
387
|
-
|
|
388
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
389
|
-
throw new Error('List.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
390
|
-
}
|
|
391
|
-
|
|
392
436
|
return this.render(`#${juxComponent._id}`);
|
|
393
437
|
}
|
|
394
438
|
}
|
|
395
439
|
|
|
396
|
-
/**
|
|
397
|
-
* Factory helper
|
|
398
|
-
*/
|
|
399
440
|
export function list(id: string, options: ListOptions = {}): List {
|
|
400
441
|
return new List(id, options);
|
|
401
442
|
}
|
package/lib/components/menu.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
import { renderIcon } from './icons.js';
|
|
2
3
|
import { req } from './req.js';
|
|
4
|
+
import { State } from '../reactivity/state.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Menu item configuration
|
|
@@ -54,6 +56,12 @@ export class Menu {
|
|
|
54
56
|
_id: string;
|
|
55
57
|
id: string;
|
|
56
58
|
|
|
59
|
+
// Store bind() instructions (DOM events only)
|
|
60
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
61
|
+
|
|
62
|
+
// Store sync() instructions (state synchronization)
|
|
63
|
+
private _syncBindings: Array<{ property: string, stateObj: State<any>, toState?: Function, toComponent?: Function }> = [];
|
|
64
|
+
|
|
57
65
|
constructor(id: string, options: MenuOptions = {}) {
|
|
58
66
|
this._id = id;
|
|
59
67
|
this.id = id;
|
|
@@ -84,6 +92,31 @@ export class Menu {
|
|
|
84
92
|
}));
|
|
85
93
|
}
|
|
86
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Bind event handler (stores for wiring in render)
|
|
97
|
+
* DOM events only: click, mouseenter, mouseleave, etc.
|
|
98
|
+
*/
|
|
99
|
+
bind(event: string, handler: Function): this {
|
|
100
|
+
this._bindings.push({ event, handler });
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Two-way sync with state (stores for wiring in render)
|
|
106
|
+
*
|
|
107
|
+
* @param property - Component property to sync ('items', 'orientation', etc)
|
|
108
|
+
* @param stateObj - State object to sync with
|
|
109
|
+
* @param toState - Optional transform function when going from component to state
|
|
110
|
+
* @param toComponent - Optional transform function when going from state to component
|
|
111
|
+
*/
|
|
112
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
113
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
114
|
+
throw new Error(`Menu.sync: Expected a State object for property "${property}"`);
|
|
115
|
+
}
|
|
116
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
87
120
|
/* -------------------------
|
|
88
121
|
* Fluent API
|
|
89
122
|
* ------------------------- */
|
|
@@ -141,7 +174,8 @@ export class Menu {
|
|
|
141
174
|
if (item.icon) {
|
|
142
175
|
const icon = document.createElement('span');
|
|
143
176
|
icon.className = 'jux-menu-icon';
|
|
144
|
-
|
|
177
|
+
const iconElement = renderIcon(item.icon);
|
|
178
|
+
icon.appendChild(iconElement);
|
|
145
179
|
link.appendChild(icon);
|
|
146
180
|
}
|
|
147
181
|
|
|
@@ -158,7 +192,8 @@ export class Menu {
|
|
|
158
192
|
if (item.icon) {
|
|
159
193
|
const icon = document.createElement('span');
|
|
160
194
|
icon.className = 'jux-menu-icon';
|
|
161
|
-
|
|
195
|
+
const iconElement = renderIcon(item.icon);
|
|
196
|
+
icon.appendChild(iconElement);
|
|
162
197
|
button.appendChild(icon);
|
|
163
198
|
}
|
|
164
199
|
|
|
@@ -195,6 +230,7 @@ export class Menu {
|
|
|
195
230
|
* ------------------------- */
|
|
196
231
|
|
|
197
232
|
render(targetId?: string): this {
|
|
233
|
+
// === 1. SETUP: Get container ===
|
|
198
234
|
let container: HTMLElement;
|
|
199
235
|
|
|
200
236
|
if (targetId) {
|
|
@@ -208,8 +244,11 @@ export class Menu {
|
|
|
208
244
|
}
|
|
209
245
|
|
|
210
246
|
this.container = container;
|
|
247
|
+
|
|
248
|
+
// === 2. PREPARE: Destructure state ===
|
|
211
249
|
const { items, orientation, style, class: className } = this.state;
|
|
212
250
|
|
|
251
|
+
// === 3. BUILD: Create DOM elements ===
|
|
213
252
|
const menu = document.createElement('nav');
|
|
214
253
|
menu.className = `jux-menu jux-menu-${orientation}`;
|
|
215
254
|
menu.id = this._id;
|
|
@@ -226,7 +265,63 @@ export class Menu {
|
|
|
226
265
|
menu.appendChild(this._renderMenuItem(item));
|
|
227
266
|
});
|
|
228
267
|
|
|
268
|
+
// === 4. WIRE: Add event listeners ===
|
|
269
|
+
|
|
270
|
+
// Wire up custom event bindings (from .bind() calls)
|
|
271
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
272
|
+
menu.addEventListener(event, handler as EventListener);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Wire up sync bindings (from .sync() calls)
|
|
276
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
277
|
+
if (property === 'items') {
|
|
278
|
+
// Sync items array
|
|
279
|
+
const transformToComponent = toComponent || ((v: any) => v);
|
|
280
|
+
|
|
281
|
+
stateObj.subscribe((val: any) => {
|
|
282
|
+
const transformed = transformToComponent(val);
|
|
283
|
+
this.state.items = transformed;
|
|
284
|
+
this._setActiveStates();
|
|
285
|
+
|
|
286
|
+
// Re-render menu items
|
|
287
|
+
const existingItems = menu.querySelectorAll('.jux-menu-item');
|
|
288
|
+
existingItems.forEach(item => item.remove());
|
|
289
|
+
|
|
290
|
+
this.state.items.forEach(item => {
|
|
291
|
+
menu.appendChild(this._renderMenuItem(item));
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Re-trigger icon rendering
|
|
295
|
+
requestAnimationFrame(() => {
|
|
296
|
+
if ((window as any).lucide) {
|
|
297
|
+
(window as any).lucide.createIcons();
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
else if (property === 'orientation') {
|
|
303
|
+
// Sync orientation
|
|
304
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
305
|
+
|
|
306
|
+
stateObj.subscribe((val: any) => {
|
|
307
|
+
const transformed = transformToComponent(val);
|
|
308
|
+
menu.classList.remove(`jux-menu-${this.state.orientation}`);
|
|
309
|
+
this.state.orientation = transformed;
|
|
310
|
+
menu.classList.add(`jux-menu-${transformed}`);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
229
316
|
container.appendChild(menu);
|
|
317
|
+
|
|
318
|
+
// Trigger Lucide icon rendering
|
|
319
|
+
requestAnimationFrame(() => {
|
|
320
|
+
if ((window as any).lucide) {
|
|
321
|
+
(window as any).lucide.createIcons();
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
230
325
|
return this;
|
|
231
326
|
}
|
|
232
327
|
|