amateras 0.1.1 → 0.3.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.
Files changed (68) hide show
  1. package/README.md +25 -8
  2. package/ext/css/src/index.ts +103 -49
  3. package/ext/css/src/lib/colorAssign.ts +6 -0
  4. package/ext/css/src/lib/colors/amber.ts +25 -0
  5. package/ext/css/src/lib/colors/blackwhite.ts +13 -0
  6. package/ext/css/src/lib/colors/blue.ts +25 -0
  7. package/ext/css/src/lib/colors/cyan.ts +25 -0
  8. package/ext/css/src/lib/colors/emerald.ts +25 -0
  9. package/ext/css/src/lib/colors/fuchsia.ts +25 -0
  10. package/ext/css/src/lib/colors/gray.ts +25 -0
  11. package/ext/css/src/lib/colors/green.ts +25 -0
  12. package/ext/css/src/lib/colors/indigo.ts +25 -0
  13. package/ext/css/src/lib/colors/lime.ts +25 -0
  14. package/ext/css/src/lib/colors/neutral.ts +25 -0
  15. package/ext/css/src/lib/colors/orange.ts +25 -0
  16. package/ext/css/src/lib/colors/pink.ts +25 -0
  17. package/ext/css/src/lib/colors/purple.ts +25 -0
  18. package/ext/css/src/lib/colors/red.ts +25 -0
  19. package/ext/css/src/lib/colors/rose.ts +25 -0
  20. package/ext/css/src/lib/colors/sky.ts +25 -0
  21. package/ext/css/src/lib/colors/slate.ts +25 -0
  22. package/ext/css/src/lib/colors/stone.ts +25 -0
  23. package/ext/css/src/lib/colors/teal.ts +25 -0
  24. package/ext/css/src/lib/colors/violet.ts +25 -0
  25. package/ext/css/src/lib/colors/yellow.ts +25 -0
  26. package/ext/css/src/lib/colors/zinc.ts +25 -0
  27. package/ext/css/src/lib/colors.ts +23 -0
  28. package/ext/css/src/structure/$CSSKeyframesRule.ts +2 -5
  29. package/ext/css/src/structure/$CSSMediaRule.ts +3 -23
  30. package/ext/css/src/structure/$CSSRule.ts +6 -18
  31. package/ext/css/src/structure/$CSSStyleRule.ts +10 -12
  32. package/ext/html/html.ts +24 -58
  33. package/ext/html/node/$Anchor.ts +49 -0
  34. package/ext/html/node/$Canvas.ts +16 -0
  35. package/ext/html/node/$Dialog.ts +16 -0
  36. package/ext/html/node/$Form.ts +16 -0
  37. package/ext/html/node/$Image.ts +72 -0
  38. package/ext/html/node/$Input.ts +169 -0
  39. package/ext/html/node/$Label.ts +16 -0
  40. package/ext/html/node/$Media.ts +16 -0
  41. package/ext/html/node/$OptGroup.ts +23 -0
  42. package/ext/html/node/$Option.ts +40 -0
  43. package/ext/html/node/$Select.ts +76 -0
  44. package/ext/html/node/$TextArea.ts +16 -0
  45. package/ext/router/README.md +81 -0
  46. package/ext/router/index.ts +66 -0
  47. package/ext/router/node/Page.ts +27 -0
  48. package/ext/router/node/Route.ts +53 -0
  49. package/ext/router/node/Router.ts +138 -0
  50. package/ext/router/node/RouterAnchor.ts +8 -0
  51. package/ext/ssr/env.ts +61 -0
  52. package/ext/ssr/index.ts +47 -0
  53. package/ext/ssr/package.json +10 -0
  54. package/package.json +8 -4
  55. package/src/core.ts +43 -30
  56. package/src/global.ts +6 -0
  57. package/src/lib/assign.ts +4 -3
  58. package/src/lib/assignHelper.ts +11 -13
  59. package/src/lib/native.ts +14 -3
  60. package/src/node/$Element.ts +204 -23
  61. package/src/node/$HTMLElement.ts +76 -0
  62. package/src/node/$Node.ts +145 -53
  63. package/src/node/node.ts +8 -6
  64. package/src/structure/Signal.ts +4 -4
  65. package/tsconfig.json +1 -1
  66. package/ext/css/src/structure/$CSSKeyframeRule.ts +0 -13
  67. package/ext/html/node/$HTMLElement.ts +0 -7
  68. package/ext/html/node/type.ts +0 -96
@@ -0,0 +1,169 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $Input extends $HTMLElement<HTMLInputElement> {
5
+ constructor() {
6
+ super('input')
7
+ }
8
+ }
9
+
10
+ export interface $Input extends $HTMLElement<HTMLInputElement> {
11
+ /** {@link HTMLInputElement.form} */
12
+ readonly form: HTMLFormElement | null;
13
+ /** {@link HTMLInputElement.labels} */
14
+ readonly labels: NodeListOf<HTMLLabelElement> | null;
15
+ /** {@link HTMLInputElement.list} */
16
+ readonly list: HTMLDataListElement | null;
17
+ /** {@link HTMLInputElement.validationMessage} */
18
+ readonly validationMessage: string;
19
+ /** {@link HTMLInputElement.validity} */
20
+ readonly validity: ValidityState;
21
+ /** {@link HTMLInputElement.webkitEntries} */
22
+ readonly webkitEntries: ReadonlyArray<FileSystemEntry>;
23
+ /** {@link HTMLInputElement.willValidate} */
24
+ readonly willValidate: boolean;
25
+
26
+ /** {@link HTMLInputElement.checkValidity} */
27
+ checkValidity(): boolean;
28
+ /** {@link HTMLInputElement.reportValidity} */
29
+ reportValidity(): boolean;
30
+ /** {@link HTMLInputElement.select} */
31
+ select(): this;
32
+ /** {@link HTMLInputElement.setCustomValidity} */
33
+ setCustomValidity(error: string): this;
34
+ /** {@link HTMLInputElement.setRangeText} */
35
+ setRangeText(replacement: string): this;
36
+ setRangeText(replacement: string, start: number, end: number, selectionMode?: SelectionMode): this;
37
+ /** {@link HTMLInputElement.setSelectionRange} */
38
+ setSelectionRange(start: number | null, end: number | null, direction?: "forward" | "backward" | "none"): this;
39
+ /** {@link HTMLInputElement.showPicker} */
40
+ showPicker(): this;
41
+ /** {@link HTMLInputElement.stepDown} */
42
+ stepDown(n?: number): this;
43
+ /** {@link HTMLInputElement.stepUp} */
44
+ stepUp(n?: number): this;
45
+
46
+ /** {@link HTMLInputElement.accept} */
47
+ accept(accept: $Parameter<string>): this;
48
+ accept(): string;
49
+ /** {@link HTMLInputElement.alt} */
50
+ alt(alt: $Parameter<string>): this;
51
+ alt(): string;
52
+ /** {@link HTMLInputElement.autoFill} */
53
+ autoFill(autoFill: $Parameter<AutoFill>): this;
54
+ autoFill(): AutoFill;
55
+ /** {@link HTMLInputElement.capture} */
56
+ capture(capture: $Parameter<string>): this;
57
+ capture(): string;
58
+ /** {@link HTMLInputElement.checked} */
59
+ checked(checked: $Parameter<boolean>): this;
60
+ checked(): boolean;
61
+ /** {@link HTMLInputElement.defaultChecked} */
62
+ defaultChecked(defaultChecked: $Parameter<boolean>): this;
63
+ defaultChecked(): string;
64
+ /** {@link HTMLInputElement.defaultValue} */
65
+ defaultValue(defaultValue: $Parameter<string>): this;
66
+ defaultValue(): string;
67
+ /** {@link HTMLInputElement.dirName} */
68
+ dirName(dirName: $Parameter<string>): this;
69
+ dirName(): string;
70
+ /** {@link HTMLInputElement.disabled} */
71
+ disabled(disabled: $Parameter<boolean>): this;
72
+ disabled(): boolean;
73
+ /** {@link HTMLInputElement.files} */
74
+ files(files: $Parameter<FileList | null>): this;
75
+ files(): string;
76
+ /** {@link HTMLInputElement.formAction} */
77
+ formAction(formAction: $Parameter<string>): this;
78
+ formAction(): string;
79
+ /** {@link HTMLInputElement.formEnctype} */
80
+ formEnctype(formEnctype: $Parameter<string>): this;
81
+ formEnctype(): string;
82
+ /** {@link HTMLInputElement.formMethod} */
83
+ formMethod(formMethod: $Parameter<string>): this;
84
+ formMethod(): string;
85
+ /** {@link HTMLInputElement.formNoValidate} */
86
+ formNoValidate(formNoValidate: $Parameter<boolean>): this;
87
+ formNoValidate(): boolean;
88
+ /** {@link HTMLInputElement.formTarget} */
89
+ formTarget(formTarget: $Parameter<string>): this;
90
+ formTarget(): string;
91
+ /** {@link HTMLInputElement.height} */
92
+ height(height: $Parameter<number>): this;
93
+ height(): number;
94
+ /** {@link HTMLInputElement.indeterminate} */
95
+ indeterminate(indeterminate: $Parameter<boolean>): this;
96
+ indeterminate(): boolean;
97
+ /** {@link HTMLInputElement.max} */
98
+ max(max: $Parameter<string>): this;
99
+ max(): string;
100
+ /** {@link HTMLInputElement.maxLength} */
101
+ maxLength(maxLength: $Parameter<number>): this;
102
+ maxLength(): number;
103
+ /** {@link HTMLInputElement.min} */
104
+ min(min: $Parameter<string>): this;
105
+ min(): string;
106
+ /** {@link HTMLInputElement.minLength} */
107
+ minLength(minLength: $Parameter<string>): this;
108
+ minLength(): string;
109
+ /** {@link HTMLInputElement.multiple} */
110
+ multiple(multiple: $Parameter<boolean>): this;
111
+ multiple(): boolean;
112
+ /** {@link HTMLInputElement.name} */
113
+ name(name: $Parameter<string>): this;
114
+ name(): string;
115
+ /** {@link HTMLInputElement.pattern} */
116
+ pattern(pattern: $Parameter<string>): this;
117
+ pattern(): string;
118
+ /** {@link HTMLInputElement.placeholder} */
119
+ placeholder(placeholder: $Parameter<string>): this;
120
+ placeholder(): string;
121
+ /** {@link HTMLInputElement.readOnly} */
122
+ readOnly(readOnly: $Parameter<boolean>): this;
123
+ readOnly(): boolean;
124
+ /** {@link HTMLInputElement.required} */
125
+ required(required: $Parameter<boolean>): this;
126
+ required(): boolean;
127
+ /** {@link HTMLInputElement.selectionDirection} */
128
+ selectionDirection(selectionDirection: $Parameter<'forward' | 'backward' | 'none' | null>): this;
129
+ selectionDirection(): 'forward' | 'backward' | 'none' | null;
130
+ /** {@link HTMLInputElement.selectionEnd} */
131
+ selectionEnd(selectionEnd: $Parameter<number | null>): this;
132
+ selectionEnd(): number | null;
133
+ /** {@link HTMLInputElement.selectionStart} */
134
+ selectionStart(selectionStart: $Parameter<number>): this;
135
+ selectionStart(): number;
136
+ /** {@link HTMLInputElement.size} */
137
+ size(size: $Parameter<number>): this;
138
+ size(): number;
139
+ /** {@link HTMLInputElement.src} */
140
+ src(src: $Parameter<string>): this;
141
+ src(): string;
142
+ /** {@link HTMLInputElement.step} */
143
+ step(step: $Parameter<string>): this;
144
+ step(): string;
145
+ /** {@link HTMLInputElement.type} */
146
+ type(type: $Parameter<string>): this;
147
+ type(): string;
148
+ /** {@link HTMLInputElement.value} */
149
+ value(value: $Parameter<string>): this;
150
+ value(): string;
151
+ /** {@link HTMLInputElement.valueAsDate} */
152
+ valueAsDate(valueAsDate: $Parameter<Date | null>): this;
153
+ valueAsDate(): Date | null;
154
+ /** {@link HTMLInputElement.valueAsNumber} */
155
+ valueAsNumber(valueAsNumber: $Parameter<number>): this;
156
+ valueAsNumber(): number;
157
+ /** {@link HTMLInputElement.webkitdirectory} */
158
+ webkitdirectory(webkitdirectory: $Parameter<boolean>): this;
159
+ webkitdirectory(): boolean;
160
+ /** {@link HTMLInputElement.width} */
161
+ width(width: $Parameter<number>): this;
162
+ width(): number;
163
+ }
164
+
165
+ assignHelper(HTMLInputElement, $Input, 'input');
166
+
167
+ declare module '#core' {
168
+ export function $(nodeName: 'input'): $Input
169
+ }
@@ -0,0 +1,16 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $Label extends $HTMLElement<HTMLLabelElement> {
5
+ constructor() {
6
+ super('label')
7
+ }
8
+ }
9
+
10
+ export interface $Label extends $HTMLElement<HTMLLabelElement> {}
11
+
12
+ assignHelper(HTMLLabelElement, $Label, 'label');
13
+
14
+ declare module '#core' {
15
+ export function $(nodeName: 'label'): $Label
16
+ }
@@ -0,0 +1,16 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $Media extends $HTMLElement<HTMLMediaElement> {
5
+ constructor() {
6
+ super('media')
7
+ }
8
+ }
9
+
10
+ export interface $Media extends $HTMLElement<HTMLMediaElement> {}
11
+
12
+ assignHelper(HTMLMediaElement, $Media, 'media');
13
+
14
+ declare module '#core' {
15
+ export function $(nodeName: 'media'): $Media
16
+ }
@@ -0,0 +1,23 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $OptGroup extends $HTMLElement<HTMLOptGroupElement> {
5
+ constructor() {
6
+ super('optgroup')
7
+ }
8
+ }
9
+
10
+ export interface $OptGroup extends $HTMLElement<HTMLOptGroupElement> {
11
+ /** {@link HTMLOptGroupElement.disabled} */
12
+ disabled(disabled: $Parameter<boolean>): this;
13
+ disabled(): boolean;
14
+ /** {@link HTMLOptGroupElement.label} */
15
+ label(label: $Parameter<string>): this;
16
+ label(): string;
17
+ }
18
+
19
+ assignHelper(HTMLOptGroupElement, $OptGroup, 'optgroup');
20
+
21
+ declare module '#core' {
22
+ export function $(nodeName: 'optgroup'): $OptGroup
23
+ }
@@ -0,0 +1,40 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $Option extends $HTMLElement<HTMLOptionElement> {
5
+ constructor() {
6
+ super('option')
7
+ }
8
+ }
9
+
10
+ export interface $Option extends $HTMLElement<HTMLOptionElement> {
11
+ /** {@link HTMLOptionElement.form} */
12
+ readonly form: HTMLFormElement | null;
13
+ /** {@link HTMLOptionElement.index} */
14
+ readonly index: number;
15
+
16
+ /** {@link HTMLOptionElement.defaultSelected} */
17
+ defaultSelected(defaultSelected: $Parameter<boolean>): this;
18
+ defaultSelected(): boolean;
19
+ /** {@link HTMLOptionElement.disabled} */
20
+ disabled(disabled: $Parameter<boolean>): this;
21
+ disabled(): boolean;
22
+ /** {@link HTMLOptionElement.label} */
23
+ label(label: $Parameter<string>): this;
24
+ label(): string;
25
+ /** {@link HTMLOptionElement.selected} */
26
+ selected(selected: $Parameter<boolean>): this;
27
+ selected(): boolean;
28
+ /** {@link HTMLOptionElement.text} */
29
+ text(text: $Parameter<string>): this;
30
+ text(): string;
31
+ /** {@link HTMLOptionElement.value} */
32
+ value(value: $Parameter<string>): this;
33
+ value(): string;
34
+ }
35
+
36
+ assignHelper(HTMLOptionElement, $Option, 'option');
37
+
38
+ declare module '#core' {
39
+ export function $(nodeName: 'option'): $Option
40
+ }
@@ -0,0 +1,76 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $Select extends $HTMLElement<HTMLSelectElement> {
5
+ constructor() {
6
+ super('select')
7
+ }
8
+ }
9
+
10
+ export interface $Select extends $HTMLElement<HTMLSelectElement> {
11
+ /** {@link HTMLSelectElement.form} */
12
+ readonly form: HTMLFormElement | null;
13
+ /** {@link HTMLSelectElement.labels} */
14
+ readonly labels: NodeListOf<HTMLLabelElement>;
15
+ /** {@link HTMLSelectElement.options} */
16
+ readonly options: HTMLOptionsCollection;
17
+ /** {@link HTMLSelectElement.selectedOptions} */
18
+ readonly selectedOptions: HTMLCollectionOf<HTMLOptionElement>;
19
+ /** {@link HTMLSelectElement.type} */
20
+ readonly type: "select-one" | "select-multiple";
21
+ /** {@link HTMLSelectElement.validationMessage} */
22
+ readonly validationMessage: string;
23
+ /** {@link HTMLSelectElement.validity} */
24
+ readonly validity: ValidityState;
25
+ /** {@link HTMLSelectElement.willValidate} */
26
+ readonly willValidate: boolean;
27
+
28
+ /** {@link HTMLSelectElement.autocomplete} */
29
+ autocomplete(autocomplete: $Parameter<AutoFill>): this;
30
+ autocomplete(): AutoFill;
31
+ /** {@link HTMLSelectElement.disabled} */
32
+ disabled(disabled: $Parameter<boolean>): this;
33
+ disabled(): boolean;
34
+ /** {@link HTMLSelectElement.length} */
35
+ length(length: $Parameter<number>): this;
36
+ length(): number;
37
+ /** {@link HTMLSelectElement.multiple} */
38
+ multiple(multiple: $Parameter<boolean>): this;
39
+ multiple(): boolean;
40
+ /** {@link HTMLSelectElement.name} */
41
+ name(name: $Parameter<string>): this;
42
+ name(): string;
43
+ /** {@link HTMLSelectElement.required} */
44
+ required(required: $Parameter<boolean>): this;
45
+ required(): boolean;
46
+ /** {@link HTMLSelectElement.selectedIndex} */
47
+ selectedIndex(selectedIndex: $Parameter<number>): this;
48
+ selectedIndex(): number;
49
+ /** {@link HTMLSelectElement.size} */
50
+ size(size: $Parameter<number>): this;
51
+ size(): number;
52
+ /** {@link HTMLSelectElement.value} */
53
+ value(value: $Parameter<string>): this;
54
+ value(): string;
55
+
56
+ /** {@link HTMLSelectElement.add} */
57
+ add(element: HTMLOptionElement | HTMLOptGroupElement, before?: HTMLElement | number | null): this;
58
+ /** {@link HTMLSelectElement.checkValidity} */
59
+ checkValidity(): boolean;
60
+ /** {@link HTMLSelectElement.item} */
61
+ item(index: number): HTMLOptionElement | null;
62
+ /** {@link HTMLSelectElement.namedItem} */
63
+ namedItem(name: string): HTMLOptionElement | null;
64
+ /** {@link HTMLSelectElement.reportValidity} */
65
+ reportValidity(): boolean;
66
+ /** {@link HTMLSelectElement.setCustomValidity} */
67
+ setCustomValidity(error: string): this;
68
+ /** {@link HTMLSelectElement.showPicker} */
69
+ showPicker(): this;
70
+ }
71
+
72
+ assignHelper(HTMLSelectElement, $Select, 'select');
73
+
74
+ declare module '#core' {
75
+ export function $(nodeName: 'select'): $Select
76
+ }
@@ -0,0 +1,16 @@
1
+ import { assignHelper } from "#lib/assignHelper";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+
4
+ export class $TextArea extends $HTMLElement<HTMLTextAreaElement> {
5
+ constructor() {
6
+ super('textarea')
7
+ }
8
+ }
9
+
10
+ export interface $TextArea extends $HTMLElement<HTMLTextAreaElement> {}
11
+
12
+ assignHelper(HTMLTextAreaElement, $TextArea, 'textarea');
13
+
14
+ declare module '#core' {
15
+ export function $(nodeName: 'textarea'): $TextArea
16
+ }
@@ -0,0 +1,81 @@
1
+ # amateras/router
2
+
3
+ ## Usage
4
+ ```ts
5
+ import 'amateras';
6
+ import 'amateras/router';
7
+ ```
8
+
9
+ ## Create Route Map
10
+ ```ts
11
+ // create home page route
12
+ const HomePage = $('route', '/', page => page
13
+ .pageTitle('Home')
14
+ .content([
15
+ $('h1').content('Home')
16
+ ])
17
+ )
18
+ // append router and mapping home page route into router
19
+ $(document.body).content([
20
+ $('router')
21
+ .route('/', HomePage)
22
+ .route('/hello', page => 'Hello!')
23
+ .listen() // start to listen path change
24
+ ])
25
+ ```
26
+
27
+ ## Router Anchor
28
+ Use `RouterAnchor` to prevent load page when open link by default `HTMLAnchorElement` element.
29
+ ```ts
30
+ $('ra').content('Contact').href('/contact');
31
+ ```
32
+
33
+ ## Common Methods
34
+ - `$.open(url)`: Open page without load page.
35
+ - `$.replace(url)`: Replace history state with url and open page.
36
+ - `$.forward()`: Forward page.
37
+ - `$.back()`: Back page.
38
+
39
+ ## Path Parameter and Query
40
+ ```ts
41
+ $('router')
42
+ .route('/user/@:username', page => {
43
+ console.log(page.params)
44
+ })
45
+ .route('/posts?search'), page => {
46
+ console.log(page.query)
47
+ }
48
+ .listen()
49
+ // simulate page open
50
+ .resolve('/user/@amateras') // { username: 'amateras' }
51
+ .resolve('/posts"') // { }
52
+ .resolve('/posts?search=tsukimi&user') // { search: 'tsukimi', user: '' }
53
+ ```
54
+
55
+ ## Nesting Route
56
+ ```ts
57
+ const ContactPage = $('route', '/contact', page => page
58
+ .pageTitle('Home')
59
+ .content([
60
+ $('h1').content('Contact'),
61
+ // append router with page, nested routes will show in this router
62
+ $('router', page)
63
+ ])
64
+ )
65
+
66
+ const ContactEmailPage = $('route', '/contact/email', () => 'amateras@example.com')
67
+
68
+ $('router')
69
+ .route('/', HomePage)
70
+ .route('/contact', ContactPage, route => route
71
+ .route('/', () => 'My name is Amateras.')
72
+ .route('/phone', () => '0123456789')
73
+ .route('/email', ContactEmailPage)
74
+ )
75
+ ```
76
+
77
+ ## Async Route
78
+ ```ts
79
+ $('router')
80
+ .route('/about', () => import('./pages/about.ts'))
81
+ ```
@@ -0,0 +1,66 @@
1
+ import type { AnchorTarget } from "#html/$Anchor";
2
+ import { _Object_assign, forEach } from "#lib/native";
3
+ import type { $NodeContentResolver } from "#node/$Node";
4
+ import type { Page } from "./node/Page";
5
+ import { Route } from "./node/Route";
6
+ import { Router } from "./node/Router";
7
+ import { RouterAnchor } from "./node/RouterAnchor";
8
+ export * from "./node/Route";
9
+ export * from "./node/Router";
10
+ export * from "./node/Page";
11
+ export * from "./node/RouterAnchor";
12
+
13
+ declare module 'amateras/core' {
14
+ export function $<P extends string>(nodeName: 'route', path: P, builder: RouteBuilder<Route<P>, RouteDataResolver<P>>): Route<P>;
15
+ export function $(nodeName: 'router', page?: Page<any>): Router;
16
+ export function $(nodeName: 'ra'): RouterAnchor;
17
+ export namespace $ {
18
+ export function open(url: string | URL | Nullish, target: AnchorTarget): typeof Router;
19
+ export function replace(url: string | URL | Nullish): typeof Router;
20
+ export function back(): typeof Router;
21
+ export function forward(): typeof Router;
22
+ }
23
+ }
24
+ // assign methods
25
+ _Object_assign($, {
26
+ open: Router.open.bind(Router),
27
+ replace: Router.replace.bind(Router),
28
+ back: Router.back.bind(Router),
29
+ forward: Router.forward.bind(Router)
30
+ });
31
+ // define styles
32
+ forEach([
33
+ `router{display:block}`,
34
+ `page{display:block}`
35
+ ], rule => $.stylesheet.insertRule(rule));
36
+ // assign nodes
37
+ $.assign([
38
+ ['router', Router],
39
+ ['route', Route],
40
+ ['ra', RouterAnchor]
41
+ ])
42
+
43
+ export type RouteData = { params: any, query: any }
44
+ export type RouteDataResolver<P extends string> = { params: Prettify<PathParams<P>>, query: Prettify<PathQuery<P>> }
45
+ export type AsyncRoute<P extends string> = () => Promise<{default: Route<P>}>
46
+ export type RouteBuilder<R extends Route<any>, D extends RouteData> = (page: Page<R, D>) => OrPromise<$NodeContentResolver<Page<R, D>>>;
47
+
48
+ type PathParams<Path> = Path extends `${infer Segment}/${infer Rest}`
49
+ ? Segment extends `${string}:${infer Param}`
50
+ ? Record<Param, string> & PathParams<Rest>
51
+ : PathParams<Rest>
52
+ : Path extends `${string}:${infer Param}?${infer Query}`
53
+ ? Record<Param, string>
54
+ : Path extends `${string}:${infer Param}`
55
+ ? Record<Param, string>
56
+ : {}
57
+
58
+ type PathQuery<Path> = Path extends `${string}?${infer Segment}`
59
+ ? PathQuery_SetRecord<Segment>
60
+ : Path extends `&${infer Segment}`
61
+ ? PathQuery_SetRecord<Segment>
62
+ : {}
63
+
64
+ type PathQuery_SetRecord<Segment extends string> = Segment extends `${infer Param}&${infer Rest}`
65
+ ? Record<Param, string> & PathQuery<`&${Rest}`>
66
+ : Record<Segment, string>
@@ -0,0 +1,27 @@
1
+ import { isUndefined } from "#lib/native";
2
+ import { $HTMLElement } from "#node/$HTMLElement";
3
+ import type { RouteData } from "..";
4
+ import type { Route } from "./Route";
5
+
6
+ export class Page<R extends Route<any> = any, Data extends RouteData = any> extends $HTMLElement {
7
+ route: R;
8
+ page: this;
9
+ params: Data['params'];
10
+ query: Data['query'];
11
+ #pageTitle: null | string = null;
12
+ constructor(route: R, data?: {params: any, query: any}) {
13
+ super('page');
14
+ this.route = route;
15
+ this.page = this;
16
+ this.params = data?.params ?? {};
17
+ this.query = data?.query ?? {};
18
+ }
19
+
20
+ pageTitle(): string | null;
21
+ pageTitle(title: string | null): this;
22
+ pageTitle(title?: string | null) {
23
+ if (!arguments.length) return this.#pageTitle;
24
+ if (!isUndefined(title)) this.#pageTitle = title;
25
+ return this;
26
+ }
27
+ }
@@ -0,0 +1,53 @@
1
+ import { _instanceof, _Object_fromEntries, _Array_from } from "#lib/native";
2
+ import { $Element } from "#node/node";
3
+ import type { AsyncRoute, RouteBuilder, RouteDataResolver } from "..";
4
+ import { Page } from "./Page";
5
+
6
+ export abstract class BaseRouteNode<Path extends string = string> extends $Element {
7
+ readonly path: Path;
8
+ routes = new Map<string, Route<any>>()
9
+ parent: BaseRouteNode<any> | null = null;
10
+ builder: RouteBuilder<Route<Path>, RouteDataResolver<Path>> | AsyncRoute<Path>
11
+ constructor(path: Path, builder: RouteBuilder<Route<Path>, RouteDataResolver<Path>> | AsyncRoute<Path>, nodeName: string) {
12
+ super(nodeName);
13
+ this.path = path;
14
+ this.builder = builder;
15
+ }
16
+
17
+ route<P extends string, J extends `${Path}${P}`>(
18
+ path: P,
19
+ resolver: RouteBuilder<Route<J>, RouteDataResolver<J>> | Route<J> | AsyncRoute<J>,
20
+ fn?: (route: Route<J>) => Route<J>
21
+ ) {
22
+ const fullPath = `${this.path}${path}`;
23
+ if (_instanceof(resolver, Route) && fullPath !== resolver.path) throw `Pathname not matched: ${path}`
24
+ const route = resolver instanceof Route ? resolver : new Route(fullPath as J, resolver);
25
+ route.parent = this;
26
+ fn && fn(route);
27
+ this.routes.set(path, route);
28
+ return this;
29
+ }
30
+ }
31
+
32
+ export class Route<Path extends string = string> extends BaseRouteNode<Path> {
33
+ constructor(path: Path, builder: RouteBuilder<Route<Path>, RouteDataResolver<Path>> | AsyncRoute<Path>) {
34
+ super(path, builder, 'route');
35
+ }
36
+
37
+ async build(data: {params: any, query: any} = {params: {}, query: {}}, page?: Page) {
38
+ page = page ?? new Page(this, data);
39
+ page.params = data.params;
40
+ let resolver: any = this.builder(page);
41
+ if (_instanceof(resolver, Promise)) {
42
+ const result = await resolver as any;
43
+ // Module import
44
+ if (result[Symbol.toStringTag] === 'Module') {
45
+ page.route = this;
46
+ resolver = result.default.builder(page);
47
+ }
48
+ else resolver = result;
49
+ }
50
+ if (!_instanceof(resolver, Page)) page.content(resolver);
51
+ return page;
52
+ }
53
+ }