@relax.js/core 1.0.3 → 1.0.5
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 +194 -188
- package/dist/DependencyInjection.d.ts +45 -27
- package/dist/collections/LinkedList.d.ts +9 -8
- package/dist/collections/index.js +1 -1
- package/dist/collections/index.js.map +3 -3
- package/dist/collections/index.mjs +1 -1
- package/dist/collections/index.mjs.map +3 -3
- package/dist/di/index.js +1 -1
- package/dist/di/index.js.map +3 -3
- package/dist/di/index.mjs +1 -1
- package/dist/di/index.mjs.map +3 -3
- package/dist/elements/index.js +1 -1
- package/dist/elements/index.js.map +1 -1
- package/dist/errors.d.ts +20 -0
- package/dist/forms/FormValidator.d.ts +3 -22
- package/dist/forms/ValidationRules.d.ts +4 -6
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +4 -4
- package/dist/forms/index.mjs +1 -1
- package/dist/forms/index.mjs.map +4 -4
- package/dist/forms/setFormData.d.ts +39 -1
- package/dist/html/TableRenderer.d.ts +1 -0
- package/dist/html/index.js +1 -1
- package/dist/html/index.js.map +3 -3
- package/dist/html/index.mjs +1 -1
- package/dist/html/index.mjs.map +3 -3
- package/dist/html/template.d.ts +4 -0
- package/dist/http/ServerSentEvents.d.ts +1 -1
- package/dist/http/SimpleWebSocket.d.ts +1 -1
- package/dist/http/http.d.ts +1 -0
- package/dist/http/index.js +1 -1
- package/dist/http/index.js.map +3 -3
- package/dist/http/index.mjs +1 -1
- package/dist/http/index.mjs.map +3 -3
- package/dist/i18n/icu.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/index.js.map +2 -2
- package/dist/i18n/index.mjs +1 -1
- package/dist/i18n/index.mjs.map +2 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +3 -3
- package/dist/routing/NavigateRouteEvent.d.ts +4 -4
- package/dist/routing/index.js +3 -3
- package/dist/routing/index.js.map +3 -3
- package/dist/routing/index.mjs +3 -3
- package/dist/routing/index.mjs.map +3 -3
- package/dist/routing/navigation.d.ts +1 -1
- package/dist/routing/routeTargetRegistry.d.ts +1 -0
- package/dist/routing/types.d.ts +2 -1
- package/dist/templates/NodeTemplate.d.ts +3 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +3 -3
- package/dist/utils/index.mjs +1 -1
- package/dist/utils/index.mjs.map +3 -3
- package/docs/Architecture.md +333 -333
- package/docs/DependencyInjection.md +277 -237
- package/docs/Errors.md +87 -87
- package/docs/GettingStarted.md +238 -231
- package/docs/Pipes.md +5 -5
- package/docs/Translations.md +167 -312
- package/docs/WhyRelaxjs.md +336 -336
- package/docs/api.json +93193 -0
- package/docs/elements/dom.md +102 -102
- package/docs/forms/creating-form-components.md +924 -924
- package/docs/forms/form-api.md +94 -94
- package/docs/forms/forms.md +99 -99
- package/docs/forms/patterns.md +311 -311
- package/docs/forms/reading-writing.md +465 -365
- package/docs/forms/validation.md +351 -351
- package/docs/html/TableRenderer.md +291 -291
- package/docs/html/html.md +175 -175
- package/docs/html/index.md +54 -54
- package/docs/html/template.md +422 -422
- package/docs/http/HttpClient.md +459 -459
- package/docs/http/ServerSentEvents.md +184 -184
- package/docs/http/index.md +109 -109
- package/docs/i18n/i18n.md +49 -4
- package/docs/i18n/intl-standard.md +178 -178
- package/docs/routing/RouteLink.md +98 -98
- package/docs/routing/Routing.md +332 -332
- package/docs/routing/layouts.md +207 -207
- package/docs/setup/bootstrapping.md +154 -0
- package/docs/setup/build-and-deploy.md +183 -0
- package/docs/setup/project-structure.md +170 -0
- package/docs/setup/vite.md +175 -0
- package/docs/utilities.md +143 -143
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,188 +1,194 @@
|
|
|
1
|
-
# Relaxjs
|
|
2
|
-
|
|
3
|
-
**Ship faster with less code.**
|
|
4
|
-
|
|
5
|
-
Web Component library with routing, forms, DI, templating, and i18n. No virtual DOM, no build magic, no surprise re-renders.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
|
116
|
-
|
|
117
|
-
| **
|
|
118
|
-
| **
|
|
119
|
-
| **
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
- **
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
|
150
|
-
|
|
151
|
-
| **
|
|
152
|
-
| **
|
|
153
|
-
| **
|
|
154
|
-
| **
|
|
155
|
-
| **
|
|
156
|
-
| **
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
-
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
- [
|
|
177
|
-
- [
|
|
178
|
-
- [
|
|
179
|
-
- [
|
|
180
|
-
- [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
1
|
+
# Relaxjs
|
|
2
|
+
|
|
3
|
+
**Ship faster with less code.**
|
|
4
|
+
|
|
5
|
+
Web Component library with routing, forms, DI, templating, and i18n. No virtual DOM, no build magic, no surprise re-renders.
|
|
6
|
+
|
|
7
|
+
- ~20KB gzipped, one dependency
|
|
8
|
+
- Native Web Components, zero vendor lock-in
|
|
9
|
+
- Use only what you need: forms, routing, DI, i18n are all independent
|
|
10
|
+
- No build step required, no compiler, no CLI
|
|
11
|
+
- Standard HTML, standard DOM, standard async/await
|
|
12
|
+
|
|
13
|
+
## Why Relaxjs?
|
|
14
|
+
|
|
15
|
+
Modern frameworks solve problems you might not have. Relaxjs takes the opposite approach:
|
|
16
|
+
|
|
17
|
+
| Framework Approach | Relaxjs Approach |
|
|
18
|
+
|-------------------|------------------|
|
|
19
|
+
| Virtual DOM diffing | Direct DOM manipulation |
|
|
20
|
+
| Reactive state management | Explicit updates |
|
|
21
|
+
| Custom template syntax | Standard HTML |
|
|
22
|
+
| Framework-specific lifecycle | Native Web Component lifecycle |
|
|
23
|
+
| Magic re-renders | You control what updates |
|
|
24
|
+
| Custom rendering pipeline | Native async/await everywhere |
|
|
25
|
+
|
|
26
|
+
> [Read the detailed comparison](docs/WhyRelaxjs.md)
|
|
27
|
+
|
|
28
|
+
**The result:** You always know *when* something ran, *why* it ran, and *what* triggered it.
|
|
29
|
+
|
|
30
|
+
## What Relaxjs Adds
|
|
31
|
+
|
|
32
|
+
Web Components give you encapsulation and lifecycle hooks. Relaxjs fills the gaps:
|
|
33
|
+
|
|
34
|
+
| Vanilla Web Components | With Relaxjs |
|
|
35
|
+
|------------------------|--------------|
|
|
36
|
+
| Manual form serialization | `readData(form)` returns typed objects (send it directly to backend) |
|
|
37
|
+
| Query string parsing | Named routes with typed parameters |
|
|
38
|
+
| DIY validation logic | `FormValidator` with HTML5 integration |
|
|
39
|
+
| No component library | Table, Tabs, TreeView, Menu ready to use |
|
|
40
|
+
| Manual service wiring | Decorator-based dependency injection |
|
|
41
|
+
| Raw fetch boilerplate | Simple HTTP client for backend calls |
|
|
42
|
+
|
|
43
|
+
You write less boilerplate while keeping full control.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @relax.js/core
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Examples
|
|
52
|
+
|
|
53
|
+
### Form Handling
|
|
54
|
+
|
|
55
|
+
Read and write form data with automatic type conversion:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { setFormData, readData } from '@relax.js/core';
|
|
59
|
+
|
|
60
|
+
// Populate a form from an object
|
|
61
|
+
const user = { name: 'John', email: 'john@example.com', age: 30 };
|
|
62
|
+
setFormData(form, user);
|
|
63
|
+
|
|
64
|
+
// Read form data back (with types!)
|
|
65
|
+
const data = readData(form);
|
|
66
|
+
// { name: 'John', email: 'john@example.com', age: 30 }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Supports nested objects (`user.address.city`), arrays (`tags[]`), and automatic type conversion for numbers, booleans, and dates.
|
|
70
|
+
|
|
71
|
+
> [Form utilities docs](docs/forms/forms.md)
|
|
72
|
+
|
|
73
|
+
### Client-Side Routing
|
|
74
|
+
|
|
75
|
+
Define routes and let the router handle navigation:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { defineRoutes, navigate, startRouting } from '@relax.js/core';
|
|
79
|
+
|
|
80
|
+
defineRoutes([
|
|
81
|
+
{ name: 'home', path: '/', componentTagName: 'app-home' },
|
|
82
|
+
{ name: 'user', path: '/users/:id', componentTagName: 'app-user' },
|
|
83
|
+
{ name: 'settings', path: '/settings', componentTagName: 'app-settings' }
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
startRouting();
|
|
87
|
+
|
|
88
|
+
// Navigate programmatically
|
|
89
|
+
navigate('user', { params: { id: '123' } });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<r-route-target></r-route-target>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
> [Routing docs](docs/routing/Routing.md)
|
|
97
|
+
|
|
98
|
+
### Form Validation
|
|
99
|
+
|
|
100
|
+
HTML5-style validation with custom rules and error summaries:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { FormValidator } from '@relax.js/core';
|
|
104
|
+
|
|
105
|
+
const validator = new FormValidator(form, {
|
|
106
|
+
useSummary: true,
|
|
107
|
+
submitCallback: () => saveData()
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> [Validation docs](docs/forms/validation.md)
|
|
112
|
+
|
|
113
|
+
## What's Included
|
|
114
|
+
|
|
115
|
+
| Feature | Description |
|
|
116
|
+
|---------|-------------|
|
|
117
|
+
| **Form Utilities** | Read/write forms, type conversion, validation |
|
|
118
|
+
| **Routing** | Named routes, typed parameters, guards, layouts, multiple targets |
|
|
119
|
+
| **HTML Templates** | `html` tagged templates with data binding and in-place updates |
|
|
120
|
+
| **HTTP Client** | Type-safe `get`/`post`/`put`/`del` with automatic JWT |
|
|
121
|
+
| **WebSocket** | Auto-reconnect, message queuing, typed messages |
|
|
122
|
+
| **SSE** | Server-Sent Events dispatched as DOM events |
|
|
123
|
+
| **Dependency Injection** | Decorator-based DI with constructor and property injection |
|
|
124
|
+
| **i18n** | ICU message format, pluralization, locale-aware formatting |
|
|
125
|
+
| **Pipes** | 15 built-in data transformations for templates |
|
|
126
|
+
|
|
127
|
+
## Where Relaxjs Fits
|
|
128
|
+
|
|
129
|
+
Relaxjs is a good choice when:
|
|
130
|
+
|
|
131
|
+
- You're building a **small-to-medium SPA** where you want direct control over the DOM
|
|
132
|
+
- Your team prefers **vanilla Web Components** over framework abstractions
|
|
133
|
+
- You want **gradual adoption** - use only the parts you need, no all-or-nothing buy-in
|
|
134
|
+
- **Bundle size and simplicity** matter more than ecosystem breadth
|
|
135
|
+
- You want to **understand what your code does** - no hidden re-renders, no magic proxies, no compiler transforms
|
|
136
|
+
|
|
137
|
+
## Where Relaxjs Doesn't Fit
|
|
138
|
+
|
|
139
|
+
Relaxjs is not the right tool for everything:
|
|
140
|
+
|
|
141
|
+
- **Large-scale apps with complex state** - Relaxjs has no reactive state management, no global store, no computed properties. If your UI has dozens of interdependent data flows, you'll be writing a lot of manual update logic.
|
|
142
|
+
- **Server-side rendering / static site generation** - Relaxjs is client-only. If you need SEO, fast first-paint from the server, or pre-rendered pages, look at Next.js, Nuxt, or SvelteKit.
|
|
143
|
+
- **Big teams that need a large talent pool** - React and Angular developers are everywhere. Finding developers who know Relaxjs (or are willing to learn a small library) is harder.
|
|
144
|
+
- **Rich ecosystem needs** - There's no component marketplace, no DevTools extension, no community middleware. You build what you need or use vanilla JS libraries.
|
|
145
|
+
- **Mobile / native targets** - No React Native equivalent, no Ionic integration. Relaxjs is for the browser.
|
|
146
|
+
|
|
147
|
+
## How It Compares
|
|
148
|
+
|
|
149
|
+
| | Relaxjs | React | Angular | Vue | Svelte |
|
|
150
|
+
|---|---|---|---|---|---|
|
|
151
|
+
| **Approach** | Web Components | Virtual DOM | Full framework | Virtual DOM | Compiler |
|
|
152
|
+
| **Bundle size** | ~20KB gzipped | ~45KB | ~150KB+ | ~33KB | ~2KB runtime |
|
|
153
|
+
| **Learning curve** | Low (vanilla TS) | Medium | High | Medium | Low-Medium |
|
|
154
|
+
| **State management** | Explicit DOM updates | Hooks / Redux / Zustand | RxJS / Signals | Reactive refs | Stores / runes |
|
|
155
|
+
| **Routing** | Built-in (simple) | react-router (separate) | Built-in (full) | vue-router (separate) | SvelteKit |
|
|
156
|
+
| **Forms** | Built-in (HTML5 native) | Controlled / uncontrolled | Reactive forms | v-model | bind: |
|
|
157
|
+
| **SSR** | No | Yes | Yes | Yes | Yes |
|
|
158
|
+
| **Ecosystem** | Small | Massive | Large | Large | Growing |
|
|
159
|
+
| **DI** | Built-in | None (Context API) | Built-in | Provide / Inject | None |
|
|
160
|
+
| **i18n** | Built-in (ICU) | i18next etc. | Built-in | vue-i18n | i18next etc. |
|
|
161
|
+
| **DevTools** | Browser DevTools | React DevTools | Angular DevTools | Vue DevTools | Svelte DevTools |
|
|
162
|
+
| **Community size** | Small | Very large | Large | Large | Medium |
|
|
163
|
+
|
|
164
|
+
## Philosophy
|
|
165
|
+
|
|
166
|
+
This isn't a framework - it's a library. Use what you need:
|
|
167
|
+
|
|
168
|
+
- Need just form handling? Import `setFormData` and `readData`.
|
|
169
|
+
- Need routing? Add `defineRoutes` and `r-route-target`.
|
|
170
|
+
- Need everything? It's all there.
|
|
171
|
+
|
|
172
|
+
No buy-in required. No migration path to worry about.
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
- [Why Relaxjs?](docs/WhyRelaxjs.md) - Detailed comparison with frameworks
|
|
177
|
+
- [Getting Started](docs/GettingStarted.md) - Progressive adoption guide (7 levels)
|
|
178
|
+
- [Architecture](docs/Architecture.md)
|
|
179
|
+
- [Form Utilities](docs/forms/forms.md) - Validation, reading/writing, custom form components
|
|
180
|
+
- [Routing](docs/routing/Routing.md) - Routes, guards, layouts, navigation
|
|
181
|
+
- [HTML Templates](docs/html/html.md) - Tagged templates with data binding
|
|
182
|
+
- [HTTP & WebSocket](docs/http/HttpClient.md) - REST calls, WebSocket, SSE
|
|
183
|
+
- [Dependency Injection](docs/DependencyInjection.md)
|
|
184
|
+
- [i18n](docs/i18n/i18n.md) - Translations, ICU format, locale switching
|
|
185
|
+
- [Pipes](docs/Pipes.md) - Data transformations for templates
|
|
186
|
+
- [Utilities](docs/utilities.md) - Sequential IDs, LinkedList, helpers
|
|
187
|
+
|
|
188
|
+
## Browser Support
|
|
189
|
+
|
|
190
|
+
Works in all browsers that support Web Components (Chrome, Firefox, Safari, Edge).
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT
|
|
@@ -65,42 +65,60 @@ export interface RegistrationOptions {
|
|
|
65
65
|
properties?: Record<string, string | Constructor>;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* Field decorator that
|
|
69
|
-
*
|
|
68
|
+
* Field decorator that injects a service from the global DI container.
|
|
69
|
+
* The service is resolved when the class instance is created (not at class definition time),
|
|
70
|
+
* so services must be registered before the first instance is created.
|
|
70
71
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* logger: Logger, // Inject by type
|
|
76
|
-
* audit: 'auditLogger' // Inject by key
|
|
77
|
-
* }
|
|
78
|
-
* })
|
|
79
|
-
* class UserService {
|
|
80
|
-
* @Inject(Logger)
|
|
81
|
-
* private logger!: Logger;
|
|
72
|
+
* Works with web components regardless of how they are created:
|
|
73
|
+
* - By the browser (HTML parsing): services are resolved during construction
|
|
74
|
+
* - By application code (`document.createElement` or `new`): same behavior
|
|
75
|
+
* - Injected fields are available in `connectedCallback` and all lifecycle methods
|
|
82
76
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Using `@Inject` in a web component
|
|
79
|
+
* class UserPanel extends HTMLElement {
|
|
80
|
+
* @Inject(UserService)
|
|
81
|
+
* private userService!: UserService;
|
|
85
82
|
*
|
|
86
|
-
*
|
|
83
|
+
* connectedCallback() {
|
|
84
|
+
* // userService is already resolved and ready to use
|
|
85
|
+
* const user = this.userService.getCurrentUser();
|
|
86
|
+
* this.render(user);
|
|
87
|
+
* }
|
|
87
88
|
* }
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* // Services must be registered before components are created.
|
|
92
|
+
* // In your app entry point (e.g. main.ts):
|
|
93
|
+
* serviceCollection.registerByType(UserService, { inject: [ApiClient] });
|
|
94
|
+
* serviceCollection.registerByType(ApiClient, { inject: [] });
|
|
95
|
+
*
|
|
96
|
+
* // Now components can be created (by browser or code)
|
|
97
|
+
* customElements.define('user-panel', UserPanel);
|
|
88
98
|
*/
|
|
89
99
|
export declare function Inject<T extends object>(typeOrKey: Constructor<T> | string): (_: undefined, context: ClassFieldDecoratorContext) => (this: any) => T;
|
|
90
100
|
/**
|
|
91
|
-
* Class decorator that
|
|
92
|
-
*
|
|
101
|
+
* Class decorator that registers a service in the global DI container.
|
|
102
|
+
* Registration happens at class definition time (when the module loads),
|
|
103
|
+
* so import the module before creating instances that depend on this service.
|
|
93
104
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
105
|
+
* For web components: use `@ContainerService` on services, not on the
|
|
106
|
+
* components themselves. Components use `@Inject` to consume services.
|
|
96
107
|
*
|
|
97
108
|
* @param options - Registration configuration including scope and dependencies
|
|
98
109
|
*
|
|
99
110
|
* @example
|
|
100
|
-
* //
|
|
101
|
-
* @ContainerService({ inject: [
|
|
102
|
-
* class
|
|
103
|
-
* constructor(private
|
|
111
|
+
* // Register a service that components can inject
|
|
112
|
+
* @ContainerService({ inject: [ApiClient] })
|
|
113
|
+
* class UserService {
|
|
114
|
+
* constructor(private api: ApiClient) {}
|
|
115
|
+
* getCurrentUser() { return this.api.get('/user'); }
|
|
116
|
+
* }
|
|
117
|
+
*
|
|
118
|
+
* // Component consumes the service
|
|
119
|
+
* class UserPanel extends HTMLElement {
|
|
120
|
+
* @Inject(UserService)
|
|
121
|
+
* private userService!: UserService;
|
|
104
122
|
* }
|
|
105
123
|
*
|
|
106
124
|
* @example
|
|
@@ -123,8 +141,8 @@ declare class Registration {
|
|
|
123
141
|
scope: ServiceScope;
|
|
124
142
|
inject: (string | Constructor)[];
|
|
125
143
|
properties: Record<string, string | Constructor>;
|
|
126
|
-
key?: string;
|
|
127
|
-
instance?: unknown;
|
|
144
|
+
key?: string | undefined;
|
|
145
|
+
instance?: unknown | undefined;
|
|
128
146
|
/**
|
|
129
147
|
* Creates a new registration record.
|
|
130
148
|
*
|
|
@@ -135,7 +153,7 @@ declare class Registration {
|
|
|
135
153
|
* @param key - Optional string identifier
|
|
136
154
|
* @param instance - Optional pre-created instance
|
|
137
155
|
*/
|
|
138
|
-
constructor(classConstructor: Constructor, scope: ServiceScope, inject: (string | Constructor)[], properties?: Record<string, string | Constructor>, key?: string, instance?: unknown);
|
|
156
|
+
constructor(classConstructor: Constructor, scope: ServiceScope, inject: (string | Constructor)[], properties?: Record<string, string | Constructor>, key?: string | undefined, instance?: unknown | undefined);
|
|
139
157
|
}
|
|
140
158
|
/**
|
|
141
159
|
* Registry that stores service registration metadata.
|
|
@@ -27,8 +27,8 @@ export declare class Node<T> {
|
|
|
27
27
|
* A trivial linked list implementation.
|
|
28
28
|
*/
|
|
29
29
|
export declare class LinkedList<T> {
|
|
30
|
-
private _first
|
|
31
|
-
private _last
|
|
30
|
+
private _first;
|
|
31
|
+
private _last;
|
|
32
32
|
private _length;
|
|
33
33
|
/**
|
|
34
34
|
* Add a value to the beginning of the list.
|
|
@@ -40,6 +40,7 @@ export declare class LinkedList<T> {
|
|
|
40
40
|
* @param value Value that should be contained in a node.
|
|
41
41
|
*/
|
|
42
42
|
addLast(value: T): void;
|
|
43
|
+
private createNode;
|
|
43
44
|
/**
|
|
44
45
|
* Remove a node from the beginning of the list.
|
|
45
46
|
* @returns Value contained in the first node.
|
|
@@ -57,19 +58,19 @@ export declare class LinkedList<T> {
|
|
|
57
58
|
*/
|
|
58
59
|
get length(): number;
|
|
59
60
|
/**
|
|
60
|
-
* First
|
|
61
|
+
* First node, or `null` if the list is empty.
|
|
61
62
|
*/
|
|
62
|
-
get first(): Node<T> |
|
|
63
|
+
get first(): Node<T> | null;
|
|
63
64
|
/**
|
|
64
|
-
* Contained value of the first node.
|
|
65
|
+
* Contained value of the first node, or `undefined` if the list is empty.
|
|
65
66
|
*/
|
|
66
67
|
get firstValue(): T | undefined;
|
|
67
68
|
/**
|
|
68
|
-
* Last node.
|
|
69
|
+
* Last node, or `null` if the list is empty.
|
|
69
70
|
*/
|
|
70
|
-
get last(): Node<T> |
|
|
71
|
+
get last(): Node<T> | null;
|
|
71
72
|
/**
|
|
72
|
-
* Contained value of the last node.
|
|
73
|
+
* Contained value of the last node, or `undefined` if the list is empty.
|
|
73
74
|
*/
|
|
74
75
|
get lastValue(): T | undefined;
|
|
75
76
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var u=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var
|
|
1
|
+
"use strict";var u=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var f=(s,e)=>{for(var t in e)u(s,t,{get:e[t],enumerable:!0})},g=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of d(e))!_.call(s,r)&&r!==t&&u(s,r,{get:()=>e[r],enumerable:!(i=p(e,r))||i.enumerable});return s};var v=s=>g(u({},"__esModule",{value:!0}),s);var m={};f(m,{LinkedList:()=>h,Node:()=>n,PageSelectedEvent:()=>a,Pager:()=>o});module.exports=v(m);var n=class{constructor(e,t){this.value=e;this.removeCallback=t;this.next=null;this.prev=null}remove(){this.prev&&(this.prev.next=this.next),this.next&&(this.next.prev=this.prev),this.removeCallback()}},h=class{constructor(){this._first=null;this._last=null;this._length=0}addFirst(e){let t=this.createNode(e);this._first?(t.next=this._first,this._first.prev=t,this._first=t):(this._first=t,this._last=this._first),this._length++}addLast(e){let t=this.createNode(e);this._last?(t.prev=this._last,this._last.next=t,this._last=t):(this._first=t,this._last=t),this._length++}createNode(e){let t;return t=new n(e,()=>{this._first===t&&(this._first=t.next),this._last===t&&(this._last=t.prev),this._length--}),t}removeFirst(){if(!this._first)throw new Error("The list is empty.");let e=this._first.value;return this._first=this._first.next,this._first||(this._last=null),this._length--,e}removeLast(){if(!this._last)throw new Error("The list is empty.");let e=this._last.value;return this._last=this._last.prev,this._last||(this._first=null),this._length--,e}get length(){return this._length}get first(){return this._first}get firstValue(){return this._first?.value}get last(){return this._last}get lastValue(){return this._last?.value}};var a=class extends Event{constructor(t){super("pageselected",{bubbles:!0,composed:!0});this.page=t}},o=class{constructor(e,t,i){this.currentPage=1;this.container=e,this.totalCount=t,this.pageSize=i,this.render()}render(){this.container.innerHTML="";let e=Math.max(1,Math.ceil(this.totalCount/this.pageSize)),t=(i,r,c=!1)=>{let l=document.createElement("button");return l.textContent=i,l.disabled=c,l.addEventListener("click",()=>this.selectPage(r)),l};this.container.appendChild(t("Previous",this.currentPage-1,this.currentPage===1));for(let i=1;i<=e;i++){let r=t(i.toString(),i);i===this.currentPage&&r.classList.add("selected"),this.container.appendChild(r)}this.container.appendChild(t("Next",this.currentPage+1,this.currentPage===e))}selectPage(e){let t=Math.max(1,Math.ceil(this.totalCount/this.pageSize));e<1||e>t||e===this.currentPage||(this.currentPage=e,this.render(),this.container.dispatchEvent(new a(this.currentPage)))}update(e){this.totalCount=e;let t=Math.max(1,Math.ceil(this.totalCount/this.pageSize));this.currentPage>t&&(this.currentPage=t),this.render()}getCurrentPage(){return this.currentPage}};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/collections/Index.ts", "../../src/collections/LinkedList.ts", "../../src/collections/Pager.ts"],
|
|
4
|
-
"sourcesContent": ["export {LinkedList, Node} from \"./LinkedList\";\nexport { Pager, PageSelectedEvent } from \"./Pager\";\nexport { type DataLoader } from \"./DataLoader\";", "/**\r\n * A node in the @see LinkedList.\r\n */\r\nexport class Node<T> {\r\n /**\r\n * Next node unless last one.\r\n */\r\n public next: Node<T> | null = null;\r\n /**\r\n * Previous node unless first one.\r\n */\r\n public prev: Node<T> | null = null;\r\n\r\n /**\r\n * Constructor.\r\n * @param value Value contained in the node.\r\n */\r\n constructor(public value: T, private removeCallback: () => void) {}\r\n\r\n /**\r\n * Remove this node.\r\n * Will notify the list of the update to ensure correct element count.\r\n */\r\n remove() {\r\n this.prev.next = this.next;\r\n this.next.prev = this.prev;\r\n this.removeCallback();\r\n }\r\n}\r\n\r\n/**\r\n * A trivial linked list implementation.\r\n */\r\nexport class LinkedList<T> {\r\n private _first
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["Index_exports", "__export", "LinkedList", "Node", "PageSelectedEvent", "Pager", "__toCommonJS", "Node", "value", "removeCallback", "LinkedList", "newNode", "PageSelectedEvent", "page", "Pager", "container", "totalCount", "pageSize", "pageCount", "createButton", "label", "disabled", "btn"
|
|
4
|
+
"sourcesContent": ["export {LinkedList, Node} from \"./LinkedList\";\nexport { Pager, PageSelectedEvent } from \"./Pager\";\nexport { type DataLoader } from \"./DataLoader\";", "/**\r\n * A node in the @see LinkedList.\r\n */\r\nexport class Node<T> {\r\n /**\r\n * Next node unless last one.\r\n */\r\n public next: Node<T> | null = null;\r\n /**\r\n * Previous node unless first one.\r\n */\r\n public prev: Node<T> | null = null;\r\n\r\n /**\r\n * Constructor.\r\n * @param value Value contained in the node.\r\n */\r\n constructor(public value: T, private removeCallback: () => void) {}\r\n\r\n /**\r\n * Remove this node.\r\n * Will notify the list of the update to ensure correct element count.\r\n */\r\n remove() {\r\n if (this.prev) this.prev.next = this.next;\r\n if (this.next) this.next.prev = this.prev;\r\n this.removeCallback();\r\n }\r\n}\r\n\r\n/**\r\n * A trivial linked list implementation.\r\n */\r\nexport class LinkedList<T> {\r\n private _first: Node<T> | null = null;\r\n private _last: Node<T> | null = null;\r\n private _length = 0;\r\n\r\n /**\r\n * Add a value to the beginning of the list.\r\n * @param value Value that should be contained in the node.\r\n */\r\n addFirst(value: T) {\r\n const newNode = this.createNode(value);\r\n if (!this._first) {\r\n this._first = newNode;\r\n this._last = this._first;\r\n } else {\r\n newNode.next = this._first;\r\n this._first.prev = newNode;\r\n this._first = newNode;\r\n }\r\n\r\n this._length++;\r\n }\r\n\r\n /**\r\n * Add a value to the end of the list.\r\n * @param value Value that should be contained in a node.\r\n */\r\n addLast(value: T) {\r\n const newNode = this.createNode(value);\r\n if (!this._last) {\r\n this._first = newNode;\r\n this._last = newNode;\r\n } else {\r\n newNode.prev = this._last;\r\n this._last.next = newNode;\r\n this._last = newNode;\r\n }\r\n\r\n this._length++;\r\n }\r\n\r\n private createNode(value: T): Node<T> {\r\n let node: Node<T>;\r\n node = new Node(value, () => {\r\n if (this._first === node) this._first = node.next;\r\n if (this._last === node) this._last = node.prev;\r\n this._length--;\r\n });\r\n return node;\r\n }\r\n\r\n /**\r\n * Remove a node from the beginning of the list.\r\n * @returns Value contained in the first node.\r\n */\r\n removeFirst(): T {\r\n if (!this._first) {\r\n throw new Error('The list is empty.');\r\n }\r\n\r\n const value = this._first.value;\r\n this._first = this._first.next;\r\n if (!this._first) this._last = null;\r\n this._length--;\r\n return value;\r\n }\r\n\r\n /**\r\n * Remove a node from the end of the list.\r\n * @returns Value contained in the last node.\r\n */\r\n removeLast(): T {\r\n if (!this._last) {\r\n throw new Error('The list is empty.');\r\n }\r\n\r\n const value = this._last.value;\r\n this._last = this._last.prev;\r\n if (!this._last) this._first = null;\r\n this._length--;\r\n return value;\r\n }\r\n\r\n /**\r\n * Number of nodes in the list.\r\n *\r\n * The count works as long as you do not manually remove nodes (by assigning next/prev to the neighbors).\r\n */\r\n get length(): number {\r\n return this._length;\r\n }\r\n\r\n /**\r\n * First node, or `null` if the list is empty.\r\n */\r\n get first(): Node<T> | null {\r\n return this._first;\r\n }\r\n\r\n /**\r\n * Contained value of the first node, or `undefined` if the list is empty.\r\n */\r\n get firstValue(): T | undefined {\r\n return this._first?.value;\r\n }\r\n\r\n /**\r\n * Last node, or `null` if the list is empty.\r\n */\r\n get last(): Node<T> | null {\r\n return this._last;\r\n }\r\n\r\n /**\r\n * Contained value of the last node, or `undefined` if the list is empty.\r\n */\r\n get lastValue(): T | undefined {\r\n return this._last?.value;\r\n }\r\n}\r\n", "export class PageSelectedEvent extends Event {\r\n constructor(public page: number) {\r\n super('pageselected', {\r\n bubbles: true,\r\n composed: true,\r\n });\r\n }\r\n}\r\n\r\ndeclare global {\r\n interface HTMLElementEventMap {\r\n 'pageselected': PageSelectedEvent;\r\n }\r\n}\r\n\r\nexport class Pager {\r\n private container: HTMLElement;\r\n private totalCount: number;\r\n private pageSize: number;\r\n private currentPage: number = 1;\r\n\r\n constructor(container: HTMLElement, totalCount: number, pageSize: number) {\r\n this.container = container;\r\n this.totalCount = totalCount;\r\n this.pageSize = pageSize;\r\n\r\n this.render();\r\n }\r\n\r\n private render() {\r\n this.container.innerHTML = '';\r\n\r\n const pageCount = Math.max(1, Math.ceil(this.totalCount / this.pageSize));\r\n\r\n const createButton = (label: string, page: number, disabled: boolean = false) => {\r\n const btn = document.createElement('button');\r\n btn.textContent = label;\r\n btn.disabled = disabled;\r\n btn.addEventListener('click', () => this.selectPage(page));\r\n return btn;\r\n };\r\n\r\n this.container.appendChild(\r\n createButton('Previous', this.currentPage - 1, this.currentPage === 1)\r\n );\r\n\r\n for (let i = 1; i <= pageCount; i++) {\r\n const btn = createButton(i.toString(), i);\r\n if (i === this.currentPage) {\r\n btn.classList.add('selected');\r\n }\r\n this.container.appendChild(btn);\r\n }\r\n\r\n this.container.appendChild(\r\n createButton('Next', this.currentPage + 1, this.currentPage === pageCount)\r\n );\r\n }\r\n\r\n private selectPage(page: number) {\r\n const pageCount = Math.max(1, Math.ceil(this.totalCount / this.pageSize));\r\n if (page < 1 || page > pageCount || page === this.currentPage) return;\r\n\r\n this.currentPage = page;\r\n this.render();\r\n\r\n this.container.dispatchEvent(new PageSelectedEvent(this.currentPage));\r\n }\r\n\r\n public update(totalCount: number) {\r\n this.totalCount = totalCount;\r\n const pageCount = Math.max(1, Math.ceil(this.totalCount / this.pageSize));\r\n if (this.currentPage > pageCount) {\r\n this.currentPage = pageCount;\r\n }\r\n this.render();\r\n }\r\n\r\n public getCurrentPage(): number {\r\n return this.currentPage;\r\n }\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Index_exports", "__export", "LinkedList", "Node", "PageSelectedEvent", "Pager", "__toCommonJS", "Node", "value", "removeCallback", "LinkedList", "newNode", "node", "PageSelectedEvent", "page", "Pager", "container", "totalCount", "pageSize", "pageCount", "createButton", "label", "disabled", "btn"]
|
|
7
7
|
}
|