@supersoniks/concorde 4.5.2 → 4.7.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 (185) hide show
  1. package/.gitlab-ci.yml +23 -0
  2. package/README.md +106 -55
  3. package/ai/AGENTS.md +52 -0
  4. package/ai/README.md +30 -0
  5. package/ai/cursor/rules/concorde-menu.mdc +15 -0
  6. package/ai/cursor/rules/concorde-scope.mdc +14 -0
  7. package/ai/cursor/rules/concorde-theme.mdc +13 -0
  8. package/ai/cursor/rules/concorde.mdc +49 -0
  9. package/ai/jetbrains/rules/concorde.md +39 -0
  10. package/ai/skills/concorde/SKILL.md +220 -0
  11. package/ai/skills/concorde-get-set-dp/SKILL.md +194 -0
  12. package/ai/skills/concorde-imports/SKILL.md +78 -0
  13. package/ai/skills/concorde-menu/SKILL.md +74 -0
  14. package/ai/skills/concorde-scope/SKILL.md +70 -0
  15. package/ai/skills/concorde-theme/SKILL.md +46 -0
  16. package/build-infos.json +1 -1
  17. package/concorde-core.bundle.js +159 -159
  18. package/concorde-core.es.js +1915 -1809
  19. package/dist/altcha-widget.js +2662 -0
  20. package/dist/concorde-core.bundle.js +159 -159
  21. package/dist/concorde-core.es.js +1915 -1809
  22. package/dist/docs-mock-api-sw.js +589 -0
  23. package/dist/docs-mock-api-sw.js.map +7 -0
  24. package/docs/altcha-widget.js +2662 -0
  25. package/docs/assets/index-D9pxaQYK.js +7508 -0
  26. package/docs/assets/index-t0-i22oI.css +1 -0
  27. package/docs/docs-mock-api-sw.js +589 -0
  28. package/docs/docs-mock-api-sw.js.map +7 -0
  29. package/docs/index.html +2 -2
  30. package/docs/src/core/components/functional/fetch/fetch.md +13 -11
  31. package/docs/src/core/components/functional/if/if.md +4 -11
  32. package/docs/src/core/components/functional/list/list.md +60 -194
  33. package/docs/src/core/components/functional/queue/queue.md +70 -85
  34. package/docs/src/core/components/functional/router/router.md +62 -97
  35. package/docs/src/core/components/functional/states/states.md +2 -2
  36. package/docs/src/core/components/functional/submit/submit.md +86 -55
  37. package/docs/src/core/components/ui/captcha/captcha.md +2 -2
  38. package/docs/src/core/components/ui/card/card.md +1 -1
  39. package/docs/src/core/components/ui/form/checkbox/checkbox.md +5 -32
  40. package/docs/src/core/components/ui/form/input/input.md +5 -30
  41. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +6 -4
  42. package/docs/src/core/components/ui/form/radio/radio.md +5 -32
  43. package/docs/src/core/components/ui/form/select/select.md +5 -31
  44. package/docs/src/core/components/ui/form/switch/switch.md +5 -32
  45. package/docs/src/core/components/ui/loader/loader.md +1 -13
  46. package/docs/src/core/components/ui/table/table.md +3 -3
  47. package/docs/src/docs/_core-concept/dataFlow.md +73 -0
  48. package/docs/src/docs/_core-concept/subscriber.md +9 -10
  49. package/docs/src/docs/_decorators/ancestor-attribute.md +4 -3
  50. package/docs/src/docs/_decorators/auto-subscribe.md +19 -16
  51. package/docs/src/docs/_decorators/bind.md +20 -17
  52. package/docs/src/docs/_decorators/get.md +7 -4
  53. package/docs/src/docs/_decorators/handle.md +171 -0
  54. package/docs/src/docs/_decorators/on-assign.md +99 -73
  55. package/docs/src/docs/_decorators/publish.md +2 -1
  56. package/docs/src/docs/_decorators/subscribe.md +70 -9
  57. package/docs/src/docs/_decorators/wait-for-ancestors.md +13 -10
  58. package/docs/src/docs/_directives/sub.md +91 -0
  59. package/docs/src/docs/_getting-started/ai-agents.md +56 -0
  60. package/docs/src/docs/_getting-started/concorde-manual-install.md +133 -0
  61. package/docs/src/docs/_getting-started/concorde-outside.md +13 -123
  62. package/docs/src/docs/_getting-started/create-a-component.md +2 -0
  63. package/docs/src/docs/_getting-started/my-first-component.md +236 -0
  64. package/docs/src/docs/_getting-started/my-first-subscriber.md +29 -83
  65. package/docs/src/docs/_getting-started/pubsub.md +21 -134
  66. package/docs/src/docs/_getting-started/start.md +26 -18
  67. package/docs/src/docs/_misc/api-configuration.md +79 -0
  68. package/docs/src/docs/_misc/dataProviderKey.md +38 -5
  69. package/docs/src/docs/_misc/docs-mock-api.md +60 -0
  70. package/docs/src/docs/_misc/endpoint.md +2 -1
  71. package/docs/src/docs/_misc/html-integration.md +13 -0
  72. package/docs/src/docs/search/docs-search.json +4163 -873
  73. package/docs/src/tsconfig.json +380 -317
  74. package/gitlab/job_tests.sh +55 -0
  75. package/package.json +37 -3
  76. package/public/altcha-widget.js +2662 -0
  77. package/public/docs-mock-api-sw.js +589 -0
  78. package/public/docs-mock-api-sw.js.map +7 -0
  79. package/scripts/ai-init.mjs +167 -0
  80. package/scripts/docs-mock-api-vite-plugin.ts +116 -0
  81. package/scripts/docs-open-in-editor-plugin.ts +130 -0
  82. package/scripts/pre-publish.mjs +2 -1
  83. package/src/core/components/functional/example/example.ts +1 -1
  84. package/src/core/components/functional/fetch/fetch.md +13 -11
  85. package/src/core/components/functional/if/if.md +4 -11
  86. package/src/core/components/functional/list/list.demo.ts +4 -4
  87. package/src/core/components/functional/list/list.md +60 -194
  88. package/src/core/components/functional/list/list.ts +8 -7
  89. package/src/core/components/functional/queue/queue.demo.ts +1 -1
  90. package/src/core/components/functional/queue/queue.md +70 -85
  91. package/src/core/components/functional/queue/queue.ts +4 -4
  92. package/src/core/components/functional/router/router.md +62 -97
  93. package/src/core/components/functional/router/router.ts +1 -1
  94. package/src/core/components/functional/states/states.md +2 -2
  95. package/src/core/components/functional/submit/submit.md +86 -55
  96. package/src/core/components/functional/submit/submit.ts +10 -3
  97. package/src/core/components/ui/captcha/captcha.md +2 -2
  98. package/src/core/components/ui/card/card.md +1 -1
  99. package/src/core/components/ui/form/checkbox/checkbox.md +5 -32
  100. package/src/core/components/ui/form/input/input.md +5 -30
  101. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +6 -4
  102. package/src/core/components/ui/form/radio/radio.md +5 -32
  103. package/src/core/components/ui/form/select/select.md +5 -31
  104. package/src/core/components/ui/form/switch/switch.md +5 -32
  105. package/src/core/components/ui/loader/loader.md +1 -13
  106. package/src/core/components/ui/table/table.md +3 -3
  107. package/src/core/decorators/Subscriber.ts +2 -0
  108. package/src/core/decorators/subscriber/handle.disambig.spec.ts +20 -0
  109. package/src/core/decorators/subscriber/handle.skip.spec.ts +37 -0
  110. package/src/core/decorators/subscriber/handle.ts +128 -0
  111. package/src/core/decorators/subscriber/onAssign.ts +94 -4
  112. package/src/core/directives/DataProvider.sub.spec.ts +96 -0
  113. package/src/core/directives/DataProvider.ts +109 -40
  114. package/src/core/utils/PublisherProxy.ts +33 -18
  115. package/src/core/utils/dataProviderKey.ts +23 -0
  116. package/src/core/utils/publisherPathKey.spec.ts +58 -0
  117. package/src/decorators.ts +6 -0
  118. package/src/docs/_core-concept/dataFlow.md +73 -0
  119. package/src/docs/_core-concept/subscriber.md +9 -10
  120. package/src/docs/_decorators/ancestor-attribute.md +4 -3
  121. package/src/docs/_decorators/auto-subscribe.md +19 -16
  122. package/src/docs/_decorators/bind.md +20 -17
  123. package/src/docs/_decorators/get.md +7 -4
  124. package/src/docs/_decorators/handle.md +171 -0
  125. package/src/docs/_decorators/on-assign.md +99 -47
  126. package/src/docs/_decorators/publish.md +2 -1
  127. package/src/docs/_decorators/subscribe.md +70 -9
  128. package/src/docs/_decorators/wait-for-ancestors.md +13 -10
  129. package/src/docs/_directives/sub.md +91 -0
  130. package/src/docs/_getting-started/ai-agents.md +56 -0
  131. package/src/docs/_getting-started/concorde-manual-install.md +133 -0
  132. package/src/docs/_getting-started/concorde-outside.md +13 -123
  133. package/src/docs/_getting-started/create-a-component.md +2 -0
  134. package/src/docs/_getting-started/my-first-component.md +236 -0
  135. package/src/docs/_getting-started/my-first-subscriber.md +29 -83
  136. package/src/docs/_getting-started/pubsub.md +21 -134
  137. package/src/docs/_getting-started/start.md +26 -18
  138. package/src/docs/_misc/api-configuration.md +79 -0
  139. package/src/docs/_misc/dataProviderKey.md +38 -5
  140. package/src/docs/_misc/docs-mock-api.md +60 -0
  141. package/src/docs/_misc/endpoint.md +2 -1
  142. package/src/docs/_misc/html-integration.md +13 -0
  143. package/src/docs/code.ts +58 -12
  144. package/src/docs/components/docs-demo-sources.ts +397 -0
  145. package/src/docs/components/docs-lit-demo-raw.ts +28 -0
  146. package/src/docs/components/docs-lit-demo.ts +166 -0
  147. package/src/docs/components/docs-source-link.ts +72 -0
  148. package/src/docs/docs-location.ts +54 -0
  149. package/src/docs/docs.ts +12 -0
  150. package/src/docs/example/decorators-demo-bind-demos.ts +41 -46
  151. package/src/docs/example/decorators-demo-geo.ts +16 -11
  152. package/src/docs/example/decorators-demo-init.ts +2 -228
  153. package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +142 -12
  154. package/src/docs/example/decorators-demo.ts +71 -70
  155. package/src/docs/example/docs-api-config-demos.ts +234 -0
  156. package/src/docs/example/docs-joke-demos.ts +297 -0
  157. package/src/docs/example/docs-list-demos.ts +179 -0
  158. package/src/docs/example/docs-provider-keys.ts +315 -0
  159. package/src/docs/example/docs-queue-demos.ts +114 -0
  160. package/src/docs/example/docs-router-demos.ts +89 -0
  161. package/src/docs/example/docs-submit-demos.ts +455 -0
  162. package/src/docs/example/docs-toggle-demos.ts +73 -0
  163. package/src/docs/example/docs-user-two-scopes.ts +37 -0
  164. package/src/docs/example/docs-users-list.ts +71 -0
  165. package/src/docs/example/users.ts +41 -24
  166. package/src/docs/mock-api/api-config-mock.ts +152 -0
  167. package/src/docs/mock-api/fixtures.ts +377 -0
  168. package/src/docs/mock-api/register.ts +25 -0
  169. package/src/docs/mock-api/router.ts +234 -0
  170. package/src/docs/mock-api/service-worker.ts +23 -0
  171. package/src/docs/mock-api/urls.ts +11 -0
  172. package/src/docs/navigation/navigation.ts +43 -7
  173. package/src/docs/search/docs-search.json +4193 -858
  174. package/src/docs/search/markdown-renderer.ts +7 -3
  175. package/src/docs/search/page.ts +11 -14
  176. package/src/docs/search/sonic-code-markdown.spec.ts +29 -0
  177. package/src/docs/search/sonic-code-markdown.ts +28 -0
  178. package/src/docs.ts +4 -0
  179. package/src/tsconfig.json +96 -0
  180. package/src/tsconfig.tsbuildinfo +1 -1
  181. package/vite.config.mts +8 -0
  182. package/docs/assets/index-CaysOMFz.js +0 -5046
  183. package/docs/assets/index-D8mGoXzF.css +0 -1
  184. package/docs/src/docs/_misc/templates-demo.md +0 -19
  185. package/src/docs/_misc/templates-demo.md +0 -19
@@ -0,0 +1,455 @@
1
+ import { html, LitElement, nothing } from "lit";
2
+ import { customElement, state } from "lit/decorators.js";
3
+ import { subscribe } from "@supersoniks/concorde/core/decorators/Subscriber";
4
+ import { Endpoint } from "@supersoniks/concorde/core/utils/endpoint";
5
+ import {
6
+ DOCS_MOCK_API_BASE,
7
+ DOCS_MOCK_REQRES_SERVICE,
8
+ } from "../mock-api/urls";
9
+ import {
10
+ submitClearFormKey,
11
+ submitEndpointFormKey,
12
+ submitEndpointResultKey,
13
+ submitEventFormKey,
14
+ submitExampleFormKey,
15
+ submitExampleResultKey,
16
+ submitFormDataFormKey,
17
+ submitFormDataResultKey,
18
+ submitGetFormKey,
19
+ submitGetResultKey,
20
+ submitNativeFormKey,
21
+ submitNestedFormKey,
22
+ submitNestedResultKey,
23
+ } from "./docs-provider-keys";
24
+ import { tailwind } from "../tailwind";
25
+
26
+ import "../../core/components/functional/sonic-scope/sonic-scope";
27
+ import "../../core/components/functional/submit/submit";
28
+ import "../../core/components/ui/button/button";
29
+ import "../../core/components/ui/divider/divider";
30
+ import "../../core/components/ui/form/input/input";
31
+
32
+ type RegisterResult = {
33
+ id?: number;
34
+ token?: string;
35
+ email?: string;
36
+ error?: string;
37
+ messages?: { content?: string; status?: string }[];
38
+ method?: string;
39
+ query?: { email?: string; password?: string };
40
+ note?: string;
41
+ };
42
+
43
+ const registerEndpoint = new Endpoint<RegisterResult>("api/register");
44
+ const registerNestedEndpoint = new Endpoint<RegisterResult>(
45
+ "api/register/nested",
46
+ );
47
+ const registerEchoEndpoint = new Endpoint<RegisterResult>("api/register/echo");
48
+ const usersListEndpoint = new Endpoint<unknown>("api/users");
49
+
50
+ function resultLines(r: RegisterResult | null) {
51
+ if (!r) return nothing;
52
+ const messageText = r.messages
53
+ ?.map((m) => m.content)
54
+ .filter(Boolean)
55
+ .join(" — ");
56
+ return html`
57
+ ${r.id != null
58
+ ? html`<div>Id : <span class="font-mono">${r.id}</span></div>`
59
+ : nothing}
60
+ ${r.email
61
+ ? html`<div>Email : <span class="font-mono">${r.email}</span></div>`
62
+ : nothing}
63
+ ${r.token
64
+ ? html`<div>Token : <span class="font-mono break-all">${r.token}</span></div>`
65
+ : nothing}
66
+ ${r.method
67
+ ? html`<div>Method : <span class="font-mono">${r.method}</span></div>`
68
+ : nothing}
69
+ ${r.query
70
+ ? html`<div>
71
+ Query :
72
+ <span class="font-mono"
73
+ >email=${r.query.email ?? ""}, password=${r.query.password ?? ""}</span
74
+ >
75
+ </div>`
76
+ : nothing}
77
+ ${r.note ? html`<p class="text-neutral-500">${r.note}</p>` : nothing}
78
+ ${r.error ? html`<div class="text-red-600">${r.error}</div>` : nothing}
79
+ ${messageText ? html`<div class="text-red-600">${messageText}</div>` : nothing}
80
+ `;
81
+ }
82
+
83
+ /**
84
+ * Form + result — `submitResultDataProvider` is filled after POST to mock `/api/register`.
85
+ */
86
+ @customElement("docs-submit-demo")
87
+ export class DocsSubmitDemo extends LitElement {
88
+ static styles = [tailwind];
89
+
90
+ @subscribe(submitExampleResultKey)
91
+ @state()
92
+ private result: RegisterResult | null = null;
93
+
94
+ private resultBlock() {
95
+ const r = this.result;
96
+ if (!r || (!r.id && !r.token && !r.error && !r.messages?.length)) {
97
+ return html`
98
+ <p class="mt-4 text-sm text-neutral-500">
99
+ Submit the form to see <code>id</code> and <code>token</code> here.
100
+ </p>
101
+ `;
102
+ }
103
+ return html`
104
+ <sonic-divider align="left" class="mt-6">Result</sonic-divider>
105
+ <div class="mt-2 space-y-1 text-sm">${resultLines(r)}</div>
106
+ `;
107
+ }
108
+
109
+ render() {
110
+ return html`
111
+ <sonic-scope
112
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
113
+ dataProvider=${registerEndpoint.path}
114
+ formDataProvider=${submitExampleFormKey.path}
115
+ submitResultDataProvider=${submitExampleResultKey.path}
116
+ class="max-w-lg block"
117
+ >
118
+ <sonic-submit onEnterKey>
119
+ <div class="mb-4 grid grid-cols-2 gap-4">
120
+ <sonic-input
121
+ required
122
+ name="email"
123
+ type="email"
124
+ value="eve.holt@reqres.in"
125
+ ></sonic-input>
126
+ <sonic-input
127
+ required
128
+ type="password"
129
+ name="password"
130
+ value="pistol"
131
+ ></sonic-input>
132
+ </div>
133
+ </sonic-submit>
134
+ <sonic-submit onClick>
135
+ <sonic-button type="success" class="w-full">Submit</sonic-button>
136
+ </sonic-submit>
137
+ </sonic-scope>
138
+ ${this.resultBlock()}
139
+ `;
140
+ }
141
+ }
142
+
143
+ /** `clearedDataOnSuccess` resets listed publishers when the API returns a result object. */
144
+ @customElement("docs-submit-clear-demo")
145
+ export class DocsSubmitClearDemo extends LitElement {
146
+ static styles = [tailwind];
147
+
148
+ render() {
149
+ return html`
150
+ <sonic-scope
151
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
152
+ dataProvider=${registerEndpoint.path}
153
+ formDataProvider=${submitClearFormKey.path}
154
+ clearedDataOnSuccess=${submitClearFormKey.path}
155
+ class="max-w-lg block"
156
+ >
157
+ <sonic-submit onClick>
158
+ <div class="mb-4 grid grid-cols-2 gap-4">
159
+ <sonic-input
160
+ required
161
+ name="email"
162
+ type="email"
163
+ value="eve.holt@reqres.in"
164
+ ></sonic-input>
165
+ <sonic-input
166
+ required
167
+ type="password"
168
+ name="password"
169
+ value="pistol"
170
+ ></sonic-input>
171
+ </div>
172
+ <sonic-button type="success" class="w-full">Submit and clear form</sonic-button>
173
+ </sonic-submit>
174
+ </sonic-scope>
175
+ <p class="mt-3 text-sm text-neutral-500">
176
+ After submit, <code>clearedDataOnSuccess="submit-clear-form"</code> resets
177
+ the publisher (inputs should empty).
178
+ </p>
179
+ `;
180
+ }
181
+ }
182
+
183
+ /** `submit-result-key` keeps only a nested part of the JSON before writing the result publisher. */
184
+ @customElement("docs-submit-result-key-demo")
185
+ export class DocsSubmitResultKeyDemo extends LitElement {
186
+ static styles = [tailwind];
187
+
188
+ @subscribe(submitNestedResultKey)
189
+ @state()
190
+ private result: RegisterResult | null = null;
191
+
192
+ render() {
193
+ const r = this.result;
194
+ return html`
195
+ <sonic-scope
196
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
197
+ dataProvider=${registerNestedEndpoint.path}
198
+ formDataProvider=${submitNestedFormKey.path}
199
+ submitResultDataProvider=${submitNestedResultKey.path}
200
+ class="max-w-lg block"
201
+ >
202
+ <sonic-submit onClick submit-result-key="data">
203
+ <div class="mb-4 grid grid-cols-2 gap-4">
204
+ <sonic-input
205
+ required
206
+ name="email"
207
+ type="email"
208
+ value="eve.holt@reqres.in"
209
+ ></sonic-input>
210
+ <sonic-input
211
+ required
212
+ type="password"
213
+ name="password"
214
+ value="pistol"
215
+ ></sonic-input>
216
+ </div>
217
+ <sonic-button type="primary" class="w-full">Submit (nested JSON)</sonic-button>
218
+ </sonic-submit>
219
+ </sonic-scope>
220
+ <sonic-divider align="left" class="mt-4">Stored in publisher</sonic-divider>
221
+ <div class="mt-2 text-sm">
222
+ ${r?.id != null
223
+ ? html`<div>id from <code>data</code> : ${r.id}</div>`
224
+ : html`<p class="text-neutral-500">Submit to extract <code>data.id</code> / <code>data.token</code>.</p>`}
225
+ </div>
226
+ `;
227
+ }
228
+ }
229
+
230
+ /** `sendAsFormData` sends multipart <code>FormData</code> instead of JSON. */
231
+ @customElement("docs-submit-formdata-demo")
232
+ export class DocsSubmitFormDataDemo extends LitElement {
233
+ static styles = [tailwind];
234
+
235
+ @subscribe(submitFormDataResultKey)
236
+ @state()
237
+ private result: RegisterResult | null = null;
238
+
239
+ render() {
240
+ return html`
241
+ <sonic-scope
242
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
243
+ dataProvider=${registerEndpoint.path}
244
+ formDataProvider=${submitFormDataFormKey.path}
245
+ submitResultDataProvider=${submitFormDataResultKey.path}
246
+ class="max-w-lg block"
247
+ >
248
+ <sonic-submit onClick sendAsFormData>
249
+ <div class="mb-4 grid grid-cols-2 gap-4">
250
+ <sonic-input
251
+ required
252
+ name="email"
253
+ type="email"
254
+ value="eve.holt@reqres.in"
255
+ ></sonic-input>
256
+ <sonic-input
257
+ required
258
+ type="password"
259
+ name="password"
260
+ value="pistol"
261
+ ></sonic-input>
262
+ </div>
263
+ <sonic-button type="primary" class="w-full">Submit as FormData</sonic-button>
264
+ </sonic-submit>
265
+ </sonic-scope>
266
+ <div class="mt-3 text-sm">
267
+ ${this.result?.token
268
+ ? html`Token received: <span class="font-mono">${this.result.token}</span>`
269
+ : html`<p class="text-neutral-500">Mock accepts JSON and multipart for <code>/api/register</code>.</p>`}
270
+ </div>
271
+ `;
272
+ }
273
+ }
274
+
275
+ /** `method="get"` appends publisher fields as query string (mock echoes them). */
276
+ @customElement("docs-submit-get-demo")
277
+ export class DocsSubmitGetDemo extends LitElement {
278
+ static styles = [tailwind];
279
+
280
+ @subscribe(submitGetResultKey)
281
+ @state()
282
+ private result: RegisterResult | null = null;
283
+
284
+ render() {
285
+ return html`
286
+ <sonic-scope
287
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
288
+ dataProvider=${registerEchoEndpoint.path}
289
+ formDataProvider=${submitGetFormKey.path}
290
+ submitResultDataProvider=${submitGetResultKey.path}
291
+ class="max-w-lg block"
292
+ >
293
+ <sonic-submit onClick method="get">
294
+ <div class="mb-4 grid grid-cols-2 gap-4">
295
+ <sonic-input name="email" type="email" value="demo@example.com"></sonic-input>
296
+ <sonic-input name="password" type="password" value="secret"></sonic-input>
297
+ </div>
298
+ <sonic-button type="neutral" class="w-full">GET with query string</sonic-button>
299
+ </sonic-submit>
300
+ </sonic-scope>
301
+ <div class="mt-3 space-y-1 text-sm">${resultLines(this.result)}</div>
302
+ `;
303
+ }
304
+ }
305
+
306
+ /** `native` — sync publisher to a real <form> and trigger browser submit (iframe target). */
307
+ @customElement("docs-submit-native-demo")
308
+ export class DocsSubmitNativeDemo extends LitElement {
309
+ static styles = [tailwind];
310
+
311
+ private formAction = `${DOCS_MOCK_API_BASE}/api/register`;
312
+
313
+ render() {
314
+ return html`
315
+ <form
316
+ action=${this.formAction}
317
+ method="post"
318
+ target="docs-submit-native-frame"
319
+ class="max-w-lg"
320
+ >
321
+ <sonic-scope formDataProvider=${submitNativeFormKey.path}>
322
+ <sonic-submit native onClick>
323
+ <div class="mb-4 grid grid-cols-2 gap-4">
324
+ <sonic-input
325
+ required
326
+ name="email"
327
+ type="email"
328
+ value="eve.holt@reqres.in"
329
+ ></sonic-input>
330
+ <sonic-input
331
+ required
332
+ type="password"
333
+ name="password"
334
+ value="pistol"
335
+ ></sonic-input>
336
+ </div>
337
+ <sonic-button type="success" class="w-full"
338
+ >Native form POST</sonic-button
339
+ >
340
+ </sonic-submit>
341
+ </sonic-scope>
342
+ </form>
343
+ <p class="mt-2 text-sm text-neutral-500">
344
+ Response loads in the iframe below (full page navigation is avoided via
345
+ <code>target</code>).
346
+ </p>
347
+ <iframe
348
+ name="docs-submit-native-frame"
349
+ title="Native form response"
350
+ class="mt-2 h-24 w-full rounded border border-neutral-200 bg-neutral-50 text-xs"
351
+ ></iframe>
352
+ `;
353
+ }
354
+ }
355
+
356
+ /** Bubbles <code>submit</code> custom event with API result in <code>detail</code>. */
357
+ @customElement("docs-submit-event-demo")
358
+ export class DocsSubmitEventDemo extends LitElement {
359
+ static styles = [tailwind];
360
+
361
+ @state()
362
+ private lastEvent: string | null = null;
363
+
364
+ private onSubmit = (e: CustomEvent<RegisterResult>) => {
365
+ const d = e.detail;
366
+ this.lastEvent = d?.token
367
+ ? `token=${d.token}`
368
+ : d?.messages?.[0]?.content ?? JSON.stringify(d);
369
+ };
370
+
371
+ render() {
372
+ return html`
373
+ <sonic-scope
374
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
375
+ dataProvider="api/register"
376
+ formDataProvider="submit-event-form"
377
+ class="max-w-lg block"
378
+ >
379
+ <sonic-submit onClick @submit=${this.onSubmit}>
380
+ <div class="mb-4 grid grid-cols-2 gap-4">
381
+ <sonic-input
382
+ required
383
+ name="email"
384
+ type="email"
385
+ value="eve.holt@reqres.in"
386
+ ></sonic-input>
387
+ <sonic-input
388
+ required
389
+ type="password"
390
+ name="password"
391
+ value="pistol"
392
+ ></sonic-input>
393
+ </div>
394
+ <sonic-button type="primary" class="w-full">Submit (listen event)</sonic-button>
395
+ </sonic-submit>
396
+ </sonic-scope>
397
+ <p class="mt-3 text-sm">
398
+ ${this.lastEvent
399
+ ? html`Last <code>submit</code> event: <span class="font-mono">${this.lastEvent}</span>`
400
+ : html`<span class="text-neutral-500">Click submit — handler reads <code>event.detail</code>.</span>`}
401
+ </p>
402
+ `;
403
+ }
404
+ }
405
+
406
+ /** <code>endPoint</code> overrides <code>dataProvider</code> for the request path. */
407
+ @customElement("docs-submit-endpoint-demo")
408
+ export class DocsSubmitEndpointDemo extends LitElement {
409
+ static styles = [tailwind];
410
+
411
+ @subscribe(submitEndpointResultKey)
412
+ @state()
413
+ private result: RegisterResult | null = null;
414
+
415
+ render() {
416
+ return html`
417
+ <sonic-scope
418
+ serviceURL=${DOCS_MOCK_REQRES_SERVICE}
419
+ dataProvider=${usersListEndpoint.path}
420
+ formDataProvider=${submitEndpointFormKey.path}
421
+ submitResultDataProvider=${submitEndpointResultKey.path}
422
+ class="max-w-lg block"
423
+ >
424
+ <sonic-submit onClick endPoint=${registerEndpoint.path}>
425
+ <div class="mb-4 grid grid-cols-2 gap-4">
426
+ <sonic-input
427
+ required
428
+ name="email"
429
+ type="email"
430
+ value="eve.holt@reqres.in"
431
+ ></sonic-input>
432
+ <sonic-input
433
+ required
434
+ type="password"
435
+ name="password"
436
+ value="pistol"
437
+ ></sonic-input>
438
+ </div>
439
+ <sonic-button type="primary" class="w-full"
440
+ >POST via <code>endPoint</code></sonic-button
441
+ >
442
+ </sonic-submit>
443
+ </sonic-scope>
444
+ <div class="mt-3 text-sm">
445
+ ${this.result?.token
446
+ ? html`Registered (scope <code>dataProvider</code> is <code>api/users</code>, request went to <code>api/register</code>).`
447
+ : html`<p class="text-neutral-500">
448
+ <code>dataProvider=${usersListEndpoint.path}</code> but
449
+ <code>endPoint=${registerEndpoint.path}</code> on
450
+ <code>sonic-submit</code>.
451
+ </p>`}
452
+ </div>
453
+ `;
454
+ }
455
+ }
@@ -0,0 +1,73 @@
1
+ import { html, LitElement } from "lit";
2
+ import { customElement, property, state } from "lit/decorators.js";
3
+ import { subscribe } from "@supersoniks/concorde/core/decorators/Subscriber";
4
+ import { tailwind } from "../tailwind";
5
+ import {
6
+ docsToggleLoaderFormKey,
7
+ docsToggleVisibilityKey,
8
+ } from "./docs-provider-keys";
9
+
10
+ import "../../core/components/functional/if/if";
11
+ import "../../core/components/ui/form/checkbox/checkbox";
12
+ import "../../core/components/ui/image/image";
13
+ import "../../core/components/ui/loader/loader";
14
+
15
+ const isChecked = (v: unknown) =>
16
+ v === true || v === "true" || v === 1 || v === "1";
17
+
18
+ @customElement("docs-toggle-if-demo")
19
+ export class DocsToggleIfDemo extends LitElement {
20
+ static styles = [tailwind];
21
+
22
+ @subscribe(docsToggleVisibilityKey.togglePlan)
23
+ @state()
24
+ togglePlan = "";
25
+
26
+ render() {
27
+ const show = isChecked(this.togglePlan);
28
+ return html`
29
+ <div formDataProvider=${docsToggleVisibilityKey.path}>
30
+ <sonic-checkbox
31
+ label="Show evacuation plan"
32
+ name="togglePlan"
33
+ unique
34
+ checked
35
+ value="true"
36
+ ></sonic-checkbox>
37
+ <sonic-if .condition=${show} class="mt-4 block">
38
+ <sonic-image
39
+ src="https://www.thebaron.info/assets/mail/concorde-evacuation.jpg"
40
+ rounded="md"
41
+ ratio="654/463"
42
+ ></sonic-image>
43
+ </sonic-if>
44
+ </div>
45
+ `;
46
+ }
47
+ }
48
+
49
+ @customElement("docs-toggle-loader-demo")
50
+ export class DocsToggleLoaderDemo extends LitElement {
51
+ static styles = [tailwind];
52
+
53
+ @subscribe(docsToggleLoaderFormKey.toggleLoader)
54
+ @state()
55
+ toggleLoader = "";
56
+
57
+ render() {
58
+ const show = isChecked(this.toggleLoader);
59
+ return html`
60
+ <div formDataProvider=${docsToggleLoaderFormKey.path}>
61
+ <sonic-checkbox
62
+ label="Show fixed loader"
63
+ name="toggleLoader"
64
+ unique
65
+ value="true"
66
+ ></sonic-checkbox>
67
+ <sonic-if .condition=${show}>
68
+ <sonic-loader></sonic-loader>
69
+ </sonic-if>
70
+ </div>
71
+ `;
72
+ }
73
+ }
@@ -0,0 +1,37 @@
1
+ import { html, LitElement } from "lit";
2
+ import { customElement } from "lit/decorators.js";
3
+ import { docsUserScopeAKey, docsUserScopeBKey } from "./docs-provider-keys";
4
+ import { tailwind } from "../tailwind";
5
+ import "./decorators-demo-init";
6
+ import "./users";
7
+
8
+ import "../../core/components/ui/divider/divider";
9
+
10
+ @customElement("docs-user-two-scopes")
11
+ export class DocsUserTwoScopes extends LitElement {
12
+ static styles = [tailwind];
13
+
14
+ render() {
15
+ return html`
16
+ <div class="grid md:grid-cols-2 gap-6">
17
+ <section>
18
+ <sonic-divider align="left">Scope A — docsUserScopeA</sonic-divider>
19
+ <div dataProvider="docsUserScopeA">
20
+ <docs-user></docs-user>
21
+ </div>
22
+ </section>
23
+ <section>
24
+ <sonic-divider align="left">Scope B — ${docsUserScopeBKey.path}</sonic-divider>
25
+ <div dataProvider=${docsUserScopeBKey.path}>
26
+ <docs-user></docs-user>
27
+ </div>
28
+ </section>
29
+ </div>
30
+ <p class="mt-4 text-sm text-neutral-500">
31
+ Same <code>docs-user</code> component: each instance reads the nearest
32
+ ancestor <code>dataProvider</code> via <code>@ancestorAttribute</code>
33
+ and <code>docsUserRowKey</code>.
34
+ </p>
35
+ `;
36
+ }
37
+ }
@@ -0,0 +1,71 @@
1
+ import { html, LitElement, nothing } from "lit";
2
+ import { customElement } from "lit/decorators.js";
3
+ import { tailwind } from "../tailwind";
4
+ import { Endpoint } from "@supersoniks/concorde/core/utils/endpoint";
5
+ import { DOCS_MOCK_REQRES_SERVICE } from "../mock-api/urls";
6
+
7
+ const usersListEndpoint = new Endpoint<unknown>("api/users");
8
+ import type { DocsUserData } from "./users";
9
+ import "../../core/components/functional/list/list";
10
+ import "../../core/components/ui/image/image";
11
+ import "../../core/components/ui/button/button";
12
+ import "../../core/components/ui/icon/icon";
13
+ import "../../core/components/ui/badge/badge";
14
+
15
+ @customElement("docs-users-list")
16
+ export class DocsUsersList extends LitElement {
17
+ static styles = [tailwind];
18
+
19
+ private items = ({ id, avatar, first_name, last_name, email }: DocsUserData) =>
20
+ html`<div
21
+ class="flex items-center gap-3 rounded-md hover:bg-neutral-50 -mx-2 p-2"
22
+ >
23
+ <sonic-image
24
+ src=${avatar}
25
+ rounded="md"
26
+ ratio="1/1"
27
+ class="w-16 block"
28
+ ></sonic-image>
29
+ <div>
30
+ <div>
31
+ ${first_name} <span class="font-bold">${last_name}</span>
32
+ </div>
33
+ <div class="text-sm text-neutral-400">${email}</div>
34
+ </div>
35
+ <div class="ml-auto relative">
36
+ ${id === "2" || id === "5"
37
+ ? html`<sonic-badge
38
+ type="danger"
39
+ size="2xs"
40
+ class="absolute left-0 -top-1 z-10"
41
+ >${id}</sonic-badge
42
+ >`
43
+ : nothing}
44
+ <sonic-button
45
+ href=${email ? `mailto:${email}` : undefined}
46
+ size="sm"
47
+ variant="outline"
48
+ shape="circle"
49
+ class="relative"
50
+ icon
51
+ >
52
+ <sonic-icon library="iconoir" name="chat-bubble"></sonic-icon>
53
+ </sonic-button>
54
+ </div>
55
+ </div>`;
56
+
57
+ render() {
58
+ return html`
59
+ <div serviceURL=${DOCS_MOCK_REQRES_SERVICE}>
60
+ <sonic-list
61
+ fetch
62
+ dataProvider=${usersListEndpoint.path}
63
+ key="data"
64
+ class="grid grid-cols-1 gap-2"
65
+ .items=${this.items}
66
+ debug
67
+ ></sonic-list>
68
+ </div>
69
+ `;
70
+ }
71
+ }