juxscript 1.0.0
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/README.md +292 -0
- package/bin/cli.js +149 -0
- package/lib/adapters/base-adapter.js +35 -0
- package/lib/adapters/index.js +33 -0
- package/lib/adapters/mysql-adapter.js +65 -0
- package/lib/adapters/postgres-adapter.js +70 -0
- package/lib/adapters/sqlite-adapter.js +56 -0
- package/lib/components/app.ts +124 -0
- package/lib/components/button.ts +136 -0
- package/lib/components/card.ts +205 -0
- package/lib/components/chart.ts +125 -0
- package/lib/components/code.ts +242 -0
- package/lib/components/container.ts +282 -0
- package/lib/components/data.ts +105 -0
- package/lib/components/docs-data.json +1211 -0
- package/lib/components/error-handler.ts +285 -0
- package/lib/components/footer.ts +146 -0
- package/lib/components/header.ts +167 -0
- package/lib/components/hero.ts +170 -0
- package/lib/components/import.ts +430 -0
- package/lib/components/input.ts +175 -0
- package/lib/components/layout.ts +113 -0
- package/lib/components/list.ts +392 -0
- package/lib/components/main.ts +111 -0
- package/lib/components/menu.ts +170 -0
- package/lib/components/modal.ts +216 -0
- package/lib/components/nav.ts +136 -0
- package/lib/components/node.ts +200 -0
- package/lib/components/reactivity.js +104 -0
- package/lib/components/script.ts +152 -0
- package/lib/components/sidebar.ts +168 -0
- package/lib/components/style.ts +129 -0
- package/lib/components/table.ts +279 -0
- package/lib/components/tabs.ts +191 -0
- package/lib/components/theme.ts +97 -0
- package/lib/components/view.ts +174 -0
- package/lib/jux.ts +203 -0
- package/lib/layouts/default.css +260 -0
- package/lib/layouts/default.jux +8 -0
- package/lib/layouts/figma.css +334 -0
- package/lib/layouts/figma.jux +0 -0
- package/lib/layouts/notion.css +258 -0
- package/lib/styles/base-theme.css +186 -0
- package/lib/styles/dark-theme.css +144 -0
- package/lib/styles/global.css +1131 -0
- package/lib/styles/light-theme.css +144 -0
- package/lib/styles/tokens/dark.css +86 -0
- package/lib/styles/tokens/light.css +86 -0
- package/lib/themes/dark.css +86 -0
- package/lib/themes/light.css +86 -0
- package/lib/utils/path-resolver.js +23 -0
- package/machinery/compiler.js +262 -0
- package/machinery/doc-generator.js +160 -0
- package/machinery/generators/css.js +128 -0
- package/machinery/generators/html.js +108 -0
- package/machinery/imports.js +155 -0
- package/machinery/server.js +185 -0
- package/machinery/validators/file-validator.js +123 -0
- package/machinery/watcher.js +148 -0
- package/package.json +58 -0
- package/types/globals.d.ts +16 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Reactive, getOrCreateContainer } from './reactivity.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 extends Reactive {
|
|
33
|
+
state!: ViewState;
|
|
34
|
+
container: HTMLElement | null = null;
|
|
35
|
+
|
|
36
|
+
constructor(componentId: string, options: ViewOptions = {}) {
|
|
37
|
+
super();
|
|
38
|
+
this._setComponentId(componentId);
|
|
39
|
+
|
|
40
|
+
this.state = this._createReactiveState({
|
|
41
|
+
title: options.title ?? '',
|
|
42
|
+
description: options.description ?? '',
|
|
43
|
+
children: Array.isArray(options.children) ? [...options.children] : [],
|
|
44
|
+
}) as ViewState;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* -------------------------
|
|
48
|
+
* Fluent API
|
|
49
|
+
* ------------------------- */
|
|
50
|
+
|
|
51
|
+
title(value: string): this {
|
|
52
|
+
this.state.title = value;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
description(value: string): this {
|
|
57
|
+
this.state.description = value;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
add(component: any | any[]): this {
|
|
62
|
+
if (Array.isArray(component)) {
|
|
63
|
+
this.state.children.push(...component);
|
|
64
|
+
} else {
|
|
65
|
+
this.state.children.push(component);
|
|
66
|
+
}
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* -------------------------
|
|
71
|
+
* Render
|
|
72
|
+
* ------------------------- */
|
|
73
|
+
|
|
74
|
+
async render(targetId?: string): Promise<this> {
|
|
75
|
+
let container: HTMLElement;
|
|
76
|
+
|
|
77
|
+
if (targetId) {
|
|
78
|
+
const target = document.querySelector(targetId);
|
|
79
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
80
|
+
throw new Error(`View: Target element "${targetId}" not found`);
|
|
81
|
+
}
|
|
82
|
+
container = target;
|
|
83
|
+
} else {
|
|
84
|
+
container = getOrCreateContainer(this._componentId) as HTMLElement;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.container = container;
|
|
88
|
+
const { title, description, children } = this.state;
|
|
89
|
+
|
|
90
|
+
const view = document.createElement('div');
|
|
91
|
+
view.className = 'jux-view';
|
|
92
|
+
view.id = this._componentId;
|
|
93
|
+
|
|
94
|
+
// View header
|
|
95
|
+
if (title || description) {
|
|
96
|
+
const header = document.createElement('div');
|
|
97
|
+
header.className = 'jux-view-header';
|
|
98
|
+
|
|
99
|
+
if (title) {
|
|
100
|
+
const titleEl = document.createElement('h1');
|
|
101
|
+
titleEl.className = 'jux-view-title';
|
|
102
|
+
titleEl.textContent = title;
|
|
103
|
+
header.appendChild(titleEl);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (description) {
|
|
107
|
+
const descEl = document.createElement('p');
|
|
108
|
+
descEl.className = 'jux-view-description';
|
|
109
|
+
descEl.textContent = description;
|
|
110
|
+
header.appendChild(descEl);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
view.appendChild(header);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// View content
|
|
117
|
+
const content = document.createElement('div');
|
|
118
|
+
content.className = 'jux-view-content';
|
|
119
|
+
|
|
120
|
+
// Render children
|
|
121
|
+
for (let i = 0; i < children.length; i++) {
|
|
122
|
+
const child = children[i];
|
|
123
|
+
if (!child) continue;
|
|
124
|
+
|
|
125
|
+
let childId = (child as any)._componentId ?? `view-child-${i}`;
|
|
126
|
+
|
|
127
|
+
const childWrapper = document.createElement('div');
|
|
128
|
+
childWrapper.className = 'jux-view-item';
|
|
129
|
+
childWrapper.id = childId;
|
|
130
|
+
content.appendChild(childWrapper);
|
|
131
|
+
|
|
132
|
+
// Event binding - render children
|
|
133
|
+
if (typeof (child as any).render === 'function') {
|
|
134
|
+
try {
|
|
135
|
+
const result = (child as any).render(childWrapper);
|
|
136
|
+
if (result && typeof (result as any).then === 'function') {
|
|
137
|
+
await result;
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error(`View: Error rendering child ${i}:`, err);
|
|
141
|
+
childWrapper.innerHTML = `<div style="color: #ff6b6b; padding: 1rem;">Error: ${(err as Error).message}</div>`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
view.appendChild(content);
|
|
147
|
+
container.appendChild(view);
|
|
148
|
+
|
|
149
|
+
this.emit('rendered');
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Render to another Jux component's container
|
|
155
|
+
*/
|
|
156
|
+
async renderTo(juxComponent: any): Promise<this> {
|
|
157
|
+
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
158
|
+
throw new Error('View.renderTo: Invalid component - not an object');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
|
|
162
|
+
throw new Error('View.renderTo: Invalid component - missing _componentId (not a Jux component)');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return this.render(`#${juxComponent._componentId}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Factory helper
|
|
171
|
+
*/
|
|
172
|
+
export function view(componentId: string, options: ViewOptions = {}): View {
|
|
173
|
+
return new View(componentId, options);
|
|
174
|
+
}
|
package/lib/jux.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jux Runtime - Shared across all pages
|
|
3
|
+
* Imports all components and provides the jux global object
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Reactive } from './components/reactivity.js';
|
|
7
|
+
import { Data, data } from './components/data.js';
|
|
8
|
+
import { table, Table, type TableOptions, type TableColumn } from './components/table.js';
|
|
9
|
+
import { hero, Hero, type HeroOptions } from './components/hero.js';
|
|
10
|
+
import { card, Card, type CardOptions, type CardAction } from './components/card.js';
|
|
11
|
+
import { button, Button, type ButtonOptions } from './components/button.js';
|
|
12
|
+
import { header, Header, type HeaderOptions } from './components/header.js';
|
|
13
|
+
import { footer, Footer, type FooterOptions } from './components/footer.js';
|
|
14
|
+
import { main, Main, type MainOptions } from './components/main.js';
|
|
15
|
+
import { sidebar, Sidebar, type SidebarOptions } from './components/sidebar.js';
|
|
16
|
+
import { container, Container, type ContainerOptions } from './components/container.js';
|
|
17
|
+
import { modal, Modal, type ModalOptions } from './components/modal.js';
|
|
18
|
+
import { tabs, Tabs, type TabsOptions, type Tab } from './components/tabs.js';
|
|
19
|
+
import { list, List, type ListOptions, type ListItem } from './components/list.js';
|
|
20
|
+
import { menu, Menu, type MenuOptions, type MenuItem } from './components/menu.js';
|
|
21
|
+
import { nav, Nav, type NavOptions, type NavItem } from './components/nav.js';
|
|
22
|
+
import { chart, Chart, type ChartOptions } from './components/chart.js';
|
|
23
|
+
import { view, View, type ViewOptions } from './components/view.js';
|
|
24
|
+
import { code, Code, type CodeOptions } from './components/code.js';
|
|
25
|
+
import { input, Input, type InputOptions } from './components/input.js';
|
|
26
|
+
import { node, Node, type NodeOptions } from './components/node.js';
|
|
27
|
+
import { App } from './components/app.js';
|
|
28
|
+
import { Style } from './components/style.js';
|
|
29
|
+
import { Script } from './components/script.js';
|
|
30
|
+
import { Layout } from './components/layout.js';
|
|
31
|
+
import { ErrorHandler } from './components/error-handler.js';
|
|
32
|
+
import { theme, Theme } from './components/theme.js';
|
|
33
|
+
|
|
34
|
+
/* -------------------------
|
|
35
|
+
* Type Exports
|
|
36
|
+
* ------------------------- */
|
|
37
|
+
|
|
38
|
+
export type {
|
|
39
|
+
HeroOptions,
|
|
40
|
+
ButtonOptions,
|
|
41
|
+
ContainerOptions,
|
|
42
|
+
ListOptions,
|
|
43
|
+
ListItem,
|
|
44
|
+
HeaderOptions,
|
|
45
|
+
FooterOptions,
|
|
46
|
+
MainOptions,
|
|
47
|
+
SidebarOptions,
|
|
48
|
+
ModalOptions,
|
|
49
|
+
TabsOptions,
|
|
50
|
+
Tab,
|
|
51
|
+
MenuOptions,
|
|
52
|
+
MenuItem,
|
|
53
|
+
NavOptions,
|
|
54
|
+
NavItem,
|
|
55
|
+
ViewOptions,
|
|
56
|
+
TableOptions,
|
|
57
|
+
TableColumn,
|
|
58
|
+
ChartOptions,
|
|
59
|
+
CodeOptions,
|
|
60
|
+
InputOptions,
|
|
61
|
+
NodeOptions,
|
|
62
|
+
CardOptions,
|
|
63
|
+
CardAction
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/* -------------------------
|
|
67
|
+
* Class Exports
|
|
68
|
+
* ------------------------- */
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
Reactive,
|
|
72
|
+
Data,
|
|
73
|
+
Hero,
|
|
74
|
+
Card,
|
|
75
|
+
Button,
|
|
76
|
+
Container,
|
|
77
|
+
List,
|
|
78
|
+
Table,
|
|
79
|
+
Header,
|
|
80
|
+
Footer,
|
|
81
|
+
Main,
|
|
82
|
+
Sidebar,
|
|
83
|
+
Modal,
|
|
84
|
+
Tabs,
|
|
85
|
+
Menu,
|
|
86
|
+
Nav,
|
|
87
|
+
Chart,
|
|
88
|
+
Code,
|
|
89
|
+
Input,
|
|
90
|
+
View,
|
|
91
|
+
Node,
|
|
92
|
+
App,
|
|
93
|
+
Style,
|
|
94
|
+
Script,
|
|
95
|
+
Layout,
|
|
96
|
+
Theme
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/* -------------------------
|
|
100
|
+
* Jux Singleton
|
|
101
|
+
* ------------------------- */
|
|
102
|
+
|
|
103
|
+
class Jux {
|
|
104
|
+
apiUrl: string = '/api/query';
|
|
105
|
+
|
|
106
|
+
// Utility methods
|
|
107
|
+
param(name: string): string | null {
|
|
108
|
+
if (typeof window !== 'undefined' && (window as any).juxContext?.[name] !== undefined) {
|
|
109
|
+
return (window as any).juxContext[name];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (typeof window !== 'undefined') {
|
|
113
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
114
|
+
return urlParams.get(name);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Component methods
|
|
121
|
+
data = data;
|
|
122
|
+
table = table;
|
|
123
|
+
hero = hero;
|
|
124
|
+
card = card;
|
|
125
|
+
button = button;
|
|
126
|
+
header = header;
|
|
127
|
+
footer = footer;
|
|
128
|
+
main = main;
|
|
129
|
+
sidebar = sidebar;
|
|
130
|
+
container = container;
|
|
131
|
+
modal = modal;
|
|
132
|
+
tabs = tabs;
|
|
133
|
+
list = list;
|
|
134
|
+
menu = menu;
|
|
135
|
+
code = code;
|
|
136
|
+
nav = nav;
|
|
137
|
+
input = input;
|
|
138
|
+
node = node;
|
|
139
|
+
view = view;
|
|
140
|
+
chart = chart;
|
|
141
|
+
theme = theme;
|
|
142
|
+
|
|
143
|
+
// App-level configuration methods
|
|
144
|
+
app(): App {
|
|
145
|
+
return new App();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
style(contentOrUrl: string): Style {
|
|
149
|
+
return new Style(contentOrUrl);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
script(contentOrSrc: string): Script {
|
|
153
|
+
return new Script(contentOrSrc);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
layout(juxFile: string): Layout {
|
|
157
|
+
return new Layout(juxFile);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const jux = new Jux();
|
|
162
|
+
|
|
163
|
+
/* -------------------------
|
|
164
|
+
* Exports
|
|
165
|
+
*
|
|
166
|
+
* ⚠️ IMPORTANT: When adding a new component:
|
|
167
|
+
* 1. Import it at the top
|
|
168
|
+
* 2. Add to Jux class as a method
|
|
169
|
+
* 3. Export as $ alias below
|
|
170
|
+
* 4. Update machinery/compiler → COMPONENT_ALIASES array
|
|
171
|
+
* ------------------------- */
|
|
172
|
+
|
|
173
|
+
export {
|
|
174
|
+
jux,
|
|
175
|
+
// $ aliases - just re-export the factory functions
|
|
176
|
+
table as $table,
|
|
177
|
+
hero as $hero,
|
|
178
|
+
card as $card,
|
|
179
|
+
button as $button,
|
|
180
|
+
header as $header,
|
|
181
|
+
footer as $footer,
|
|
182
|
+
main as $main,
|
|
183
|
+
sidebar as $sidebar,
|
|
184
|
+
container as $container,
|
|
185
|
+
modal as $modal,
|
|
186
|
+
tabs as $tabs,
|
|
187
|
+
list as $list,
|
|
188
|
+
menu as $menu,
|
|
189
|
+
code as $code,
|
|
190
|
+
nav as $nav,
|
|
191
|
+
input as $input,
|
|
192
|
+
node as $node,
|
|
193
|
+
view as $view,
|
|
194
|
+
chart as $chart,
|
|
195
|
+
data as $data,
|
|
196
|
+
App as $app,
|
|
197
|
+
Style as $style,
|
|
198
|
+
Script as $script,
|
|
199
|
+
Layout as $layout,
|
|
200
|
+
theme as $theme,
|
|
201
|
+
|
|
202
|
+
ErrorHandler
|
|
203
|
+
};
|
|
@@ -0,0 +1,260 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//empty layout file
|
|
2
|
+
import { $hero } from '/lib/jux.js';
|
|
3
|
+
|
|
4
|
+
$hero('layout-hero', {
|
|
5
|
+
title: '...Default Layout.... ',
|
|
6
|
+
subtitle: 'This is the default layout provided by JUX.',
|
|
7
|
+
description: 'You can customize this layout by creating your own layout files.'
|
|
8
|
+
}).render();
|