juxscript 1.1.350 → 1.1.353
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/dist/lib/components/blocks/menu.d.ts +40 -0
- package/dist/lib/components/blocks/menu.d.ts.map +1 -0
- package/dist/lib/components/blocks/menu.js +136 -0
- package/dist/lib/components/button.d.ts +33 -0
- package/dist/lib/components/button.d.ts.map +1 -0
- package/dist/lib/components/button.js +107 -0
- package/dist/lib/components/checkbox.d.ts +62 -0
- package/dist/lib/components/checkbox.d.ts.map +1 -0
- package/dist/lib/components/checkbox.js +178 -0
- package/dist/lib/components/container.d.ts +58 -0
- package/dist/lib/components/container.d.ts.map +1 -0
- package/dist/lib/components/container.js +151 -0
- package/dist/lib/components/data.d.ts +58 -0
- package/dist/lib/components/data.d.ts.map +1 -0
- package/dist/lib/components/data.js +130 -0
- package/dist/lib/components/grid.d.ts +58 -0
- package/dist/lib/components/grid.d.ts.map +1 -0
- package/dist/lib/components/grid.js +127 -0
- package/dist/lib/components/include.d.ts +86 -0
- package/dist/lib/components/include.d.ts.map +1 -0
- package/dist/lib/components/include.js +238 -0
- package/dist/lib/components/input.d.ts +58 -0
- package/dist/lib/components/input.d.ts.map +1 -0
- package/dist/lib/components/input.js +161 -0
- package/dist/lib/components/link.d.ts +35 -0
- package/dist/lib/components/link.d.ts.map +1 -0
- package/dist/lib/components/link.js +135 -0
- package/dist/lib/components/list.d.ts +48 -0
- package/dist/lib/components/list.d.ts.map +1 -0
- package/dist/lib/components/list.js +178 -0
- package/dist/lib/components/nav.d.ts +46 -0
- package/dist/lib/components/nav.d.ts.map +1 -0
- package/dist/lib/components/nav.js +189 -0
- package/dist/lib/components/radio.d.ts +40 -0
- package/dist/lib/components/radio.d.ts.map +1 -0
- package/dist/lib/components/radio.js +112 -0
- package/dist/lib/components/select.d.ts +41 -0
- package/dist/lib/components/select.d.ts.map +1 -0
- package/dist/lib/components/select.js +111 -0
- package/dist/lib/components/sidebar.d.ts +40 -0
- package/dist/lib/components/sidebar.d.ts.map +1 -0
- package/dist/lib/components/sidebar.js +141 -0
- package/dist/lib/components/store.d.ts +78 -0
- package/dist/lib/components/store.d.ts.map +1 -0
- package/dist/lib/components/store.js +248 -0
- package/dist/lib/components/style.d.ts +27 -0
- package/dist/lib/components/style.d.ts.map +1 -0
- package/dist/lib/components/style.js +52 -0
- package/dist/lib/components/table.d.ts +56 -0
- package/dist/lib/components/table.d.ts.map +1 -0
- package/dist/lib/components/table.js +199 -0
- package/dist/lib/components/tabs.d.ts +52 -0
- package/dist/lib/components/tabs.d.ts.map +1 -0
- package/dist/lib/components/tabs.js +206 -0
- package/dist/lib/components/tag.d.ts +41 -0
- package/dist/lib/components/tag.d.ts.map +1 -0
- package/dist/lib/components/tag.js +103 -0
- package/dist/lib/devtools/devtools.d.ts +3 -0
- package/dist/lib/devtools/devtools.d.ts.map +1 -0
- package/dist/lib/devtools/devtools.js +181 -0
- package/dist/lib/index.d.ts +68 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +63 -0
- package/dist/lib/state/pageState.d.ts +19 -0
- package/dist/lib/state/pageState.d.ts.map +1 -0
- package/dist/lib/state/pageState.js +360 -0
- package/dist/lib/utils/codeHighlight.d.ts +7 -0
- package/dist/lib/utils/codeHighlight.d.ts.map +1 -0
- package/dist/lib/utils/codeHighlight.js +105 -0
- package/dist/lib/utils/codeparser.d.ts +29 -0
- package/dist/lib/utils/codeparser.d.ts.map +1 -0
- package/dist/lib/utils/codeparser.js +384 -0
- package/dist/lib/utils/fetch.d.ts +176 -0
- package/dist/lib/utils/fetch.d.ts.map +1 -0
- package/dist/lib/utils/fetch.js +427 -0
- package/dist/lib/utils/formatId.d.ts +16 -0
- package/dist/lib/utils/formatId.d.ts.map +1 -0
- package/dist/lib/utils/formatId.js +27 -0
- package/dist/lib/utils/idgen.d.ts +2 -0
- package/dist/lib/utils/idgen.d.ts.map +1 -0
- package/dist/lib/utils/idgen.js +4 -0
- package/dist/lib/utils/niceName.d.ts +14 -0
- package/dist/lib/utils/niceName.d.ts.map +1 -0
- package/dist/lib/utils/niceName.js +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import generateId from '../utils/idgen.js';
|
|
2
|
+
import { pageState } from '../state/pageState.js';
|
|
3
|
+
const JUSTIFY_MAP = {
|
|
4
|
+
start: 'flex-start',
|
|
5
|
+
center: 'center',
|
|
6
|
+
end: 'flex-end',
|
|
7
|
+
between: 'space-between',
|
|
8
|
+
around: 'space-around',
|
|
9
|
+
evenly: 'space-evenly'
|
|
10
|
+
};
|
|
11
|
+
const ALIGN_MAP = {
|
|
12
|
+
start: 'flex-start',
|
|
13
|
+
center: 'center',
|
|
14
|
+
end: 'flex-end',
|
|
15
|
+
stretch: 'stretch',
|
|
16
|
+
baseline: 'baseline'
|
|
17
|
+
};
|
|
18
|
+
class Container {
|
|
19
|
+
constructor(id, options = {}) {
|
|
20
|
+
this.id = id || generateId();
|
|
21
|
+
this.opts = {
|
|
22
|
+
layout: 'flex',
|
|
23
|
+
direction: 'column',
|
|
24
|
+
tag: 'div',
|
|
25
|
+
...options
|
|
26
|
+
};
|
|
27
|
+
this._element = document.createElement(this.opts.tag);
|
|
28
|
+
this._element.id = this.id;
|
|
29
|
+
// Classes
|
|
30
|
+
if (this.opts.class)
|
|
31
|
+
this._element.className = this.opts.class;
|
|
32
|
+
// Build computed style from layout options
|
|
33
|
+
const styles = this._buildStyles();
|
|
34
|
+
if (this.opts.style)
|
|
35
|
+
styles.push(this.opts.style);
|
|
36
|
+
if (styles.length)
|
|
37
|
+
this._element.setAttribute('style', styles.join(';'));
|
|
38
|
+
// Content (rare for containers, but supported)
|
|
39
|
+
if (this.opts.content)
|
|
40
|
+
this._element.textContent = this.opts.content;
|
|
41
|
+
// Extra attributes
|
|
42
|
+
for (const [key, value] of Object.entries(this.opts)) {
|
|
43
|
+
if ([
|
|
44
|
+
'layout', 'direction', 'wrap', 'gap', 'padding', 'align', 'justify',
|
|
45
|
+
'columns', 'width', 'height', 'maxWidth', 'minHeight',
|
|
46
|
+
'class', 'style', 'target', 'tag', 'content'
|
|
47
|
+
].includes(key))
|
|
48
|
+
continue;
|
|
49
|
+
this._element.setAttribute(`data-${key}`, String(value));
|
|
50
|
+
}
|
|
51
|
+
// Mount
|
|
52
|
+
const resolvedTarget = this.opts.target;
|
|
53
|
+
const parent = resolvedTarget
|
|
54
|
+
? document.getElementById(resolvedTarget) || document.querySelector(resolvedTarget)
|
|
55
|
+
: document.getElementById('app');
|
|
56
|
+
parent?.appendChild(this._element);
|
|
57
|
+
}
|
|
58
|
+
// ═══════════════════════════════════════════════════════════
|
|
59
|
+
// LAYOUT HELPERS
|
|
60
|
+
// ═══════════════════════════════════════════════════════════
|
|
61
|
+
_buildStyles() {
|
|
62
|
+
const s = [];
|
|
63
|
+
const o = this.opts;
|
|
64
|
+
if (o.layout === 'grid') {
|
|
65
|
+
s.push('display:grid');
|
|
66
|
+
if (o.columns) {
|
|
67
|
+
const cols = typeof o.columns === 'number'
|
|
68
|
+
? `repeat(${o.columns}, 1fr)`
|
|
69
|
+
: o.columns;
|
|
70
|
+
s.push(`grid-template-columns:${cols}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (o.layout === 'flex') {
|
|
74
|
+
s.push('display:flex');
|
|
75
|
+
if (o.direction)
|
|
76
|
+
s.push(`flex-direction:${o.direction}`);
|
|
77
|
+
if (o.wrap)
|
|
78
|
+
s.push('flex-wrap:wrap');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
s.push('display:block');
|
|
82
|
+
}
|
|
83
|
+
if (o.gap)
|
|
84
|
+
s.push(`gap:${o.gap}`);
|
|
85
|
+
if (o.padding)
|
|
86
|
+
s.push(`padding:${o.padding}`);
|
|
87
|
+
if (o.align)
|
|
88
|
+
s.push(`align-items:${ALIGN_MAP[o.align] || o.align}`);
|
|
89
|
+
if (o.justify)
|
|
90
|
+
s.push(`justify-content:${JUSTIFY_MAP[o.justify] || o.justify}`);
|
|
91
|
+
if (o.width)
|
|
92
|
+
s.push(`width:${o.width}`);
|
|
93
|
+
if (o.height)
|
|
94
|
+
s.push(`height:${o.height}`);
|
|
95
|
+
if (o.maxWidth)
|
|
96
|
+
s.push(`max-width:${o.maxWidth}`);
|
|
97
|
+
if (o.minHeight)
|
|
98
|
+
s.push(`min-height:${o.minHeight}`);
|
|
99
|
+
return s;
|
|
100
|
+
}
|
|
101
|
+
// ═══════════════════════════════════════════════════════════
|
|
102
|
+
// PAGESTATE INTEGRATION
|
|
103
|
+
// ═══════════════════════════════════════════════════════════
|
|
104
|
+
getValue() { return this._element.textContent ?? ''; }
|
|
105
|
+
getContent() { return this._element.textContent ?? this.opts.content ?? ''; }
|
|
106
|
+
setValue(val) {
|
|
107
|
+
this.opts.content = val;
|
|
108
|
+
this._element.textContent = val;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
setContent(val) { return this.setValue(val); }
|
|
112
|
+
setClass(val) {
|
|
113
|
+
this._element.className = val;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
setStyle(val) {
|
|
117
|
+
this._element.setAttribute('style', val);
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
setInnerHTML(val) {
|
|
121
|
+
this._element.innerHTML = val;
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
getElement() { return this._element; }
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a layout container.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Flex column (default)
|
|
131
|
+
* jux.container('main', { gap: '16px', padding: '24px' });
|
|
132
|
+
*
|
|
133
|
+
* // Flex row
|
|
134
|
+
* jux.container('toolbar', { direction: 'row', gap: '8px', align: 'center' });
|
|
135
|
+
*
|
|
136
|
+
* // Grid
|
|
137
|
+
* jux.container('grid', { layout: 'grid', columns: 3, gap: '16px' });
|
|
138
|
+
*
|
|
139
|
+
* // Centered page wrapper
|
|
140
|
+
* jux.container('page', { maxWidth: '960px', padding: '24px 16px', align: 'center' });
|
|
141
|
+
*
|
|
142
|
+
* // Nested
|
|
143
|
+
* jux.container('card', { gap: '8px', padding: '16px', target: 'grid' });
|
|
144
|
+
*/
|
|
145
|
+
export function container(id, options = {}) {
|
|
146
|
+
const c = new Container(id, options);
|
|
147
|
+
pageState.__register(c);
|
|
148
|
+
return c;
|
|
149
|
+
}
|
|
150
|
+
export { Container };
|
|
151
|
+
export default container;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface DataOptions {
|
|
2
|
+
url?: string;
|
|
3
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
body?: any;
|
|
6
|
+
transform?: (raw: any) => any;
|
|
7
|
+
auto?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare class Data {
|
|
10
|
+
id: string;
|
|
11
|
+
options: DataOptions;
|
|
12
|
+
private _value;
|
|
13
|
+
private _loading;
|
|
14
|
+
private _error;
|
|
15
|
+
private _onChange;
|
|
16
|
+
constructor(id: string, options?: DataOptions);
|
|
17
|
+
url(value: string): this;
|
|
18
|
+
method(value: DataOptions['method']): this;
|
|
19
|
+
headers(value: Record<string, string>): this;
|
|
20
|
+
body(value: any): this;
|
|
21
|
+
transform(fn: (raw: any) => any): this;
|
|
22
|
+
onChange(fn: (value: any) => void): this;
|
|
23
|
+
getValue(): any;
|
|
24
|
+
getLoading(): boolean;
|
|
25
|
+
getError(): string | null;
|
|
26
|
+
setValue(val: any): this;
|
|
27
|
+
/**
|
|
28
|
+
* Re-fetch the data and notify pageState.
|
|
29
|
+
* Triggers all watchers that depend on this data's value.
|
|
30
|
+
*/
|
|
31
|
+
refresh(): Promise<any>;
|
|
32
|
+
fetch(urlOverride?: string): Promise<any>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a data source that integrates with pageState.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Auto-fetch on creation
|
|
39
|
+
* const api = await jux.data('api', { url: '/api/test' });
|
|
40
|
+
* // pageState['api'].value → { users: [...], posts: [...] }
|
|
41
|
+
*
|
|
42
|
+
* // With transform
|
|
43
|
+
* const users = await jux.data('users', {
|
|
44
|
+
* url: '/api/test',
|
|
45
|
+
* transform: raw => raw.users
|
|
46
|
+
* });
|
|
47
|
+
* // pageState['users'].value → [{ id: 1, name: 'Alice' }, ...]
|
|
48
|
+
*
|
|
49
|
+
* // Manual fetch
|
|
50
|
+
* const manual = jux.data('manual', { url: '/api/test', auto: false });
|
|
51
|
+
* await manual.fetch(); // fetch later
|
|
52
|
+
*
|
|
53
|
+
* // Re-fetch
|
|
54
|
+
* await pageState['api'].refresh();
|
|
55
|
+
*/
|
|
56
|
+
export declare function data(id: string, options?: DataOptions): Promise<Data>;
|
|
57
|
+
export { Data, DataOptions };
|
|
58
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../lib/components/data.ts"],"names":[],"mappings":"AAGA,UAAU,WAAW;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,cAAM,IAAI;IACN,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,SAAS,CAAuC;gBAE5C,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB;IAMjD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IACxB,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,IAAI;IAC1C,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAC5C,IAAI,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IACtB,SAAS,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,GAAG,IAAI;IAEtC,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAMxC,QAAQ,IAAI,GAAG;IACf,UAAU,IAAI,OAAO;IACrB,QAAQ,IAAI,MAAM,GAAG,IAAI;IAEzB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI;IAMxB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC;IAQvB,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;CA+DlD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAW/E;AAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import generateId from '../utils/idgen.js';
|
|
2
|
+
import { pageState } from '../state/pageState.js';
|
|
3
|
+
class Data {
|
|
4
|
+
constructor(id, options = {}) {
|
|
5
|
+
this._value = null;
|
|
6
|
+
this._loading = false;
|
|
7
|
+
this._error = null;
|
|
8
|
+
this._onChange = null;
|
|
9
|
+
this.id = id || generateId();
|
|
10
|
+
this.options = { method: 'GET', auto: true, ...options };
|
|
11
|
+
}
|
|
12
|
+
// Fluent API
|
|
13
|
+
url(value) { this.options.url = value; return this; }
|
|
14
|
+
method(value) { this.options.method = value; return this; }
|
|
15
|
+
headers(value) { this.options.headers = value; return this; }
|
|
16
|
+
body(value) { this.options.body = value; return this; }
|
|
17
|
+
transform(fn) { this.options.transform = fn; return this; }
|
|
18
|
+
onChange(fn) {
|
|
19
|
+
this._onChange = fn;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
// Getters for pageState integration
|
|
23
|
+
getValue() { return this._value; }
|
|
24
|
+
getLoading() { return this._loading; }
|
|
25
|
+
getError() { return this._error; }
|
|
26
|
+
setValue(val) {
|
|
27
|
+
this._value = val;
|
|
28
|
+
if (this._onChange)
|
|
29
|
+
this._onChange(val);
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Re-fetch the data and notify pageState.
|
|
34
|
+
* Triggers all watchers that depend on this data's value.
|
|
35
|
+
*/
|
|
36
|
+
async refresh() {
|
|
37
|
+
const result = await this.fetch();
|
|
38
|
+
// Notify pageState so watchers re-run
|
|
39
|
+
pageState.__notify(`${this.id}.value`);
|
|
40
|
+
pageState.__notify(`${this.id}.loading`);
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
async fetch(urlOverride) {
|
|
44
|
+
const fetchUrl = urlOverride || this.options.url;
|
|
45
|
+
if (!fetchUrl) {
|
|
46
|
+
this._error = 'No URL provided';
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
this._loading = true;
|
|
50
|
+
this._error = null;
|
|
51
|
+
// Notify pageState of loading change
|
|
52
|
+
if (pageState.__notify) {
|
|
53
|
+
pageState.__notify(`${this.id}.loading`);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const fetchOptions = {
|
|
57
|
+
method: this.options.method,
|
|
58
|
+
headers: this.options.headers,
|
|
59
|
+
};
|
|
60
|
+
if (this.options.body && this.options.method !== 'GET') {
|
|
61
|
+
fetchOptions.body = typeof this.options.body === 'string'
|
|
62
|
+
? this.options.body
|
|
63
|
+
: JSON.stringify(this.options.body);
|
|
64
|
+
if (!this.options.headers?.['Content-Type']) {
|
|
65
|
+
fetchOptions.headers = {
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
...fetchOptions.headers
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const res = await globalThis.fetch(fetchUrl, fetchOptions);
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
74
|
+
}
|
|
75
|
+
const contentType = res.headers.get('content-type') || '';
|
|
76
|
+
let raw;
|
|
77
|
+
if (contentType.includes('application/json')) {
|
|
78
|
+
raw = await res.json();
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
raw = await res.text();
|
|
82
|
+
}
|
|
83
|
+
this._value = this.options.transform ? this.options.transform(raw) : raw;
|
|
84
|
+
this._loading = false;
|
|
85
|
+
this._error = null;
|
|
86
|
+
if (this._onChange)
|
|
87
|
+
this._onChange(this._value);
|
|
88
|
+
return this._value;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
this._loading = false;
|
|
92
|
+
this._error = err.message || 'Fetch failed';
|
|
93
|
+
console.error(`❌ jux.data('${this.id}') fetch error:`, err.message);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a data source that integrates with pageState.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // Auto-fetch on creation
|
|
103
|
+
* const api = await jux.data('api', { url: '/api/test' });
|
|
104
|
+
* // pageState['api'].value → { users: [...], posts: [...] }
|
|
105
|
+
*
|
|
106
|
+
* // With transform
|
|
107
|
+
* const users = await jux.data('users', {
|
|
108
|
+
* url: '/api/test',
|
|
109
|
+
* transform: raw => raw.users
|
|
110
|
+
* });
|
|
111
|
+
* // pageState['users'].value → [{ id: 1, name: 'Alice' }, ...]
|
|
112
|
+
*
|
|
113
|
+
* // Manual fetch
|
|
114
|
+
* const manual = jux.data('manual', { url: '/api/test', auto: false });
|
|
115
|
+
* await manual.fetch(); // fetch later
|
|
116
|
+
*
|
|
117
|
+
* // Re-fetch
|
|
118
|
+
* await pageState['api'].refresh();
|
|
119
|
+
*/
|
|
120
|
+
export async function data(id, options = {}) {
|
|
121
|
+
const d = new Data(id, options);
|
|
122
|
+
pageState.__register(d);
|
|
123
|
+
if (options.auto !== false && options.url) {
|
|
124
|
+
await d.fetch();
|
|
125
|
+
// Re-register so pageState picks up the fetched value
|
|
126
|
+
pageState.__register(d);
|
|
127
|
+
}
|
|
128
|
+
return d;
|
|
129
|
+
}
|
|
130
|
+
export { Data };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
type GridDensity = 'dense' | 'normal' | 'inflated';
|
|
2
|
+
interface GridColumn {
|
|
3
|
+
name: string;
|
|
4
|
+
width?: string;
|
|
5
|
+
}
|
|
6
|
+
interface GridOptions {
|
|
7
|
+
rows?: number;
|
|
8
|
+
cols?: number | GridColumn[];
|
|
9
|
+
density?: GridDensity;
|
|
10
|
+
class?: string;
|
|
11
|
+
style?: string;
|
|
12
|
+
target?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
declare class Grid {
|
|
16
|
+
id: string;
|
|
17
|
+
opts: GridOptions;
|
|
18
|
+
private _element;
|
|
19
|
+
private _columns;
|
|
20
|
+
constructor(id: string, options?: GridOptions);
|
|
21
|
+
getValue(): string;
|
|
22
|
+
getContent(): string;
|
|
23
|
+
setContent(val: string): this;
|
|
24
|
+
setClass(val: string): this;
|
|
25
|
+
setStyle(val: string): this;
|
|
26
|
+
setInnerHTML(val: string): this;
|
|
27
|
+
getElement(): HTMLElement;
|
|
28
|
+
/** Get a named column cell element */
|
|
29
|
+
getColumn(name: string): HTMLElement | null;
|
|
30
|
+
getColumns(): GridColumn[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a CSS grid layout container.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Simple: 3 equal columns
|
|
37
|
+
* jux.grid('cards', { cols: 3 });
|
|
38
|
+
*
|
|
39
|
+
* // Named columns with custom widths
|
|
40
|
+
* jux.grid('layout', {
|
|
41
|
+
* cols: [
|
|
42
|
+
* { name: 'sidebar', width: '260px' },
|
|
43
|
+
* { name: 'content', width: '3fr' },
|
|
44
|
+
* { name: 'aside', width: '1fr' },
|
|
45
|
+
* ]
|
|
46
|
+
* });
|
|
47
|
+
* // Then target into them:
|
|
48
|
+
* jux.nav('main-nav', { target: 'sidebar' });
|
|
49
|
+
* jux.h1('title', { content: 'Hello', target: 'content' });
|
|
50
|
+
*
|
|
51
|
+
* // Density options
|
|
52
|
+
* jux.grid('tight', { cols: 4, density: 'dense' });
|
|
53
|
+
* jux.grid('spacious', { cols: 2, density: 'inflated' });
|
|
54
|
+
*/
|
|
55
|
+
export declare function grid(id: string, options?: GridOptions): Grid;
|
|
56
|
+
export { Grid, GridOptions, GridColumn, GridDensity };
|
|
57
|
+
export default grid;
|
|
58
|
+
//# sourceMappingURL=grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../lib/components/grid.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD,UAAU,UAAU;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,WAAW;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAQD,cAAM,IAAI;IACN,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAoB;gBAExB,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB;IAoEjD,QAAQ,IAAI,MAAM;IAClB,UAAU,IAAI,MAAM;IAEpB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK7B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK3B,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK/B,UAAU,IAAI,WAAW;IAEzB,sCAAsC;IACtC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAI3C,UAAU,IAAI,UAAU,EAAE;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,IAAI,CAIhE;AAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACtD,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import generateId from '../utils/idgen.js';
|
|
2
|
+
import { pageState } from '../state/pageState.js';
|
|
3
|
+
const DENSITY_MAP = {
|
|
4
|
+
dense: { gap: '4px', padding: '4px' },
|
|
5
|
+
normal: { gap: '12px', padding: '12px' },
|
|
6
|
+
inflated: { gap: '24px', padding: '24px' },
|
|
7
|
+
};
|
|
8
|
+
class Grid {
|
|
9
|
+
constructor(id, options = {}) {
|
|
10
|
+
this._columns = [];
|
|
11
|
+
this.id = id || generateId();
|
|
12
|
+
this.opts = {
|
|
13
|
+
cols: 2,
|
|
14
|
+
density: 'normal',
|
|
15
|
+
...options
|
|
16
|
+
};
|
|
17
|
+
this._element = document.createElement('div');
|
|
18
|
+
this._element.id = this.id;
|
|
19
|
+
if (this.opts.class)
|
|
20
|
+
this._element.className = this.opts.class;
|
|
21
|
+
// Resolve columns
|
|
22
|
+
const cols = this.opts.cols;
|
|
23
|
+
let templateCols;
|
|
24
|
+
if (cols && typeof cols === 'object' && Array.isArray(cols)) {
|
|
25
|
+
this._columns = cols.map(c => ({
|
|
26
|
+
name: c.name,
|
|
27
|
+
width: c.width || '1fr'
|
|
28
|
+
}));
|
|
29
|
+
templateCols = this._columns.map(c => c.width).join(' ');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const count = typeof cols === 'number' ? cols : 2;
|
|
33
|
+
templateCols = `repeat(${count}, 1fr)`;
|
|
34
|
+
}
|
|
35
|
+
const d = DENSITY_MAP[this.opts.density] || DENSITY_MAP.normal;
|
|
36
|
+
const parts = [
|
|
37
|
+
'display:grid',
|
|
38
|
+
`grid-template-columns:${templateCols}`,
|
|
39
|
+
`gap:${d.gap}`,
|
|
40
|
+
`padding:${d.padding}`,
|
|
41
|
+
];
|
|
42
|
+
if (this.opts.rows) {
|
|
43
|
+
parts.push(`grid-template-rows:repeat(${this.opts.rows}, auto)`);
|
|
44
|
+
}
|
|
45
|
+
if (this.opts.style)
|
|
46
|
+
parts.push(this.opts.style);
|
|
47
|
+
this._element.setAttribute('style', parts.join(';'));
|
|
48
|
+
// Extra attributes
|
|
49
|
+
for (const [key, value] of Object.entries(this.opts)) {
|
|
50
|
+
if (['rows', 'cols', 'density', 'class', 'style', 'target'].includes(key))
|
|
51
|
+
continue;
|
|
52
|
+
this._element.setAttribute(`data-${key}`, String(value));
|
|
53
|
+
}
|
|
54
|
+
// Mount
|
|
55
|
+
const resolvedTarget = this.opts.target;
|
|
56
|
+
const parent = resolvedTarget
|
|
57
|
+
? document.getElementById(resolvedTarget) || document.querySelector(resolvedTarget)
|
|
58
|
+
: document.getElementById('app');
|
|
59
|
+
parent?.appendChild(this._element);
|
|
60
|
+
// Create named column cells if using array cols
|
|
61
|
+
if (this._columns.length > 0) {
|
|
62
|
+
for (const col of this._columns) {
|
|
63
|
+
const cell = document.createElement('div');
|
|
64
|
+
cell.id = col.name;
|
|
65
|
+
cell.setAttribute('data-grid-col', col.name);
|
|
66
|
+
this._element.appendChild(cell);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ═══════════════════════════════════════════════════════════
|
|
71
|
+
// PAGESTATE INTEGRATION
|
|
72
|
+
// ═══════════════════════════════════════════════════════════
|
|
73
|
+
getValue() { return ''; }
|
|
74
|
+
getContent() { return this._element.textContent ?? ''; }
|
|
75
|
+
setContent(val) {
|
|
76
|
+
this._element.textContent = val;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
setClass(val) {
|
|
80
|
+
this._element.className = val;
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
setStyle(val) {
|
|
84
|
+
this._element.setAttribute('style', val);
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
setInnerHTML(val) {
|
|
88
|
+
this._element.innerHTML = val;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
getElement() { return this._element; }
|
|
92
|
+
/** Get a named column cell element */
|
|
93
|
+
getColumn(name) {
|
|
94
|
+
return this._element.querySelector(`[data-grid-col="${name}"]`);
|
|
95
|
+
}
|
|
96
|
+
getColumns() { return [...this._columns]; }
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a CSS grid layout container.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // Simple: 3 equal columns
|
|
103
|
+
* jux.grid('cards', { cols: 3 });
|
|
104
|
+
*
|
|
105
|
+
* // Named columns with custom widths
|
|
106
|
+
* jux.grid('layout', {
|
|
107
|
+
* cols: [
|
|
108
|
+
* { name: 'sidebar', width: '260px' },
|
|
109
|
+
* { name: 'content', width: '3fr' },
|
|
110
|
+
* { name: 'aside', width: '1fr' },
|
|
111
|
+
* ]
|
|
112
|
+
* });
|
|
113
|
+
* // Then target into them:
|
|
114
|
+
* jux.nav('main-nav', { target: 'sidebar' });
|
|
115
|
+
* jux.h1('title', { content: 'Hello', target: 'content' });
|
|
116
|
+
*
|
|
117
|
+
* // Density options
|
|
118
|
+
* jux.grid('tight', { cols: 4, density: 'dense' });
|
|
119
|
+
* jux.grid('spacious', { cols: 2, density: 'inflated' });
|
|
120
|
+
*/
|
|
121
|
+
export function grid(id, options = {}) {
|
|
122
|
+
const g = new Grid(id, options);
|
|
123
|
+
pageState.__register(g);
|
|
124
|
+
return g;
|
|
125
|
+
}
|
|
126
|
+
export { Grid };
|
|
127
|
+
export default grid;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Include - Simplified resource injection for bundled and external resources
|
|
3
|
+
* Supports page-specific scoping and cleanup
|
|
4
|
+
*/
|
|
5
|
+
type ResourceType = 'css' | 'js' | 'module';
|
|
6
|
+
interface IncludeOptions {
|
|
7
|
+
type?: ResourceType;
|
|
8
|
+
target?: string;
|
|
9
|
+
async?: boolean;
|
|
10
|
+
defer?: boolean;
|
|
11
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
12
|
+
integrity?: string;
|
|
13
|
+
pageScoped?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class Include {
|
|
16
|
+
private url;
|
|
17
|
+
private options;
|
|
18
|
+
private element;
|
|
19
|
+
private pageId;
|
|
20
|
+
constructor(url: string, options?: IncludeOptions);
|
|
21
|
+
css(): this;
|
|
22
|
+
js(): this;
|
|
23
|
+
module(): this;
|
|
24
|
+
async(): this;
|
|
25
|
+
defer(): this;
|
|
26
|
+
/**
|
|
27
|
+
* Inject into specific container instead of <head>
|
|
28
|
+
* Useful for page-specific styles
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* jux.include('/css/page1.css').into('#page1-container');
|
|
32
|
+
*/
|
|
33
|
+
into(selector: string): this;
|
|
34
|
+
/**
|
|
35
|
+
* Mark as page-scoped for automatic cleanup
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* jux.include('/css/dashboard.css').forPage('dashboard');
|
|
39
|
+
* // Later: Include.cleanupPage('dashboard');
|
|
40
|
+
*/
|
|
41
|
+
forPage(pageId: string): this;
|
|
42
|
+
render(): this;
|
|
43
|
+
private createStylesheet;
|
|
44
|
+
private createScript;
|
|
45
|
+
private getContainer;
|
|
46
|
+
private isAlreadyLoaded;
|
|
47
|
+
remove(): this;
|
|
48
|
+
/**
|
|
49
|
+
* Remove all resources for a specific page
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* Include.cleanupPage('dashboard');
|
|
53
|
+
*/
|
|
54
|
+
static cleanupPage(pageId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Remove all page-scoped resources
|
|
57
|
+
*/
|
|
58
|
+
static cleanupAll(): void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Factory function - simplified usage
|
|
62
|
+
*
|
|
63
|
+
* Usage:
|
|
64
|
+
* // Basic (auto-detects from extension)
|
|
65
|
+
* jux.include('/css/styles.css');
|
|
66
|
+
* jux.include('/js/app.js');
|
|
67
|
+
*
|
|
68
|
+
* // Page-specific with cleanup
|
|
69
|
+
* jux.include('/css/dashboard.css').forPage('dashboard');
|
|
70
|
+
* jux.include('/js/dashboard.js').forPage('dashboard');
|
|
71
|
+
*
|
|
72
|
+
* // Later cleanup:
|
|
73
|
+
* Include.cleanupPage('dashboard');
|
|
74
|
+
*
|
|
75
|
+
* // Inject into specific container
|
|
76
|
+
* jux.include('/css/widget.css').into('#widget-container');
|
|
77
|
+
*
|
|
78
|
+
* // External CDN
|
|
79
|
+
* jux.include('https://cdn.tailwindcss.com').js();
|
|
80
|
+
*
|
|
81
|
+
* // Module
|
|
82
|
+
* jux.include('/js/app.mjs').module();
|
|
83
|
+
*/
|
|
84
|
+
export declare function include(url: string, options?: IncludeOptions): Include;
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=include.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../../../lib/components/include.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,YAAY,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;AAE5C,UAAU,cAAc;IACpB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAKD,qBAAa,OAAO;IAChB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,MAAM,CAAuB;gBAEzB,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;IAoBrD,GAAG,IAAI,IAAI;IAKX,EAAE,IAAI,IAAI;IAKV,MAAM,IAAI,IAAI;IAKd,KAAK,IAAI,IAAI;IAKb,KAAK,IAAI,IAAI;IAKb;;;;;;OAMG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK5B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAU7B,MAAM,IAAI,IAAI;IAoDd,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,eAAe;IAKvB,MAAM,IAAI,IAAI;IAYd;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAYxC;;OAEG;IACH,MAAM,CAAC,UAAU,IAAI,IAAI;CAS5B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAItE"}
|