application.ts 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Xoboto Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,358 @@
1
+ # Application.Ts
2
+
3
+ A lightweight, dependency-free except template.ts and stackview.ts TypeScript framework for building single-page applications (SPAs) with pure vanilla JavaScript and CSS. No build tools required, just modern web standards.
4
+
5
+ ## What is Application.Ts?
6
+
7
+ Application.Ts is a minimalist SPA framework that combines:
8
+ - **Routing**: URL-based navigation with parameters and guards
9
+ - **Templating**: Reactive data binding with Template.Ts
10
+ - **View Management**: Stack-based view transitions with StackView.Ts
11
+ - **Component Model**: Web Components for reusable UI elements
12
+
13
+ Built on web standards, Application.Ts provides a simple yet powerful foundation for creating modern web applications without the complexity of larger frameworks.
14
+
15
+ ## Quick Setup
16
+
17
+ ```bash
18
+ npm install application.ts
19
+ ```
20
+
21
+ ```typescript
22
+ import { App } from 'application.ts';
23
+ import { HomeView } from './views/home.view';
24
+
25
+ const app = new App('#root');
26
+
27
+ app.router
28
+ .map('/', HomeView)
29
+ .notFound(NotFoundView);
30
+
31
+ app.start();
32
+ ```
33
+
34
+ ```html
35
+ <!doctype html>
36
+ <html lang="en">
37
+ <head>
38
+ <meta charset="UTF-8" />
39
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
40
+ <title>My App</title>
41
+ </head>
42
+ <body>
43
+ <div id="root"></div>
44
+ <script type="module" src="/main.ts"></script>
45
+ </body>
46
+ </html>
47
+ ```
48
+
49
+ ## Features
50
+
51
+ ### 🚀 Routing
52
+ - Pattern-based routing with URL parameters
53
+ - Navigation guards (canEnter)
54
+ - Programmatic navigation
55
+ - Browser history support
56
+ - Query string handling
57
+
58
+ ```typescript
59
+ app.router
60
+ .map('/', HomeView)
61
+ .map('/user/:id', UserView)
62
+ .map('/dashboard', DashboardView, {
63
+ canEnter: () => AuthService.isLoggedIn()
64
+ })
65
+ .notFound(NotFoundView);
66
+ ```
67
+
68
+ #### Route Guards with canEnter
69
+
70
+ Protect routes with navigation guards. The `canEnter` function can:
71
+ - Return `true` to allow navigation
72
+ - Return `false` to block navigation
73
+ - Return a string path to redirect
74
+
75
+ ```typescript
76
+ // Simple authentication check
77
+ app.router.map('/dashboard', DashboardView, {
78
+ canEnter: () => {
79
+ return AuthService.isLoggedIn();
80
+ }
81
+ });
82
+
83
+ // Redirect to login if not authenticated
84
+ app.router.map('/profile', ProfileView, {
85
+ canEnter: () => {
86
+ if (!AuthService.isLoggedIn()) {
87
+ return '/login'; // Redirect to login page
88
+ }
89
+ return true; // Allow access
90
+ }
91
+ });
92
+
93
+ // Access route parameters in guard
94
+ app.router.map('/admin/:section', AdminView, {
95
+ canEnter: (params) => {
96
+ if (!AuthService.isAdmin()) {
97
+ return '/'; // Redirect to home
98
+ }
99
+ return true;
100
+ }
101
+ });
102
+
103
+ // Async guards for API checks
104
+ app.router.map('/document/:id', DocumentView, {
105
+ canEnter: async (params) => {
106
+ const hasAccess = await checkDocumentPermission(params.id);
107
+ return hasAccess ? true : '/unauthorized';
108
+ }
109
+ });
110
+ ```
111
+
112
+ ### 🎨 Reactive Templates
113
+ Data binding with Template.Ts v2:
114
+ - `@on:` - Event handlers
115
+ - `@prop:` - Property binding
116
+ - `@att` - Attribute binding
117
+ - `@batt` - Boolean attribute binding
118
+ - `@if` - Conditional rendering
119
+ - `@for` - List rendering
120
+ - `{{ }}` - Expression interpolation
121
+
122
+ ```typescript
123
+ const template = `
124
+ <div>
125
+ <h1>{{ title }}</h1>
126
+ <button @on:click="increment">Count: {{ count }}</button>
127
+ <ul>
128
+ <li @for="items">{{ item.name }}</li>
129
+ </ul>
130
+ </div>`;
131
+ ```
132
+
133
+ ### 🧩 Component System
134
+ Build reusable components with AppView base class:
135
+
136
+ ```typescript
137
+ import { AppView, Register } from 'application.ts';
138
+
139
+ @Register
140
+ export class MyComponent extends AppView {
141
+ template() {
142
+ return `<div>{{ message }}</div>`;
143
+ }
144
+
145
+ state() {
146
+ return { message: 'Hello World' };
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### 📐 Layouts
152
+ Wrap views with shared layouts:
153
+
154
+ ```typescript
155
+ app.router
156
+ .map('/', HomeView)
157
+ .map('/about', AboutView)
158
+ .setLayout(DefaultLayout);
159
+ ```
160
+
161
+ ### 🎯 View Lifecycle
162
+ Hook into view lifecycle events:
163
+
164
+ ```typescript
165
+ export class MyView extends AppView {
166
+ async onMounted() {
167
+ // View mounted to DOM
168
+ }
169
+
170
+ async stackViewShown() {
171
+ // View became visible
172
+ }
173
+
174
+ async stackViewHidden() {
175
+ // View hidden
176
+ }
177
+ }
178
+ ```
179
+
180
+ ## How to Use
181
+
182
+ ### 1. Create a View
183
+
184
+ ```typescript
185
+ // views/home.view.ts
186
+ import { AppView, Register } from 'application.ts';
187
+
188
+ const template = `
189
+ <div class="home">
190
+ <h1>{{ title }}</h1>
191
+ <p>Counter: {{ count }}</p>
192
+ <button @on:click="increment">Increment</button>
193
+ </div>`;
194
+
195
+ class State {
196
+ title: string = 'Home Page';
197
+ count: number = 0;
198
+
199
+ increment: () => void = () => {
200
+ this.count++;
201
+ };
202
+ }
203
+
204
+ @Register
205
+ export class HomeView extends AppView {
206
+ template() {
207
+ return template;
208
+ }
209
+
210
+ state() {
211
+ return new State();
212
+ }
213
+ }
214
+ ```
215
+
216
+ ### 2. Set Up Routes
217
+
218
+ ```typescript
219
+ // main.ts
220
+ import { App } from 'application.ts';
221
+ import { HomeView } from './views/home.view';
222
+ import { AboutView } from './views/about.view';
223
+ import { UserView } from './views/user.view';
224
+
225
+ const app = new App('#root');
226
+
227
+ app.router
228
+ .map('/', HomeView)
229
+ .map('/about', AboutView)
230
+ .map('/user/:id', UserView);
231
+
232
+ app.start();
233
+ ```
234
+
235
+ ### 3. Navigate Between Views
236
+
237
+ ```typescript
238
+ // Programmatic navigation
239
+ this.navigate('/about');
240
+ this.navigate('/user/123');
241
+
242
+ // In templates with links
243
+ <a href="/about">About</a>
244
+ <a href="/user/42">User Profile</a>
245
+ ```
246
+
247
+ ### 4. Access Route Parameters
248
+
249
+ ```typescript
250
+ export class UserView extends AppView {
251
+ async onMounted() {
252
+ const userId = this.params?.id;
253
+ console.log('User ID:', userId);
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### 5. Create Components
259
+
260
+ ```typescript
261
+ // components/button.component.ts
262
+ import { AppView, Register } from 'application.ts';
263
+
264
+ const template = `
265
+ <button @on:click="handleClick" class="btn">
266
+ {{ label }}
267
+ </button>`;
268
+
269
+ @Register
270
+ export class AppButton extends AppView {
271
+ template() { return template; }
272
+
273
+ state() {
274
+ return {
275
+ label: 'Click me',
276
+ handleClick: () => {
277
+ this.dispatchEvent(new CustomEvent('buttonclick', {
278
+ bubbles: true
279
+ }));
280
+ }
281
+ };
282
+ }
283
+
284
+ get label() { return this.viewState.label; }
285
+ set label(value: string) { this.setState('label', value); }
286
+ }
287
+ ```
288
+
289
+ Use in templates:
290
+ ```html
291
+ <app-button @prop:label="'Save'" @on:buttonclick="handleSave"></app-button>
292
+ ```
293
+
294
+ ## Examples
295
+
296
+ Explore the `/examples` folder for complete working examples:
297
+
298
+ - **Minimal** - The simplest possible app
299
+ - **Basic** - Routing, layouts, and components
300
+ - **Advanced** - Full-featured SPA with services, state management, and more
301
+
302
+ ## API Reference
303
+
304
+ ### App
305
+
306
+ ```typescript
307
+ const app = new App(selector: string, options?: AppOptions);
308
+ app.router // Access router
309
+ app.start() // Start the application
310
+ ```
311
+
312
+ ### Router
313
+
314
+ ```typescript
315
+ router.map(path: string, view: typeof AppView, options?: RouteOptions)
316
+ router.setLayout(layout: typeof AppView)
317
+ router.notFound(view: typeof AppView)
318
+ router.navigate(path: string)
319
+ router.start()
320
+ ```
321
+
322
+ ### AppView
323
+
324
+ ```typescript
325
+ abstract class AppView {
326
+ template(): string // Define HTML template
327
+ state() // Define reactive state
328
+
329
+ // Lifecycle hooks
330
+ onBeforeMount()
331
+ onMounted()
332
+ onBeforeUnmount()
333
+ onUnmounted()
334
+ onStateChanged()
335
+ onParamsChanged()
336
+
337
+ // Navigation
338
+ navigate(path: string)
339
+
340
+ // State management
341
+ setState(key: string, value: any)
342
+ setStates(updates: Record<string, any>)
343
+ update() // Force re-render
344
+ }
345
+ ```
346
+
347
+ ## License
348
+
349
+ MIT
350
+
351
+ ## Contributing
352
+
353
+ Contributions are welcome! Please feel free to submit issues or pull requests.
354
+
355
+ ## Links
356
+
357
+ - [Template.Ts Documentation](https://www.npmjs.com/package/template.ts)
358
+ - [StackView.Ts Documentation](https://www.npmjs.com/package/stackview.ts)
@@ -0,0 +1,124 @@
1
+ import { Router } from '../navigation/router';
2
+ import type { AppView } from './AppView';
3
+ /**
4
+ * View constructor type
5
+ */
6
+ type ViewConstructor = new () => AppView<any>;
7
+ /**
8
+ * Main App class that manages routing and view rendering
9
+ * Integrates Router with StackView.Ts for navigation
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { App } from 'application.ts';
14
+ * import { HomeView, AboutView, NotFoundView } from './views';
15
+ *
16
+ * const app = new App('#root');
17
+ *
18
+ * app.router
19
+ * .map('/', HomeView)
20
+ * .map('/about', AboutView)
21
+ * .notFound(NotFoundView);
22
+ *
23
+ * app.start();
24
+ * ```
25
+ */
26
+ export declare class App {
27
+ readonly router: Router;
28
+ private stackView;
29
+ private rootElement;
30
+ private viewRegistry;
31
+ private layoutRegistry;
32
+ private currentViewInstance;
33
+ private currentLayoutInstance;
34
+ private currentRoutePath;
35
+ private defaultLayout;
36
+ /**
37
+ * Get the App instance from any element by traversing up the DOM tree
38
+ * @param element - Any HTMLElement in the app
39
+ * @returns App instance or null if not found
40
+ */
41
+ static fromElement(element: HTMLElement | null): App | null;
42
+ constructor(rootSelector?: string);
43
+ /**
44
+ * Register a view class with a handler name
45
+ * @param handler - The view handler/identifier used in router.map()
46
+ * @param viewClass - The AppView class constructor
47
+ */
48
+ registerView(handler: string, viewClass: ViewConstructor): this;
49
+ /**
50
+ * Register multiple views at once
51
+ * @param views - Object mapping handler names to view classes
52
+ */
53
+ registerViews(views: Record<string, ViewConstructor>): this;
54
+ /**
55
+ * Register a layout class with a handler name
56
+ * @param handler - The layout handler/identifier
57
+ * @param layoutClass - The AppView layout class constructor
58
+ */
59
+ registerLayout(handler: string, layoutClass: ViewConstructor): this;
60
+ /**
61
+ * Register multiple layouts at once
62
+ * @param layouts - Object mapping handler names to layout classes
63
+ */
64
+ registerLayouts(layouts: Record<string, ViewConstructor>): this;
65
+ /**
66
+ * Set the default layout for all routes
67
+ * @param handler - The layout handler name, or null for no layout
68
+ */
69
+ setDefaultLayout(handler: string | null): this;
70
+ /**
71
+ * Initialize the root element and StackView
72
+ * @param selector - CSS selector for the root element
73
+ */
74
+ private initializeRoot;
75
+ /**
76
+ * Start the application
77
+ * @param rootSelector - Optional root selector if not provided in constructor
78
+ */
79
+ start(rootSelector?: string): void;
80
+ /**
81
+ * Resolve view class from handler (supports both class constructor and string lookup)
82
+ * @param handler - View class constructor or string identifier
83
+ * @returns View class constructor or null
84
+ */
85
+ private resolveViewClass;
86
+ /**
87
+ * Handle navigation event from router
88
+ */
89
+ private handleNavigation;
90
+ /**
91
+ * Render view wrapped in a layout
92
+ */
93
+ private renderWithLayout;
94
+ /**
95
+ * Render view without layout
96
+ */
97
+ private renderWithoutLayout;
98
+ /**
99
+ * Handle not found event from router
100
+ */
101
+ private handleNotFound;
102
+ /**
103
+ * Get the route pattern that matches the given path
104
+ */
105
+ getRoutePattern(path: string): string | null;
106
+ /**
107
+ * Get the current view instance
108
+ */
109
+ get currentView(): AppView<any> | null;
110
+ /**
111
+ * Get the current route pattern (e.g., '/user/:id')
112
+ */
113
+ get currentRoute(): string | null;
114
+ /**
115
+ * Navigate to a specific path
116
+ * @param path - The path to navigate to
117
+ */
118
+ navigate(path: string): void;
119
+ /**
120
+ * Go back in navigation history
121
+ */
122
+ goBack(): Promise<void>;
123
+ }
124
+ export {};