@supersoniks/concorde 5.1.2 → 5.1.3-immutable.1
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/.agents/skills/concorde-docs/SKILL.md +1 -1
- package/.claude/skills +1 -0
- package/.github/workflows/ci.yml +21 -0
- package/.github-export-ignore +30 -0
- package/CONTRIBUTING.md +59 -0
- package/DEV.md +54 -0
- package/LICENSE +21 -0
- package/OPEN_SOURCE_CHECKPOINT.md +133 -0
- package/README.md +1 -1
- package/ai/README.md +29 -41
- package/ai/skills/concorde/SKILL.md +54 -324
- package/ai/skills/concorde/rules/composition.md +32 -0
- package/ai/skills/concorde/rules/data.md +47 -0
- package/ai/skills/concorde/rules/http.md +25 -0
- package/ai/skills/concorde/rules/lists-forms.md +35 -0
- package/ai/skills/concorde/rules/styling.md +46 -0
- package/ai/skills/concorde/rules/troubleshooting.md +45 -0
- package/build-infos.json +1 -1
- package/concorde-core.bundle.js +177 -177
- package/concorde-core.es.js +4993 -4833
- package/custom-elements-manifest.public.config.mjs +8 -0
- package/{.tmp-cem/custom-elements.json → custom-elements.json} +11722 -8868
- package/dist/concorde-core.bundle.js +177 -177
- package/dist/concorde-core.es.js +4993 -4833
- package/dist/docs-mock-api-sw.js +58 -223
- package/dist/docs-mock-api-sw.js.map +4 -4
- package/dist/img/og-concorde.jpg +0 -0
- package/dist/robots.txt +11 -10
- package/docs/assets/index-CD4WyL6o.css +1 -0
- package/docs/assets/index-D_alEsng.js +12122 -0
- package/docs/crawl/core/components/functional/date/date.html +190 -0
- package/docs/crawl/core/components/functional/fetch/fetch.html +109 -0
- package/docs/crawl/core/components/functional/if/if.html +27 -0
- package/docs/crawl/core/components/functional/jsonata/jsonata.html +79 -0
- package/docs/crawl/core/components/functional/list/list.html +70 -0
- package/docs/crawl/core/components/functional/mix/mix.html +45 -0
- package/docs/crawl/core/components/functional/queue/queue.html +96 -0
- package/docs/crawl/core/components/functional/router/router.html +106 -0
- package/docs/{core → crawl/core}/components/functional/sdui/sdui-extension-points.html +8 -9
- package/docs/crawl/core/components/functional/sdui/sdui.html +368 -0
- package/docs/crawl/core/components/functional/states/states.html +66 -0
- package/docs/crawl/core/components/functional/submit/submit.html +160 -0
- package/docs/{core → crawl/core}/components/functional/subscriber/subscriber.html +5 -6
- package/docs/crawl/core/components/functional/value/value.html +51 -0
- package/docs/{core → crawl/core}/components/ui/alert/alert.html +63 -53
- package/docs/{core → crawl/core}/components/ui/alert-messages/alert-messages.html +5 -6
- package/docs/{core → crawl/core}/components/ui/badge/badge.html +33 -33
- package/docs/crawl/core/components/ui/button/button.html +150 -0
- package/docs/crawl/core/components/ui/captcha/captcha.html +112 -0
- package/docs/{core → crawl/core}/components/ui/card/card.html +11 -12
- package/docs/{core → crawl/core}/components/ui/divider/divider.html +14 -14
- package/docs/crawl/core/components/ui/form/checkbox/checkbox.html +73 -0
- package/docs/crawl/core/components/ui/form/fieldset/fieldset.html +266 -0
- package/docs/{core → crawl/core}/components/ui/form/form-actions/form-actions.html +23 -23
- package/docs/crawl/core/components/ui/form/form-layout/form-layout.html +121 -0
- package/docs/crawl/core/components/ui/form/input/input.html +109 -0
- package/docs/crawl/core/components/ui/form/input-autocomplete/input-autocomplete.html +38 -0
- package/docs/crawl/core/components/ui/form/radio/radio.html +56 -0
- package/docs/crawl/core/components/ui/form/select/select.html +73 -0
- package/docs/crawl/core/components/ui/form/switch/switch.html +56 -0
- package/docs/crawl/core/components/ui/form/textarea/textarea.html +59 -0
- package/docs/crawl/core/components/ui/group/group.html +104 -0
- package/docs/{core → crawl/core}/components/ui/icon/icon.html +9 -10
- package/docs/crawl/core/components/ui/image/image.html +298 -0
- package/docs/{core → crawl/core}/components/ui/link/link.html +11 -12
- package/docs/crawl/core/components/ui/loader/loader.html +124 -0
- package/docs/{core → crawl/core}/components/ui/menu/menu.html +33 -34
- package/docs/crawl/core/components/ui/modal/modal.html +342 -0
- package/docs/crawl/core/components/ui/pop/pop.html +296 -0
- package/docs/{core → crawl/core}/components/ui/progress/progress.html +7 -8
- package/docs/crawl/core/components/ui/table/table.html +545 -0
- package/docs/crawl/core/components/ui/theme/theme.html +131 -0
- package/docs/crawl/core/components/ui/toast/toast.html +233 -0
- package/docs/crawl/core/components/ui/tooltip/tooltip.html +140 -0
- package/docs/{docs/getting-started → crawl/docs/_core-concept}/dataFlow.html +29 -26
- package/docs/crawl/docs/_core-concept/overview.html +66 -0
- package/docs/crawl/docs/_core-concept/subscriber.html +75 -0
- package/docs/crawl/docs/_decorators/ancestor-attribute.html +117 -0
- package/docs/crawl/docs/_decorators/auto-subscribe.html +202 -0
- package/docs/crawl/docs/_decorators/bind.html +141 -0
- package/docs/crawl/docs/_decorators/get.html +149 -0
- package/docs/crawl/docs/_decorators/handle.html +165 -0
- package/docs/crawl/docs/_decorators/on-assign.html +355 -0
- package/docs/crawl/docs/_decorators/patch.html +78 -0
- package/docs/crawl/docs/_decorators/post.html +139 -0
- package/docs/crawl/docs/_decorators/publish.html +61 -0
- package/docs/crawl/docs/_decorators/put.html +78 -0
- package/docs/crawl/docs/_decorators/subscribe.html +109 -0
- package/docs/crawl/docs/_decorators/wait-for-ancestors.html +137 -0
- package/docs/crawl/docs/_directives/sub.html +93 -0
- package/docs/{docs/getting-started → crawl/docs/_getting-started}/ai-agents.html +11 -12
- package/docs/crawl/docs/_getting-started/concorde-manual-install.html +100 -0
- package/docs/crawl/docs/_getting-started/concorde-outside.html +58 -0
- package/docs/{docs/getting-started → crawl/docs/_getting-started}/create-a-component.html +14 -11
- package/docs/{docs/getting-started → crawl/docs/_getting-started}/my-first-component.html +22 -13
- package/docs/crawl/docs/_getting-started/my-first-subscriber.html +110 -0
- package/docs/crawl/docs/_getting-started/pubsub.html +62 -0
- package/docs/crawl/docs/_getting-started/start.html +76 -0
- package/docs/{docs/getting-started → crawl/docs/_getting-started}/theming.html +9 -10
- package/docs/{docs/misc → crawl/docs/_misc}/api-configuration.html +18 -18
- package/docs/{docs/misc → crawl/docs/_misc}/dataProviderKey.html +11 -12
- package/docs/{docs/misc → crawl/docs/_misc}/docs-mock-api.html +28 -25
- package/docs/{docs/misc → crawl/docs/_misc}/dynamic-path.html +15 -16
- package/docs/{docs/misc → crawl/docs/_misc}/endpoint.html +15 -16
- package/docs/crawl/docs/_misc/formelement-subscriber-removal.html +138 -0
- package/docs/{docs/misc → crawl/docs/_misc}/html-integration.html +15 -12
- package/docs/crawl/hubs/decorators.html +547 -0
- package/docs/crawl/hubs/form-components.html +381 -0
- package/docs/crawl/hubs/remote-data.html +452 -0
- package/docs/crawl/index.html +103 -0
- package/docs/docs-mock-api-sw.js +223 -58
- package/docs/docs-mock-api-sw.js.map +4 -4
- package/docs/index.html +68 -95
- package/docs/llms-full.txt +3610 -2231
- package/docs/llms.txt +86 -94
- package/docs/robots.txt +10 -11
- package/docs/sitemap.xml +84 -78
- package/docs/src/core/components/functional/date/date.md +231 -91
- package/docs/src/core/components/functional/fetch/fetch.md +83 -66
- package/docs/src/core/components/functional/if/if.md +3 -28
- package/docs/src/core/components/functional/jsonata/jsonata.md +37 -42
- package/docs/src/core/components/functional/list/list.md +43 -99
- package/docs/src/core/components/functional/mix/mix.md +26 -31
- package/docs/src/core/components/functional/queue/queue.md +61 -40
- package/docs/src/core/components/functional/router/router.md +65 -64
- package/docs/src/core/components/functional/sdui/component-catalog.gaps.json +1 -1
- package/docs/src/core/components/functional/sdui/component-catalog.json +1 -1
- package/docs/src/core/components/functional/sdui/example.json +86 -19
- package/docs/src/core/components/functional/sdui/sdui-extension-points.md +1 -1
- package/docs/src/core/components/functional/sdui/sdui.md +358 -187
- package/docs/src/core/components/functional/states/states.md +61 -86
- package/docs/src/core/components/functional/submit/submit.md +72 -64
- package/docs/src/core/components/functional/value/value.md +22 -34
- package/docs/src/core/components/ui/alert/alert.md +58 -48
- package/docs/src/core/components/ui/badge/badge.md +34 -27
- package/docs/src/core/components/ui/button/button.md +106 -139
- package/docs/src/core/components/ui/captcha/captcha.md +32 -17
- package/docs/src/core/components/ui/card/card.md +6 -6
- package/docs/src/core/components/ui/divider/divider.md +11 -8
- package/docs/src/core/components/ui/form/checkbox/checkbox.md +42 -90
- package/docs/src/core/components/ui/form/fieldset/fieldset.md +75 -31
- package/docs/src/core/components/ui/form/form-actions/form-actions.md +16 -14
- package/docs/src/core/components/ui/form/form-layout/form-layout.md +29 -19
- package/docs/src/core/components/ui/form/input/input.md +95 -95
- package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +29 -0
- package/docs/src/core/components/ui/form/radio/radio.md +34 -55
- package/docs/src/core/components/ui/form/select/select.md +45 -60
- package/docs/src/core/components/ui/form/switch/switch.md +34 -57
- package/docs/src/core/components/ui/form/textarea/textarea.md +38 -55
- package/docs/src/core/components/ui/group/group.md +33 -35
- package/docs/src/core/components/ui/icon/icon.md +4 -4
- package/docs/src/core/components/ui/image/image.md +107 -41
- package/docs/src/core/components/ui/link/link.md +6 -6
- package/docs/src/core/components/ui/loader/loader.md +55 -30
- package/docs/src/core/components/ui/menu/menu.md +27 -27
- package/docs/src/core/components/ui/modal/modal.md +96 -72
- package/docs/src/core/components/ui/pop/pop.md +158 -80
- package/docs/src/core/components/ui/progress/progress.md +2 -2
- package/docs/src/core/components/ui/table/table.md +255 -91
- package/docs/src/core/components/ui/theme/theme.md +36 -11
- package/docs/src/core/components/ui/toast/toast.md +117 -48
- package/docs/src/core/components/ui/tooltip/tooltip.md +62 -27
- package/docs/{docs/getting-started → src/docs/_core-concept}/dataFlow.md +18 -17
- package/docs/src/docs/_core-concept/overview.md +57 -0
- package/docs/src/docs/_core-concept/subscriber.md +75 -0
- package/docs/src/docs/_decorators/ancestor-attribute.md +128 -0
- package/docs/src/docs/_decorators/auto-subscribe.md +202 -0
- package/docs/src/docs/_decorators/bind.md +167 -0
- package/docs/src/docs/_decorators/get.md +162 -0
- package/docs/src/docs/_decorators/handle.md +175 -0
- package/docs/src/docs/_decorators/on-assign.md +390 -0
- package/docs/src/docs/_decorators/patch.md +70 -0
- package/docs/src/docs/_decorators/post.md +146 -0
- package/docs/src/docs/_decorators/publish.md +55 -0
- package/docs/src/docs/_decorators/put.md +70 -0
- package/docs/src/docs/_decorators/subscribe.md +100 -0
- package/docs/src/docs/_decorators/wait-for-ancestors.md +163 -0
- package/docs/src/docs/_directives/sub.md +91 -0
- package/docs/src/docs/{getting-started → _getting-started}/ai-agents.md +3 -3
- package/docs/src/docs/_getting-started/concorde-manual-install.md +133 -0
- package/docs/src/docs/_getting-started/concorde-outside.md +33 -0
- package/docs/{docs/getting-started → src/docs/_getting-started}/create-a-component.md +6 -3
- package/docs/src/docs/{getting-started → _getting-started}/my-first-component.md +14 -5
- package/docs/src/docs/_getting-started/my-first-subscriber.md +120 -0
- package/docs/src/docs/_getting-started/pubsub.md +37 -0
- package/docs/src/docs/{getting-started → _getting-started}/start.md +13 -1
- package/docs/src/docs/{getting-started → _getting-started}/theming.md +2 -2
- package/docs/src/docs/{misc → _misc}/api-configuration.md +12 -10
- package/docs/src/docs/{misc → _misc}/dataProviderKey.md +4 -4
- package/docs/{docs/misc → src/docs/_misc}/docs-mock-api.md +13 -12
- package/docs/{docs/misc → src/docs/_misc}/dynamic-path.md +8 -8
- package/docs/{docs/misc → src/docs/_misc}/endpoint.md +8 -8
- package/docs/src/docs/_misc/formelement-subscriber-removal.md +115 -0
- package/docs/{docs/misc → src/docs/_misc}/html-integration.md +5 -4
- package/docs/src/docs/search/docs-search.json +4375 -2420
- package/docs/src/tsconfig.declarations.json +22 -0
- package/docs/src/tsconfig.emit.json +23 -0
- package/docs/src/tsconfig.json +2 -23
- package/gitlab/job_github_mirror.sh +39 -0
- package/index.html +89 -0
- package/package.json +22 -4
- package/public/img/og-concorde.jpg +0 -0
- package/scripts/ai-init.mjs +104 -379
- package/scripts/ai-init.test.mjs +81 -239
- package/scripts/docs-markdown-vite-plugin.ts +11 -5
- package/scripts/docs-public-url.mjs +6 -0
- package/scripts/docs.htaccess +7 -0
- package/scripts/generate-cem.mjs +244 -0
- package/scripts/generate-cem.test.mjs +122 -0
- package/scripts/generate-crawl-docs.mjs +29 -5
- package/scripts/generate-sdui-catalog.mjs +11 -10
- package/scripts/github-export.mjs +150 -0
- package/scripts/post-build-docs.js +3 -0
- package/scripts/post-build.mjs +0 -0
- package/scripts/pre-publish.mjs +2 -1
- package/scripts/site-pages.mjs +212 -0
- package/scripts/site-pages.test.mjs +116 -0
- package/site-pages/about.md +22 -0
- package/site-pages/privacy.md +32 -0
- package/src/core/components/functional/date/date.md +1 -1
- package/src/core/components/functional/example/example.ts +3 -1
- package/src/core/components/functional/if/if.ts +0 -0
- package/src/core/components/functional/list/list.demo.ts +0 -0
- package/src/core/components/functional/list/list.md +6 -2
- package/src/core/components/functional/list/list.ts +17 -4
- package/src/core/components/functional/mix/mix.md +0 -0
- package/src/core/components/functional/router/router.demo.ts +0 -0
- package/src/core/components/functional/router/router.ts +0 -0
- package/src/core/components/functional/sdui/SDUIDescriptorTransformer.ts +0 -0
- package/src/core/components/functional/sdui/component-catalog.gaps.json +1 -1
- package/src/core/components/functional/sdui/component-catalog.json +2 -2
- package/src/core/components/functional/sdui/example.json +0 -0
- package/src/core/components/functional/sdui/sdui-immutable.spec.ts +74 -0
- package/src/core/components/functional/sdui/sdui.ts +33 -8
- package/src/core/components/functional/sdui/types.ts +0 -0
- package/src/core/components/functional/states/states.ts +0 -0
- package/src/core/components/functional/submit/submit.spec.ts +60 -0
- package/src/core/components/functional/submit/submit.ts +6 -3
- package/src/core/components/functional/subscriber/subscriber.ts +0 -0
- package/src/core/components/functional/value/value.md +0 -0
- package/src/core/components/functional/value/value.ts +0 -0
- package/src/core/components/ui/_css/size.ts +0 -0
- package/src/core/components/ui/captcha/altchaStyles.ts +0 -0
- package/src/core/components/ui/card/card.md +1 -1
- package/src/core/components/ui/form/input/input.md +19 -1
- package/src/core/components/ui/form/input/input.ts +8 -2
- package/src/core/components/ui/form/radio/radio.md +14 -0
- package/src/core/components/ui/image/image.md +17 -17
- package/src/core/components/ui/modal/modal.md +2 -2
- package/src/core/decorators/subscriber/bind.ts +9 -14
- package/src/core/decorators/subscriber/subscribe.ts +15 -9
- package/src/core/decorators/subscriber/subscribe.type-test.ts +14 -5
- package/src/core/mixins/Subscriber.ts +0 -0
- package/src/core/utils/DataBindObserver.ts +0 -0
- package/src/core/utils/Objects.ts +0 -0
- package/src/core/utils/PublisherProxy.ts +166 -125
- package/src/core/utils/deepReadonly.spec.ts +66 -0
- package/src/core/utils/deepReadonly.ts +71 -0
- package/src/core/utils/immutableValue.spec.ts +54 -0
- package/src/core/utils/immutableValue.ts +102 -0
- package/src/core/utils/nestedDotForm.spec.ts +88 -0
- package/src/core/utils/publisherImmutable.spec.ts +76 -0
- package/src/core/utils/route.ts +0 -0
- package/src/core/utils/strictGet.spec.ts +61 -0
- package/src/core/utils/strictGet.ts +102 -0
- package/src/css.d.ts +19 -0
- package/src/docs/components/docs-demo-sources.ts +3 -3
- package/src/docs/decorators/bind.md +23 -24
- package/src/docs/docs-location.spec.ts +10 -0
- package/src/docs/docs-location.ts +10 -0
- package/src/docs/example/decorators-demo-bind-demos.ts +11 -28
- package/src/docs/example/docs-toggle-demos.ts +1 -1
- package/src/docs/example/users.ts +0 -0
- package/src/docs/getting-started/ai-agents.md +29 -55
- package/src/docs/getting-started/dataFlow.md +2 -2
- package/src/docs/home/home.ts +3 -1
- package/src/docs/misc/publisher-get-immutability-docs-crawl.json +902 -0
- package/src/docs/misc/publisher-get-immutability-docs-recheck.json +13 -0
- package/src/docs/misc/publisher-get-immutability-study.md +229 -0
- package/src/docs/navigation/navigation.ts +5 -1
- package/src/docs/search/docs-search.json +358 -58
- package/src/docs/search/markdown-renderer.ts +6 -1
- package/src/docs/tailwind/css/docs/content.css +4 -5
- package/src/tsconfig.declarations.json +22 -0
- package/src/tsconfig.emit.json +23 -0
- package/src/tsconfig.json +53 -5
- package/src/tsconfig.tsbuildinfo +1 -1
- package/.taste-skill/after/desktop-fold.jpg +0 -0
- package/.taste-skill/after/desktop-page-01.jpg +0 -0
- package/.taste-skill/after/desktop-page-02.jpg +0 -0
- package/.taste-skill/after/desktop-page-03.jpg +0 -0
- package/.taste-skill/after/mobile-fold.jpg +0 -0
- package/.taste-skill/after/mobile-page-01.jpg +0 -0
- package/.taste-skill/after/report.json +0 -4103
- package/.taste-skill/after/tablet-fold.jpg +0 -0
- package/.taste-skill/after/tablet-page-01.jpg +0 -0
- package/.taste-skill/after/tablet-page-02.jpg +0 -0
- package/.taste-skill/before/desktop-fold.jpg +0 -0
- package/.taste-skill/before/desktop-page-01.jpg +0 -0
- package/.taste-skill/before/desktop-page-02.jpg +0 -0
- package/.taste-skill/before/mobile-fold.jpg +0 -0
- package/.taste-skill/before/mobile-page-01.jpg +0 -0
- package/.taste-skill/before/report.json +0 -2556
- package/.taste-skill/before/tablet-fold.jpg +0 -0
- package/.taste-skill/before/tablet-page-01.jpg +0 -0
- package/.taste-skill/direction.json +0 -510
- package/.taste-skill/notes.md +0 -84
- package/.taste-skill/prod/desktop-fold.jpg +0 -0
- package/.taste-skill/prod/desktop-page-01.jpg +0 -0
- package/.taste-skill/prod/desktop-page-02.jpg +0 -0
- package/.taste-skill/prod/desktop-page-03.jpg +0 -0
- package/.taste-skill/prod/mobile-fold.jpg +0 -0
- package/.taste-skill/prod/mobile-page-01.jpg +0 -0
- package/.taste-skill/prod/mobile-page-02.jpg +0 -0
- package/.taste-skill/prod/report.json +0 -1973
- package/.taste-skill/prod/tablet-fold.jpg +0 -0
- package/.taste-skill/prod/tablet-page-01.jpg +0 -0
- package/.taste-skill/prod/tablet-page-02.jpg +0 -0
- package/AUDIT.md +0 -228
- package/PLAN-AMELIORATION.md +0 -220
- package/PLAN-DOCS-AGENTS.md +0 -80
- package/ai/AGENTS.md +0 -56
- package/ai/cursor/rules/concorde-menu.mdc +0 -15
- package/ai/cursor/rules/concorde-scope.mdc +0 -14
- package/ai/cursor/rules/concorde-theme.mdc +0 -13
- package/ai/cursor/rules/concorde.mdc +0 -66
- package/ai/jetbrains/rules/concorde.md +0 -53
- package/ai/skills/concorde-get-set-dp/SKILL.md +0 -194
- package/ai/skills/concorde-imports/SKILL.md +0 -78
- package/ai/skills/concorde-menu/SKILL.md +0 -74
- package/ai/skills/concorde-scope/SKILL.md +0 -70
- package/ai/skills/concorde-theme/SKILL.md +0 -57
- package/docs/.htaccess +0 -75
- package/docs/404.html +0 -15
- package/docs/404.md +0 -5
- package/docs/assets/index-BgG6FFPt.js +0 -11765
- package/docs/assets/index-x89XINSG.css +0 -1
- package/docs/core/components/functional/date/date.html +0 -211
- package/docs/core/components/functional/date/date.md +0 -150
- package/docs/core/components/functional/fetch/fetch.html +0 -180
- package/docs/core/components/functional/fetch/fetch.md +0 -108
- package/docs/core/components/functional/if/if.html +0 -65
- package/docs/core/components/functional/if/if.md +0 -34
- package/docs/core/components/functional/jsonata/jsonata.html +0 -110
- package/docs/core/components/functional/jsonata/jsonata.md +0 -61
- package/docs/core/components/functional/list/list.html +0 -198
- package/docs/core/components/functional/list/list.md +0 -121
- package/docs/core/components/functional/mix/mix.html +0 -78
- package/docs/core/components/functional/mix/mix.md +0 -46
- package/docs/core/components/functional/queue/queue.html +0 -155
- package/docs/core/components/functional/queue/queue.md +0 -51
- package/docs/core/components/functional/router/router.html +0 -154
- package/docs/core/components/functional/router/router.md +0 -93
- package/docs/core/components/functional/sdui/sdui-extension-points.md +0 -94
- package/docs/core/components/functional/sdui/sdui.html +0 -397
- package/docs/core/components/functional/sdui/sdui.md +0 -307
- package/docs/core/components/functional/states/states.html +0 -141
- package/docs/core/components/functional/states/states.md +0 -112
- package/docs/core/components/functional/submit/submit.html +0 -178
- package/docs/core/components/functional/submit/submit.md +0 -106
- package/docs/core/components/functional/subscriber/subscriber.md +0 -91
- package/docs/core/components/functional/translation/translation.html +0 -60
- package/docs/core/components/functional/translation/translation.md +0 -38
- package/docs/core/components/functional/value/value.html +0 -81
- package/docs/core/components/functional/value/value.md +0 -47
- package/docs/core/components/ui/alert/alert.md +0 -186
- package/docs/core/components/ui/alert-messages/alert-messages.md +0 -85
- package/docs/core/components/ui/badge/badge.md +0 -181
- package/docs/core/components/ui/button/button.html +0 -309
- package/docs/core/components/ui/button/button.md +0 -216
- package/docs/core/components/ui/captcha/captcha.html +0 -80
- package/docs/core/components/ui/captcha/captcha.md +0 -56
- package/docs/core/components/ui/card/card.md +0 -215
- package/docs/core/components/ui/divider/divider.md +0 -126
- package/docs/core/components/ui/form/checkbox/checkbox.html +0 -200
- package/docs/core/components/ui/form/checkbox/checkbox.md +0 -125
- package/docs/core/components/ui/form/fieldset/fieldset.html +0 -238
- package/docs/core/components/ui/form/fieldset/fieldset.md +0 -161
- package/docs/core/components/ui/form/form-actions/form-actions.md +0 -98
- package/docs/core/components/ui/form/form-layout/form-layout.html +0 -115
- package/docs/core/components/ui/form/form-layout/form-layout.md +0 -109
- package/docs/core/components/ui/form/input/input.html +0 -235
- package/docs/core/components/ui/form/input/input.md +0 -142
- package/docs/core/components/ui/form/radio/radio.html +0 -144
- package/docs/core/components/ui/form/radio/radio.md +0 -78
- package/docs/core/components/ui/form/select/select.html +0 -178
- package/docs/core/components/ui/form/select/select.md +0 -86
- package/docs/core/components/ui/form/switch/switch.html +0 -146
- package/docs/core/components/ui/form/switch/switch.md +0 -80
- package/docs/core/components/ui/form/textarea/textarea.html +0 -162
- package/docs/core/components/ui/form/textarea/textarea.md +0 -82
- package/docs/core/components/ui/group/group.html +0 -113
- package/docs/core/components/ui/group/group.md +0 -77
- package/docs/core/components/ui/icon/icon.md +0 -214
- package/docs/core/components/ui/image/image.html +0 -194
- package/docs/core/components/ui/image/image.md +0 -167
- package/docs/core/components/ui/link/link.md +0 -81
- package/docs/core/components/ui/loader/loader.html +0 -114
- package/docs/core/components/ui/loader/loader.md +0 -87
- package/docs/core/components/ui/menu/menu.md +0 -370
- package/docs/core/components/ui/modal/modal.html +0 -324
- package/docs/core/components/ui/modal/modal.md +0 -211
- package/docs/core/components/ui/pop/pop.html +0 -250
- package/docs/core/components/ui/pop/pop.md +0 -216
- package/docs/core/components/ui/progress/progress.md +0 -109
- package/docs/core/components/ui/table/table.html +0 -333
- package/docs/core/components/ui/table/table.md +0 -267
- package/docs/core/components/ui/theme/theme.html +0 -116
- package/docs/core/components/ui/theme/theme.md +0 -97
- package/docs/core/components/ui/toast/toast.html +0 -185
- package/docs/core/components/ui/toast/toast.md +0 -147
- package/docs/core/components/ui/tooltip/tooltip.html +0 -112
- package/docs/core/components/ui/tooltip/tooltip.md +0 -89
- package/docs/docs/decorators/ancestor-attribute.html +0 -71
- package/docs/docs/decorators/ancestor-attribute.md +0 -59
- package/docs/docs/decorators/auto-subscribe.html +0 -61
- package/docs/docs/decorators/auto-subscribe.md +0 -40
- package/docs/docs/decorators/bind.html +0 -86
- package/docs/docs/decorators/bind.md +0 -77
- package/docs/docs/decorators/get.html +0 -166
- package/docs/docs/decorators/get.md +0 -160
- package/docs/docs/decorators/handle.html +0 -139
- package/docs/docs/decorators/handle.md +0 -119
- package/docs/docs/decorators/on-assign.html +0 -72
- package/docs/docs/decorators/on-assign.md +0 -63
- package/docs/docs/decorators/patch.html +0 -76
- package/docs/docs/decorators/patch.md +0 -44
- package/docs/docs/decorators/post.html +0 -150
- package/docs/docs/decorators/post.md +0 -134
- package/docs/docs/decorators/publish.html +0 -74
- package/docs/docs/decorators/publish.md +0 -56
- package/docs/docs/decorators/put.html +0 -83
- package/docs/docs/decorators/put.md +0 -51
- package/docs/docs/decorators/subscribe.html +0 -91
- package/docs/docs/decorators/subscribe.md +0 -87
- package/docs/docs/decorators/wait-for-ancestors.html +0 -82
- package/docs/docs/decorators/wait-for-ancestors.md +0 -76
- package/docs/docs/directives/sub.html +0 -79
- package/docs/docs/directives/sub.md +0 -75
- package/docs/docs/directives/t.html +0 -91
- package/docs/docs/directives/t.md +0 -73
- package/docs/docs/getting-started/ai-agents.md +0 -76
- package/docs/docs/getting-started/components.html +0 -27
- package/docs/docs/getting-started/components.md +0 -5
- package/docs/docs/getting-started/installation.html +0 -69
- package/docs/docs/getting-started/installation.md +0 -36
- package/docs/docs/getting-started/my-first-component.md +0 -227
- package/docs/docs/getting-started/start.html +0 -47
- package/docs/docs/getting-started/start.md +0 -35
- package/docs/docs/getting-started/theming.md +0 -253
- package/docs/docs/index.html +0 -99
- package/docs/docs/misc/api-configuration.md +0 -79
- package/docs/docs/misc/dataProviderKey.md +0 -168
- package/docs/hubs/decorators.html +0 -443
- package/docs/hubs/form-components.html +0 -404
- package/docs/hubs/remote-data.html +0 -371
- package/docs/img/archive/concorde-001-2cv.webp +0 -0
- package/docs/img/archive/concorde-red-arrows-2002-poster.jpg +0 -0
- package/docs/img/archive/concorde-red-arrows-2002.webm +0 -0
- package/docs/img/archive/credits.json +0 -28
- package/docs/index.md +0 -22
- package/docs/src/core/components/functional/translation/translation.md +0 -38
- package/docs/src/docs/decorators/ancestor-attribute.md +0 -59
- package/docs/src/docs/decorators/auto-subscribe.md +0 -40
- package/docs/src/docs/decorators/bind.md +0 -77
- package/docs/src/docs/decorators/get.md +0 -160
- package/docs/src/docs/decorators/handle.md +0 -119
- package/docs/src/docs/decorators/on-assign.md +0 -63
- package/docs/src/docs/decorators/patch.md +0 -44
- package/docs/src/docs/decorators/post.md +0 -134
- package/docs/src/docs/decorators/publish.md +0 -56
- package/docs/src/docs/decorators/put.md +0 -51
- package/docs/src/docs/decorators/subscribe.md +0 -87
- package/docs/src/docs/decorators/wait-for-ancestors.md +0 -76
- package/docs/src/docs/directives/sub.md +0 -75
- package/docs/src/docs/directives/t.md +0 -73
- package/docs/src/docs/getting-started/components.md +0 -5
- package/docs/src/docs/getting-started/create-a-component.md +0 -136
- package/docs/src/docs/getting-started/dataFlow.md +0 -87
- package/docs/src/docs/getting-started/installation.md +0 -36
- package/docs/src/docs/misc/docs-mock-api.md +0 -62
- package/docs/src/docs/misc/dynamic-path.md +0 -71
- package/docs/src/docs/misc/endpoint.md +0 -45
- package/docs/src/docs/misc/html-integration.md +0 -14
- /package/{docs → dist}/img/concorde/plane_01.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_02.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_03.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_04.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_05.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_06.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_07.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_08.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_09.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_10.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_11.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_12.jpg +0 -0
- /package/{docs → dist}/img/concorde/plane_13.jpg +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { STATIC_PAGES, homepageJsonLd } from "./site-pages.mjs";
|
|
7
|
+
import {
|
|
8
|
+
generateHomepageMarkdown,
|
|
9
|
+
generateHtaccess,
|
|
10
|
+
generateSitemap,
|
|
11
|
+
writeAgentSiteArtifacts,
|
|
12
|
+
} from "./generate-crawl-docs.mjs";
|
|
13
|
+
|
|
14
|
+
function stripTags(html) {
|
|
15
|
+
return html
|
|
16
|
+
.replace(/<[^>]+>/g, " ")
|
|
17
|
+
.replace(/\s+/g, " ")
|
|
18
|
+
.trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("homepage JSON-LD has SoftwareApplication and complete Organization", () => {
|
|
22
|
+
const ld = homepageJsonLd();
|
|
23
|
+
assert.equal(ld["@context"], "https://schema.org");
|
|
24
|
+
const byType = Object.fromEntries(ld["@graph"].map((n) => [n["@type"], n]));
|
|
25
|
+
const app = byType.SoftwareApplication;
|
|
26
|
+
assert.ok(app.name && app.description && app.url && app.offers);
|
|
27
|
+
assert.equal(app.publisher["@id"], byType.Organization["@id"]);
|
|
28
|
+
const org = byType.Organization;
|
|
29
|
+
assert.equal(org.url, "https://supersoniks.com/");
|
|
30
|
+
assert.equal(org.address["@type"], "PostalAddress");
|
|
31
|
+
assert.equal(org.address.addressLocality, "Tours");
|
|
32
|
+
assert.equal(org.contactPoint["@type"], "ContactPoint");
|
|
33
|
+
assert.ok(org.contactPoint.contactType && org.contactPoint.telephone);
|
|
34
|
+
assert.ok(org.sameAs.every((u) => u.startsWith("https://")));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("source index.html embeds the generated JSON-LD and Open Graph tags", () => {
|
|
38
|
+
const html = fs.readFileSync(path.resolve("index.html"), "utf8");
|
|
39
|
+
const block = html.match(
|
|
40
|
+
/<script type="application\/ld\+json">([\s\S]*?)<\/script>/,
|
|
41
|
+
);
|
|
42
|
+
assert.ok(block, "JSON-LD script missing");
|
|
43
|
+
assert.deepEqual(JSON.parse(block[1]), homepageJsonLd());
|
|
44
|
+
for (const prop of ["og:type", "og:title", "og:description", "og:url", "og:image"]) {
|
|
45
|
+
assert.match(html, new RegExp(`<meta property="${prop}" content="[^"]+"`), prop);
|
|
46
|
+
}
|
|
47
|
+
assert.match(
|
|
48
|
+
html,
|
|
49
|
+
/<meta property="og:image" content="https:\/\/concorde\.supersoniks\.org\/img\/og-concorde\.jpg"/,
|
|
50
|
+
);
|
|
51
|
+
assert.ok(fs.existsSync(path.resolve("public/img/og-concorde.jpg")));
|
|
52
|
+
assert.match(html, /<meta name="twitter:card" content="summary_large_image"/);
|
|
53
|
+
assert.match(html, /href="\/about"/);
|
|
54
|
+
assert.match(html, /href="\/privacy"/);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("site page Markdown is read from site-pages/", () => {
|
|
58
|
+
assert.deepEqual(
|
|
59
|
+
STATIC_PAGES.map((p) => p.slug),
|
|
60
|
+
["about", "privacy"],
|
|
61
|
+
);
|
|
62
|
+
for (const { slug, markdown } of STATIC_PAGES) {
|
|
63
|
+
assert.equal(
|
|
64
|
+
markdown,
|
|
65
|
+
fs.readFileSync(path.resolve(`site-pages/${slug}.md`), "utf8"),
|
|
66
|
+
);
|
|
67
|
+
assert.match(markdown, /^# /);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("site pages are SPA shells with their own metadata and inline content", () => {
|
|
72
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "concorde-pages-"));
|
|
73
|
+
try {
|
|
74
|
+
writeAgentSiteArtifacts(dir);
|
|
75
|
+
for (const { slug } of STATIC_PAGES) {
|
|
76
|
+
const md = fs.readFileSync(path.join(dir, `${slug}.md`), "utf8");
|
|
77
|
+
const html = fs.readFileSync(path.join(dir, `${slug}.html`), "utf8");
|
|
78
|
+
assert.ok(md.length >= 500, `${slug}.md too short (${md.length})`);
|
|
79
|
+
const section = html.match(
|
|
80
|
+
/<section id="docs-agent-content"[^>]*>([\s\S]*?)<\/section>/,
|
|
81
|
+
);
|
|
82
|
+
assert.ok(section, `${slug}.html page section missing`);
|
|
83
|
+
assert.ok(stripTags(section[1]).length >= 500, `${slug}.html too short`);
|
|
84
|
+
assert.match(section[1], /<h1/);
|
|
85
|
+
assert.doesNotMatch(section[1], /<h1>Concorde by Supersoniks<\/h1>/);
|
|
86
|
+
assert.match(html, /<sonic-router>/, "SPA shell expected");
|
|
87
|
+
const url = `https://concorde\\.supersoniks\\.org/${slug}`;
|
|
88
|
+
assert.match(html, new RegExp(`<link rel="canonical" href="${url}" />`));
|
|
89
|
+
assert.match(html, new RegExp(`<meta property="og:url" content="${url}"`));
|
|
90
|
+
assert.match(html, new RegExp(`href="/${slug}\\.md"`));
|
|
91
|
+
assert.equal((html.match(/<title>/g) || []).length, 1);
|
|
92
|
+
const ld = JSON.parse(
|
|
93
|
+
html.match(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/)[1],
|
|
94
|
+
);
|
|
95
|
+
assert.equal(ld.url, `https://concorde.supersoniks.org/${slug}`);
|
|
96
|
+
assert.equal(ld.publisher.address.addressLocality, "Tours");
|
|
97
|
+
}
|
|
98
|
+
} finally {
|
|
99
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("sitemap and homepage Markdown link the site pages", () => {
|
|
104
|
+
const xml = generateSitemap([], []);
|
|
105
|
+
const md = generateHomepageMarkdown();
|
|
106
|
+
for (const slug of ["about", "privacy"]) {
|
|
107
|
+
assert.match(xml, new RegExp(`<loc>https://concorde\\.supersoniks\\.org/${slug}</loc>`));
|
|
108
|
+
assert.match(md, new RegExp(`https://concorde\\.supersoniks\\.org/${slug}\\)`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("htaccess serves site pages as HTML or Markdown", () => {
|
|
113
|
+
const conf = generateHtaccess();
|
|
114
|
+
assert.match(conf, /\^\(about\|privacy\)\$ \/\$1\.md \[END\]/);
|
|
115
|
+
assert.match(conf, /\^\(about\|privacy\)\$ \/\$1\.html \[END\]/);
|
|
116
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# About Concorde
|
|
2
|
+
|
|
3
|
+
Concorde is an open-source web components framework built on [Lit](https://lit.dev). It is designed, written and maintained by [Supersoniks](https://supersoniks.com/), a company based in Tours, France, which uses it in production.
|
|
4
|
+
|
|
5
|
+
## Why it exists
|
|
6
|
+
|
|
7
|
+
Supersoniks works on many stacks: PHP back offices, static pages, React and Vue front ends. Rewriting the same buttons, forms and data lists for each stack was slow and led to inconsistent interfaces. Concorde solves this with standard custom elements (`sonic-*`) that work the same way in any host page, plus a small data layer (DataProvider, decorators) that connects components to each other and to REST APIs without a framework.
|
|
8
|
+
|
|
9
|
+
## What it includes
|
|
10
|
+
|
|
11
|
+
- UI components: buttons, inputs, selects, checkboxes, modals, toasts, cards, menus, tooltips and more.
|
|
12
|
+
- Data components: lists, queues, fetch, submit, router, states and server-driven UI from JSON.
|
|
13
|
+
- Lit decorators such as `@get`, `@post`, `@subscribe` and `@bind`.
|
|
14
|
+
- Theming through CSS custom properties, with light and dark modes.
|
|
15
|
+
|
|
16
|
+
## Licence and distribution
|
|
17
|
+
|
|
18
|
+
Concorde is published on npm as [`@supersoniks/concorde`](https://www.npmjs.com/package/@supersoniks/concorde) under the MIT licence. This site (https://concorde.supersoniks.org) is its official documentation.
|
|
19
|
+
|
|
20
|
+
## Publisher
|
|
21
|
+
|
|
22
|
+
Supersoniks, 22 rue du Docteur Bretonneau, 37000 Tours, France · +33 2 47 68 93 25 · [contact Supersoniks](https://supersoniks.com/un-projet). See also the [privacy notice](/privacy).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Privacy notice
|
|
2
|
+
|
|
3
|
+
This notice covers the Concorde documentation site, https://concorde.supersoniks.org, published by [Supersoniks](https://supersoniks.com/), 22 rue du Docteur Bretonneau, 37000 Tours, France.
|
|
4
|
+
|
|
5
|
+
## What this site does not do
|
|
6
|
+
|
|
7
|
+
- It has no user accounts, sign-up or login.
|
|
8
|
+
- It sets no cookies and runs no analytics, advertising or tracking scripts.
|
|
9
|
+
- It has no forms that send personal data to Supersoniks.
|
|
10
|
+
|
|
11
|
+
## What is stored in your browser
|
|
12
|
+
|
|
13
|
+
- `sonicTheme` in localStorage remembers your light or dark theme choice. It never leaves your browser.
|
|
14
|
+
- A service worker serves the mock API used by live examples (`/docs-mock-api/…`). It runs locally in your browser.
|
|
15
|
+
- Some examples demonstrate features that store values in localStorage (for example dismissed alerts or cached labels). They only run when you open those pages.
|
|
16
|
+
|
|
17
|
+
## Third-party services
|
|
18
|
+
|
|
19
|
+
To display pages, your browser loads files from these providers, which receive your IP address and browser details as part of any web request:
|
|
20
|
+
|
|
21
|
+
- Google Fonts (fonts.googleapis.com, fonts.gstatic.com) for the Schibsted Grotesk font.
|
|
22
|
+
- jsDelivr (cdn.jsdelivr.net) for the search and Markdown libraries.
|
|
23
|
+
- cdn.supersoniks.com for icons.
|
|
24
|
+
- A few live examples call public demo APIs (geo.api.gouv.fr, reqres.in, i.pravatar.cc) when you open them.
|
|
25
|
+
|
|
26
|
+
## Server logs
|
|
27
|
+
|
|
28
|
+
Like any web server, the host records technical request data (IP address, URL, date, user agent) to operate and secure the site.
|
|
29
|
+
|
|
30
|
+
## Your rights
|
|
31
|
+
|
|
32
|
+
Under the GDPR you can ask for access to, correction or deletion of data about you. Contact Supersoniks through [supersoniks.com/un-projet](https://supersoniks.com/un-projet), by phone at +33 2 47 68 93 25, or by mail at 22 rue du Docteur Bretonneau, 37000 Tours, France. See also the [Supersoniks privacy policy](https://supersoniks.com/politique-de-confidentialite) and [legal notice](https://supersoniks.com/mentions-legales).
|
|
@@ -116,7 +116,7 @@ A child `<template data-value="<part>">` wraps a part in your own markup: `weekd
|
|
|
116
116
|
<template data-value="year"><span class="block text-lg font-bold leading-tight"></span></template>
|
|
117
117
|
<template data-value="to"><span class="block my-1 text-3xl leading-none">↓</span></template>
|
|
118
118
|
</sonic-date>
|
|
119
|
-
<sonic-image src="img/concorde/plane_03.jpg" alt="Concorde specifications board" ratio="1/1" cover></sonic-image>
|
|
119
|
+
<sonic-image src="/img/concorde/plane_03.jpg" alt="Concorde specifications board" ratio="1/1" cover></sonic-image>
|
|
120
120
|
</div>
|
|
121
121
|
</template>
|
|
122
122
|
</sonic-code>
|
|
@@ -2,6 +2,7 @@ import { html, LitElement } from "lit";
|
|
|
2
2
|
import { customElement, property, state } from "lit/decorators.js";
|
|
3
3
|
import { Endpoint } from "@supersoniks/concorde/utils";
|
|
4
4
|
import { DataProviderKey } from "@supersoniks/concorde/core/utils/dataProviderKey";
|
|
5
|
+
import type { Frozen } from "@supersoniks/concorde/utils/deepReadonly";
|
|
5
6
|
import {
|
|
6
7
|
get,
|
|
7
8
|
publish,
|
|
@@ -19,6 +20,7 @@ const endpoint = new Endpoint<City[], { limit: number }>(
|
|
|
19
20
|
/** Même path que l’endpoint ; type = charge utile `@get` pour `@publish`. */
|
|
20
21
|
const dpKey = new DataProviderKey<ApiResult<City[]>>(endpoint.path);
|
|
21
22
|
|
|
23
|
+
|
|
22
24
|
const tagName = "sonic-example"; // For Astro.build
|
|
23
25
|
@customElement(tagName)
|
|
24
26
|
export class SonicComponent extends LitElement {
|
|
@@ -31,7 +33,7 @@ export class SonicComponent extends LitElement {
|
|
|
31
33
|
|
|
32
34
|
@state()
|
|
33
35
|
@subscribe(dpKey.result)
|
|
34
|
-
geoCommunesResult?: City[]
|
|
36
|
+
geoCommunesResult?: Frozen<City[]>;
|
|
35
37
|
|
|
36
38
|
render() {
|
|
37
39
|
return html` <span part="api-get-demo">
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# List
|
|
2
2
|
|
|
3
|
-
**`sonic-list`** renders one row per entry of its `props` array, either set directly or loaded with `fetch`.
|
|
3
|
+
**`sonic-list`** renders one row per entry of its `props` array, either set directly or loaded with `fetch`. In TypeScript, prefer the `.items` row renderer (with `.separator` · `.noItems` · `.skeleton`); `<template>` children are for plain HTML hosts without Lit. Each row gets its own data provider at `<dataProvider>/list-item/<index>`.
|
|
4
4
|
|
|
5
5
|
## Basic example
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ Add `fetch` to load `props` from `serviceURL` + `endPoint` (or `dataProvider` wh
|
|
|
16
16
|
</template>
|
|
17
17
|
</sonic-code>
|
|
18
18
|
|
|
19
|
-
Fetch attributes (`lazyload` · `refetchEveryMs` · headers · authentication) are shared with [Fetch](/core/components/functional/fetch).
|
|
19
|
+
This example uses HTML templates; from Lit, use the [row renderer](#row-renderer) instead. Fetch attributes (`lazyload` · `refetchEveryMs` · headers · authentication) are shared with [Fetch](/core/components/functional/fetch).
|
|
20
20
|
|
|
21
21
|
## Row renderer
|
|
22
22
|
|
|
@@ -98,6 +98,8 @@ With no `loader`, nothing is shown while fetching. Set `loader` (`fixed`) or `lo
|
|
|
98
98
|
| `loader` | `fixed` (when empty) · `inline` | Loader shown while fetching. None by default |
|
|
99
99
|
| `debug` | boolean | Shows each row's data on hover |
|
|
100
100
|
|
|
101
|
+
Properties (TypeScript / Lit, preferred):
|
|
102
|
+
|
|
101
103
|
| Property | Type | Description |
|
|
102
104
|
|----------|------|-------------|
|
|
103
105
|
| `props` | array · object | Rows to render |
|
|
@@ -107,6 +109,8 @@ With no `loader`, nothing is shown while fetching. Set `loader` (`fixed`) or `lo
|
|
|
107
109
|
| `skeleton` | `() => TemplateResult` | Rendered while fetching |
|
|
108
110
|
| `itemPropertyMap` | object | `propertyMap` passed to each row subscriber |
|
|
109
111
|
|
|
112
|
+
Templates (plain HTML hosts):
|
|
113
|
+
|
|
110
114
|
| Template | Description |
|
|
111
115
|
|----------|-------------|
|
|
112
116
|
| `<template>` | Row markup; several are used in turn |
|
|
@@ -339,10 +339,23 @@ export class List extends Fetcher(Subscriber(TemplatesContainer(LitElement))) {
|
|
|
339
339
|
};
|
|
340
340
|
|
|
341
341
|
childPublisher._key_ = key + "";
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
// Immutable store: assigning `_metadata_` every render rebuilds the item + list
|
|
343
|
+
// publisher and retriggers render → infinite loop. Only write when values change.
|
|
344
|
+
const previousMetadata =
|
|
345
|
+
childPublisher._metadata_?.get() as ListItemMetadata | null;
|
|
346
|
+
if (
|
|
347
|
+
previousMetadata?.key !== metadata.key ||
|
|
348
|
+
previousMetadata?.even !== metadata.even ||
|
|
349
|
+
previousMetadata?.odd !== metadata.odd ||
|
|
350
|
+
previousMetadata?.onlyChild !== metadata.onlyChild ||
|
|
351
|
+
previousMetadata?.firstChild !== metadata.firstChild ||
|
|
352
|
+
previousMetadata?.lastChild !== metadata.lastChild
|
|
353
|
+
) {
|
|
354
|
+
childPublisher._metadata_ = {
|
|
355
|
+
...(previousMetadata || {}),
|
|
356
|
+
...metadata,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
346
359
|
|
|
347
360
|
// Essayer d'utiliser les templates programmatiques
|
|
348
361
|
const programmaticTemplate = this.handleProgrammaticTemplates(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-29T21:35:24.462Z",
|
|
4
4
|
"componentCount": 67,
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
@@ -755,7 +755,7 @@
|
|
|
755
755
|
{
|
|
756
756
|
"name": "geoCommunesResult",
|
|
757
757
|
"attribute": "geoCommunesResult",
|
|
758
|
-
"type": "City[] | undefined"
|
|
758
|
+
"type": "Frozen<City[]> | undefined"
|
|
759
759
|
}
|
|
760
760
|
]
|
|
761
761
|
},
|
|
File without changes
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import TestUtils from "@supersoniks/concorde/test-utils/TestUtils";
|
|
3
|
+
import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
|
|
4
|
+
import { snapshotLightDom } from "./sdui-dom-snapshot";
|
|
5
|
+
import type { SDUIDescriptor } from "./types";
|
|
6
|
+
import "./sdui";
|
|
7
|
+
import type { SonicSDUI } from "./sdui";
|
|
8
|
+
|
|
9
|
+
async function flushSdui(elt: SonicSDUI) {
|
|
10
|
+
await elt.updateComplete;
|
|
11
|
+
await Promise.resolve();
|
|
12
|
+
await Promise.resolve();
|
|
13
|
+
await elt.updateComplete;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
document.body.innerHTML = "";
|
|
18
|
+
PublisherManager.getInstance().delete("sdui-immutable-spec");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("sonic-sdui + immutable publisher props", () => {
|
|
22
|
+
it("rend un descripteur servi par un publisher frozen (cas doc / dataProvider)", async () => {
|
|
23
|
+
const descriptor: SDUIDescriptor = {
|
|
24
|
+
nodes: [
|
|
25
|
+
{
|
|
26
|
+
tagName: "div",
|
|
27
|
+
attributes: { class: "sdui-imm-ok" },
|
|
28
|
+
textContent: "FROM_FROZEN_PROPS",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const [elt] = TestUtils.bootstrap(
|
|
34
|
+
`<sonic-sdui dataProvider="sdui-immutable-spec"></sonic-sdui>`
|
|
35
|
+
) as unknown as SonicSDUI[];
|
|
36
|
+
|
|
37
|
+
// Même chemin que Subscriber : props → publisher.set → get() frozen → willUpdate
|
|
38
|
+
elt.props = descriptor as never;
|
|
39
|
+
await flushSdui(elt);
|
|
40
|
+
|
|
41
|
+
expect(Object.isFrozen(elt.props)).toBe(true);
|
|
42
|
+
expect(elt.querySelector(".sdui-imm-ok")?.textContent).toBe(
|
|
43
|
+
"FROM_FROZEN_PROPS"
|
|
44
|
+
);
|
|
45
|
+
expect(JSON.stringify(snapshotLightDom(elt))).toContain("FROM_FROZEN_PROPS");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("supporte libraryKey avec props frozen (merge library mutable en local)", async () => {
|
|
49
|
+
const descriptor: SDUIDescriptor = {
|
|
50
|
+
library: {
|
|
51
|
+
card: {
|
|
52
|
+
tagName: "div",
|
|
53
|
+
attributes: { class: "lib-card" },
|
|
54
|
+
nodes: [{ tagName: "span" }],
|
|
55
|
+
contentElementSelector: "span",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
nodes: [
|
|
59
|
+
{
|
|
60
|
+
libraryKey: "card",
|
|
61
|
+
nodes: [{ tagName: "em", textContent: "LIB_OK" }],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const [elt] = TestUtils.bootstrap(
|
|
67
|
+
`<sonic-sdui dataProvider="sdui-immutable-spec"></sonic-sdui>`
|
|
68
|
+
) as unknown as SonicSDUI[];
|
|
69
|
+
elt.props = descriptor as never;
|
|
70
|
+
await flushSdui(elt);
|
|
71
|
+
|
|
72
|
+
expect(elt.querySelector(".lib-card em")?.textContent).toBe("LIB_OK");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -69,6 +69,9 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
69
69
|
*/
|
|
70
70
|
@property({ type: String }) opsDataProvider?: string;
|
|
71
71
|
private sduiDescriptor: SDUIDescriptor = {};
|
|
72
|
+
/** Ref publisher/props d’origine (peut être frozen) — pour skip willUpdate. */
|
|
73
|
+
private sduiPropsRef: SDUIDescriptor | Record<string, SDUIDescriptor> | null =
|
|
74
|
+
null;
|
|
72
75
|
private nodeIndex = new Map<string, SDUINode>();
|
|
73
76
|
/** Garde-fou cycles childNodeIds. */
|
|
74
77
|
private renderingStack = new Set<string>();
|
|
@@ -78,6 +81,16 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
78
81
|
private incrementalActive = false;
|
|
79
82
|
private opsUnsub?: () => void;
|
|
80
83
|
|
|
84
|
+
/** Copie de travail mutable : updateContents / parseRootNodes mutent library & nodes. */
|
|
85
|
+
private cloneDescriptor(source: SDUIDescriptor | null | undefined): SDUIDescriptor {
|
|
86
|
+
if (source == null) return {};
|
|
87
|
+
try {
|
|
88
|
+
return structuredClone(source) as SDUIDescriptor;
|
|
89
|
+
} catch {
|
|
90
|
+
return JSON.parse(JSON.stringify(source)) as SDUIDescriptor;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
81
94
|
private get isSafeProfile(): boolean {
|
|
82
95
|
return this.profile === "safe";
|
|
83
96
|
}
|
|
@@ -100,21 +113,33 @@ export class SonicSDUI extends Fetcher(Subscriber(LitElement)) {
|
|
|
100
113
|
return;
|
|
101
114
|
}
|
|
102
115
|
if (this.props == null) {
|
|
103
|
-
this.sduiDescriptor
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
if (this.sduiPropsRef != null || Object.keys(this.sduiDescriptor).length) {
|
|
117
|
+
this.sduiPropsRef = null;
|
|
118
|
+
this.sduiDescriptor = {};
|
|
119
|
+
this.updateContents();
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
const propsRoot = this.props as
|
|
123
|
+
| SDUIDescriptor
|
|
124
|
+
| Record<string, SDUIDescriptor>;
|
|
106
125
|
const newSduiDescriptor = this.sduiKey
|
|
107
|
-
? (
|
|
108
|
-
: (
|
|
126
|
+
? (propsRoot as Record<string, SDUIDescriptor>)[this.sduiKey]
|
|
127
|
+
: (propsRoot as SDUIDescriptor);
|
|
109
128
|
const profileChanged = changedProperties.has("profile");
|
|
110
129
|
const partialChanged = changedProperties.has("partial");
|
|
130
|
+
// Comparer la ref d’entrée (frozen publisher), pas la copie locale mutable.
|
|
111
131
|
if (
|
|
112
|
-
this.
|
|
132
|
+
this.sduiPropsRef == newSduiDescriptor &&
|
|
113
133
|
!profileChanged &&
|
|
114
134
|
!partialChanged
|
|
115
|
-
)
|
|
135
|
+
) {
|
|
136
|
+
super.willUpdate(changedProperties);
|
|
116
137
|
return;
|
|
117
|
-
|
|
138
|
+
}
|
|
139
|
+
this.sduiPropsRef = newSduiDescriptor ?? null;
|
|
140
|
+
// Ownership locale : le store immutable freeze props ; SDUI doit muter
|
|
141
|
+
// library / nodes / toast pendant le rendu.
|
|
142
|
+
this.sduiDescriptor = this.cloneDescriptor(newSduiDescriptor);
|
|
118
143
|
this.updateContents();
|
|
119
144
|
}
|
|
120
145
|
super.willUpdate(changedProperties);
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import TestUtils from "@supersoniks/concorde/test-utils/TestUtils";
|
|
3
|
+
import { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
|
|
4
|
+
import "./submit";
|
|
5
|
+
import type { Submit } from "./submit";
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
document.body.innerHTML = "";
|
|
9
|
+
vi.restoreAllMocks();
|
|
10
|
+
PublisherManager.getInstance().delete("submit-spec-form");
|
|
11
|
+
PublisherManager.getInstance().delete("submit-spec-result");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("sonic-submit + immutable publisher", () => {
|
|
15
|
+
it("envoie le formulaire sans muter le get() frozen (delete isFormValid)", async () => {
|
|
16
|
+
const [elt] = TestUtils.bootstrap(`
|
|
17
|
+
<div
|
|
18
|
+
formDataProvider="submit-spec-form"
|
|
19
|
+
submitResultDataProvider="submit-spec-result"
|
|
20
|
+
serviceURL="https://example.test"
|
|
21
|
+
dataProvider="api/register"
|
|
22
|
+
>
|
|
23
|
+
<sonic-submit></sonic-submit>
|
|
24
|
+
</div>
|
|
25
|
+
`) as unknown as [HTMLElement];
|
|
26
|
+
|
|
27
|
+
const submit = elt.querySelector("sonic-submit") as Submit;
|
|
28
|
+
await submit.updateComplete;
|
|
29
|
+
|
|
30
|
+
const formPub = PublisherManager.get("submit-spec-form");
|
|
31
|
+
formPub.set({
|
|
32
|
+
email: "ada@example.com",
|
|
33
|
+
password: "secret",
|
|
34
|
+
isFormValid: true,
|
|
35
|
+
needsCaptchaValidation: false,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const frozen = formPub.get() as Record<string, unknown>;
|
|
39
|
+
expect(Object.isFrozen(frozen)).toBe(true);
|
|
40
|
+
|
|
41
|
+
const post = vi.fn(async () => ({ id: 1, token: "t" }));
|
|
42
|
+
(submit as any).api = { post, lastResult: { ok: true } };
|
|
43
|
+
|
|
44
|
+
await submit.submit();
|
|
45
|
+
|
|
46
|
+
expect(post).toHaveBeenCalledTimes(1);
|
|
47
|
+
const call = post.mock.calls[0] as unknown as [
|
|
48
|
+
unknown,
|
|
49
|
+
Record<string, unknown>,
|
|
50
|
+
];
|
|
51
|
+
const body = call[1];
|
|
52
|
+
expect(body).toMatchObject({
|
|
53
|
+
email: "ada@example.com",
|
|
54
|
+
password: "secret",
|
|
55
|
+
});
|
|
56
|
+
expect(body.isFormValid).toBeUndefined();
|
|
57
|
+
// Store still frozen and untouched for isFormValid key presence as set
|
|
58
|
+
expect(formPub.get().email).toBe("ada@example.com");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -137,7 +137,8 @@ export class Submit extends Subscriber(LitElement) {
|
|
|
137
137
|
const method = this.getAttribute("method")?.toLocaleLowerCase() || "post";
|
|
138
138
|
|
|
139
139
|
const sendAsFormData = this.hasAttribute("sendAsFormData");
|
|
140
|
-
|
|
140
|
+
// Shallow copies: publisher get() is frozen under the immutable store.
|
|
141
|
+
const formData: Record<string, any> = { ...(formPublisher.get() as object) };
|
|
141
142
|
delete formData.isFormValid;
|
|
142
143
|
const headesDataProvider = this.getAncestorAttributeValue(
|
|
143
144
|
"headersDataProvider"
|
|
@@ -145,8 +146,10 @@ export class Submit extends Subscriber(LitElement) {
|
|
|
145
146
|
const headerPublisher = headesDataProvider
|
|
146
147
|
? PublisherManager.getInstance().get(headesDataProvider)
|
|
147
148
|
: null;
|
|
148
|
-
let headerData: any = {};
|
|
149
|
-
if (headerPublisher)
|
|
149
|
+
let headerData: Record<string, any> = {};
|
|
150
|
+
if (headerPublisher) {
|
|
151
|
+
headerData = { ...(headerPublisher.get() as object) };
|
|
152
|
+
}
|
|
150
153
|
let result: SubmitResult = null;
|
|
151
154
|
const dataProvider = this.getAncestorAttributeValue("dataProvider");
|
|
152
155
|
const endPoint = this.endPoint || dataProvider;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -89,7 +89,25 @@ Fields sharing the same `formDataProvider` and `name` stay in sync. The attribut
|
|
|
89
89
|
</template>
|
|
90
90
|
</sonic-code>
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
## Nested keys
|
|
93
|
+
|
|
94
|
+
A dot in `name` stores a nested object in the form publisher.
|
|
95
|
+
|
|
96
|
+
<sonic-code>
|
|
97
|
+
<template>
|
|
98
|
+
<div formDataProvider="input-nested-demo" class="grid grid-cols-2 gap-3">
|
|
99
|
+
<sonic-input label="Passenger" name="passenger.name" value="Ada Lindqvist"></sonic-input>
|
|
100
|
+
<sonic-input label="Seat" name="passenger.seat" value="2A"></sonic-input>
|
|
101
|
+
<div class="col-span-2">
|
|
102
|
+
<sonic-value dataProvider="input-nested-demo" key="passenger.name"></sonic-value>
|
|
103
|
+
·
|
|
104
|
+
<sonic-value dataProvider="input-nested-demo" key="passenger.seat"></sonic-value>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</template>
|
|
108
|
+
</sonic-code>
|
|
109
|
+
|
|
110
|
+
`sameValueAs` sets `pattern` to the value of another field in the same store, for confirmation fields. Dot paths are supported.
|
|
93
111
|
|
|
94
112
|
<sonic-code>
|
|
95
113
|
<template>
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "@supersoniks/concorde/core/components/ui/form/css/form-control";
|
|
16
16
|
import FormElement from "@supersoniks/concorde/core/mixins/FormElement";
|
|
17
17
|
import FormInput from "@supersoniks/concorde/core/mixins/FormInput";
|
|
18
|
+
import { traverseDotNotation } from "@supersoniks/concorde/core/utils/Objects";
|
|
18
19
|
import { classMap } from "lit/directives/class-map.js";
|
|
19
20
|
import {
|
|
20
21
|
fontSize,
|
|
@@ -107,7 +108,10 @@ export class Input extends FormInput(FormElement(LitElement)) {
|
|
|
107
108
|
(this.pattern = this.escapeRegExp(v));
|
|
108
109
|
const formPublisher = this.getFormPublisher();
|
|
109
110
|
if (!formPublisher || !this.sameValueAsName) return;
|
|
110
|
-
formPublisher[
|
|
111
|
+
// Dot notation (same as FormElement name) — not formPublisher["a.b"].
|
|
112
|
+
traverseDotNotation(formPublisher, this.sameValueAsName).onAssign(
|
|
113
|
+
this.sameValueAsHandle,
|
|
114
|
+
);
|
|
111
115
|
}
|
|
112
116
|
if (this.type == "password") {
|
|
113
117
|
this.isPassword = true;
|
|
@@ -120,7 +124,9 @@ export class Input extends FormInput(FormElement(LitElement)) {
|
|
|
120
124
|
if (this.hasAttribute("sameValueAs") && this.sameValueAsName) {
|
|
121
125
|
const formPublisher = this.getFormPublisher();
|
|
122
126
|
if (!formPublisher) return;
|
|
123
|
-
formPublisher
|
|
127
|
+
traverseDotNotation(formPublisher, this.sameValueAsName).offAssign(
|
|
128
|
+
this.sameValueAsHandle,
|
|
129
|
+
);
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
|
|
@@ -47,6 +47,20 @@ With no `size`, the radio follows `md` (1rem).
|
|
|
47
47
|
</template>
|
|
48
48
|
</sonic-code>
|
|
49
49
|
|
|
50
|
+
## Nested keys
|
|
51
|
+
|
|
52
|
+
A dot in `name` writes a nested object in the form store (same as [Submit](/core/components/functional/submit) nested fields).
|
|
53
|
+
|
|
54
|
+
<sonic-code>
|
|
55
|
+
<template>
|
|
56
|
+
<div formDataProvider="radio-nested-demo" class="grid gap-2">
|
|
57
|
+
<sonic-radio name="passenger.seat" value="window" checked>Window</sonic-radio>
|
|
58
|
+
<sonic-radio name="passenger.seat" value="aisle">Aisle</sonic-radio>
|
|
59
|
+
<div>Seat: <sonic-value dataProvider="radio-nested-demo" key="passenger.seat"></sonic-value></div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
</sonic-code>
|
|
63
|
+
|
|
50
64
|
## Filtering a list
|
|
51
65
|
|
|
52
66
|
The radio group writes one `status` value to the store used as `dataFilterProvider` by a `sonic-queue`. See [Checkbox](/core/components/ui/form/checkbox) for the multi-value version.
|