kopular 0.1.0 → 0.2.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 +5 -3
- package/package.json +10 -3
- package/src/component.js +1 -1
- package/src/dom.js +2 -0
- package/src/dom.ks +8 -1
- package/src/router.js +5 -4
- package/src/router.ks +18 -12
package/README.md
CHANGED
|
@@ -19,9 +19,11 @@ dependency-injection container, no template DSL.
|
|
|
19
19
|
constructor argument. No injector hierarchy, no provider tokens, no decorators — and a
|
|
20
20
|
service stays fully testable with zero `Component`/DOM involvement, since it's just a
|
|
21
21
|
class.
|
|
22
|
-
- **`Router`**:
|
|
23
|
-
|
|
24
|
-
server
|
|
22
|
+
- **`Router`**: real URLs (`/about`, not `#/about`) via the History API
|
|
23
|
+
(`pushState`/`popstate`), with route registration as plain method calls, not a config
|
|
24
|
+
DSL. Needs a server that falls back to the app shell for unrecognized paths — see
|
|
25
|
+
[KopularDemo](https://dev.azure.com/koppinator/Koppindependence/_git/KopularDemo)'s
|
|
26
|
+
`scripts/serve.mjs`.
|
|
25
27
|
|
|
26
28
|
## What's here
|
|
27
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kopular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Kopular: a small component framework for KopScript — components, reactive state, constructor-injected services, and routing, with no template DSL and no DI container",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://dev.azure.com/koppinator/Koppindependence/_git/Kopular"
|
|
11
11
|
},
|
|
12
|
-
"keywords": [
|
|
12
|
+
"keywords": [
|
|
13
|
+
"kopscript",
|
|
14
|
+
"framework",
|
|
15
|
+
"components",
|
|
16
|
+
"ui"
|
|
17
|
+
],
|
|
13
18
|
"main": "./src/component.js",
|
|
14
19
|
"exports": {
|
|
15
20
|
".": "./src/component.js",
|
|
@@ -17,7 +22,9 @@
|
|
|
17
22
|
"./router": "./src/router.js",
|
|
18
23
|
"./dom": "./src/dom.js"
|
|
19
24
|
},
|
|
20
|
-
"files": [
|
|
25
|
+
"files": [
|
|
26
|
+
"src"
|
|
27
|
+
],
|
|
21
28
|
"scripts": {
|
|
22
29
|
"build": "ks build src/router.ks",
|
|
23
30
|
"prepublishOnly": "npm run build",
|
package/src/component.js
CHANGED
package/src/dom.js
CHANGED
|
@@ -2,7 +2,9 @@ export const Event = globalThis.Event;
|
|
|
2
2
|
export const Element = globalThis.Element;
|
|
3
3
|
export const Document = globalThis.Document;
|
|
4
4
|
export const Location = globalThis.Location;
|
|
5
|
+
export const History = globalThis.History;
|
|
5
6
|
export const Window = globalThis.Window;
|
|
6
7
|
export const document = globalThis.document;
|
|
7
8
|
export const location = globalThis.location;
|
|
9
|
+
export const history = globalThis.history;
|
|
8
10
|
export const window = globalThis.window;
|
package/src/dom.ks
CHANGED
|
@@ -27,7 +27,13 @@ extern class Document {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
extern class Location {
|
|
30
|
-
string
|
|
30
|
+
string pathname { get; }
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
extern class History {
|
|
34
|
+
// Param named `historyState`, not `state` — `state` is a KopScript
|
|
35
|
+
// keyword (state<T>), not a valid parameter name.
|
|
36
|
+
void pushState(string historyState, string title, string url);
|
|
31
37
|
};
|
|
32
38
|
|
|
33
39
|
extern class Window {
|
|
@@ -36,4 +42,5 @@ extern class Window {
|
|
|
36
42
|
|
|
37
43
|
extern Document document;
|
|
38
44
|
extern Location location;
|
|
45
|
+
extern History history;
|
|
39
46
|
extern Window window;
|
package/src/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Event, Element, Document, Location, Window, document, location, window } from "./dom.js";
|
|
1
|
+
import { Event, Element, Document, Location, History, Window, document, location, history, window } from "./dom.js";
|
|
2
2
|
import { Component } from "./component.js";
|
|
3
3
|
|
|
4
4
|
export class Router extends Component {
|
|
@@ -7,7 +7,7 @@ export class Router extends Component {
|
|
|
7
7
|
(this.Paths = []);
|
|
8
8
|
(this.Pages = []);
|
|
9
9
|
(this.NotFoundPage = notFoundPage);
|
|
10
|
-
window.addEventListener("
|
|
10
|
+
window.addEventListener("popstate", (e) => (this.Update()));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
AddRoute(path, page) {
|
|
@@ -16,7 +16,8 @@ export class Router extends Component {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
Navigate(path) {
|
|
19
|
-
|
|
19
|
+
history.pushState("", "", path);
|
|
20
|
+
this.Update();
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
Match(path) {
|
|
@@ -32,7 +33,7 @@ export class Router extends Component {
|
|
|
32
33
|
Render() {
|
|
33
34
|
let outlet = document.createElement("div");
|
|
34
35
|
(outlet.className = "router-outlet");
|
|
35
|
-
let path = location.
|
|
36
|
+
let path = location.pathname;
|
|
36
37
|
let page = this.Match(path);
|
|
37
38
|
page.Mount(outlet);
|
|
38
39
|
return outlet;
|
package/src/router.ks
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
using "./dom";
|
|
2
2
|
using "./component";
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
4
|
+
// History-API routing (real paths — `/about`, not `#/about`) via
|
|
5
|
+
// `history.pushState`/the browser's native `popstate` event. This needs a
|
|
6
|
+
// server that falls back to the app shell for any path it doesn't
|
|
7
|
+
// recognize as a real file (see KopularDemo's scripts/serve.mjs) — a plain
|
|
8
|
+
// static file server has nothing to serve at `/about` on a direct
|
|
9
|
+
// visit/refresh otherwise, unlike hash-based routing, which never leaves
|
|
10
|
+
// the one HTML file the server actually has. Route registration is
|
|
11
|
+
// imperative (`AddRoute` calls), not a config object — one thing to learn,
|
|
12
|
+
// not a routing DSL.
|
|
10
13
|
//
|
|
11
14
|
// Routes are registered as already-constructed Component instances, not
|
|
12
15
|
// factories — `Component[]`, not `(() => Component)[]` (KopScript's type
|
|
@@ -29,7 +32,12 @@ class Router : Component {
|
|
|
29
32
|
this.Paths = [];
|
|
30
33
|
this.Pages = [];
|
|
31
34
|
this.NotFoundPage = notFoundPage;
|
|
32
|
-
|
|
35
|
+
// 'popstate' only fires on browser back/forward (or history.go/back/
|
|
36
|
+
// forward) — never on pushState itself, unlike hashchange firing
|
|
37
|
+
// whenever location.hash is set. Navigate() below calls Update()
|
|
38
|
+
// directly for that reason; this listener only covers the back/forward
|
|
39
|
+
// case, where the URL changes without any of our own code running.
|
|
40
|
+
window.addEventListener("popstate", (Event e) => this.Update());
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
public void AddRoute(string path, Component page) {
|
|
@@ -37,11 +45,9 @@ class Router : Component {
|
|
|
37
45
|
this.Pages = this.Pages.Push(page);
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
// Setting location.hash fires a real 'hashchange' event, which the
|
|
41
|
-
// constructor already subscribed to — no direct call to Update() needed
|
|
42
|
-
// here, the same "set the value, listeners react" shape as state<T>.
|
|
43
48
|
public void Navigate(string path) {
|
|
44
|
-
|
|
49
|
+
history.pushState("", "", path);
|
|
50
|
+
this.Update();
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
private Component Match(string path) {
|
|
@@ -62,7 +68,7 @@ class Router : Component {
|
|
|
62
68
|
public override Element Render() {
|
|
63
69
|
Element outlet = document.createElement("div");
|
|
64
70
|
outlet.className = "router-outlet";
|
|
65
|
-
string path = location.
|
|
71
|
+
string path = location.pathname;
|
|
66
72
|
Component page = this.Match(path);
|
|
67
73
|
page.Mount(outlet);
|
|
68
74
|
return outlet;
|