@supersoniks/concorde 4.9.7 → 4.9.91
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/build-infos.json +1 -1
- package/concorde-core.bundle.js +265 -265
- package/concorde-core.es.js +4600 -4385
- package/dist/concorde-core.bundle.js +265 -265
- package/dist/concorde-core.es.js +4600 -4385
- package/dist/js/core/components/functional/queue/queue.js +25 -153
- package/dist/js/core/components/functional/queue/queue.js.map +1 -1
- package/dist/js/core/components/ui/_css/scroll.js +2 -0
- package/dist/js/core/components/ui/_css/scroll.js.map +1 -1
- package/dist/js/core/decorators/api.js +92 -2
- package/dist/js/core/decorators/api.js.map +1 -1
- package/dist/js/core/utils/SafariScrollGuard.js +384 -0
- package/dist/js/core/utils/SafariScrollGuard.js.map +1 -0
- package/dist/js/decorators.js +4 -0
- package/dist/js/decorators.js.map +1 -1
- package/dist/js/utils.js +3 -0
- package/dist/js/utils.js.map +1 -1
- package/dist/types/core/components/functional/queue/queue.d.ts +8 -24
- package/dist/types/core/components/functional/queue/queue.d.ts.map +1 -1
- package/dist/types/core/components/ui/_css/scroll.d.ts.map +1 -1
- package/dist/types/core/decorators/api.d.ts +82 -60
- package/dist/types/core/decorators/api.d.ts.map +1 -1
- package/dist/types/core/utils/SafariScrollGuard.d.ts +36 -0
- package/dist/types/core/utils/SafariScrollGuard.d.ts.map +1 -0
- package/dist/types/decorators.d.ts +3 -1
- package/dist/types/decorators.d.ts.map +1 -1
- package/dist/types/utils.d.ts +1 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/docs/assets/index-BL6WVF7g.css +1 -0
- package/docs/assets/{index-hElUgV9_.js → index-DVn8AeyA.js} +572 -555
- package/docs/crawl/docs/_decorators/get.html +64 -81
- package/docs/crawl/docs/_decorators/patch.html +35 -23
- package/docs/crawl/docs/_decorators/post.html +72 -72
- package/docs/crawl/docs/_decorators/put.html +36 -21
- package/docs/crawl/docs/_getting-started/ai-agents.html +45 -6
- package/docs/crawl/hubs/decorators.html +141 -69
- package/docs/crawl/hubs/remote-data.html +141 -69
- package/docs/index.html +2 -2
- package/docs/llms-full.txt +203 -149
- package/docs/llms.txt +4 -4
- package/docs/src/core/components/functional/queue/queue.md +0 -0
- package/docs/src/core/components/ui/button/button.md +0 -0
- package/docs/src/docs/_decorators/get.md +55 -81
- package/docs/src/docs/_decorators/patch.md +36 -25
- package/docs/src/docs/_decorators/post.md +71 -62
- package/docs/src/docs/_decorators/put.md +37 -24
- package/docs/src/docs/_getting-started/ai-agents.md +24 -4
- package/docs/src/docs/search/docs-search.json +151 -1
- package/docs/src/tsconfig.json +3 -0
- package/package.json +6 -2
- package/src/core/components/functional/queue/queue.ts +33 -164
- package/src/core/components/ui/_css/scroll.ts +3 -0
- package/src/core/decorators/api.post.spec.ts +79 -1
- package/src/core/decorators/api.spec.ts +100 -1
- package/src/core/decorators/api.ts +252 -138
- package/src/core/utils/SafariScrollGuard.ts +429 -0
- package/src/decorators.ts +7 -0
- package/src/docs/_decorators/get.md +81 -80
- package/src/docs/_decorators/patch.md +36 -25
- package/src/docs/_decorators/post.md +99 -62
- package/src/docs/_decorators/put.md +37 -24
- package/src/docs/components/docs-demo-sources.ts +1 -6
- package/src/docs/example/decorators-demo-geo.ts +0 -21
- package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +41 -132
- package/src/docs/search/docs-search.json +156 -241
- package/src/docs/tailwind/css/tailwind.css +0 -0
- package/src/tsconfig.json +3 -0
- package/src/tsconfig.tsbuildinfo +1 -1
- package/src/utils.ts +13 -0
- package/vite.config.mts +4 -0
- package/vitest.setup.ts +22 -0
- package/docs/assets/index-Bnp7gVaA.css +0 -1
|
@@ -1,45 +1,56 @@
|
|
|
1
1
|
# @patch
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@patch` applies a partial update with the body from a publisher. It uses the
|
|
4
|
+
same model as [@post](#docs/_decorators/post.md/post):
|
|
5
|
+
`Endpoint<T>`, `DataProviderKey<B>`, `ApiResult<T>`, options, and the
|
|
6
|
+
`send(component)` handle.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
## Minimal usage
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
```typescript
|
|
11
|
+
const bodyKey = new DataProviderKey<Partial<User>>("users.patch");
|
|
12
|
+
const patchUser = patch(new Endpoint<User>("users/42"), bodyKey);
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
@patchUser
|
|
15
|
+
@state()
|
|
16
|
+
payload?: ApiResult<User>;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
To run the operation again from the component:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
savePatch() {
|
|
23
|
+
patchUser.send(this);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The PATCH is also sent automatically when the body mutates, unless
|
|
28
|
+
`autoPostOnBodyMutation: false` is set. The available options are described in
|
|
29
|
+
[@post](#docs/_decorators/post.md/post):
|
|
10
30
|
|
|
11
|
-
|
|
31
|
+
- `autoPostOnBodyMutation`
|
|
32
|
+
- `skipIfBodyMissing`
|
|
33
|
+
- `refetchEveryMs`
|
|
34
|
+
- `skipEmptyPlaceholder`
|
|
12
35
|
|
|
13
|
-
|
|
36
|
+
Pass explicit API configuration as the third argument:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
@patch(endpoint, bodyKey, apiConfigurationKey)
|
|
40
|
+
```
|
|
14
41
|
|
|
15
42
|
## Import
|
|
16
43
|
|
|
17
44
|
<sonic-code language="typescript">
|
|
18
45
|
<template>
|
|
19
46
|
import { patch, type ApiResult } from "@supersoniks/concorde/decorators";
|
|
20
|
-
import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
|
|
21
47
|
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
|
|
22
|
-
|
|
23
|
-
</sonic-code>
|
|
24
|
-
|
|
25
|
-
## Example
|
|
26
|
-
|
|
27
|
-
<sonic-code language="typescript">
|
|
28
|
-
<template>
|
|
29
|
-
const patchBodyKey = new DataProviderKey<Partial<Resource>>("resourcePatch");
|
|
30
|
-
|
|
31
|
-
@patch(
|
|
32
|
-
new Endpoint<Resource, { resourceId: string }>("resources/${resourceId}"),
|
|
33
|
-
patchBodyKey,
|
|
34
|
-
{ skipEmptyPlaceholder: true },
|
|
35
|
-
)
|
|
36
|
-
@state()
|
|
37
|
-
payload?: ApiResult<Resource> | null;
|
|
48
|
+
import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
|
|
38
49
|
</template>
|
|
39
50
|
</sonic-code>
|
|
40
51
|
|
|
41
52
|
## See also
|
|
42
53
|
|
|
43
|
-
- [@post](#docs/_decorators/post.md/post) —
|
|
44
|
-
- [@put](#docs/_decorators/put.md/put) — full replacement
|
|
54
|
+
- [@post](#docs/_decorators/post.md/post) — options and triggers
|
|
55
|
+
- [@put](#docs/_decorators/put.md/put) — full replacement
|
|
45
56
|
- [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path)
|
|
@@ -1,93 +1,130 @@
|
|
|
1
1
|
# @post
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@post` sends the contents of a publisher and stores the response in an
|
|
4
|
+
optional `ApiResult<T>` property. The property is `undefined` until a result
|
|
5
|
+
is available.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
## Minimal usage
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Pass an `Endpoint<T>` and a `DataProviderKey<B>` for the request body.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
```typescript
|
|
12
|
+
const userBodyKey = new DataProviderKey<CreateUser>("users.form");
|
|
13
|
+
const createUser = post(new Endpoint<User>("users"), userBodyKey);
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
@createUser
|
|
16
|
+
@state()
|
|
17
|
+
payload?: ApiResult<User>;
|
|
18
|
+
```
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| `skipIfBodyMissing` | Skip when body publisher is `null`/`undefined` (default: `true`). |
|
|
17
|
-
| `autoPostOnBodyMutation` | Re-post when the body publisher mutates (default: `true`). `false` = manual via `triggerKey.invalidate()`. |
|
|
18
|
-
| `skipEmptyPlaceholder` | If `true`, a placeholder resolved to `''` blocks the request (empty string only — not `0` or `false`). See [Dynamic paths](#docs/_misc/dynamic-path.md/dynamic-path). |
|
|
19
|
-
| `triggerKey` | `DataProviderKey` — `invalidate()` re-runs the POST with the current body. |
|
|
20
|
+
The current body is read from `userBodyKey`. By default, the POST is sent when
|
|
21
|
+
the component connects and after each body mutation. Mutations in the same
|
|
22
|
+
frame are coalesced into one request.
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
API configuration is read from the HTML scope, as with
|
|
25
|
+
[@get](#docs/_decorators/get.md/get).
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
- Endpoint path dependency changes (`${sessionId}` on the host).
|
|
25
|
-
- Configuration publisher mutation.
|
|
26
|
-
- `triggerKey` publisher `invalidate()`.
|
|
27
|
-
- `refetchEveryMs` timer (if set).
|
|
27
|
+
## Sending the POST again
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
### From the component
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
Keep the decorator to access `send(component)`. The body is read again when the
|
|
32
|
+
operation runs.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
saveAgain() {
|
|
36
|
+
createUser.send(this);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`send()` is useful for “Save” or “Retry” buttons. It can repeat an operation
|
|
41
|
+
with side effects.
|
|
42
|
+
|
|
43
|
+
### From a method decorator
|
|
44
|
+
|
|
45
|
+
The send action can decorate a local method. It runs after the method by
|
|
46
|
+
default and waits for an eventual Promise to resolve:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { send } from "@supersoniks/concorde/decorators";
|
|
50
|
+
|
|
51
|
+
@send(createUser)
|
|
52
|
+
async save() {
|
|
53
|
+
// Prepare local state or validate the form.
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use `{ when: "before" }` when the body must be sent before the method runs:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
@send(createUser, { when: "before" })
|
|
61
|
+
saveBefore() {
|
|
62
|
+
// The POST is scheduled before this method runs.
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The method decorator adds an explicit send. If the method also mutates the body
|
|
67
|
+
publisher, set `autoPostOnBodyMutation: false` when the decorated method must
|
|
68
|
+
be the only trigger.
|
|
69
|
+
|
|
70
|
+
## Options (`PostOptions`)
|
|
71
|
+
|
|
72
|
+
Options are the third argument, or the fourth argument when a configuration key
|
|
73
|
+
is provided.
|
|
74
|
+
|
|
75
|
+
- `autoPostOnBodyMutation`: send after a body mutation (default: `true`). Set
|
|
76
|
+
it to `false` to use `send()`.
|
|
77
|
+
- `skipIfBodyMissing`: skip when the body is `null` or `undefined` (default:
|
|
78
|
+
`true`).
|
|
79
|
+
- `refetchEveryMs`: repeat the POST at this interval. Use it only when
|
|
80
|
+
repeating the operation is intended.
|
|
81
|
+
- `skipEmptyPlaceholder`: block the request when a `${...}` placeholder is
|
|
82
|
+
`""`.
|
|
83
|
+
|
|
84
|
+
With a configuration key:
|
|
38
85
|
|
|
39
|
-
|
|
86
|
+
```typescript
|
|
87
|
+
@post(endpoint, userBodyKey, apiConfigurationKey)
|
|
88
|
+
```
|
|
40
89
|
|
|
41
|
-
|
|
90
|
+
See [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path) for
|
|
91
|
+
dynamic path details.
|
|
92
|
+
|
|
93
|
+
## When the POST is sent
|
|
94
|
+
|
|
95
|
+
- when the component connects and the body and configuration are available;
|
|
96
|
+
- after a body mutation, unless `autoPostOnBodyMutation: false`;
|
|
97
|
+
- when a property used in `${...}` changes;
|
|
98
|
+
- when a configuration publisher changes;
|
|
99
|
+
- when `send()` is called;
|
|
100
|
+
- after each response when `refetchEveryMs` is set.
|
|
101
|
+
|
|
102
|
+
## Import
|
|
42
103
|
|
|
43
104
|
<sonic-code language="typescript">
|
|
44
105
|
<template>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@post(
|
|
49
|
-
new Endpoint<SyncResponse, { sessionId: string }>("sessions/${sessionId}/sync"),
|
|
50
|
-
syncRequest,
|
|
51
|
-
{ triggerKey: syncTrigger },
|
|
52
|
-
)
|
|
53
|
-
@state()
|
|
54
|
-
payload?: ApiResult<SyncResponse> | null;
|
|
106
|
+
import { post, type ApiResult } from "@supersoniks/concorde/decorators";
|
|
107
|
+
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
|
|
108
|
+
import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
|
|
55
109
|
</template>
|
|
56
110
|
</sonic-code>
|
|
57
111
|
|
|
58
|
-
##
|
|
112
|
+
## Demos
|
|
59
113
|
|
|
60
114
|
<sonic-code>
|
|
61
115
|
<template>
|
|
62
116
|
<docs-demo-sources for="demo-api-post"></docs-demo-sources>
|
|
63
117
|
<demo-api-post></demo-api-post>
|
|
64
|
-
</template>
|
|
65
|
-
</sonic-code>
|
|
66
|
-
|
|
67
|
-
Dynamic endpoint path — changing `sessionId` on the host re-runs the POST (`POST /docs-mock-api/api/sessions/{id}/sync`):
|
|
68
|
-
|
|
69
|
-
<sonic-code>
|
|
70
|
-
<template>
|
|
71
118
|
<docs-demo-sources for="demo-api-post-dynamic"></docs-demo-sources>
|
|
72
119
|
<demo-api-post-dynamic></demo-api-post-dynamic>
|
|
73
120
|
</template>
|
|
74
121
|
</sonic-code>
|
|
75
122
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<template>
|
|
80
|
-
<docs-demo-sources for="demo-api-post-publish"></docs-demo-sources>
|
|
81
|
-
<demo-api-post-publish></demo-api-post-publish>
|
|
82
|
-
</template>
|
|
83
|
-
</sonic-code>
|
|
84
|
-
|
|
85
|
-
Stale responses are ignored if the path or generation changed before the request finished.
|
|
86
|
-
|
|
87
|
-
**Note:** plusieurs composants `@post` sur la même page qui partagent le même `bodyKey` enverront chacun une requête à chaque mutation du body — utiliser une clé par composant (voir les deux démos live ci-dessus).
|
|
88
|
-
|
|
89
|
-
Path placeholders (`${sessionId}`, …): [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path).
|
|
123
|
+
See [@put](#docs/_decorators/put.md/put) and
|
|
124
|
+
[@patch](#docs/_decorators/patch.md/patch): they use the same model with a
|
|
125
|
+
different HTTP method.
|
|
90
126
|
|
|
91
|
-
##
|
|
127
|
+
## Advanced
|
|
92
128
|
|
|
93
|
-
|
|
129
|
+
External invalidation is currently available through `triggerKey`; it is kept
|
|
130
|
+
out of the main usage examples.
|
|
@@ -1,43 +1,56 @@
|
|
|
1
1
|
# @put
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@put` replaces a resource with the body from a publisher. It uses exactly the
|
|
4
|
+
same model as [@post](#docs/_decorators/post.md/post):
|
|
5
|
+
`Endpoint<T>`, `DataProviderKey<B>`, `ApiResult<T>`, options, and the
|
|
6
|
+
`send(component)` handle.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
## Minimal usage
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
```typescript
|
|
11
|
+
const bodyKey = new DataProviderKey<UpdateUser>("users.form");
|
|
12
|
+
const updateUser = put(new Endpoint<User>("users/42"), bodyKey);
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
@updateUser
|
|
15
|
+
@state()
|
|
16
|
+
payload?: ApiResult<User>;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
To run the operation again from the component:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
update() {
|
|
23
|
+
updateUser.send(this);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The PUT is also sent automatically when the body mutates, unless
|
|
28
|
+
`autoPostOnBodyMutation: false` is set. See
|
|
29
|
+
[@post](#docs/_decorators/post.md/post) for the option details:
|
|
10
30
|
|
|
11
|
-
|
|
31
|
+
- `autoPostOnBodyMutation`
|
|
32
|
+
- `skipIfBodyMissing`
|
|
33
|
+
- `refetchEveryMs`
|
|
34
|
+
- `skipEmptyPlaceholder`
|
|
12
35
|
|
|
13
|
-
|
|
36
|
+
Pass explicit API configuration as the third argument:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
@put(endpoint, bodyKey, apiConfigurationKey)
|
|
40
|
+
```
|
|
14
41
|
|
|
15
42
|
## Import
|
|
16
43
|
|
|
17
44
|
<sonic-code language="typescript">
|
|
18
45
|
<template>
|
|
19
46
|
import { put, type ApiResult } from "@supersoniks/concorde/decorators";
|
|
20
|
-
import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
|
|
21
47
|
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
|
|
48
|
+
import { Endpoint } from "@supersoniks/concorde/utils/endpoint";
|
|
22
49
|
</template>
|
|
23
50
|
</sonic-code>
|
|
24
51
|
|
|
25
|
-
## Example
|
|
26
|
-
|
|
27
|
-
<sonic-code language="typescript">
|
|
28
|
-
<template>
|
|
29
|
-
const bodyKey = new DataProviderKey<UpdatePayload>("resourceBody");
|
|
30
|
-
|
|
31
|
-
@put(new Endpoint<Resource, { resourceId: string }>("resources/${resourceId}"), bodyKey)
|
|
32
|
-
@state()
|
|
33
|
-
payload?: ApiResult<Resource> | null;
|
|
34
|
-
</template>
|
|
35
|
-
</sonic-code>
|
|
36
|
-
|
|
37
|
-
Changing `resourceId` on the host re-runs the PUT when the path becomes ready — see [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path).
|
|
38
|
-
|
|
39
52
|
## See also
|
|
40
53
|
|
|
41
|
-
- [@post](#docs/_decorators/post.md/post) —
|
|
42
|
-
- [@patch](#docs/_decorators/patch.md/patch) — partial update
|
|
43
|
-
- [
|
|
54
|
+
- [@post](#docs/_decorators/post.md/post) — options and triggers
|
|
55
|
+
- [@patch](#docs/_decorators/patch.md/patch) — partial update
|
|
56
|
+
- [Dynamic path placeholders](#docs/_misc/dynamic-path.md/dynamic-path)
|
|
@@ -258,12 +258,7 @@ export const DOCS_DEMO_SOURCE_REGISTRY: Record<string, DocsSource[]> = {
|
|
|
258
258
|
line: 147,
|
|
259
259
|
label: "demo-api-get-configuration-key",
|
|
260
260
|
},
|
|
261
|
-
{
|
|
262
|
-
path: "src/docs/example/decorators-demo-subscribe-publish-get-demos.ts",
|
|
263
|
-
line: 239,
|
|
264
|
-
label: "demo-api-get-refresh-remote",
|
|
265
|
-
},
|
|
266
|
-
{ path: "src/docs/example/decorators-demo-geo.ts", label: "geo API + triggerKey" },
|
|
261
|
+
{ path: "src/docs/example/decorators-demo-geo.ts", label: "geo API" },
|
|
267
262
|
],
|
|
268
263
|
"demo-api-get-publish-subscribe": [
|
|
269
264
|
{
|
|
@@ -26,22 +26,6 @@ export const geoCommunesApiGetEndpoint = new Endpoint<GeoCommuneRow[]>(
|
|
|
26
26
|
"communes?limit=5&fields=nom,code",
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
export const docsDemoDynApiConfKeyTemplate =
|
|
30
|
-
new DataProviderKey<APIConfiguration>("docsDemoDynApiConf${configSlot}");
|
|
31
|
-
|
|
32
|
-
export const docsDemoDynApiConfAKey = new DataProviderKey<APIConfiguration>(
|
|
33
|
-
"docsDemoDynApiConfA",
|
|
34
|
-
);
|
|
35
|
-
export const docsDemoDynApiConfBKey = new DataProviderKey<APIConfiguration>(
|
|
36
|
-
"docsDemoDynApiConfB",
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
set(docsDemoDynApiConfAKey, geoApiDemoConfiguration);
|
|
40
|
-
set(docsDemoDynApiConfBKey, {
|
|
41
|
-
...geoApiDemoConfiguration,
|
|
42
|
-
serviceURL: DOCS_MOCK_GEO_SERVICE,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
29
|
export const geoCommunesApiGetEndpointDynamic = new Endpoint<GeoCommuneRow[]>(
|
|
46
30
|
"communes?limit=${communeLimit}&fields=nom,code",
|
|
47
31
|
);
|
|
@@ -49,8 +33,3 @@ export const geoCommunesApiGetEndpointDynamic = new Endpoint<GeoCommuneRow[]>(
|
|
|
49
33
|
export const geoCommunesApiGetPublishKey = new DataProviderKey<
|
|
50
34
|
ApiResult<GeoCommuneRow[]>
|
|
51
35
|
>(geoCommunesApiGetEndpoint.path);
|
|
52
|
-
|
|
53
|
-
/** Signal de refetch manuel pour les démos `@get` (voir `triggerKey`). */
|
|
54
|
-
export const docsDemoGeoCommunesRefreshKey = new DataProviderKey<void>(
|
|
55
|
-
"docsDemoGeoCommunesRefresh",
|
|
56
|
-
);
|
|
@@ -5,7 +5,6 @@ import type { ApiResult } from "@supersoniks/concorde/core/utils/api";
|
|
|
5
5
|
import { DataProviderKey } from "@supersoniks/concorde/core/utils/dataProviderKey";
|
|
6
6
|
import { sub } from "@supersoniks/concorde/directives";
|
|
7
7
|
import {
|
|
8
|
-
dp,
|
|
9
8
|
get as storeGet,
|
|
10
9
|
set,
|
|
11
10
|
} from "@supersoniks/concorde/core/utils/PublisherProxy";
|
|
@@ -13,30 +12,23 @@ import { publishDemoKey } from "./docs-provider-keys";
|
|
|
13
12
|
import { tailwind } from "../tailwind";
|
|
14
13
|
import "./decorators-demo-init";
|
|
15
14
|
import {
|
|
16
|
-
docsDemoDynApiConfKeyTemplate,
|
|
17
15
|
docsDemoGeoApiConfigurationKey,
|
|
18
|
-
docsDemoGeoCommunesRefreshKey,
|
|
19
16
|
geoCommunesApiGetEndpoint,
|
|
20
17
|
geoCommunesApiGetEndpointDynamic,
|
|
21
18
|
geoCommunesApiGetPublishKey,
|
|
22
19
|
type GeoCommuneRow,
|
|
23
20
|
} from "./decorators-demo-geo";
|
|
24
21
|
|
|
25
|
-
function formatFetchedAt(response: Response | null | undefined): string {
|
|
26
|
-
const raw = response?.headers.get("X-Fetched-At");
|
|
27
|
-
if (!raw) return "—";
|
|
28
|
-
try {
|
|
29
|
-
return new Date(raw).toLocaleTimeString();
|
|
30
|
-
} catch {
|
|
31
|
-
return raw;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
22
|
type PublishDemoData = {
|
|
36
23
|
email: string;
|
|
37
24
|
message: string;
|
|
38
25
|
};
|
|
39
26
|
|
|
27
|
+
const geoCommunesGet = get(
|
|
28
|
+
geoCommunesApiGetEndpoint,
|
|
29
|
+
docsDemoGeoApiConfigurationKey,
|
|
30
|
+
);
|
|
31
|
+
|
|
40
32
|
@customElement("demo-publish")
|
|
41
33
|
export class DemoPublish extends LitElement {
|
|
42
34
|
static styles = [tailwind];
|
|
@@ -95,50 +87,35 @@ export class DemoPublish extends LitElement {
|
|
|
95
87
|
export class DemoApiGet extends LitElement {
|
|
96
88
|
static styles = [tailwind];
|
|
97
89
|
|
|
98
|
-
@
|
|
99
|
-
triggerKey: docsDemoGeoCommunesRefreshKey,
|
|
100
|
-
})
|
|
90
|
+
@geoCommunesGet
|
|
101
91
|
@state()
|
|
102
92
|
geoApiPayload?: ApiResult<GeoCommuneRow[]>;
|
|
103
93
|
|
|
104
94
|
private refresh() {
|
|
105
|
-
|
|
95
|
+
geoCommunesGet.refetch(this);
|
|
106
96
|
}
|
|
107
97
|
|
|
108
98
|
render() {
|
|
109
99
|
const rows = this.geoApiPayload?.result;
|
|
110
|
-
const status = this.geoApiPayload?.response?.status;
|
|
111
|
-
const fetchedAt = formatFetchedAt(this.geoApiPayload?.response ?? null);
|
|
112
100
|
return html`
|
|
113
|
-
<div class="space-y-
|
|
101
|
+
<div class="space-y-3">
|
|
102
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
103
|
+
<span class="text-sm font-medium">🗺️ GET simple</span>
|
|
104
|
+
<sonic-button size="sm" @click=${this.refresh}>Rafraîchir</sonic-button>
|
|
105
|
+
</div>
|
|
114
106
|
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
|
115
|
-
<code>@get</code
|
|
116
|
-
<code>sonic-queue</code> (<code>/docs-mock-api/geo/</code>).
|
|
117
|
-
Propriété typée <code>ApiResult<T></code>.
|
|
118
|
-
Refetch via <code>triggerKey</code> (<code
|
|
119
|
-
>docsDemoGeoCommunesRefresh</code
|
|
120
|
-
>).
|
|
107
|
+
Une liste de communes chargée avec <code>@get</code>.
|
|
121
108
|
</p>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
? html`<p class="text-xs text-neutral-500">
|
|
125
|
-
HTTP ${status} · fetch ${fetchedAt}
|
|
126
|
-
</p>`
|
|
127
|
-
: ""}
|
|
128
|
-
${this.geoApiPayload === null
|
|
129
|
-
? html`<p class="text-neutral-500">Chargement…</p>`
|
|
109
|
+
${this.geoApiPayload == null
|
|
110
|
+
? html`<p class="text-sm text-neutral-500">Chargement…</p>`
|
|
130
111
|
: !rows || rows.length === 0
|
|
131
|
-
? html`<p class="text-neutral-500">Aucune commune</p>`
|
|
112
|
+
? html`<p class="text-sm text-neutral-500">Aucune commune</p>`
|
|
132
113
|
: html`
|
|
133
|
-
<ul class="
|
|
114
|
+
<ul class="grid gap-1 sm:grid-cols-2">
|
|
134
115
|
${rows.map(
|
|
135
|
-
(c) =>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<span class="text-neutral-500 text-sm">
|
|
139
|
-
— ${c.code}</span
|
|
140
|
-
>
|
|
141
|
-
</li>`,
|
|
116
|
+
(c) => html`<li class="text-sm">
|
|
117
|
+
${c.nom} <span class="text-neutral-500">(${c.code})</span>
|
|
118
|
+
</li>`,
|
|
142
119
|
)}
|
|
143
120
|
</ul>
|
|
144
121
|
`}
|
|
@@ -151,116 +128,48 @@ export class DemoApiGet extends LitElement {
|
|
|
151
128
|
export class DemoApiGetConfigurationKey extends LitElement {
|
|
152
129
|
static styles = [tailwind];
|
|
153
130
|
|
|
154
|
-
@property({ type: String })
|
|
155
|
-
configSlot: "A" | "B" = "A";
|
|
156
|
-
|
|
157
131
|
@property({ type: Number })
|
|
158
|
-
communeLimit =
|
|
132
|
+
communeLimit = 3;
|
|
159
133
|
|
|
160
|
-
@get(geoCommunesApiGetEndpointDynamic,
|
|
161
|
-
triggerKey: docsDemoGeoCommunesRefreshKey,
|
|
162
|
-
})
|
|
134
|
+
@get(geoCommunesApiGetEndpointDynamic, docsDemoGeoApiConfigurationKey)
|
|
163
135
|
@state()
|
|
164
136
|
geoApiPayloadDyn?: ApiResult<GeoCommuneRow[]>;
|
|
165
137
|
|
|
166
138
|
render() {
|
|
167
139
|
const rows = this.geoApiPayloadDyn?.result;
|
|
168
|
-
const status = this.geoApiPayloadDyn?.response?.status;
|
|
169
|
-
const fetchedAt = formatFetchedAt(this.geoApiPayloadDyn?.response ?? null);
|
|
170
140
|
return html`
|
|
171
141
|
<div class="space-y-3">
|
|
172
142
|
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
|
173
|
-
|
|
174
|
-
path d’endpoint avec <code>communeLimit</code>. Le refetch manuel
|
|
175
|
-
passe par <code>triggerKey</code> — voir le bouton « ailleurs »
|
|
176
|
-
ci-dessous dans la doc.
|
|
143
|
+
<code>@get</code> avec un endpoint et une configuration dynamiques.
|
|
177
144
|
</p>
|
|
178
|
-
<div class="flex flex-wrap gap-2
|
|
179
|
-
<span class="text-
|
|
145
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
146
|
+
<span class="text-sm">Nombre de communes :</span>
|
|
180
147
|
${[3, 5, 10].map(
|
|
181
|
-
(
|
|
148
|
+
(limit) => html`
|
|
182
149
|
<sonic-button
|
|
183
150
|
size="sm"
|
|
184
|
-
type=${this.communeLimit ===
|
|
151
|
+
type=${this.communeLimit === limit ? "primary" : "neutral"}
|
|
185
152
|
@click=${() => {
|
|
186
|
-
this.communeLimit =
|
|
153
|
+
this.communeLimit = limit;
|
|
187
154
|
}}
|
|
188
|
-
>${n}</sonic-button
|
|
189
155
|
>
|
|
156
|
+
${limit}
|
|
157
|
+
</sonic-button>
|
|
190
158
|
`,
|
|
191
159
|
)}
|
|
192
160
|
</div>
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
<sonic-button
|
|
196
|
-
size="sm"
|
|
197
|
-
type=${this.configSlot === "A" ? "primary" : "neutral"}
|
|
198
|
-
@click=${() => {
|
|
199
|
-
this.configSlot = "A";
|
|
200
|
-
}}
|
|
201
|
-
>docsDemoDynApiConfA</sonic-button
|
|
202
|
-
>
|
|
203
|
-
<sonic-button
|
|
204
|
-
size="sm"
|
|
205
|
-
type=${this.configSlot === "B" ? "primary" : "neutral"}
|
|
206
|
-
@click=${() => {
|
|
207
|
-
this.configSlot = "B";
|
|
208
|
-
}}
|
|
209
|
-
>docsDemoDynApiConfB</sonic-button
|
|
210
|
-
>
|
|
211
|
-
</div>
|
|
212
|
-
${status != null
|
|
213
|
-
? html`<p class="text-xs text-neutral-500">
|
|
214
|
-
HTTP ${status} · fetch ${fetchedAt} · limit=${this.communeLimit} ·
|
|
215
|
-
conf=docsDemoDynApiConf${this.configSlot}
|
|
216
|
-
</p>`
|
|
217
|
-
: ""}
|
|
218
|
-
${this.geoApiPayloadDyn === null
|
|
219
|
-
? html`<p class="text-neutral-500">Chargement…</p>`
|
|
220
|
-
: !rows || rows.length === 0
|
|
221
|
-
? html`<p class="text-neutral-500">Aucune commune</p>`
|
|
222
|
-
: html`
|
|
223
|
-
<ul class="list-disc pl-5 space-y-1">
|
|
224
|
-
${rows.map(
|
|
225
|
-
(c) =>
|
|
226
|
-
html`<li>
|
|
227
|
-
<span class="font-medium">${c.nom}</span>
|
|
228
|
-
<span class="text-neutral-500 text-sm">
|
|
229
|
-
— ${c.code}</span
|
|
230
|
-
>
|
|
231
|
-
</li>`,
|
|
232
|
-
)}
|
|
233
|
-
</ul>
|
|
234
|
-
`}
|
|
235
|
-
</div>
|
|
236
|
-
`;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/** Déclenche le refetch d’un `@get` distant via `triggerKey` (sans toucher la config). */
|
|
241
|
-
@customElement("demo-api-get-refresh-remote")
|
|
242
|
-
export class DemoApiGetRefreshRemote extends LitElement {
|
|
243
|
-
static styles = [tailwind];
|
|
244
|
-
|
|
245
|
-
private refreshFromElsewhere() {
|
|
246
|
-
dp(docsDemoGeoCommunesRefreshKey).invalidate();
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
render() {
|
|
250
|
-
return html`
|
|
251
|
-
<div
|
|
252
|
-
class="rounded border border-dashed border-neutral-300 dark:border-neutral-600 p-3 space-y-2"
|
|
253
|
-
>
|
|
254
|
-
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
|
255
|
-
Composant séparé — même publisher
|
|
256
|
-
<code>docsDemoGeoCommunesRefresh</code> :
|
|
161
|
+
<p class="font-mono text-xs text-neutral-500">
|
|
162
|
+
communes?limit=${this.communeLimit}
|
|
257
163
|
</p>
|
|
258
|
-
|
|
259
|
-
class="text-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
164
|
+
${this.geoApiPayloadDyn == null
|
|
165
|
+
? html`<p class="text-sm text-neutral-500">Chargement…</p>`
|
|
166
|
+
: html`
|
|
167
|
+
<ul class="grid gap-1 sm:grid-cols-2">
|
|
168
|
+
${(rows ?? []).map(
|
|
169
|
+
(c) => html`<li class="text-sm">${c.nom}</li>`,
|
|
170
|
+
)}
|
|
171
|
+
</ul>
|
|
172
|
+
`}
|
|
264
173
|
</div>
|
|
265
174
|
`;
|
|
266
175
|
}
|