juxscript 1.1.2 → 1.1.4
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/machinery/build3.js +7 -91
- package/machinery/compiler3.js +3 -209
- package/machinery/config.js +93 -6
- package/machinery/serve.js +255 -0
- package/machinery/watcher.js +49 -161
- package/package.json +19 -5
- package/lib/components/alert.ts +0 -200
- package/lib/components/app.ts +0 -247
- package/lib/components/badge.ts +0 -101
- package/lib/components/base/BaseComponent.ts +0 -421
- package/lib/components/base/FormInput.ts +0 -227
- package/lib/components/button.ts +0 -178
- package/lib/components/card.ts +0 -173
- package/lib/components/chart.ts +0 -231
- package/lib/components/checkbox.ts +0 -242
- package/lib/components/code.ts +0 -123
- package/lib/components/container.ts +0 -140
- package/lib/components/data.ts +0 -135
- package/lib/components/datepicker.ts +0 -234
- package/lib/components/dialog.ts +0 -172
- package/lib/components/divider.ts +0 -100
- package/lib/components/dropdown.ts +0 -186
- package/lib/components/element.ts +0 -267
- package/lib/components/fileupload.ts +0 -309
- package/lib/components/grid.ts +0 -291
- package/lib/components/guard.ts +0 -92
- package/lib/components/heading.ts +0 -96
- package/lib/components/helpers.ts +0 -41
- package/lib/components/hero.ts +0 -224
- package/lib/components/icon.ts +0 -178
- package/lib/components/icons.ts +0 -464
- package/lib/components/include.ts +0 -410
- package/lib/components/input.ts +0 -457
- package/lib/components/list.ts +0 -419
- package/lib/components/loading.ts +0 -100
- package/lib/components/menu.ts +0 -275
- package/lib/components/modal.ts +0 -284
- package/lib/components/nav.ts +0 -257
- package/lib/components/paragraph.ts +0 -97
- package/lib/components/progress.ts +0 -159
- package/lib/components/radio.ts +0 -278
- package/lib/components/req.ts +0 -303
- package/lib/components/script.ts +0 -41
- package/lib/components/select.ts +0 -252
- package/lib/components/sidebar.ts +0 -275
- package/lib/components/style.ts +0 -41
- package/lib/components/switch.ts +0 -246
- package/lib/components/table.ts +0 -1249
- package/lib/components/tabs.ts +0 -250
- package/lib/components/theme-toggle.ts +0 -293
- package/lib/components/tooltip.ts +0 -144
- package/lib/components/view.ts +0 -190
- package/lib/components/write.ts +0 -272
- package/lib/layouts/default.css +0 -260
- package/lib/layouts/figma.css +0 -334
- package/lib/reactivity/state.ts +0 -78
- package/lib/utils/fetch.ts +0 -553
- package/machinery/ast.js +0 -347
- package/machinery/build.js +0 -466
- package/machinery/bundleAssets.js +0 -0
- package/machinery/bundleJux.js +0 -0
- package/machinery/bundleVendors.js +0 -0
- package/machinery/doc-generator.js +0 -136
- package/machinery/imports.js +0 -155
- package/machinery/server.js +0 -166
- package/machinery/ts-shim.js +0 -46
- package/machinery/validators/file-validator.js +0 -123
package/lib/components/view.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* View component options
|
|
5
|
-
*/
|
|
6
|
-
export interface ViewOptions {
|
|
7
|
-
title?: string;
|
|
8
|
-
description?: string;
|
|
9
|
-
children?: any[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* View component state
|
|
14
|
-
*/
|
|
15
|
-
type ViewState = {
|
|
16
|
-
title: string;
|
|
17
|
-
description: string;
|
|
18
|
-
children: any[];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* View component - container for organizing page content
|
|
23
|
-
*
|
|
24
|
-
* Usage:
|
|
25
|
-
* const view = jux.view('myView', {
|
|
26
|
-
* title: 'Dashboard',
|
|
27
|
-
* description: 'Main dashboard view'
|
|
28
|
-
* });
|
|
29
|
-
* view.add(component1);
|
|
30
|
-
* await view.render();
|
|
31
|
-
*/
|
|
32
|
-
export class View {
|
|
33
|
-
state: ViewState;
|
|
34
|
-
container: HTMLElement | null = null;
|
|
35
|
-
_id: string;
|
|
36
|
-
id: string;
|
|
37
|
-
|
|
38
|
-
constructor(id: string, options: ViewOptions = {}) {
|
|
39
|
-
this._id = id;
|
|
40
|
-
this.id = id;
|
|
41
|
-
|
|
42
|
-
this.state = {
|
|
43
|
-
title: options.title ?? '',
|
|
44
|
-
description: options.description ?? '',
|
|
45
|
-
children: Array.isArray(options.children) ? [...options.children] : []
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/* -------------------------
|
|
50
|
-
* Fluent API
|
|
51
|
-
* ------------------------- */
|
|
52
|
-
|
|
53
|
-
title(value: string): this {
|
|
54
|
-
this.state.title = value;
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
description(value: string): this {
|
|
59
|
-
this.state.description = value;
|
|
60
|
-
return this;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
add(component: any | any[]): this {
|
|
64
|
-
if (Array.isArray(component)) {
|
|
65
|
-
this.state.children.push(...component);
|
|
66
|
-
} else {
|
|
67
|
-
this.state.children.push(component);
|
|
68
|
-
}
|
|
69
|
-
return this;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* -------------------------
|
|
73
|
-
* Render
|
|
74
|
-
* ------------------------- */
|
|
75
|
-
|
|
76
|
-
async render(targetId?: string): Promise<this> {
|
|
77
|
-
let container: HTMLElement;
|
|
78
|
-
|
|
79
|
-
if (targetId) {
|
|
80
|
-
const target = document.querySelector(targetId);
|
|
81
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
82
|
-
throw new Error(`View: Target element "${targetId}" not found`);
|
|
83
|
-
}
|
|
84
|
-
container = target;
|
|
85
|
-
} else {
|
|
86
|
-
container = getOrCreateContainer(this._id);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
this.container = container;
|
|
90
|
-
const { title, description, children } = this.state;
|
|
91
|
-
|
|
92
|
-
const view = document.createElement('div');
|
|
93
|
-
view.className = 'jux-view';
|
|
94
|
-
view.id = this._id;
|
|
95
|
-
|
|
96
|
-
// View header
|
|
97
|
-
if (title || description) {
|
|
98
|
-
const header = document.createElement('div');
|
|
99
|
-
header.className = 'jux-view-header';
|
|
100
|
-
|
|
101
|
-
if (title) {
|
|
102
|
-
const titleEl = document.createElement('h1');
|
|
103
|
-
titleEl.className = 'jux-view-title';
|
|
104
|
-
titleEl.textContent = title;
|
|
105
|
-
header.appendChild(titleEl);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (description) {
|
|
109
|
-
const descEl = document.createElement('p');
|
|
110
|
-
descEl.className = 'jux-view-description';
|
|
111
|
-
descEl.textContent = description;
|
|
112
|
-
header.appendChild(descEl);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
view.appendChild(header);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// View content
|
|
119
|
-
const content = document.createElement('div');
|
|
120
|
-
content.className = 'jux-view-content';
|
|
121
|
-
|
|
122
|
-
// Render children
|
|
123
|
-
for (let i = 0; i < children.length; i++) {
|
|
124
|
-
const child = children[i];
|
|
125
|
-
if (!child) continue;
|
|
126
|
-
|
|
127
|
-
// Get child ID
|
|
128
|
-
let childId: string | null = null;
|
|
129
|
-
if ((child as any).id) {
|
|
130
|
-
childId = (child as any).id;
|
|
131
|
-
} else if ((child as any)._id) {
|
|
132
|
-
childId = (child as any)._id;
|
|
133
|
-
} else {
|
|
134
|
-
childId = `${this._id}-child-${i}`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Create wrapper for child
|
|
138
|
-
const childWrapper = document.createElement('div');
|
|
139
|
-
childWrapper.className = 'jux-view-item';
|
|
140
|
-
childWrapper.id = `${childId}-wrapper`;
|
|
141
|
-
content.appendChild(childWrapper);
|
|
142
|
-
|
|
143
|
-
// Render child INTO the wrapper
|
|
144
|
-
if (typeof (child as any).render === 'function') {
|
|
145
|
-
try {
|
|
146
|
-
// Pass the wrapper element directly
|
|
147
|
-
const result = (child as any).render(childWrapper);
|
|
148
|
-
if (result && typeof (result as any).then === 'function') {
|
|
149
|
-
await result;
|
|
150
|
-
}
|
|
151
|
-
} catch (err) {
|
|
152
|
-
console.error(`View: Error rendering child ${i}:`, err);
|
|
153
|
-
childWrapper.innerHTML = `<div style="color: #ff6b6b; padding: 1rem;">Error: ${(err as Error).message}</div>`;
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
// If no render method, try to append directly
|
|
157
|
-
if (child instanceof HTMLElement) {
|
|
158
|
-
childWrapper.appendChild(child);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
view.appendChild(content);
|
|
164
|
-
container.appendChild(view);
|
|
165
|
-
|
|
166
|
-
return this;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Render to another Jux component's container
|
|
171
|
-
*/
|
|
172
|
-
async renderTo(juxComponent: any): Promise<this> {
|
|
173
|
-
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
174
|
-
throw new Error('View.renderTo: Invalid component - not an object');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
178
|
-
throw new Error('View.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return this.render(`#${juxComponent._id}`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Factory helper
|
|
187
|
-
*/
|
|
188
|
-
export function view(id: string, options: ViewOptions = {}): View {
|
|
189
|
-
return new View(id, options);
|
|
190
|
-
}
|
package/lib/components/write.ts
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Write - Simple content writer with no component tracking
|
|
3
|
-
* Perfect for quick HTML output without ID management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface WriteOptions {
|
|
7
|
-
tagType?: string;
|
|
8
|
-
className?: string;
|
|
9
|
-
style?: string;
|
|
10
|
-
attributes?: Record<string, string>;
|
|
11
|
-
html?: boolean; // If true, treat content as HTML; if false, treat as text
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Write content directly to a target element
|
|
16
|
-
*
|
|
17
|
-
* Usage:
|
|
18
|
-
* // Write text (defaults to body)
|
|
19
|
-
* jux.write('Hello World!').render();
|
|
20
|
-
*
|
|
21
|
-
* // Write to specific target
|
|
22
|
-
* jux.write('Content').render('#container');
|
|
23
|
-
*
|
|
24
|
-
* // Write HTML
|
|
25
|
-
* jux.write('<strong>Bold text</strong>').html(true).render('#container');
|
|
26
|
-
*
|
|
27
|
-
* // Write with styling
|
|
28
|
-
* jux.write('Styled text').style('color: red;').render('#container');
|
|
29
|
-
*/
|
|
30
|
-
export class Write {
|
|
31
|
-
private content: string;
|
|
32
|
-
private options: WriteOptions;
|
|
33
|
-
|
|
34
|
-
constructor(content: string, options: WriteOptions = {}) {
|
|
35
|
-
this.content = content;
|
|
36
|
-
this.options = {
|
|
37
|
-
tagType: 'div',
|
|
38
|
-
className: '',
|
|
39
|
-
style: '',
|
|
40
|
-
attributes: {},
|
|
41
|
-
html: false,
|
|
42
|
-
...options
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
47
|
-
* FLUENT API
|
|
48
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Set HTML mode (treat content as HTML)
|
|
52
|
-
*/
|
|
53
|
-
html(enabled: boolean = true): this {
|
|
54
|
-
this.options.html = enabled;
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Set tag type
|
|
60
|
-
*/
|
|
61
|
-
tagType(value: string): this {
|
|
62
|
-
this.options.tagType = value;
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Set CSS class
|
|
68
|
-
*/
|
|
69
|
-
className(value: string): this {
|
|
70
|
-
this.options.className = value;
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Set inline styles
|
|
76
|
-
*/
|
|
77
|
-
style(value: string): this {
|
|
78
|
-
this.options.style = value;
|
|
79
|
-
return this;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Set custom attributes
|
|
84
|
-
*/
|
|
85
|
-
attrs(attributes: Record<string, string>): this {
|
|
86
|
-
this.options.attributes = attributes;
|
|
87
|
-
return this;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
91
|
-
* RENDER METHODS
|
|
92
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Render content to target element
|
|
96
|
-
*/
|
|
97
|
-
render(targetSelector?: string): this {
|
|
98
|
-
const target = this._getTarget(targetSelector);
|
|
99
|
-
if (!target) return this;
|
|
100
|
-
|
|
101
|
-
const element = this._createElement();
|
|
102
|
-
target.appendChild(element);
|
|
103
|
-
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Replace target content (clear first, then render)
|
|
109
|
-
*/
|
|
110
|
-
replace(targetSelector?: string): this {
|
|
111
|
-
const target = this._getTarget(targetSelector);
|
|
112
|
-
if (!target) return this;
|
|
113
|
-
|
|
114
|
-
target.innerHTML = '';
|
|
115
|
-
const element = this._createElement();
|
|
116
|
-
target.appendChild(element);
|
|
117
|
-
|
|
118
|
-
return this;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Render before target element
|
|
123
|
-
*/
|
|
124
|
-
before(targetSelector: string): this {
|
|
125
|
-
const target = document.querySelector(targetSelector);
|
|
126
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
127
|
-
console.warn(`Write: Target element "${targetSelector}" not found`);
|
|
128
|
-
return this;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const element = this._createElement();
|
|
132
|
-
target.parentNode?.insertBefore(element, target);
|
|
133
|
-
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Render after target element
|
|
139
|
-
*/
|
|
140
|
-
after(targetSelector: string): this {
|
|
141
|
-
const target = document.querySelector(targetSelector);
|
|
142
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
143
|
-
console.warn(`Write: Target element "${targetSelector}" not found`);
|
|
144
|
-
return this;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const element = this._createElement();
|
|
148
|
-
target.parentNode?.insertBefore(element, target.nextSibling);
|
|
149
|
-
|
|
150
|
-
return this;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Prepend to target (insert as first child)
|
|
155
|
-
*/
|
|
156
|
-
prepend(targetSelector?: string): this {
|
|
157
|
-
const target = this._getTarget(targetSelector);
|
|
158
|
-
if (!target) return this;
|
|
159
|
-
|
|
160
|
-
const element = this._createElement();
|
|
161
|
-
target.insertBefore(element, target.firstChild);
|
|
162
|
-
|
|
163
|
-
return this;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Append to target (alias for render)
|
|
168
|
-
*/
|
|
169
|
-
append(targetSelector?: string): this {
|
|
170
|
-
return this.render(targetSelector);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
174
|
-
* PRIVATE HELPERS
|
|
175
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
176
|
-
|
|
177
|
-
private _getTarget(selector?: string): HTMLElement | null {
|
|
178
|
-
const targetSelector = selector || 'body';
|
|
179
|
-
const target = document.querySelector(targetSelector);
|
|
180
|
-
|
|
181
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
182
|
-
console.warn(`Write: Target element "${targetSelector}" not found`);
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return target;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private _createElement(): HTMLElement {
|
|
190
|
-
const element = document.createElement(this.options.tagType!);
|
|
191
|
-
|
|
192
|
-
// Set content (text or HTML)
|
|
193
|
-
if (this.options.html) {
|
|
194
|
-
element.innerHTML = this.content;
|
|
195
|
-
} else {
|
|
196
|
-
element.textContent = this.content;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Apply className
|
|
200
|
-
if (this.options.className) {
|
|
201
|
-
element.className = this.options.className;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Apply inline styles
|
|
205
|
-
if (this.options.style) {
|
|
206
|
-
element.setAttribute('style', this.options.style);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Apply custom attributes
|
|
210
|
-
if (this.options.attributes) {
|
|
211
|
-
Object.entries(this.options.attributes).forEach(([key, value]) => {
|
|
212
|
-
element.setAttribute(key, value);
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return element;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Factory function - simple, no overloads
|
|
222
|
-
*/
|
|
223
|
-
export function write(content: string, options: WriteOptions = {}): Write {
|
|
224
|
-
return new Write(content, options);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
228
|
-
* SHORTHAND HELPERS
|
|
229
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Write text (explicit)
|
|
233
|
-
*/
|
|
234
|
-
export function writeText(content: string, options: Omit<WriteOptions, 'html'> = {}): Write {
|
|
235
|
-
return new Write(content, { ...options, html: false });
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Write HTML (explicit)
|
|
240
|
-
*/
|
|
241
|
-
export function writeHtml(content: string, options: Omit<WriteOptions, 'html'> = {}): Write {
|
|
242
|
-
return new Write(content, { ...options, html: true });
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Write paragraph
|
|
247
|
-
*/
|
|
248
|
-
export function writeParagraph(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
249
|
-
return new Write(content, { ...options, tagType: 'p' });
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Write heading
|
|
254
|
-
*/
|
|
255
|
-
export function writeHeading(content: string, level: 1 | 2 | 3 | 4 | 5 | 6 = 2, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
256
|
-
return new Write(content, { ...options, tagType: `h${level}` });
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Write span
|
|
261
|
-
*/
|
|
262
|
-
export function writeSpan(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
263
|
-
return new Write(content, { ...options, tagType: 'span' });
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Write div (explicit)
|
|
268
|
-
*/
|
|
269
|
-
export function writeDiv(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
270
|
-
return new Write(content, { ...options, tagType: 'div' });
|
|
271
|
-
}
|
|
272
|
-
|
package/lib/layouts/default.css
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/* Default Layout Grid (Notion-style) */
|
|
2
|
-
|
|
3
|
-
#app {
|
|
4
|
-
|
|
5
|
-
display: grid;
|
|
6
|
-
grid-template-areas:
|
|
7
|
-
"header header"
|
|
8
|
-
"sidebar main"
|
|
9
|
-
"footer footer";
|
|
10
|
-
grid-template-columns: 250px 1fr;
|
|
11
|
-
grid-template-rows: auto 1fr auto;
|
|
12
|
-
min-height: 100vh;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/* Header */
|
|
16
|
-
#appheader {
|
|
17
|
-
grid-area: header;
|
|
18
|
-
position: sticky;
|
|
19
|
-
top: 0;
|
|
20
|
-
z-index: 100;
|
|
21
|
-
background: var(--color-surface-elevated);
|
|
22
|
-
border-bottom: var(--border-width) solid var(--color-border);
|
|
23
|
-
display: flex;
|
|
24
|
-
align-items: center;
|
|
25
|
-
padding: var(--space-md) var(--space-lg);
|
|
26
|
-
gap: var(--space-lg);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
#appheader-logo {
|
|
30
|
-
flex-shrink: 0;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
#appheader-nav {
|
|
34
|
-
flex: 1;
|
|
35
|
-
display: flex;
|
|
36
|
-
gap: var(--space-md);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
#appheader-actions {
|
|
40
|
-
flex-shrink: 0;
|
|
41
|
-
display: flex;
|
|
42
|
-
gap: var(--space-sm);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/* Subheader - Hidden in default layout */
|
|
46
|
-
#appsubheader {
|
|
47
|
-
display: none;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
#appsubheader-breadcrumbs,
|
|
51
|
-
#appsubheader-tabs,
|
|
52
|
-
#appsubheader-actions {
|
|
53
|
-
display: none;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/* Sidebar */
|
|
57
|
-
#appsidebar {
|
|
58
|
-
grid-area: sidebar;
|
|
59
|
-
overflow-y: auto;
|
|
60
|
-
background: var(--color-surface-base);
|
|
61
|
-
border-right: var(--border-width) solid var(--color-border);
|
|
62
|
-
transition: transform var(--transition-base);
|
|
63
|
-
display: flex;
|
|
64
|
-
flex-direction: column;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
#appsidebar-header {
|
|
68
|
-
padding: var(--space-md);
|
|
69
|
-
border-bottom: var(--border-width) solid var(--color-border);
|
|
70
|
-
flex-shrink: 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
#appsidebar-content {
|
|
74
|
-
flex: 1;
|
|
75
|
-
overflow-y: auto;
|
|
76
|
-
padding: var(--space-sm);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
#appsidebar-footer {
|
|
80
|
-
padding: var(--space-md);
|
|
81
|
-
border-top: var(--border-width) solid var(--color-border);
|
|
82
|
-
flex-shrink: 0;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/* Main */
|
|
86
|
-
#appmain {
|
|
87
|
-
grid-area: main;
|
|
88
|
-
overflow-y: auto;
|
|
89
|
-
padding: var(--space-xl);
|
|
90
|
-
background: var(--color-background);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/* Aside - Hidden in default layout */
|
|
94
|
-
#appaside {
|
|
95
|
-
display: none;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
#appaside-header,
|
|
99
|
-
#appaside-content,
|
|
100
|
-
#appaside-footer {
|
|
101
|
-
display: none;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/* Footer */
|
|
105
|
-
#appfooter {
|
|
106
|
-
grid-area: footer;
|
|
107
|
-
background: var(--color-surface-elevated);
|
|
108
|
-
border-top: var(--border-width) solid var(--color-border);
|
|
109
|
-
padding: var(--space-lg) var(--space-xl);
|
|
110
|
-
display: flex;
|
|
111
|
-
justify-content: space-between;
|
|
112
|
-
align-items: center;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
#appfooter-content {
|
|
116
|
-
flex: 1;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
#appfooter-legal {
|
|
120
|
-
flex-shrink: 0;
|
|
121
|
-
font-size: var(--font-size-sm);
|
|
122
|
-
color: var(--color-text-secondary);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* Modal */
|
|
126
|
-
#appmodal {
|
|
127
|
-
position: fixed;
|
|
128
|
-
top: 0;
|
|
129
|
-
left: 0;
|
|
130
|
-
right: 0;
|
|
131
|
-
bottom: 0;
|
|
132
|
-
z-index: 2000;
|
|
133
|
-
display: none;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
#appmodal[aria-hidden="false"] {
|
|
137
|
-
display: flex;
|
|
138
|
-
align-items: center;
|
|
139
|
-
justify-content: center;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
#appmodal-backdrop {
|
|
143
|
-
position: absolute;
|
|
144
|
-
inset: 0;
|
|
145
|
-
background: rgba(0, 0, 0, 0.6);
|
|
146
|
-
backdrop-filter: blur(4px);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
#appmodal-container {
|
|
150
|
-
position: relative;
|
|
151
|
-
background: var(--color-surface-elevated);
|
|
152
|
-
border-radius: var(--radius-lg);
|
|
153
|
-
box-shadow: var(--shadow-2xl);
|
|
154
|
-
max-width: 90vw;
|
|
155
|
-
max-height: 90vh;
|
|
156
|
-
display: flex;
|
|
157
|
-
flex-direction: column;
|
|
158
|
-
overflow: hidden;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
#appmodal-header {
|
|
162
|
-
padding: var(--space-lg);
|
|
163
|
-
border-bottom: var(--border-width) solid var(--color-border);
|
|
164
|
-
flex-shrink: 0;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
#appmodal-content {
|
|
168
|
-
flex: 1;
|
|
169
|
-
overflow-y: auto;
|
|
170
|
-
padding: var(--space-lg);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
#appmodal-footer {
|
|
174
|
-
padding: var(--space-lg);
|
|
175
|
-
border-top: var(--border-width) solid var(--color-border);
|
|
176
|
-
flex-shrink: 0;
|
|
177
|
-
display: flex;
|
|
178
|
-
gap: var(--space-sm);
|
|
179
|
-
justify-content: flex-end;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/* Tablet (portrait) */
|
|
183
|
-
@media (max-width: 1024px) {
|
|
184
|
-
#app {
|
|
185
|
-
grid-template-columns: 200px 1fr;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
#appmain {
|
|
189
|
-
padding: var(--space-lg);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
#appheader {
|
|
193
|
-
padding: var(--space-sm) var(--space-md);
|
|
194
|
-
gap: var(--space-md);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/* Mobile */
|
|
199
|
-
@media (max-width: 768px) {
|
|
200
|
-
#app {
|
|
201
|
-
grid-template-areas:
|
|
202
|
-
"header"
|
|
203
|
-
"main"
|
|
204
|
-
"footer";
|
|
205
|
-
grid-template-columns: 1fr;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
#appsidebar {
|
|
209
|
-
position: fixed;
|
|
210
|
-
top: 60px;
|
|
211
|
-
left: 0;
|
|
212
|
-
bottom: 0;
|
|
213
|
-
width: 280px;
|
|
214
|
-
max-width: 80vw;
|
|
215
|
-
z-index: 99;
|
|
216
|
-
transform: translateX(-100%);
|
|
217
|
-
box-shadow: var(--shadow-xl);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
#appsidebar.visible {
|
|
221
|
-
transform: translateX(0);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
#appmain {
|
|
225
|
-
padding: var(--space-md);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
#appheader {
|
|
229
|
-
padding: var(--space-xs) var(--space-md);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
#appfooter {
|
|
233
|
-
padding: var(--space-md) var(--space-lg);
|
|
234
|
-
flex-direction: column;
|
|
235
|
-
gap: var(--space-sm);
|
|
236
|
-
text-align: center;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/* Small mobile */
|
|
241
|
-
@media (max-width: 480px) {
|
|
242
|
-
#appsidebar {
|
|
243
|
-
width: 100%;
|
|
244
|
-
max-width: 100vw;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
#appmain {
|
|
248
|
-
padding: var(--space-sm);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
#appfooter {
|
|
252
|
-
padding: var(--space-sm) var(--space-md);
|
|
253
|
-
font-size: var(--font-size-sm);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
#appmodal-container {
|
|
257
|
-
max-width: 95vw;
|
|
258
|
-
max-height: 95vh;
|
|
259
|
-
}
|
|
260
|
-
}
|