@zeix/le-truc 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ai-context.md +234 -0
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
- package/.editorconfig +12 -0
- package/.github/copilot-instructions.md +62 -0
- package/.github/workflows/codeql.yml +108 -0
- package/.github/workflows/static.yml +43 -0
- package/.prettierrc +17 -0
- package/CLAUDE.md +215 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +160 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/biome.json +295 -0
- package/bun.lock +239 -0
- package/docs/about.html +105 -0
- package/docs/assets/main.css +1 -0
- package/docs/assets/main.js +10 -0
- package/docs/assets/main.js.map +66 -0
- package/docs/components.html +293 -0
- package/docs/data-flow.html +308 -0
- package/docs/examples/basic-button.html +367 -0
- package/docs/examples/basic-counter.html +188 -0
- package/docs/examples/basic-hello.html +138 -0
- package/docs/examples/basic-number.html +271 -0
- package/docs/examples/basic-pluralize.html +214 -0
- package/docs/examples/card-callout.html +152 -0
- package/docs/examples/card-mediaqueries.html +138 -0
- package/docs/examples/context-media.html +198 -0
- package/docs/examples/empty.html +37 -0
- package/docs/examples/form-checkbox.html +233 -0
- package/docs/examples/form-combobox.html +420 -0
- package/docs/examples/form-listbox.html +434 -0
- package/docs/examples/form-radiogroup.html +296 -0
- package/docs/examples/form-spinbutton.html +402 -0
- package/docs/examples/form-textbox.html +361 -0
- package/docs/examples/layout.html +67 -0
- package/docs/examples/module-carousel.html +552 -0
- package/docs/examples/module-catalog.html +241 -0
- package/docs/examples/module-codeblock.html +270 -0
- package/docs/examples/module-dialog.html +343 -0
- package/docs/examples/module-lazyload.html +289 -0
- package/docs/examples/module-list.html +197 -0
- package/docs/examples/module-pagination.html +283 -0
- package/docs/examples/module-scrollarea.html +447 -0
- package/docs/examples/module-tabgroup.html +526 -0
- package/docs/examples/module-todo.html +367 -0
- package/docs/examples/module-with-type.html +63 -0
- package/docs/examples/nested-components.html +88 -0
- package/docs/examples/recursive.html +56 -0
- package/docs/examples/simple-text.html +39 -0
- package/docs/examples/snippet.html +93 -0
- package/docs/examples/with-styles.html +75 -0
- package/docs/getting-started.html +143 -0
- package/docs/index.html +112 -0
- package/docs/sitemap.xml +28 -0
- package/docs/styling.html +160 -0
- package/docs/sw.js +112 -0
- package/docs-src/api/README.md +478 -0
- package/docs-src/api/_media/LICENSE +21 -0
- package/docs-src/api/classes/CircularDependencyError.md +299 -0
- package/docs-src/api/classes/CircularMutationError.md +301 -0
- package/docs-src/api/classes/ContextRequestEvent.md +590 -0
- package/docs-src/api/classes/DependencyTimeoutError.md +301 -0
- package/docs-src/api/classes/InvalidCallbackError.md +303 -0
- package/docs-src/api/classes/InvalidComponentNameError.md +295 -0
- package/docs-src/api/classes/InvalidCustomElementError.md +301 -0
- package/docs-src/api/classes/InvalidEffectsError.md +301 -0
- package/docs-src/api/classes/InvalidPropertyNameError.md +307 -0
- package/docs-src/api/classes/InvalidReactivesError.md +307 -0
- package/docs-src/api/classes/InvalidSignalValueError.md +303 -0
- package/docs-src/api/classes/MissingElementError.md +307 -0
- package/docs-src/api/classes/NullishSignalValueError.md +299 -0
- package/docs-src/api/classes/StoreKeyExistsError.md +303 -0
- package/docs-src/api/classes/StoreKeyRangeError.md +299 -0
- package/docs-src/api/classes/StoreKeyReadonlyError.md +303 -0
- package/docs-src/api/functions/asBoolean.md +21 -0
- package/docs-src/api/functions/asEnum.md +31 -0
- package/docs-src/api/functions/asInteger.md +39 -0
- package/docs-src/api/functions/asJSON.md +49 -0
- package/docs-src/api/functions/asNumber.md +37 -0
- package/docs-src/api/functions/asString.md +37 -0
- package/docs-src/api/functions/createCollection.md +83 -0
- package/docs-src/api/functions/createSensor.md +71 -0
- package/docs-src/api/functions/dangerouslySetInnerHTML.md +48 -0
- package/docs-src/api/functions/defineComponent.md +65 -0
- package/docs-src/api/functions/isCollection.md +37 -0
- package/docs-src/api/functions/isParser.md +41 -0
- package/docs-src/api/functions/match.md +47 -0
- package/docs-src/api/functions/on.md +58 -0
- package/docs-src/api/functions/pass.md +53 -0
- package/docs-src/api/functions/provideContexts.md +47 -0
- package/docs-src/api/functions/read.md +47 -0
- package/docs-src/api/functions/requestContext.md +51 -0
- package/docs-src/api/functions/resolve.md +40 -0
- package/docs-src/api/functions/runEffects.md +51 -0
- package/docs-src/api/functions/runElementEffects.md +57 -0
- package/docs-src/api/functions/schedule.md +33 -0
- package/docs-src/api/functions/setAttribute.md +48 -0
- package/docs-src/api/functions/setProperty.md +52 -0
- package/docs-src/api/functions/setStyle.md +48 -0
- package/docs-src/api/functions/setText.md +42 -0
- package/docs-src/api/functions/show.md +42 -0
- package/docs-src/api/functions/toSignal.md +37 -0
- package/docs-src/api/functions/toggleAttribute.md +48 -0
- package/docs-src/api/functions/toggleClass.md +48 -0
- package/docs-src/api/functions/updateElement.md +53 -0
- package/docs-src/api/globals.md +131 -0
- package/docs-src/api/type-aliases/Cleanup.md +27 -0
- package/docs-src/api/type-aliases/Collection.md +91 -0
- package/docs-src/api/type-aliases/CollectionListener.md +27 -0
- package/docs-src/api/type-aliases/Component.md +17 -0
- package/docs-src/api/type-aliases/ComponentProp.md +11 -0
- package/docs-src/api/type-aliases/ComponentProps.md +11 -0
- package/docs-src/api/type-aliases/ComponentSetup.md +31 -0
- package/docs-src/api/type-aliases/ComponentUI.md +27 -0
- package/docs-src/api/type-aliases/Computed.md +49 -0
- package/docs-src/api/type-aliases/ComputedCallback.md +29 -0
- package/docs-src/api/type-aliases/Context.md +33 -0
- package/docs-src/api/type-aliases/ContextType.md +19 -0
- package/docs-src/api/type-aliases/DangerouslySetInnerHTMLOptions.md +27 -0
- package/docs-src/api/type-aliases/DiffResult.md +61 -0
- package/docs-src/api/type-aliases/Effect.md +35 -0
- package/docs-src/api/type-aliases/EffectCallback.md +23 -0
- package/docs-src/api/type-aliases/Effects.md +21 -0
- package/docs-src/api/type-aliases/ElementEffects.md +21 -0
- package/docs-src/api/type-aliases/ElementFromKey.md +21 -0
- package/docs-src/api/type-aliases/ElementQueries.md +27 -0
- package/docs-src/api/type-aliases/ElementUpdater.md +131 -0
- package/docs-src/api/type-aliases/EventHandler.md +31 -0
- package/docs-src/api/type-aliases/EventType.md +17 -0
- package/docs-src/api/type-aliases/Fallback.md +21 -0
- package/docs-src/api/type-aliases/Initializers.md +21 -0
- package/docs-src/api/type-aliases/LooseReader.md +31 -0
- package/docs-src/api/type-aliases/MatchHandlers.md +77 -0
- package/docs-src/api/type-aliases/MaybeCleanup.md +23 -0
- package/docs-src/api/type-aliases/MaybeSignal.md +17 -0
- package/docs-src/api/type-aliases/Parser.md +39 -0
- package/docs-src/api/type-aliases/ParserOrFallback.md +21 -0
- package/docs-src/api/type-aliases/PassedProp.md +25 -0
- package/docs-src/api/type-aliases/PassedProps.md +21 -0
- package/docs-src/api/type-aliases/Reactive.md +25 -0
- package/docs-src/api/type-aliases/Reader.md +31 -0
- package/docs-src/api/type-aliases/ReservedWords.md +11 -0
- package/docs-src/api/type-aliases/ResolveResult.md +29 -0
- package/docs-src/api/type-aliases/SensorEvents.md +25 -0
- package/docs-src/api/type-aliases/Signal.md +41 -0
- package/docs-src/api/type-aliases/State.md +85 -0
- package/docs-src/api/type-aliases/Store.md +29 -0
- package/docs-src/api/type-aliases/UI.md +11 -0
- package/docs-src/api/type-aliases/UnknownContext.md +13 -0
- package/docs-src/api/variables/CONTEXT_REQUEST.md +11 -0
- package/docs-src/api/variables/UNSET.md +23 -0
- package/docs-src/api/variables/batch.md +25 -0
- package/docs-src/api/variables/createComputed.md +41 -0
- package/docs-src/api/variables/createEffect.md +35 -0
- package/docs-src/api/variables/createState.md +37 -0
- package/docs-src/api/variables/createStore.md +42 -0
- package/docs-src/api/variables/diff.md +43 -0
- package/docs-src/api/variables/isAbortError.md +33 -0
- package/docs-src/api/variables/isAsyncFunction.md +39 -0
- package/docs-src/api/variables/isComputed.md +37 -0
- package/docs-src/api/variables/isEqual.md +49 -0
- package/docs-src/api/variables/isFunction.md +39 -0
- package/docs-src/api/variables/isMutableSignal.md +37 -0
- package/docs-src/api/variables/isNumber.md +33 -0
- package/docs-src/api/variables/isRecord.md +39 -0
- package/docs-src/api/variables/isRecordOrArray.md +39 -0
- package/docs-src/api/variables/isSignal.md +37 -0
- package/docs-src/api/variables/isState.md +37 -0
- package/docs-src/api/variables/isStore.md +37 -0
- package/docs-src/api/variables/isString.md +33 -0
- package/docs-src/api/variables/isSymbol.md +33 -0
- package/docs-src/api/variables/toError.md +33 -0
- package/docs-src/api/variables/valueString.md +33 -0
- package/docs-src/includes/menu.html +44 -0
- package/docs-src/pages/about.md +89 -0
- package/docs-src/pages/components.md +437 -0
- package/docs-src/pages/data-flow.md +449 -0
- package/docs-src/pages/getting-started.md +170 -0
- package/docs-src/pages/index.md +98 -0
- package/docs-src/pages/styling.md +165 -0
- package/eslint.config.js +64 -0
- package/examples/_common/clear.ts +49 -0
- package/examples/_common/fetch.ts +160 -0
- package/examples/_common/focus.ts +45 -0
- package/examples/_common/highlight.ts +5 -0
- package/examples/_global.css +463 -0
- package/examples/basic-button/basic-button.css +176 -0
- package/examples/basic-button/basic-button.html +46 -0
- package/examples/basic-button/basic-button.spec.ts +160 -0
- package/examples/basic-button/basic-button.ts +45 -0
- package/examples/basic-button/copyToClipboard.ts +37 -0
- package/examples/basic-counter/basic-counter.css +21 -0
- package/examples/basic-counter/basic-counter.html +24 -0
- package/examples/basic-counter/basic-counter.spec.ts +85 -0
- package/examples/basic-counter/basic-counter.ts +43 -0
- package/examples/basic-hello/basic-hello.html +34 -0
- package/examples/basic-hello/basic-hello.spec.ts +110 -0
- package/examples/basic-hello/basic-hello.ts +36 -0
- package/examples/basic-number/basic-number.html +79 -0
- package/examples/basic-number/basic-number.spec.ts +175 -0
- package/examples/basic-number/basic-number.ts +124 -0
- package/examples/basic-pluralize/basic-pluralize.html +64 -0
- package/examples/basic-pluralize/basic-pluralize.spec.ts +258 -0
- package/examples/basic-pluralize/basic-pluralize.ts +82 -0
- package/examples/card-callout/card-callout.css +79 -0
- package/examples/card-callout/card-callout.html +5 -0
- package/examples/card-mediaqueries/card-mediaqueries.html +29 -0
- package/examples/card-mediaqueries/card-mediaqueries.spec.ts +300 -0
- package/examples/card-mediaqueries/card-mediaqueries.ts +41 -0
- package/examples/context-media/context-media.html +3 -0
- package/examples/context-media/context-media.ts +127 -0
- package/examples/form-checkbox/form-checkbox.css +70 -0
- package/examples/form-checkbox/form-checkbox.html +13 -0
- package/examples/form-checkbox/form-checkbox.spec.ts +357 -0
- package/examples/form-checkbox/form-checkbox.ts +50 -0
- package/examples/form-checkbox/vanilla-checkbox.ts +101 -0
- package/examples/form-combobox/form-combobox.css +118 -0
- package/examples/form-combobox/form-combobox.html +74 -0
- package/examples/form-combobox/form-combobox.spec.ts +977 -0
- package/examples/form-combobox/form-combobox.ts +128 -0
- package/examples/form-listbox/form-listbox.css +71 -0
- package/examples/form-listbox/form-listbox.html +67 -0
- package/examples/form-listbox/form-listbox.spec.ts +1050 -0
- package/examples/form-listbox/form-listbox.ts +196 -0
- package/examples/form-listbox/mocks/timezones.json +495 -0
- package/examples/form-radiogroup/form-radiogroup.css +87 -0
- package/examples/form-radiogroup/form-radiogroup.html +51 -0
- package/examples/form-radiogroup/form-radiogroup.spec.ts +515 -0
- package/examples/form-radiogroup/form-radiogroup.ts +58 -0
- package/examples/form-spinbutton/form-spinbutton.css +95 -0
- package/examples/form-spinbutton/form-spinbutton.html +96 -0
- package/examples/form-spinbutton/form-spinbutton.spec.ts +688 -0
- package/examples/form-spinbutton/form-spinbutton.ts +111 -0
- package/examples/form-textbox/form-textbox.css +104 -0
- package/examples/form-textbox/form-textbox.html +53 -0
- package/examples/form-textbox/form-textbox.spec.ts +542 -0
- package/examples/form-textbox/form-textbox.ts +104 -0
- package/examples/main.css +22 -0
- package/examples/main.ts +23 -0
- package/examples/module-carousel/module-carousel.css +113 -0
- package/examples/module-carousel/module-carousel.html +208 -0
- package/examples/module-carousel/module-carousel.spec.ts +523 -0
- package/examples/module-carousel/module-carousel.ts +131 -0
- package/examples/module-catalog/module-catalog.css +22 -0
- package/examples/module-catalog/module-catalog.html +82 -0
- package/examples/module-catalog/module-catalog.spec.ts +396 -0
- package/examples/module-catalog/module-catalog.ts +37 -0
- package/examples/module-codeblock/module-codeblock.css +95 -0
- package/examples/module-codeblock/module-codeblock.html +28 -0
- package/examples/module-codeblock/module-codeblock.ts +47 -0
- package/examples/module-demo/module-demo.css +13 -0
- package/examples/module-dialog/module-dialog.css +96 -0
- package/examples/module-dialog/module-dialog.html +66 -0
- package/examples/module-dialog/module-dialog.spec.ts +557 -0
- package/examples/module-dialog/module-dialog.ts +81 -0
- package/examples/module-lazyload/mocks/empty.html +1 -0
- package/examples/module-lazyload/mocks/module-with-type.html +27 -0
- package/examples/module-lazyload/mocks/nested-components.html +52 -0
- package/examples/module-lazyload/mocks/recursive.html +20 -0
- package/examples/module-lazyload/mocks/simple-text.html +3 -0
- package/examples/module-lazyload/mocks/snippet.html +57 -0
- package/examples/module-lazyload/mocks/with-styles.html +39 -0
- package/examples/module-lazyload/module-lazyload.html +132 -0
- package/examples/module-lazyload/module-lazyload.spec.ts +734 -0
- package/examples/module-lazyload/module-lazyload.ts +89 -0
- package/examples/module-list/module-list.html +30 -0
- package/examples/module-list/module-list.spec.ts +592 -0
- package/examples/module-list/module-list.ts +99 -0
- package/examples/module-pagination/module-pagination.css +79 -0
- package/examples/module-pagination/module-pagination.html +16 -0
- package/examples/module-pagination/module-pagination.spec.ts +701 -0
- package/examples/module-pagination/module-pagination.ts +88 -0
- package/examples/module-scrollarea/module-scrollarea.css +77 -0
- package/examples/module-scrollarea/module-scrollarea.html +189 -0
- package/examples/module-scrollarea/module-scrollarea.spec.ts +445 -0
- package/examples/module-scrollarea/module-scrollarea.ts +81 -0
- package/examples/module-tabgroup/module-tabgroup.css +55 -0
- package/examples/module-tabgroup/module-tabgroup.html +269 -0
- package/examples/module-tabgroup/module-tabgroup.spec.ts +631 -0
- package/examples/module-tabgroup/module-tabgroup.ts +102 -0
- package/examples/module-toc/module-toc.css +34 -0
- package/examples/module-todo/module-todo.css +84 -0
- package/examples/module-todo/module-todo.html +92 -0
- package/examples/module-todo/module-todo.spec.ts +528 -0
- package/examples/module-todo/module-todo.ts +91 -0
- package/examples/section-hero/section-hero.css +37 -0
- package/examples/section-menu/section-menu.css +81 -0
- package/examples/server.ts +95 -0
- package/examples/test-setup.md +314 -0
- package/index.dev.js +1688 -0
- package/index.dev.ts +127 -0
- package/index.js +3 -0
- package/index.js.map +42 -0
- package/index.ts +127 -0
- package/package.json +64 -0
- package/playwright.config.ts +31 -0
- package/server/BUILD_SYSTEM.md +428 -0
- package/server/SERVER.md +286 -0
- package/server/build.ts +91 -0
- package/server/config.ts +130 -0
- package/server/effects/api.ts +28 -0
- package/server/effects/css.ts +31 -0
- package/server/effects/examples.ts +109 -0
- package/server/effects/js.ts +32 -0
- package/server/effects/menu.ts +34 -0
- package/server/effects/pages.ts +178 -0
- package/server/effects/service-worker.ts +57 -0
- package/server/effects/sitemap.ts +27 -0
- package/server/file-signals.ts +361 -0
- package/server/file-watcher.ts +77 -0
- package/server/io.ts +174 -0
- package/server/layout-engine.ts +470 -0
- package/server/layout-utils.ts +615 -0
- package/server/layouts/api.html +76 -0
- package/server/layouts/base.html +37 -0
- package/server/layouts/blog.html +115 -0
- package/server/layouts/example.html +104 -0
- package/server/layouts/overview.html +165 -0
- package/server/layouts/page.html +36 -0
- package/server/layouts/test.html +24 -0
- package/server/markdoc-helpers.ts +217 -0
- package/server/markdoc.config.ts +29 -0
- package/server/schema/callout.markdoc.ts +17 -0
- package/server/schema/carousel.markdoc.ts +118 -0
- package/server/schema/demo.markdoc.ts +74 -0
- package/server/schema/fence.markdoc.ts +84 -0
- package/server/schema/heading.markdoc.ts +23 -0
- package/server/schema/hero.markdoc.ts +59 -0
- package/server/schema/section.markdoc.ts +10 -0
- package/server/schema/slide.markdoc.ts +17 -0
- package/server/schema/source.markdoc.ts +53 -0
- package/server/schema/tabgroup.markdoc.ts +102 -0
- package/server/serve.ts +635 -0
- package/server/templates/README.md +352 -0
- package/server/templates/constants.ts +236 -0
- package/server/templates/fragments.ts +159 -0
- package/server/templates/hmr.ts +269 -0
- package/server/templates/menu.ts +33 -0
- package/server/templates/performance-hints.ts +94 -0
- package/server/templates/service-worker.ts +403 -0
- package/server/templates/sitemap.ts +57 -0
- package/server/templates/toc.ts +41 -0
- package/server/templates/utils.ts +378 -0
- package/src/component.ts +215 -0
- package/src/context.ts +156 -0
- package/src/effects/attribute.ts +82 -0
- package/src/effects/class.ts +28 -0
- package/src/effects/event.ts +67 -0
- package/src/effects/html.ts +60 -0
- package/src/effects/method.ts +57 -0
- package/src/effects/pass.ts +103 -0
- package/src/effects/property.ts +57 -0
- package/src/effects/style.ts +34 -0
- package/src/effects/text.ts +28 -0
- package/src/effects.ts +412 -0
- package/src/errors.ts +160 -0
- package/src/parsers/boolean.ts +14 -0
- package/src/parsers/json.ts +33 -0
- package/src/parsers/number.ts +55 -0
- package/src/parsers/string.ts +32 -0
- package/src/parsers.ts +90 -0
- package/src/scheduler.ts +47 -0
- package/src/signals/collection.ts +253 -0
- package/src/signals/sensor.ts +131 -0
- package/src/ui.ts +236 -0
- package/src/util.ts +187 -0
- package/tsconfig.json +34 -0
- package/types/examples/basic-button/basic-button.d.ts +16 -0
- package/types/examples/basic-hello/basic-hello.d.ts +18 -0
- package/types/index.d.ts +27 -0
- package/types/index.dev.d.ts +27 -0
- package/types/src/collection.d.ts +27 -0
- package/types/src/component.d.ts +32 -0
- package/types/src/context.d.ts +85 -0
- package/types/src/effects/attribute.d.ts +23 -0
- package/types/src/effects/callMethod.d.ts +23 -0
- package/types/src/effects/class.d.ts +13 -0
- package/types/src/effects/dangerouslySetInnerHTML.d.ts +18 -0
- package/types/src/effects/event.d.ts +18 -0
- package/types/src/effects/html.d.ts +17 -0
- package/types/src/effects/method.d.ts +22 -0
- package/types/src/effects/pass.d.ts +18 -0
- package/types/src/effects/property.d.ts +22 -0
- package/types/src/effects/setAttribute.d.ts +24 -0
- package/types/src/effects/setProperty.d.ts +23 -0
- package/types/src/effects/setStyle.d.ts +14 -0
- package/types/src/effects/setText.d.ts +13 -0
- package/types/src/effects/style.d.ts +13 -0
- package/types/src/effects/text.d.ts +12 -0
- package/types/src/effects/toggleClass.d.ts +14 -0
- package/types/src/effects.d.ts +153 -0
- package/types/src/errors.d.ts +99 -0
- package/types/src/events.d.ts +27 -0
- package/types/src/extractors.d.ts +23 -0
- package/types/src/parsers/boolean.d.ts +10 -0
- package/types/src/parsers/json.d.ts +13 -0
- package/types/src/parsers/number.d.ts +21 -0
- package/types/src/parsers/string.d.ts +19 -0
- package/types/src/parsers.d.ts +41 -0
- package/types/src/scheduler.d.ts +11 -0
- package/types/src/sensor.d.ts +27 -0
- package/types/src/signals/collection.d.ts +32 -0
- package/types/src/signals/sensor.d.ts +27 -0
- package/types/src/ui.d.ts +37 -0
- package/types/src/util.d.ts +65 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Data Flow'
|
|
3
|
+
emoji: '🔄'
|
|
4
|
+
description: 'Passing state, events, context'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
{% hero %}
|
|
8
|
+
# 🔄 Data Flow
|
|
9
|
+
|
|
10
|
+
**Learn how Le Truc components can work together seamlessly.** Start with simple parent-child relationships, then explore advanced patterns like custom events and shared state. Build modular, loosely coupled components that communicate efficiently.
|
|
11
|
+
{% /hero %}
|
|
12
|
+
|
|
13
|
+
{% section %}
|
|
14
|
+
## Component Coordination
|
|
15
|
+
|
|
16
|
+
Let's consider a **product catalog** where users can add items to a shopping cart. We have **three independent components** that work together:
|
|
17
|
+
|
|
18
|
+
- `ModuleCatalog` **(Parent)**:
|
|
19
|
+
- **Tracks all `SpinButton` components** in its subtree and calculates the total count of items in the shopping cart.
|
|
20
|
+
- **Passes that total** to a `BasicButton`.
|
|
21
|
+
- `BasicButton` **(Child)**:
|
|
22
|
+
- Displays a **badge** in the top-right corner when the `badge` property is set.
|
|
23
|
+
- **Does not track any state** – it simply renders whatever value is passed to it.
|
|
24
|
+
- `FormSpinbutton` **(Child)**:
|
|
25
|
+
- Displays an **Add to Cart** button initially.
|
|
26
|
+
- When an item is added, it transforms into a **stepper** (increment/decrement buttons).
|
|
27
|
+
|
|
28
|
+
Although `BasicButton` and `FormSpinbutton` are completely independent, they need to work together. So `ModuleCatalog` **coordinates the data flow between them**.
|
|
29
|
+
|
|
30
|
+
### Parent Component: ModuleCatalog
|
|
31
|
+
|
|
32
|
+
The **parent component (`ModuleCatalog`) knows about its children**, meaning it can **read state from and pass state to** them.
|
|
33
|
+
|
|
34
|
+
First, we need to observe the quantities of all `FormSpinbutton` components. For this, we create a `Collection` of all children matching the `form-spinbutton` selector using the `all` function:
|
|
35
|
+
|
|
36
|
+
```js#module-catalog.js
|
|
37
|
+
defineComponent(
|
|
38
|
+
'module-catalog',
|
|
39
|
+
{},
|
|
40
|
+
({ all, first }) => ({
|
|
41
|
+
button: first('basic-button', 'Add a button to go go the Shopping Cart'),
|
|
42
|
+
spinbuttons: all(
|
|
43
|
+
'form-spinbutton',
|
|
44
|
+
'Add spinbutton components to calculate sum from.',
|
|
45
|
+
),
|
|
46
|
+
}),
|
|
47
|
+
ui => {
|
|
48
|
+
// Component setup
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
In contrast to a static `querySelectorAll()` call, the `Collection` signal returned by `all()` is reactive and updates whenever new elements are added or removed from the DOM.
|
|
54
|
+
|
|
55
|
+
Then, we need to convert the total of all product quantities to a string and pass it on to the `BasicButton` component. In Le Truc we use the `pass()` function to share state with descendant components:
|
|
56
|
+
|
|
57
|
+
```js#module-catalog.js
|
|
58
|
+
defineComponent(
|
|
59
|
+
'module-catalog',
|
|
60
|
+
{},
|
|
61
|
+
({ all, first }) => ({
|
|
62
|
+
button: first('basic-button', 'Add a button to go go the Shopping Cart'),
|
|
63
|
+
spinbuttons: all(
|
|
64
|
+
'form-spinbutton',
|
|
65
|
+
'Add spinbutton components to calculate sum from.',
|
|
66
|
+
),
|
|
67
|
+
}),
|
|
68
|
+
({ spinbuttons }) => {
|
|
69
|
+
const total = createComputed(() =>
|
|
70
|
+
spinbuttons.get().reduce((sum, item) => sum + item.value, 0),
|
|
71
|
+
)
|
|
72
|
+
return {
|
|
73
|
+
button: pass({
|
|
74
|
+
disabled: () => !total.get(),
|
|
75
|
+
badge: () => (total.get() > 0 ? String(total.get()) : ''),
|
|
76
|
+
}),
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Allright, that's it!
|
|
83
|
+
|
|
84
|
+
- Whenever one of the `value` signals of a `<form-spinbutton>` updates, the total in the badge of `<basic-button>` automatically updates.
|
|
85
|
+
- No need for event listeners or manual updates!
|
|
86
|
+
|
|
87
|
+
### Child Component: BasicButton
|
|
88
|
+
|
|
89
|
+
The `BasicButton` component **displays a badge when needed** – it does not know about any other component nor track state itself. It just exposes a reactive properties `badge` of type `string` and `disabled` of type `boolean` and has effects to react to state changes that updates the DOM subtree.
|
|
90
|
+
|
|
91
|
+
```js#basic-button.js
|
|
92
|
+
defineComponent(
|
|
93
|
+
'basic-button',
|
|
94
|
+
{
|
|
95
|
+
disabled: asBoolean(),
|
|
96
|
+
badge: asString(ui => ui.badge?.textContent ?? ''),
|
|
97
|
+
},
|
|
98
|
+
({ first }) => ({
|
|
99
|
+
button: first('button', 'Add a native button as descendant.'),
|
|
100
|
+
badge: first('span.badge'),
|
|
101
|
+
}),
|
|
102
|
+
() => ({
|
|
103
|
+
button: setProperty('disabled'),
|
|
104
|
+
badge: setText('badge'),
|
|
105
|
+
}),
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- Whenever the `disabled` property is updated by a parent component, the button is disabled or enabled.
|
|
110
|
+
- Whenever the `badge` property is updated by a parent component, the badge text updates.
|
|
111
|
+
- If `badge` is an empty string, the badge indicator is hidden (via CSS).
|
|
112
|
+
|
|
113
|
+
### Child Component: FormSpinbutton
|
|
114
|
+
|
|
115
|
+
The `FormSpinbutton` component reacts to user interactions and exposes a reactive property `value` of type `number`. It updates its own internal DOM subtree, but doesn't know about any other component nor where the value is used.
|
|
116
|
+
|
|
117
|
+
```js#form-spinbutton.js
|
|
118
|
+
defineComponent(
|
|
119
|
+
'form-spinbutton',
|
|
120
|
+
{
|
|
121
|
+
value: createSensor(
|
|
122
|
+
read(ui => ui.input.value, asInteger()),
|
|
123
|
+
'controls',
|
|
124
|
+
{
|
|
125
|
+
change: ({ ui, target, prev }) => {
|
|
126
|
+
if (!(target instanceof HTMLInputElement)) return prev
|
|
127
|
+
|
|
128
|
+
const resetTo = (next: number) => {
|
|
129
|
+
target.value = String(next)
|
|
130
|
+
target.checkValidity()
|
|
131
|
+
return next
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const next = Number(target.value)
|
|
135
|
+
if (!Number.isInteger(next)) return resetTo(prev)
|
|
136
|
+
const clamped = Math.min(ui.host.max, Math.max(0, next))
|
|
137
|
+
if (next !== clamped) return resetTo(clamped)
|
|
138
|
+
return clamped
|
|
139
|
+
},
|
|
140
|
+
click: ({ target, prev }) =>
|
|
141
|
+
prev +
|
|
142
|
+
(target.classList.contains('decrement')
|
|
143
|
+
? -1
|
|
144
|
+
: target.classList.contains('increment')
|
|
145
|
+
? 1
|
|
146
|
+
: 0),
|
|
147
|
+
keydown: ({ ui, event, prev }) => {
|
|
148
|
+
const { key } = event
|
|
149
|
+
if (['ArrowUp', 'ArrowDown', '-', '+'].includes(key)) {
|
|
150
|
+
event.stopPropagation()
|
|
151
|
+
event.preventDefault()
|
|
152
|
+
const next = prev + (key === 'ArrowDown' || key === '-' ? -1 : 1)
|
|
153
|
+
return Math.min(ui.host.max, Math.max(0, next))
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
),
|
|
158
|
+
max: read(ui => ui.input.max, asInteger(10)),
|
|
159
|
+
},
|
|
160
|
+
({ all, first }) => ({
|
|
161
|
+
controls: all(
|
|
162
|
+
'button, input:not([disabled])',
|
|
163
|
+
),
|
|
164
|
+
increment: first(
|
|
165
|
+
'button.increment',
|
|
166
|
+
'Add a native button to increment the value',
|
|
167
|
+
),
|
|
168
|
+
decrement: first(
|
|
169
|
+
'button.decrement',
|
|
170
|
+
'Add a native button to decrement the value',
|
|
171
|
+
),
|
|
172
|
+
input: first('input.value', 'Add a native input to display the value'),
|
|
173
|
+
zero: first('.zero'),
|
|
174
|
+
other: first('.other'),
|
|
175
|
+
}),
|
|
176
|
+
({ host, increment, zero }) => {
|
|
177
|
+
const nonZero = createComputed(() => host.value !== 0)
|
|
178
|
+
const incrementLabel = increment.ariaLabel || 'Increment'
|
|
179
|
+
const ariaLabel = createComputed(() =>
|
|
180
|
+
nonZero.get() || !zero ? incrementLabel : zero.textContent,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
input: [
|
|
185
|
+
show(nonZero),
|
|
186
|
+
setProperty('value'),
|
|
187
|
+
setProperty('max', () => String(host.max)),
|
|
188
|
+
],
|
|
189
|
+
decrement: show(nonZero),
|
|
190
|
+
increment: [
|
|
191
|
+
setProperty('disabled', () => host.value >= host.max),
|
|
192
|
+
setProperty('ariaLabel', ariaLabel),
|
|
193
|
+
],
|
|
194
|
+
zero: show(() => !nonZero.get()),
|
|
195
|
+
other: show(nonZero),
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- Whenever the user clicks a button or presses a handled key, the value property is updated.
|
|
202
|
+
- The component sets hidden and disabled states of buttons and updates the text of the `input` element.
|
|
203
|
+
|
|
204
|
+
### Full Example
|
|
205
|
+
|
|
206
|
+
Here's how everything comes together:
|
|
207
|
+
|
|
208
|
+
- Each `FormSpinbutton` tracks its own value.
|
|
209
|
+
- The `ModuleCatalog` sums all quantities and passes the total to `BasicButton`.
|
|
210
|
+
- The `BasicButton` displays the total if it's greater than zero.
|
|
211
|
+
|
|
212
|
+
**No custom events are needed – state flows naturally!**
|
|
213
|
+
|
|
214
|
+
{% demo %}
|
|
215
|
+
<module-catalog>
|
|
216
|
+
<header>
|
|
217
|
+
<p>Shop</p>
|
|
218
|
+
<basic-button disabled>
|
|
219
|
+
<button type="button" disabled>
|
|
220
|
+
<span class="label">🛒 Shopping Cart</span>
|
|
221
|
+
<span class="badge"></span>
|
|
222
|
+
</button>
|
|
223
|
+
</basic-button>
|
|
224
|
+
</header>
|
|
225
|
+
<ul>
|
|
226
|
+
<li>
|
|
227
|
+
<p>Product 1</p>
|
|
228
|
+
<form-spinbutton>
|
|
229
|
+
<button type="button" class="decrement" aria-label="Decrement" hidden>
|
|
230
|
+
−
|
|
231
|
+
</button>
|
|
232
|
+
<input
|
|
233
|
+
type="number"
|
|
234
|
+
class="value"
|
|
235
|
+
name="amount-product1"
|
|
236
|
+
value="0"
|
|
237
|
+
min="0"
|
|
238
|
+
max="10"
|
|
239
|
+
readonly
|
|
240
|
+
disabled
|
|
241
|
+
hidden
|
|
242
|
+
/>
|
|
243
|
+
<button type="button" class="increment" aria-label="Increment">
|
|
244
|
+
<span class="zero">Add to Cart</span>
|
|
245
|
+
<span class="other" hidden>+</span>
|
|
246
|
+
</button>
|
|
247
|
+
</form-spinbutton>
|
|
248
|
+
</li>
|
|
249
|
+
<li>
|
|
250
|
+
<p>Product 2</p>
|
|
251
|
+
<form-spinbutton>
|
|
252
|
+
<button type="button" class="decrement" aria-label="Decrement" hidden>
|
|
253
|
+
−
|
|
254
|
+
</button>
|
|
255
|
+
<input
|
|
256
|
+
type="number"
|
|
257
|
+
class="value"
|
|
258
|
+
name="amount-product2"
|
|
259
|
+
value="0"
|
|
260
|
+
min="0"
|
|
261
|
+
max="5"
|
|
262
|
+
readonly
|
|
263
|
+
disabled
|
|
264
|
+
hidden
|
|
265
|
+
/>
|
|
266
|
+
<button type="button" class="increment" aria-label="Increment">
|
|
267
|
+
<span class="zero">Add to Cart</span>
|
|
268
|
+
<span class="other" hidden>+</span>
|
|
269
|
+
</button>
|
|
270
|
+
</form-spinbutton>
|
|
271
|
+
</li>
|
|
272
|
+
<li>
|
|
273
|
+
<p>Product 3</p>
|
|
274
|
+
<form-spinbutton>
|
|
275
|
+
<button type="button" class="decrement" aria-label="Decrement" hidden>
|
|
276
|
+
−
|
|
277
|
+
</button>
|
|
278
|
+
<input
|
|
279
|
+
type="number"
|
|
280
|
+
class="value"
|
|
281
|
+
name="amount-product3"
|
|
282
|
+
value="0"
|
|
283
|
+
min="0"
|
|
284
|
+
max="20"
|
|
285
|
+
readonly
|
|
286
|
+
disabled
|
|
287
|
+
hidden
|
|
288
|
+
/>
|
|
289
|
+
<button type="button" class="increment" aria-label="Increment">
|
|
290
|
+
<span class="zero">Add to Cart</span>
|
|
291
|
+
<span class="other" hidden>+</span>
|
|
292
|
+
</button>
|
|
293
|
+
</form-spinbutton>
|
|
294
|
+
</li>
|
|
295
|
+
</ul>
|
|
296
|
+
</module-catalog>
|
|
297
|
+
---
|
|
298
|
+
{% source title="ModuleCatalog source code" src="./examples/module-catalog.html" /%}
|
|
299
|
+
{% source title="BasicButton source code" src="./examples/basic-button.html" /%}
|
|
300
|
+
{% source title="FormSpinbutton source code" src="./examples/form-spinbutton.html" /%}
|
|
301
|
+
{% /demo %}
|
|
302
|
+
|
|
303
|
+
{% /section %}
|
|
304
|
+
|
|
305
|
+
{% section %}
|
|
306
|
+
|
|
307
|
+
## Providing Context
|
|
308
|
+
|
|
309
|
+
Context allows **parent components to share state** with any descendant components in the DOM tree, **without prop drilling**. This is perfect for application-wide settings like user preferences, theme data, or authentication state.
|
|
310
|
+
|
|
311
|
+
### Creating Context Keys
|
|
312
|
+
|
|
313
|
+
First, define typed context keys for the values you want to share:
|
|
314
|
+
|
|
315
|
+
```ts#context-media.ts
|
|
316
|
+
// Define context keys with types
|
|
317
|
+
export const MEDIA_MOTION = 'media-motion' as Context<
|
|
318
|
+
'media-motion',
|
|
319
|
+
() => 'no-preference' | 'reduce'
|
|
320
|
+
>
|
|
321
|
+
export const MEDIA_THEME = 'media-theme' as Context<
|
|
322
|
+
'media-theme',
|
|
323
|
+
() => 'light' | 'dark'
|
|
324
|
+
>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Provider Component
|
|
328
|
+
|
|
329
|
+
The **provider component** creates the shared state and makes it available to descendants:
|
|
330
|
+
|
|
331
|
+
```ts#context-media.ts
|
|
332
|
+
export type ContextMediaProps = {
|
|
333
|
+
readonly 'media-motion': 'no-preference' | 'reduce'
|
|
334
|
+
readonly 'media-theme': 'light' | 'dark'
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
declare global {
|
|
338
|
+
interface HTMLElementTagNameMap {
|
|
339
|
+
'context-media': Component<ContextMediaProps>
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export default defineComponent<ContextMediaProps>(
|
|
344
|
+
'context-media',
|
|
345
|
+
{
|
|
346
|
+
[MEDIA_MOTION]: () => {
|
|
347
|
+
const mql = matchMedia('(prefers-reduced-motion: reduce)')
|
|
348
|
+
const motion = createState(mql.matches ? 'reduce' : 'no-preference')
|
|
349
|
+
mql.addEventListener('change', e => {
|
|
350
|
+
motion.set(e.matches ? 'reduce' : 'no-preference')
|
|
351
|
+
})
|
|
352
|
+
return motion
|
|
353
|
+
},
|
|
354
|
+
[MEDIA_THEME]: () => {
|
|
355
|
+
const mql = matchMedia('(prefers-color-scheme: dark)')
|
|
356
|
+
const theme = createState(mql.matches ? 'dark' : 'light')
|
|
357
|
+
mql.addEventListener('change', e => {
|
|
358
|
+
theme.set(e.matches ? 'dark' : 'light')
|
|
359
|
+
})
|
|
360
|
+
return theme
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
undefined, // Component has no own descendant elements
|
|
364
|
+
() => ({
|
|
365
|
+
host: provideContexts([MEDIA_MOTION, MEDIA_THEME]),
|
|
366
|
+
}),
|
|
367
|
+
)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Usage in HTML
|
|
371
|
+
|
|
372
|
+
The provider component wraps your entire application or a section that needs shared state:
|
|
373
|
+
|
|
374
|
+
```html#index.html
|
|
375
|
+
<context-media>
|
|
376
|
+
<!-- Arbitrarily nested HTML with one or many context consumers -->
|
|
377
|
+
<main>
|
|
378
|
+
<card-mediaqueries>
|
|
379
|
+
<dl>
|
|
380
|
+
<dt>Motion Preference:</dt>
|
|
381
|
+
<dd class="motion"></dd>
|
|
382
|
+
<dt>Theme Preference:</dt>
|
|
383
|
+
<dd class="theme"></dd>
|
|
384
|
+
</dl>
|
|
385
|
+
</card-mediaqueries>
|
|
386
|
+
</main>
|
|
387
|
+
</context-media>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Key Benefits:**
|
|
391
|
+
|
|
392
|
+
- **Centralized State**: All global state is managed in one place
|
|
393
|
+
- **Type Safety**: Full TypeScript support with autocomplete
|
|
394
|
+
- **Reactive Updates**: All consumers automatically update when context changes
|
|
395
|
+
- **No Prop Drilling**: Deep components access context directly
|
|
396
|
+
|
|
397
|
+
{% /section %}
|
|
398
|
+
|
|
399
|
+
{% section %}
|
|
400
|
+
|
|
401
|
+
## Consuming Context
|
|
402
|
+
|
|
403
|
+
**Consumer components** use `requestContext()` to access shared state from ancestor providers. The context is automatically reactive - when the provider updates the context, all consumers update immediately.
|
|
404
|
+
|
|
405
|
+
### Consumer Component
|
|
406
|
+
|
|
407
|
+
Here's a simple card that displays the current motion and theme preferences:
|
|
408
|
+
|
|
409
|
+
```js#card-mediaqueries.js
|
|
410
|
+
export default defineComponent(
|
|
411
|
+
'card-mediaqueries',
|
|
412
|
+
{
|
|
413
|
+
motion: requestContext(MEDIA_MOTION, 'unknown'),
|
|
414
|
+
theme: requestContext(MEDIA_THEME, 'unknown'),
|
|
415
|
+
},
|
|
416
|
+
({ first }) => ({
|
|
417
|
+
motion: first('.motion'),
|
|
418
|
+
theme: first('.theme'),
|
|
419
|
+
}),
|
|
420
|
+
() => ({
|
|
421
|
+
motion: setText('motion'),
|
|
422
|
+
theme: setText('theme'),
|
|
423
|
+
}),
|
|
424
|
+
)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Full Example
|
|
428
|
+
|
|
429
|
+
{% demo %}
|
|
430
|
+
<context-media>
|
|
431
|
+
<card-mediaqueries>
|
|
432
|
+
<dl>
|
|
433
|
+
<dt>Motion Preference:</dt>
|
|
434
|
+
<dd class="motion"></dd>
|
|
435
|
+
<dt>Theme Preference:</dt>
|
|
436
|
+
<dd class="theme"></dd>
|
|
437
|
+
<dt>Device Viewport:</dt>
|
|
438
|
+
<dd class="viewport"></dd>
|
|
439
|
+
<dt>Device Orientation:</dt>
|
|
440
|
+
<dd class="orientation"></dd>
|
|
441
|
+
</dl>
|
|
442
|
+
</card-mediaqueries>
|
|
443
|
+
</context-media>
|
|
444
|
+
---
|
|
445
|
+
{% source title="ContextMedia source code" src="./examples/context-media.html" /%}
|
|
446
|
+
{% source title="CardMediaqueries source code" src="./examples/card-mediaqueries.html" /%}
|
|
447
|
+
{% /demo %}
|
|
448
|
+
|
|
449
|
+
{% /section %}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Getting Started'
|
|
3
|
+
emoji: '🚀'
|
|
4
|
+
description: 'Installation, setup, and first steps'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
{% hero %}
|
|
8
|
+
# 🚀 Getting Started
|
|
9
|
+
|
|
10
|
+
**Set up Le Truc in minutes – no build tools required**. Or use any package manager and bundler to take advantage of TypeScript support and optimize frontend assets.
|
|
11
|
+
{% /hero %}
|
|
12
|
+
|
|
13
|
+
{% section %}
|
|
14
|
+
## How to Install Le Truc
|
|
15
|
+
|
|
16
|
+
Le Truc works **without build tools** but also supports **package managers and bundlers** for larger projects. Choose the option that best fits your needs.
|
|
17
|
+
|
|
18
|
+
### Using a CDN
|
|
19
|
+
|
|
20
|
+
For the easiest setup, include Le Truc via a CDN. This is ideal for **testing or quick projects** where you want lightweight interactivity without additional tooling.
|
|
21
|
+
|
|
22
|
+
```html#page.html
|
|
23
|
+
<script src="https://cdn.jsdelivr.net/npm/@zeix/le-truc@latest/index.js"></script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Self-Hosting Le Truc
|
|
27
|
+
|
|
28
|
+
For production use, you may want to **self-host Le Truc** to avoid relying on a CDN. You can download the latest version from:
|
|
29
|
+
|
|
30
|
+
[index.js in Github Repository](https://github.com/zeixcom/le-truc/blob/main/index.js)
|
|
31
|
+
|
|
32
|
+
Simply host the file on your server and include it like this:
|
|
33
|
+
|
|
34
|
+
```html#page.html
|
|
35
|
+
<script src="/path/to/your/hosted/le-truc.js"></script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Why self-host?**
|
|
39
|
+
|
|
40
|
+
- You **control updates** and avoid breaking changes from external CDNs.
|
|
41
|
+
- Works for **projects with stricter Content Security Policy rules**.
|
|
42
|
+
|
|
43
|
+
Remember to keep the hosted file updated to use the latest features and bug fixes.
|
|
44
|
+
|
|
45
|
+
### Installing via Package Managers
|
|
46
|
+
|
|
47
|
+
If you're using a **bundler** like **Vite, Webpack, or Rollup**, install Le Truc via NPM or Bun:
|
|
48
|
+
|
|
49
|
+
{% tabgroup %}
|
|
50
|
+
#### NPM
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm install @zeix/le-truc
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
#### Bun
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
bun add @zeix/le-truc
|
|
62
|
+
```
|
|
63
|
+
{% /tabgroup %}
|
|
64
|
+
|
|
65
|
+
Then import the needed functions in your JavaScript:
|
|
66
|
+
|
|
67
|
+
```js#main.js
|
|
68
|
+
import { asString, component, on, setText } from '@zeix/le-truc'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
{% /section %}
|
|
72
|
+
|
|
73
|
+
{% section %}
|
|
74
|
+
|
|
75
|
+
## Creating Your First Component
|
|
76
|
+
|
|
77
|
+
Now, let's create an interactive Web Component to verify your setup.
|
|
78
|
+
|
|
79
|
+
**What This Component Does**
|
|
80
|
+
|
|
81
|
+
- Displays `Hello, World!` by default.
|
|
82
|
+
- Updates dynamically when you type into the input field.
|
|
83
|
+
|
|
84
|
+
### Markup
|
|
85
|
+
|
|
86
|
+
Include the following in your server-rendered HTML:
|
|
87
|
+
|
|
88
|
+
```html#page.html
|
|
89
|
+
<basic-hello>
|
|
90
|
+
<label>
|
|
91
|
+
Your name<br />
|
|
92
|
+
<input name="name" type="text" autocomplete="given-name" />
|
|
93
|
+
</label>
|
|
94
|
+
<p>Hello, <output>World</output>!</p>
|
|
95
|
+
</basic-hello>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Component Definition
|
|
99
|
+
|
|
100
|
+
Save the following inside a `<script type="module">` tag or an external JavaScript file.
|
|
101
|
+
|
|
102
|
+
```html#page.html
|
|
103
|
+
<script type="module">
|
|
104
|
+
import {
|
|
105
|
+
asString,
|
|
106
|
+
defineComponent,
|
|
107
|
+
on,
|
|
108
|
+
setText,
|
|
109
|
+
} from 'https://cdn.jsdelivr.net/npm/@zeix/le-truc@latest/index.js'
|
|
110
|
+
|
|
111
|
+
defineComponent(
|
|
112
|
+
'basic-hello',
|
|
113
|
+
{
|
|
114
|
+
name: asString(ui => ui.output.textContent),
|
|
115
|
+
},
|
|
116
|
+
({ first }) => ({
|
|
117
|
+
input: first('input', 'Needed to enter the name.'),
|
|
118
|
+
output: first('output', 'Needed to display the name.'),
|
|
119
|
+
}),
|
|
120
|
+
({ host, input }) => {
|
|
121
|
+
const fallback = host.name
|
|
122
|
+
return {
|
|
123
|
+
input: on('input', () => {
|
|
124
|
+
host.name = input.value || fallback
|
|
125
|
+
}),
|
|
126
|
+
output: setText('name'),
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
)
|
|
130
|
+
</script>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Understanding Your First Component
|
|
134
|
+
|
|
135
|
+
This component demonstrates Le Truc's core concepts:
|
|
136
|
+
|
|
137
|
+
- **Reactive Properties**: `name: asString(...)` creates a reactive property that syncs with the `name` attribute and falls back to the `<output>` content
|
|
138
|
+
- **Effects**: The setup function returns effects that handle user input and update the display text
|
|
139
|
+
- **Element Selection**: `first()` selects descendant element to apply effects to
|
|
140
|
+
|
|
141
|
+
Learn more about these concepts in the [Components](components.html) guide.
|
|
142
|
+
|
|
143
|
+
{% /section %}
|
|
144
|
+
|
|
145
|
+
{% section %}
|
|
146
|
+
|
|
147
|
+
## Verifying Your Installation
|
|
148
|
+
|
|
149
|
+
If everything is set up correctly, you should see:
|
|
150
|
+
|
|
151
|
+
- A text input field
|
|
152
|
+
- A greeting (`Hello, World!`)
|
|
153
|
+
- The greeting updates as you type
|
|
154
|
+
|
|
155
|
+
{% demo %}
|
|
156
|
+
<basic-hello>
|
|
157
|
+
<label>Your name<br>
|
|
158
|
+
<input name="name" type="text" autocomplete="given-name">
|
|
159
|
+
</label>
|
|
160
|
+
<p>Hello, <output>World</output>!</p>
|
|
161
|
+
</basic-hello>
|
|
162
|
+
{% /demo %}
|
|
163
|
+
|
|
164
|
+
If it's not working:
|
|
165
|
+
|
|
166
|
+
- Check the browser console for errors (missing imports, typos).
|
|
167
|
+
- Ensure your `<script>` tag is set to `type="module"` when using ES modules.
|
|
168
|
+
- If using NPM, confirm Le Truc is installed inside `node_modules/@zeix/le-truc`.
|
|
169
|
+
|
|
170
|
+
{% /section %}
|