@ssgoi/angular 7.1.1 → 7.1.3-beta.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 +21 -8
- package/dist/README.md +138 -0
- package/dist/fesm2022/ssgoi-angular-router.mjs +90 -0
- package/dist/fesm2022/ssgoi-angular-router.mjs.map +1 -0
- package/dist/fesm2022/ssgoi-angular-view-transitions.mjs +6 -0
- package/dist/fesm2022/ssgoi-angular-view-transitions.mjs.map +1 -0
- package/dist/fesm2022/ssgoi-angular.mjs +119 -0
- package/dist/fesm2022/ssgoi-angular.mjs.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/router/index.d.ts +46 -0
- package/dist/view-transitions/index.d.ts +1 -0
- package/package.json +42 -9
- package/eslint.config.mjs +0 -75
- package/ng-package.json +0 -10
- package/src/lib/context.ts +0 -23
- package/src/lib/index.ts +0 -6
- package/src/lib/ssgoi-transition.directive.ts +0 -50
- package/src/lib/ssgoi.directive.ts +0 -65
- package/src/lib/types.ts +0 -1
- package/src/public-api.ts +0 -6
- package/tsconfig.lib.json +0 -26
- package/view-transitions/ng-package.json +0 -6
- package/view-transitions/public-api.ts +0 -1
package/README.md
CHANGED
|
@@ -12,6 +12,17 @@ npm install @ssgoi/angular
|
|
|
12
12
|
|
|
13
13
|
Agent setup guide: https://ssgoi.dev/llms.txt
|
|
14
14
|
|
|
15
|
+
## Router helpers
|
|
16
|
+
|
|
17
|
+
Install this framework package once; router helpers are optional subpaths.
|
|
18
|
+
These router helpers are experimental APIs and may change.
|
|
19
|
+
|
|
20
|
+
| Router | Import | API |
|
|
21
|
+
| -------------- | ----------------------- | -------------------- |
|
|
22
|
+
| Angular Router | `@ssgoi/angular/router` | `SsgoiRouteBoundary` |
|
|
23
|
+
|
|
24
|
+
See the [framework guide](https://ssgoi.dev/docs/frameworks/angular) for wiring and limits.
|
|
25
|
+
|
|
15
26
|
## Setup
|
|
16
27
|
|
|
17
28
|
Create one SSGOI root above the router outlet.
|
|
@@ -45,22 +56,24 @@ export class AppComponent {
|
|
|
45
56
|
|
|
46
57
|
## Route boundary
|
|
47
58
|
|
|
48
|
-
|
|
59
|
+
Use the experimental standalone directive on the routed page's single DOM root:
|
|
49
60
|
|
|
50
61
|
```ts
|
|
62
|
+
import { Component } from "@angular/core";
|
|
63
|
+
import { SsgoiRouteBoundary } from "@ssgoi/angular/router";
|
|
64
|
+
|
|
51
65
|
@Component({
|
|
52
|
-
selector: "app-post",
|
|
53
66
|
standalone: true,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<!-- page -->
|
|
57
|
-
</article>
|
|
58
|
-
`,
|
|
67
|
+
imports: [SsgoiRouteBoundary],
|
|
68
|
+
template: `<article *ssgoiRouteBoundary="let route">{{ route.id }}</article>`,
|
|
59
69
|
})
|
|
60
70
|
export class PostComponent {}
|
|
61
71
|
```
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
It recreates the embedded view on pathname changes, including parameterized
|
|
74
|
+
routes that reuse the same component. Query-only navigation preserves it. Use
|
|
75
|
+
`key: 'shell'` for a persistent region. Keep RouterOutlet in the application;
|
|
76
|
+
custom reuse caches and auxiliary outlet coordination are not handled.
|
|
64
77
|
|
|
65
78
|
## Config
|
|
66
79
|
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @ssgoi/angular
|
|
2
|
+
|
|
3
|
+
Angular bindings for SSGOI.
|
|
4
|
+
|
|
5
|
+
[](https://ssgoi.dev)
|
|
6
|
+
|
|
7
|
+
[Live demos](https://ssgoi.dev) · [Hero, Zoom, Film, and Sheet in motion](https://ssgoi.dev/blog/view-transition-api-limitations)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ssgoi/angular
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Agent setup guide: https://ssgoi.dev/llms.txt
|
|
14
|
+
|
|
15
|
+
## Router helpers
|
|
16
|
+
|
|
17
|
+
Install this framework package once; router helpers are optional subpaths.
|
|
18
|
+
These router helpers are experimental APIs and may change.
|
|
19
|
+
|
|
20
|
+
| Router | Import | API |
|
|
21
|
+
| -------------- | ----------------------- | -------------------- |
|
|
22
|
+
| Angular Router | `@ssgoi/angular/router` | `SsgoiRouteBoundary` |
|
|
23
|
+
|
|
24
|
+
See the [framework guide](https://ssgoi.dev/docs/frameworks/angular) for wiring and limits.
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
Create one SSGOI root above the router outlet.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Component, signal } from "@angular/core";
|
|
32
|
+
import { RouterOutlet } from "@angular/router";
|
|
33
|
+
import { Ssgoi, type SsgoiConfig } from "@ssgoi/angular";
|
|
34
|
+
import { drill } from "@ssgoi/angular/view-transitions";
|
|
35
|
+
|
|
36
|
+
@Component({
|
|
37
|
+
selector: "app-root",
|
|
38
|
+
standalone: true,
|
|
39
|
+
imports: [RouterOutlet, Ssgoi],
|
|
40
|
+
template: `
|
|
41
|
+
<main
|
|
42
|
+
ssgoi
|
|
43
|
+
[config]="config()"
|
|
44
|
+
style="position:relative;z-index:0;overflow-x:clip"
|
|
45
|
+
>
|
|
46
|
+
<router-outlet />
|
|
47
|
+
</main>
|
|
48
|
+
`,
|
|
49
|
+
})
|
|
50
|
+
export class AppComponent {
|
|
51
|
+
protected readonly config = signal<SsgoiConfig>({
|
|
52
|
+
transitions: [{ on: "/posts/**", except: "/posts", transition: drill() }],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Route boundary
|
|
58
|
+
|
|
59
|
+
Use the experimental standalone directive on the routed page's single DOM root:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Component } from "@angular/core";
|
|
63
|
+
import { SsgoiRouteBoundary } from "@ssgoi/angular/router";
|
|
64
|
+
|
|
65
|
+
@Component({
|
|
66
|
+
standalone: true,
|
|
67
|
+
imports: [SsgoiRouteBoundary],
|
|
68
|
+
template: `<article *ssgoiRouteBoundary="let route">{{ route.id }}</article>`,
|
|
69
|
+
})
|
|
70
|
+
export class PostComponent {}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
It recreates the embedded view on pathname changes, including parameterized
|
|
74
|
+
routes that reuse the same component. Query-only navigation preserves it. Use
|
|
75
|
+
`key: 'shell'` for a persistent region. Keep RouterOutlet in the application;
|
|
76
|
+
custom reuse caches and auxiliary outlet coordination are not handled.
|
|
77
|
+
|
|
78
|
+
## Config
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import type { SsgoiConfig } from "@ssgoi/angular";
|
|
82
|
+
import { drill, slide, zoom } from "@ssgoi/angular/view-transitions";
|
|
83
|
+
|
|
84
|
+
const config: SsgoiConfig = {
|
|
85
|
+
transitions: [
|
|
86
|
+
{
|
|
87
|
+
on: "/posts/**",
|
|
88
|
+
except: "/posts",
|
|
89
|
+
transition: drill(),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
from: "/gallery",
|
|
93
|
+
to: "/gallery/*",
|
|
94
|
+
transition: zoom(),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
ordered: ["/tabs/a", "/tabs/b"],
|
|
98
|
+
transition: slide(),
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- `on`: route family.
|
|
105
|
+
- `from`/`to`: precise pair.
|
|
106
|
+
- `ordered`: directional sequence.
|
|
107
|
+
- Scroll is automatic: `on` and `from`/`to` restore `from` and reset `to`;
|
|
108
|
+
`ordered` restores both. Override with
|
|
109
|
+
`preserveScroll: { from: boolean, to: boolean }`.
|
|
110
|
+
- Patterns support exact paths, a `*` path segment, and suffix `**`.
|
|
111
|
+
- Higher `priority` wins before path specificity.
|
|
112
|
+
|
|
113
|
+
## Effect index
|
|
114
|
+
|
|
115
|
+
- `fade`: unrelated pages.
|
|
116
|
+
- `drill`: list → detail.
|
|
117
|
+
- `slide`: ordered tabs.
|
|
118
|
+
- `axis`: sibling destinations.
|
|
119
|
+
- `sheet`: modal-like routes.
|
|
120
|
+
- `zoom`: card/image → detail.
|
|
121
|
+
- `hero`: shared elements and page chrome.
|
|
122
|
+
- `scroll`: vertical sequences.
|
|
123
|
+
- `strip`, `film`, `rotate`, `blind`, `jaemin`: expressive transitions.
|
|
124
|
+
|
|
125
|
+
Options: https://ssgoi.dev/llms.txt#7-transition-index
|
|
126
|
+
|
|
127
|
+
## API
|
|
128
|
+
|
|
129
|
+
- `[ssgoi]`: creates the root transition context.
|
|
130
|
+
- `[config]`: accepts `SsgoiConfig`.
|
|
131
|
+
- `[host]`: accepts an optional `HostAnimation`.
|
|
132
|
+
- `data-ssgoi-transition`: marks a route boundary.
|
|
133
|
+
- `injectSsgoi()`: returns the transition context.
|
|
134
|
+
- `[ssgoiTransition]`: deprecated; use the data attribute.
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, TemplateRef, ViewContainerRef, Renderer2, Input, Directive } from '@angular/core';
|
|
3
|
+
import { ActivatedRoute, Router, NavigationEnd, createUrlTreeFromSnapshot } from '@angular/router';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @experimental Angular Router integration. The API may change.
|
|
7
|
+
* Use on a routed page's single DOM root, e.g. <article *ssgoiRouteBoundary>.
|
|
8
|
+
* Recreates the embedded view when its route key changes, including reused
|
|
9
|
+
* parameterized routes. It does not replace RouterOutlet or a RouteReuseStrategy.
|
|
10
|
+
*/
|
|
11
|
+
class SsgoiRouteBoundary {
|
|
12
|
+
ssgoiRouteBoundary;
|
|
13
|
+
ssgoiRouteBoundaryKey;
|
|
14
|
+
route = inject(ActivatedRoute);
|
|
15
|
+
router = inject(Router);
|
|
16
|
+
template = inject(TemplateRef);
|
|
17
|
+
container = inject(ViewContainerRef);
|
|
18
|
+
renderer = inject(Renderer2);
|
|
19
|
+
view;
|
|
20
|
+
element;
|
|
21
|
+
key;
|
|
22
|
+
subscription;
|
|
23
|
+
initialized = false;
|
|
24
|
+
ngOnInit() {
|
|
25
|
+
this.initialized = true;
|
|
26
|
+
this.renderBoundary();
|
|
27
|
+
// Read committed snapshots once per successful navigation. Ancestor URL
|
|
28
|
+
// streams may emit separately while the router advances its route tree.
|
|
29
|
+
this.subscription = this.router.events.subscribe((event) => {
|
|
30
|
+
if (event instanceof NavigationEnd)
|
|
31
|
+
this.renderBoundary();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
ngOnChanges() {
|
|
35
|
+
if (this.initialized)
|
|
36
|
+
this.renderBoundary();
|
|
37
|
+
}
|
|
38
|
+
ngOnDestroy() {
|
|
39
|
+
this.subscription?.unsubscribe();
|
|
40
|
+
}
|
|
41
|
+
renderBoundary() {
|
|
42
|
+
const route = this.route.snapshot;
|
|
43
|
+
const pathname = this.router.serializeUrl(createUrlTreeFromSnapshot(route, [], null, null));
|
|
44
|
+
const boundary = typeof this.ssgoiRouteBoundary === "function"
|
|
45
|
+
? this.ssgoiRouteBoundary({ pathname, route })
|
|
46
|
+
: { id: pathname };
|
|
47
|
+
const key = this.ssgoiRouteBoundaryKey ?? boundary.key ?? boundary.id;
|
|
48
|
+
if (!this.view || key !== this.key) {
|
|
49
|
+
this.container.clear();
|
|
50
|
+
this.view = this.container.createEmbeddedView(this.template, {
|
|
51
|
+
$implicit: boundary,
|
|
52
|
+
});
|
|
53
|
+
const roots = this.view.rootNodes.filter((node) => node.nodeType === 1);
|
|
54
|
+
if (roots.length !== 1) {
|
|
55
|
+
this.container.clear();
|
|
56
|
+
this.view = undefined;
|
|
57
|
+
throw new Error("SSGOI: *ssgoiRouteBoundary requires exactly one DOM root.");
|
|
58
|
+
}
|
|
59
|
+
this.element = roots[0];
|
|
60
|
+
this.key = key;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.view.context.$implicit = boundary;
|
|
64
|
+
}
|
|
65
|
+
this.renderer.setAttribute(this.element, "data-ssgoi-transition", boundary.id);
|
|
66
|
+
this.view.markForCheck();
|
|
67
|
+
}
|
|
68
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SsgoiRouteBoundary, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
72
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: SsgoiRouteBoundary, isStandalone: true, selector: "[ssgoiRouteBoundary]", inputs: { ssgoiRouteBoundary: "ssgoiRouteBoundary", ssgoiRouteBoundaryKey: "ssgoiRouteBoundaryKey" }, usesOnChanges: true, ngImport: i0 });
|
|
73
|
+
}
|
|
74
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SsgoiRouteBoundary, decorators: [{
|
|
75
|
+
type: Directive,
|
|
76
|
+
args: [{ selector: "[ssgoiRouteBoundary]", standalone: true }]
|
|
77
|
+
}], propDecorators: { ssgoiRouteBoundary: [{
|
|
78
|
+
type: Input
|
|
79
|
+
}], ssgoiRouteBoundaryKey: [{
|
|
80
|
+
type: Input
|
|
81
|
+
}] } });
|
|
82
|
+
|
|
83
|
+
/** @experimental Angular Router integration. The API may change. */
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generated bundle index. Do not edit.
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
export { SsgoiRouteBoundary };
|
|
90
|
+
//# sourceMappingURL=ssgoi-angular-router.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssgoi-angular-router.mjs","sources":["../../router/src/ssgoi-route-boundary.ts","../../router/public-api.ts","../../router/ssgoi-angular-router.ts"],"sourcesContent":["import {\n Directive,\n Input,\n Renderer2,\n TemplateRef,\n ViewContainerRef,\n inject,\n type EmbeddedViewRef,\n type OnChanges,\n type OnDestroy,\n type OnInit,\n} from \"@angular/core\";\nimport {\n ActivatedRoute,\n NavigationEnd,\n Router,\n createUrlTreeFromSnapshot,\n type ActivatedRouteSnapshot,\n} from \"@angular/router\";\n\nexport interface RouteBoundaryState {\n id: string;\n key?: string | number;\n}\n\nexport interface AngularRouteLocation {\n pathname: string;\n route: ActivatedRouteSnapshot;\n}\n\ntype BoundaryContext = { $implicit: RouteBoundaryState };\ntype ResolveBoundary = (location: AngularRouteLocation) => RouteBoundaryState;\n\n/**\n * @experimental Angular Router integration. The API may change.\n * Use on a routed page's single DOM root, e.g. <article *ssgoiRouteBoundary>.\n * Recreates the embedded view when its route key changes, including reused\n * parameterized routes. It does not replace RouterOutlet or a RouteReuseStrategy.\n */\n@Directive({ selector: \"[ssgoiRouteBoundary]\", standalone: true })\nexport class SsgoiRouteBoundary implements OnInit, OnChanges, OnDestroy {\n @Input() ssgoiRouteBoundary?: ResolveBoundary | \"\";\n @Input() ssgoiRouteBoundaryKey?: string | number;\n\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly template = inject<TemplateRef<BoundaryContext>>(TemplateRef);\n private readonly container = inject(ViewContainerRef);\n private readonly renderer = inject(Renderer2);\n private view?: EmbeddedViewRef<BoundaryContext>;\n private element?: HTMLElement;\n private key?: string | number;\n private subscription?: { unsubscribe(): void };\n private initialized = false;\n\n ngOnInit(): void {\n this.initialized = true;\n this.renderBoundary();\n // Read committed snapshots once per successful navigation. Ancestor URL\n // streams may emit separately while the router advances its route tree.\n this.subscription = this.router.events.subscribe((event) => {\n if (event instanceof NavigationEnd) this.renderBoundary();\n });\n }\n\n ngOnChanges(): void {\n if (this.initialized) this.renderBoundary();\n }\n\n ngOnDestroy(): void {\n this.subscription?.unsubscribe();\n }\n\n private renderBoundary(): void {\n const route = this.route.snapshot;\n const pathname = this.router.serializeUrl(\n createUrlTreeFromSnapshot(route, [], null, null),\n );\n const boundary =\n typeof this.ssgoiRouteBoundary === \"function\"\n ? this.ssgoiRouteBoundary({ pathname, route })\n : { id: pathname };\n const key = this.ssgoiRouteBoundaryKey ?? boundary.key ?? boundary.id;\n\n if (!this.view || key !== this.key) {\n this.container.clear();\n this.view = this.container.createEmbeddedView(this.template, {\n $implicit: boundary,\n });\n const roots = this.view.rootNodes.filter((node) => node.nodeType === 1);\n if (roots.length !== 1) {\n this.container.clear();\n this.view = undefined;\n throw new Error(\n \"SSGOI: *ssgoiRouteBoundary requires exactly one DOM root.\",\n );\n }\n this.element = roots[0];\n this.key = key;\n } else {\n this.view.context.$implicit = boundary;\n }\n this.renderer.setAttribute(\n this.element,\n \"data-ssgoi-transition\",\n boundary.id,\n );\n this.view.markForCheck();\n }\n\n static ngTemplateContextGuard(\n _directive: SsgoiRouteBoundary,\n context: unknown,\n ): context is BoundaryContext {\n return true;\n }\n}\n","/** @experimental Angular Router integration. The API may change. */\nexport { SsgoiRouteBoundary } from \"./src/ssgoi-route-boundary\";\nexport type {\n AngularRouteLocation,\n RouteBoundaryState,\n} from \"./src/ssgoi-route-boundary\";\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssgoi-angular-view-transitions.mjs","sources":["../../view-transitions/ssgoi-angular-view-transitions.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;AAEG"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, input, ElementRef, PLATFORM_ID, forwardRef, Directive } from '@angular/core';
|
|
3
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
4
|
+
import { createSggoiTransitionContext, observeSsgoiTransitions } from '@ssgoi/core/internal';
|
|
5
|
+
export * from '@ssgoi/core';
|
|
6
|
+
|
|
7
|
+
const SSGOI_CONTEXT = new InjectionToken("ssgoi-context");
|
|
8
|
+
const noopContext = {
|
|
9
|
+
register: () => { },
|
|
10
|
+
refFor: () => () => { },
|
|
11
|
+
};
|
|
12
|
+
function injectSsgoi() {
|
|
13
|
+
const context = inject(SSGOI_CONTEXT, { optional: true });
|
|
14
|
+
if (!context) {
|
|
15
|
+
// During SSR or when not wrapped in <ssgoi>, return a no-op context
|
|
16
|
+
// This prevents errors during server-side rendering
|
|
17
|
+
return noopContext;
|
|
18
|
+
}
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createSsgoiContext(component) {
|
|
23
|
+
return component.getContext();
|
|
24
|
+
}
|
|
25
|
+
class Ssgoi {
|
|
26
|
+
config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : []));
|
|
27
|
+
host = input(undefined, ...(ngDevMode ? [{ debugName: "host" }] : []));
|
|
28
|
+
el = inject((ElementRef));
|
|
29
|
+
platformId = inject(PLATFORM_ID);
|
|
30
|
+
contextValue;
|
|
31
|
+
stopObserving;
|
|
32
|
+
getContext() {
|
|
33
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
this.contextValue ??= createSggoiTransitionContext(this.config(), {
|
|
37
|
+
host: this.host(),
|
|
38
|
+
});
|
|
39
|
+
return this.contextValue;
|
|
40
|
+
}
|
|
41
|
+
ngAfterViewInit() {
|
|
42
|
+
const ssgoi = this.getContext();
|
|
43
|
+
if (!ssgoi)
|
|
44
|
+
return;
|
|
45
|
+
this.stopObserving = observeSsgoiTransitions(this.el.nativeElement, ssgoi);
|
|
46
|
+
}
|
|
47
|
+
ngOnDestroy() {
|
|
48
|
+
this.stopObserving?.();
|
|
49
|
+
}
|
|
50
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: Ssgoi, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
51
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: Ssgoi, isStandalone: true, selector: "[ssgoi]", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, host: { classPropertyName: "host", publicName: "host", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
52
|
+
{
|
|
53
|
+
provide: SSGOI_CONTEXT,
|
|
54
|
+
useFactory: createSsgoiContext,
|
|
55
|
+
deps: [forwardRef(() => Ssgoi)],
|
|
56
|
+
},
|
|
57
|
+
], ngImport: i0 });
|
|
58
|
+
}
|
|
59
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: Ssgoi, decorators: [{
|
|
60
|
+
type: Directive,
|
|
61
|
+
args: [{
|
|
62
|
+
selector: "[ssgoi]",
|
|
63
|
+
standalone: true,
|
|
64
|
+
providers: [
|
|
65
|
+
{
|
|
66
|
+
provide: SSGOI_CONTEXT,
|
|
67
|
+
useFactory: createSsgoiContext,
|
|
68
|
+
deps: [forwardRef(() => Ssgoi)],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
}]
|
|
72
|
+
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], host: [{ type: i0.Input, args: [{ isSignal: true, alias: "host", required: false }] }] } });
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated Set `data-ssgoi-transition` directly on the page boundary
|
|
76
|
+
* element inside `[ssgoi]` instead.
|
|
77
|
+
*/
|
|
78
|
+
class SsgoiTransition {
|
|
79
|
+
// The directive attribute value becomes the transition ID
|
|
80
|
+
ssgoiTransition = input.required(...(ngDevMode ? [{ debugName: "ssgoiTransition" }] : []));
|
|
81
|
+
ssgoi = injectSsgoi();
|
|
82
|
+
el = inject((ElementRef));
|
|
83
|
+
platformId = inject(PLATFORM_ID);
|
|
84
|
+
ngOnInit() {
|
|
85
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Set data attribute on host element
|
|
89
|
+
this.el.nativeElement.setAttribute("data-ssgoi-transition", this.ssgoiTransition());
|
|
90
|
+
}
|
|
91
|
+
ngAfterViewInit() {
|
|
92
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Use the host element itself as the transition target
|
|
96
|
+
const targetElement = this.el.nativeElement;
|
|
97
|
+
this.ssgoi.refFor(this.ssgoiTransition())(targetElement);
|
|
98
|
+
}
|
|
99
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SsgoiTransition, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
100
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: SsgoiTransition, isStandalone: true, selector: "[ssgoiTransition]", inputs: { ssgoiTransition: { classPropertyName: "ssgoiTransition", publicName: "ssgoiTransition", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
101
|
+
}
|
|
102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SsgoiTransition, decorators: [{
|
|
103
|
+
type: Directive,
|
|
104
|
+
args: [{
|
|
105
|
+
selector: "[ssgoiTransition]",
|
|
106
|
+
standalone: true,
|
|
107
|
+
}]
|
|
108
|
+
}], propDecorators: { ssgoiTransition: [{ type: i0.Input, args: [{ isSignal: true, alias: "ssgoiTransition", required: true }] }] } });
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* Public API Surface of @ssgoi/angular
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generated bundle index. Do not edit.
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
export { SSGOI_CONTEXT, Ssgoi, SsgoiTransition, injectSsgoi };
|
|
119
|
+
//# sourceMappingURL=ssgoi-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssgoi-angular.mjs","sources":["../../src/lib/context.ts","../../src/lib/ssgoi.directive.ts","../../src/lib/ssgoi-transition.directive.ts","../../src/public-api.ts","../../src/ssgoi-angular.ts"],"sourcesContent":["import { InjectionToken, inject } from \"@angular/core\";\nimport type { SsgoiContext } from \"@ssgoi/core/types\";\n\nexport const SSGOI_CONTEXT = new InjectionToken<SsgoiContext | undefined>(\n \"ssgoi-context\",\n);\n\nconst noopContext: SsgoiContext = {\n register: () => {},\n refFor: () => () => {},\n};\n\nexport function injectSsgoi(): SsgoiContext {\n const context = inject(SSGOI_CONTEXT, { optional: true });\n\n if (!context) {\n // During SSR or when not wrapped in <ssgoi>, return a no-op context\n // This prevents errors during server-side rendering\n return noopContext;\n }\n\n return context;\n}\n","import {\n AfterViewInit,\n Directive,\n ElementRef,\n OnDestroy,\n input,\n forwardRef,\n PLATFORM_ID,\n inject,\n} from \"@angular/core\";\nimport { isPlatformBrowser } from \"@angular/common\";\nimport {\n createSggoiTransitionContext,\n observeSsgoiTransitions,\n} from \"@ssgoi/core/internal\";\nimport type { HostAnimation } from \"@ssgoi/core/internal\";\nimport type { SsgoiConfig, SsgoiContext } from \"@ssgoi/core/types\";\nimport { SSGOI_CONTEXT } from \"./context\";\n\nfunction createSsgoiContext(component: Ssgoi): SsgoiContext | undefined {\n return component.getContext();\n}\n\n@Directive({\n selector: \"[ssgoi]\",\n standalone: true,\n providers: [\n {\n provide: SSGOI_CONTEXT,\n useFactory: createSsgoiContext,\n deps: [forwardRef(() => Ssgoi)],\n },\n ],\n})\nexport class Ssgoi implements AfterViewInit, OnDestroy {\n readonly config = input<SsgoiConfig>({});\n readonly host = input<HostAnimation | undefined>(undefined);\n\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly platformId = inject(PLATFORM_ID);\n private contextValue?: SsgoiContext;\n private stopObserving?: () => void;\n\n getContext(): SsgoiContext | undefined {\n if (!isPlatformBrowser(this.platformId)) {\n return undefined;\n }\n\n this.contextValue ??= createSggoiTransitionContext(this.config(), {\n host: this.host(),\n });\n return this.contextValue;\n }\n\n ngAfterViewInit(): void {\n const ssgoi = this.getContext();\n if (!ssgoi) return;\n\n this.stopObserving = observeSsgoiTransitions(this.el.nativeElement, ssgoi);\n }\n\n ngOnDestroy(): void {\n this.stopObserving?.();\n }\n}\n","import {\n Directive,\n input,\n OnInit,\n AfterViewInit,\n ElementRef,\n inject,\n PLATFORM_ID,\n} from \"@angular/core\";\nimport { isPlatformBrowser } from \"@angular/common\";\nimport { injectSsgoi } from \"./context\";\n\n/**\n * @deprecated Set `data-ssgoi-transition` directly on the page boundary\n * element inside `[ssgoi]` instead.\n */\n@Directive({\n selector: \"[ssgoiTransition]\",\n standalone: true,\n})\nexport class SsgoiTransition implements OnInit, AfterViewInit {\n // The directive attribute value becomes the transition ID\n readonly ssgoiTransition = input.required<string>();\n\n private readonly ssgoi = injectSsgoi();\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly platformId = inject(PLATFORM_ID);\n\n ngOnInit(): void {\n if (!isPlatformBrowser(this.platformId)) {\n return;\n }\n\n // Set data attribute on host element\n this.el.nativeElement.setAttribute(\n \"data-ssgoi-transition\",\n this.ssgoiTransition(),\n );\n }\n\n ngAfterViewInit(): void {\n if (!isPlatformBrowser(this.platformId)) {\n return;\n }\n\n // Use the host element itself as the transition target\n const targetElement = this.el.nativeElement;\n this.ssgoi.refFor(this.ssgoiTransition())(targetElement);\n }\n}\n","/*\n * Public API Surface of @ssgoi/angular\n */\n\nexport * from \"./lib/index\";\nexport * from \"./lib/types\";\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { AfterViewInit, OnDestroy, OnInit, InjectionToken } from '@angular/core';
|
|
3
|
+
import { HostAnimation } from '@ssgoi/core/internal';
|
|
4
|
+
import { SsgoiConfig, SsgoiContext } from '@ssgoi/core/types';
|
|
5
|
+
export * from '@ssgoi/core/types';
|
|
6
|
+
export * from '@ssgoi/core';
|
|
7
|
+
|
|
8
|
+
declare class Ssgoi implements AfterViewInit, OnDestroy {
|
|
9
|
+
readonly config: i0.InputSignal<SsgoiConfig>;
|
|
10
|
+
readonly host: i0.InputSignal<HostAnimation>;
|
|
11
|
+
private readonly el;
|
|
12
|
+
private readonly platformId;
|
|
13
|
+
private contextValue?;
|
|
14
|
+
private stopObserving?;
|
|
15
|
+
getContext(): SsgoiContext | undefined;
|
|
16
|
+
ngAfterViewInit(): void;
|
|
17
|
+
ngOnDestroy(): void;
|
|
18
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Ssgoi, never>;
|
|
19
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<Ssgoi, "[ssgoi]", never, { "config": { "alias": "config"; "required": false; "isSignal": true; }; "host": { "alias": "host"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated Set `data-ssgoi-transition` directly on the page boundary
|
|
24
|
+
* element inside `[ssgoi]` instead.
|
|
25
|
+
*/
|
|
26
|
+
declare class SsgoiTransition implements OnInit, AfterViewInit {
|
|
27
|
+
readonly ssgoiTransition: i0.InputSignal<string>;
|
|
28
|
+
private readonly ssgoi;
|
|
29
|
+
private readonly el;
|
|
30
|
+
private readonly platformId;
|
|
31
|
+
ngOnInit(): void;
|
|
32
|
+
ngAfterViewInit(): void;
|
|
33
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SsgoiTransition, never>;
|
|
34
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<SsgoiTransition, "[ssgoiTransition]", never, { "ssgoiTransition": { "alias": "ssgoiTransition"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare const SSGOI_CONTEXT: InjectionToken<SsgoiContext>;
|
|
38
|
+
declare function injectSsgoi(): SsgoiContext;
|
|
39
|
+
|
|
40
|
+
export { SSGOI_CONTEXT, Ssgoi, SsgoiTransition, injectSsgoi };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { OnInit, OnChanges, OnDestroy } from '@angular/core';
|
|
3
|
+
import { ActivatedRouteSnapshot } from '@angular/router';
|
|
4
|
+
|
|
5
|
+
interface RouteBoundaryState {
|
|
6
|
+
id: string;
|
|
7
|
+
key?: string | number;
|
|
8
|
+
}
|
|
9
|
+
interface AngularRouteLocation {
|
|
10
|
+
pathname: string;
|
|
11
|
+
route: ActivatedRouteSnapshot;
|
|
12
|
+
}
|
|
13
|
+
type BoundaryContext = {
|
|
14
|
+
$implicit: RouteBoundaryState;
|
|
15
|
+
};
|
|
16
|
+
type ResolveBoundary = (location: AngularRouteLocation) => RouteBoundaryState;
|
|
17
|
+
/**
|
|
18
|
+
* @experimental Angular Router integration. The API may change.
|
|
19
|
+
* Use on a routed page's single DOM root, e.g. <article *ssgoiRouteBoundary>.
|
|
20
|
+
* Recreates the embedded view when its route key changes, including reused
|
|
21
|
+
* parameterized routes. It does not replace RouterOutlet or a RouteReuseStrategy.
|
|
22
|
+
*/
|
|
23
|
+
declare class SsgoiRouteBoundary implements OnInit, OnChanges, OnDestroy {
|
|
24
|
+
ssgoiRouteBoundary?: ResolveBoundary | "";
|
|
25
|
+
ssgoiRouteBoundaryKey?: string | number;
|
|
26
|
+
private readonly route;
|
|
27
|
+
private readonly router;
|
|
28
|
+
private readonly template;
|
|
29
|
+
private readonly container;
|
|
30
|
+
private readonly renderer;
|
|
31
|
+
private view?;
|
|
32
|
+
private element?;
|
|
33
|
+
private key?;
|
|
34
|
+
private subscription?;
|
|
35
|
+
private initialized;
|
|
36
|
+
ngOnInit(): void;
|
|
37
|
+
ngOnChanges(): void;
|
|
38
|
+
ngOnDestroy(): void;
|
|
39
|
+
private renderBoundary;
|
|
40
|
+
static ngTemplateContextGuard(_directive: SsgoiRouteBoundary, context: unknown): context is BoundaryContext;
|
|
41
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SsgoiRouteBoundary, never>;
|
|
42
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<SsgoiRouteBoundary, "[ssgoiRouteBoundary]", never, { "ssgoiRouteBoundary": { "alias": "ssgoiRouteBoundary"; "required": false; }; "ssgoiRouteBoundaryKey": { "alias": "ssgoiRouteBoundaryKey"; "required": false; }; }, {}, never, never, true, never>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { SsgoiRouteBoundary };
|
|
46
|
+
export type { AngularRouteLocation, RouteBoundaryState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@ssgoi/core';
|
package/package.json
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssgoi/angular",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.3-beta.0",
|
|
4
4
|
"description": "Angular bindings for SSGOI - Native app-like page transitions for Angular applications",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
|
+
"module": "./dist/fesm2022/ssgoi-angular.mjs",
|
|
7
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/fesm2022/ssgoi-angular.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./view-transitions": {
|
|
18
|
+
"types": "./dist/view-transitions/index.d.ts",
|
|
19
|
+
"default": "./dist/fesm2022/ssgoi-angular-view-transitions.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": {
|
|
22
|
+
"default": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"./router": {
|
|
25
|
+
"types": "./dist/router/index.d.ts",
|
|
26
|
+
"default": "./dist/fesm2022/ssgoi-angular-router.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
8
29
|
"dependencies": {
|
|
9
|
-
"@ssgoi/core": "^7.1.
|
|
30
|
+
"@ssgoi/core": "^7.1.3-beta.0"
|
|
10
31
|
},
|
|
11
32
|
"peerDependencies": {
|
|
12
33
|
"@angular/common": "^20.0.0",
|
|
13
|
-
"@angular/core": "^20.0.0"
|
|
34
|
+
"@angular/core": "^20.0.0",
|
|
35
|
+
"@angular/router": "*"
|
|
14
36
|
},
|
|
15
37
|
"devDependencies": {
|
|
16
38
|
"@angular-eslint/builder": "^20.3.0",
|
|
@@ -18,15 +40,20 @@
|
|
|
18
40
|
"@angular-eslint/eslint-plugin-template": "^20.3.0",
|
|
19
41
|
"@angular-eslint/schematics": "^20.3.0",
|
|
20
42
|
"@angular-eslint/template-parser": "^20.3.0",
|
|
21
|
-
"@angular/common": "
|
|
22
|
-
"@angular/compiler": "
|
|
23
|
-
"@angular/compiler-cli": "
|
|
24
|
-
"@angular/core": "
|
|
43
|
+
"@angular/common": "20.3.9",
|
|
44
|
+
"@angular/compiler": "20.3.9",
|
|
45
|
+
"@angular/compiler-cli": "20.3.9",
|
|
46
|
+
"@angular/core": "20.3.9",
|
|
25
47
|
"@typescript-eslint/eslint-plugin": "^8.46.0",
|
|
26
48
|
"@typescript-eslint/parser": "^8.46.0",
|
|
27
49
|
"eslint": "^9.37.0",
|
|
28
50
|
"ng-packagr": "^20.3.0",
|
|
29
|
-
"typescript": "~5.8.3"
|
|
51
|
+
"typescript": "~5.8.3",
|
|
52
|
+
"@angular/router": "20.3.9",
|
|
53
|
+
"@angular/platform-browser": "20.3.9",
|
|
54
|
+
"@angular/platform-browser-dynamic": "20.3.9",
|
|
55
|
+
"vitest": "^3.2.4",
|
|
56
|
+
"jsdom": "^26.1.0"
|
|
30
57
|
},
|
|
31
58
|
"keywords": [
|
|
32
59
|
"angular",
|
|
@@ -54,10 +81,16 @@
|
|
|
54
81
|
"node": ">=18.0.0"
|
|
55
82
|
},
|
|
56
83
|
"sideEffects": false,
|
|
84
|
+
"peerDependenciesMeta": {
|
|
85
|
+
"@angular/router": {
|
|
86
|
+
"optional": true
|
|
87
|
+
}
|
|
88
|
+
},
|
|
57
89
|
"scripts": {
|
|
58
90
|
"build": "ng-packagr -p ng-package.json",
|
|
59
91
|
"watch": "ng-packagr -p ng-package.json --watch",
|
|
60
92
|
"lint": "eslint .",
|
|
61
|
-
"lint:fix": "eslint . --fix"
|
|
93
|
+
"lint:fix": "eslint . --fix",
|
|
94
|
+
"test:run": "vitest run"
|
|
62
95
|
}
|
|
63
96
|
}
|
package/eslint.config.mjs
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import tseslint from "@typescript-eslint/eslint-plugin";
|
|
2
|
-
import tsparser from "@typescript-eslint/parser";
|
|
3
|
-
import angular from "@angular-eslint/eslint-plugin";
|
|
4
|
-
import angularTemplate from "@angular-eslint/eslint-plugin-template";
|
|
5
|
-
import angularTemplateParser from "@angular-eslint/template-parser";
|
|
6
|
-
|
|
7
|
-
export default [
|
|
8
|
-
{
|
|
9
|
-
ignores: ["dist/**", "node_modules/**", "*.js", "*.mjs"],
|
|
10
|
-
},
|
|
11
|
-
// TypeScript files
|
|
12
|
-
{
|
|
13
|
-
files: ["**/*.ts"],
|
|
14
|
-
languageOptions: {
|
|
15
|
-
parser: tsparser,
|
|
16
|
-
parserOptions: {
|
|
17
|
-
project: ["./tsconfig.lib.json"],
|
|
18
|
-
tsconfigRootDir: import.meta.dirname,
|
|
19
|
-
ecmaVersion: 2022,
|
|
20
|
-
sourceType: "module",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
plugins: {
|
|
24
|
-
"@typescript-eslint": tseslint,
|
|
25
|
-
"@angular-eslint": angular,
|
|
26
|
-
},
|
|
27
|
-
rules: {
|
|
28
|
-
// TypeScript recommended rules
|
|
29
|
-
"@typescript-eslint/no-explicit-any": "warn",
|
|
30
|
-
"@typescript-eslint/no-unused-vars": [
|
|
31
|
-
"error",
|
|
32
|
-
{ argsIgnorePattern: "^_" },
|
|
33
|
-
],
|
|
34
|
-
"@typescript-eslint/explicit-function-return-type": "off",
|
|
35
|
-
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
36
|
-
|
|
37
|
-
// Angular recommended rules
|
|
38
|
-
"@angular-eslint/directive-selector": [
|
|
39
|
-
"error",
|
|
40
|
-
{
|
|
41
|
-
type: "attribute",
|
|
42
|
-
prefix: ["ssgoi", "transition"],
|
|
43
|
-
style: "camelCase",
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
"@angular-eslint/component-selector": [
|
|
47
|
-
"error",
|
|
48
|
-
{
|
|
49
|
-
type: "element",
|
|
50
|
-
prefix: "ssgoi",
|
|
51
|
-
style: "kebab-case",
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
"@angular-eslint/no-input-rename": "error",
|
|
55
|
-
"@angular-eslint/no-output-rename": "error",
|
|
56
|
-
"@angular-eslint/use-lifecycle-interface": "error",
|
|
57
|
-
"@angular-eslint/use-pipe-transform-interface": "error",
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
// HTML template files
|
|
61
|
-
{
|
|
62
|
-
files: ["**/*.html"],
|
|
63
|
-
languageOptions: {
|
|
64
|
-
parser: angularTemplateParser,
|
|
65
|
-
},
|
|
66
|
-
plugins: {
|
|
67
|
-
"@angular-eslint/template": angularTemplate,
|
|
68
|
-
},
|
|
69
|
-
rules: {
|
|
70
|
-
"@angular-eslint/template/no-negated-async": "error",
|
|
71
|
-
"@angular-eslint/template/eqeqeq": "error",
|
|
72
|
-
"@angular-eslint/template/no-any": "warn",
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
];
|
package/ng-package.json
DELETED
package/src/lib/context.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { InjectionToken, inject } from "@angular/core";
|
|
2
|
-
import type { SsgoiContext } from "@ssgoi/core/types";
|
|
3
|
-
|
|
4
|
-
export const SSGOI_CONTEXT = new InjectionToken<SsgoiContext | undefined>(
|
|
5
|
-
"ssgoi-context",
|
|
6
|
-
);
|
|
7
|
-
|
|
8
|
-
const noopContext: SsgoiContext = {
|
|
9
|
-
register: () => {},
|
|
10
|
-
refFor: () => () => {},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function injectSsgoi(): SsgoiContext {
|
|
14
|
-
const context = inject(SSGOI_CONTEXT, { optional: true });
|
|
15
|
-
|
|
16
|
-
if (!context) {
|
|
17
|
-
// During SSR or when not wrapped in <ssgoi>, return a no-op context
|
|
18
|
-
// This prevents errors during server-side rendering
|
|
19
|
-
return noopContext;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return context;
|
|
23
|
-
}
|
package/src/lib/index.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { Ssgoi } from "./ssgoi.directive";
|
|
2
|
-
/** @deprecated Set `data-ssgoi-transition` directly on an element inside `[ssgoi]` instead. */
|
|
3
|
-
export { SsgoiTransition } from "./ssgoi-transition.directive";
|
|
4
|
-
export { injectSsgoi, SSGOI_CONTEXT } from "./context";
|
|
5
|
-
export * from "./types";
|
|
6
|
-
export * from "@ssgoi/core";
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Directive,
|
|
3
|
-
input,
|
|
4
|
-
OnInit,
|
|
5
|
-
AfterViewInit,
|
|
6
|
-
ElementRef,
|
|
7
|
-
inject,
|
|
8
|
-
PLATFORM_ID,
|
|
9
|
-
} from "@angular/core";
|
|
10
|
-
import { isPlatformBrowser } from "@angular/common";
|
|
11
|
-
import { injectSsgoi } from "./context";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @deprecated Set `data-ssgoi-transition` directly on the page boundary
|
|
15
|
-
* element inside `[ssgoi]` instead.
|
|
16
|
-
*/
|
|
17
|
-
@Directive({
|
|
18
|
-
selector: "[ssgoiTransition]",
|
|
19
|
-
standalone: true,
|
|
20
|
-
})
|
|
21
|
-
export class SsgoiTransition implements OnInit, AfterViewInit {
|
|
22
|
-
// The directive attribute value becomes the transition ID
|
|
23
|
-
readonly ssgoiTransition = input.required<string>();
|
|
24
|
-
|
|
25
|
-
private readonly ssgoi = injectSsgoi();
|
|
26
|
-
private readonly el = inject(ElementRef<HTMLElement>);
|
|
27
|
-
private readonly platformId = inject(PLATFORM_ID);
|
|
28
|
-
|
|
29
|
-
ngOnInit(): void {
|
|
30
|
-
if (!isPlatformBrowser(this.platformId)) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Set data attribute on host element
|
|
35
|
-
this.el.nativeElement.setAttribute(
|
|
36
|
-
"data-ssgoi-transition",
|
|
37
|
-
this.ssgoiTransition(),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
ngAfterViewInit(): void {
|
|
42
|
-
if (!isPlatformBrowser(this.platformId)) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Use the host element itself as the transition target
|
|
47
|
-
const targetElement = this.el.nativeElement;
|
|
48
|
-
this.ssgoi.refFor(this.ssgoiTransition())(targetElement);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AfterViewInit,
|
|
3
|
-
Directive,
|
|
4
|
-
ElementRef,
|
|
5
|
-
OnDestroy,
|
|
6
|
-
input,
|
|
7
|
-
forwardRef,
|
|
8
|
-
PLATFORM_ID,
|
|
9
|
-
inject,
|
|
10
|
-
} from "@angular/core";
|
|
11
|
-
import { isPlatformBrowser } from "@angular/common";
|
|
12
|
-
import {
|
|
13
|
-
createSggoiTransitionContext,
|
|
14
|
-
observeSsgoiTransitions,
|
|
15
|
-
} from "@ssgoi/core/internal";
|
|
16
|
-
import type { HostAnimation } from "@ssgoi/core/internal";
|
|
17
|
-
import type { SsgoiConfig, SsgoiContext } from "@ssgoi/core/types";
|
|
18
|
-
import { SSGOI_CONTEXT } from "./context";
|
|
19
|
-
|
|
20
|
-
function createSsgoiContext(component: Ssgoi): SsgoiContext | undefined {
|
|
21
|
-
return component.getContext();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Directive({
|
|
25
|
-
selector: "[ssgoi]",
|
|
26
|
-
standalone: true,
|
|
27
|
-
providers: [
|
|
28
|
-
{
|
|
29
|
-
provide: SSGOI_CONTEXT,
|
|
30
|
-
useFactory: createSsgoiContext,
|
|
31
|
-
deps: [forwardRef(() => Ssgoi)],
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
})
|
|
35
|
-
export class Ssgoi implements AfterViewInit, OnDestroy {
|
|
36
|
-
readonly config = input<SsgoiConfig>({});
|
|
37
|
-
readonly host = input<HostAnimation | undefined>(undefined);
|
|
38
|
-
|
|
39
|
-
private readonly el = inject(ElementRef<HTMLElement>);
|
|
40
|
-
private readonly platformId = inject(PLATFORM_ID);
|
|
41
|
-
private contextValue?: SsgoiContext;
|
|
42
|
-
private stopObserving?: () => void;
|
|
43
|
-
|
|
44
|
-
getContext(): SsgoiContext | undefined {
|
|
45
|
-
if (!isPlatformBrowser(this.platformId)) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
this.contextValue ??= createSggoiTransitionContext(this.config(), {
|
|
50
|
-
host: this.host(),
|
|
51
|
-
});
|
|
52
|
-
return this.contextValue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
ngAfterViewInit(): void {
|
|
56
|
-
const ssgoi = this.getContext();
|
|
57
|
-
if (!ssgoi) return;
|
|
58
|
-
|
|
59
|
-
this.stopObserving = observeSsgoiTransitions(this.el.nativeElement, ssgoi);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
ngOnDestroy(): void {
|
|
63
|
-
this.stopObserving?.();
|
|
64
|
-
}
|
|
65
|
-
}
|
package/src/lib/types.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type * from "@ssgoi/core/types";
|
package/src/public-api.ts
DELETED
package/tsconfig.lib.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"lib": ["ES2022", "DOM"],
|
|
6
|
-
"moduleResolution": "node",
|
|
7
|
-
"experimentalDecorators": true,
|
|
8
|
-
"emitDecoratorMetadata": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"esModuleInterop": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"outDir": "./dist",
|
|
14
|
-
"declaration": true,
|
|
15
|
-
"declarationMap": true,
|
|
16
|
-
"inlineSources": true,
|
|
17
|
-
"types": []
|
|
18
|
-
},
|
|
19
|
-
"exclude": ["src/test.ts", "**/*.spec.ts", "**/*.test.ts"],
|
|
20
|
-
"angularCompilerOptions": {
|
|
21
|
-
"skipTemplateCodegen": true,
|
|
22
|
-
"strictMetadataEmit": true,
|
|
23
|
-
"enableResourceInlining": true,
|
|
24
|
-
"compilationMode": "partial"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "@ssgoi/core";
|