@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,82 @@
|
|
|
1
|
+
<module-catalog>
|
|
2
|
+
<header>
|
|
3
|
+
<p>Shop</p>
|
|
4
|
+
<basic-button disabled>
|
|
5
|
+
<button type="button" disabled>
|
|
6
|
+
<span class="label">🛒 Shopping Cart</span>
|
|
7
|
+
<span class="badge"></span>
|
|
8
|
+
</button>
|
|
9
|
+
</basic-button>
|
|
10
|
+
</header>
|
|
11
|
+
<ul>
|
|
12
|
+
<li>
|
|
13
|
+
<p>Product 1</p>
|
|
14
|
+
<form-spinbutton>
|
|
15
|
+
<button type="button" class="decrement" aria-label="Decrement" hidden>
|
|
16
|
+
−
|
|
17
|
+
</button>
|
|
18
|
+
<input
|
|
19
|
+
type="number"
|
|
20
|
+
class="value"
|
|
21
|
+
name="amount-product1"
|
|
22
|
+
value="0"
|
|
23
|
+
min="0"
|
|
24
|
+
max="10"
|
|
25
|
+
readonly
|
|
26
|
+
disabled
|
|
27
|
+
hidden
|
|
28
|
+
/>
|
|
29
|
+
<button type="button" class="increment" aria-label="Increment">
|
|
30
|
+
<span class="zero">Add to Cart</span>
|
|
31
|
+
<span class="other" hidden>+</span>
|
|
32
|
+
</button>
|
|
33
|
+
</form-spinbutton>
|
|
34
|
+
</li>
|
|
35
|
+
<li>
|
|
36
|
+
<p>Product 2</p>
|
|
37
|
+
<form-spinbutton>
|
|
38
|
+
<button type="button" class="decrement" aria-label="Decrement" hidden>
|
|
39
|
+
−
|
|
40
|
+
</button>
|
|
41
|
+
<input
|
|
42
|
+
type="number"
|
|
43
|
+
class="value"
|
|
44
|
+
name="amount-product2"
|
|
45
|
+
value="0"
|
|
46
|
+
min="0"
|
|
47
|
+
max="5"
|
|
48
|
+
readonly
|
|
49
|
+
disabled
|
|
50
|
+
hidden
|
|
51
|
+
/>
|
|
52
|
+
<button type="button" class="increment" aria-label="Increment">
|
|
53
|
+
<span class="zero">Add to Cart</span>
|
|
54
|
+
<span class="other" hidden>+</span>
|
|
55
|
+
</button>
|
|
56
|
+
</form-spinbutton>
|
|
57
|
+
</li>
|
|
58
|
+
<li>
|
|
59
|
+
<p>Product 3</p>
|
|
60
|
+
<form-spinbutton>
|
|
61
|
+
<button type="button" class="decrement" aria-label="Decrement" hidden>
|
|
62
|
+
−
|
|
63
|
+
</button>
|
|
64
|
+
<input
|
|
65
|
+
type="number"
|
|
66
|
+
class="value"
|
|
67
|
+
name="amount-product3"
|
|
68
|
+
value="0"
|
|
69
|
+
min="0"
|
|
70
|
+
max="20"
|
|
71
|
+
readonly
|
|
72
|
+
disabled
|
|
73
|
+
hidden
|
|
74
|
+
/>
|
|
75
|
+
<button type="button" class="increment" aria-label="Increment">
|
|
76
|
+
<span class="zero">Add to Cart</span>
|
|
77
|
+
<span class="other" hidden>+</span>
|
|
78
|
+
</button>
|
|
79
|
+
</form-spinbutton>
|
|
80
|
+
</li>
|
|
81
|
+
</ul>
|
|
82
|
+
</module-catalog>
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
test.describe('module-catalog component', () => {
|
|
4
|
+
test.beforeEach(async ({ page }) => {
|
|
5
|
+
page.on('console', msg => {
|
|
6
|
+
console.log(`[browser] ${msg.type()}: ${msg.text()}`)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
await page.goto('http://localhost:3000/test/module-catalog.html')
|
|
10
|
+
await page.waitForSelector('module-catalog')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('renders initial state correctly', async ({ page }) => {
|
|
14
|
+
const catalog = page.locator('module-catalog')
|
|
15
|
+
const button = catalog.locator('basic-button button')
|
|
16
|
+
const badge = catalog.locator('basic-button .badge')
|
|
17
|
+
const spinbuttons = catalog.locator('form-spinbutton')
|
|
18
|
+
|
|
19
|
+
// Should have 3 spinbutton components
|
|
20
|
+
await expect(spinbuttons).toHaveCount(3)
|
|
21
|
+
|
|
22
|
+
// Button should be disabled initially (no items in cart)
|
|
23
|
+
await expect(button).toHaveAttribute('disabled')
|
|
24
|
+
await expect(button).toBeDisabled()
|
|
25
|
+
|
|
26
|
+
// Badge should be empty initially
|
|
27
|
+
await expect(badge).toHaveText('')
|
|
28
|
+
|
|
29
|
+
// All spinbuttons should start at 0
|
|
30
|
+
const inputs = catalog.locator('form-spinbutton input.value')
|
|
31
|
+
await expect(inputs.nth(0)).toHaveValue('0')
|
|
32
|
+
await expect(inputs.nth(1)).toHaveValue('0')
|
|
33
|
+
await expect(inputs.nth(2)).toHaveValue('0')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('calculates total and enables button when items are added', async ({
|
|
37
|
+
page,
|
|
38
|
+
}) => {
|
|
39
|
+
const catalog = page.locator('module-catalog')
|
|
40
|
+
const button = catalog.locator('basic-button button')
|
|
41
|
+
const badge = catalog.locator('basic-button .badge')
|
|
42
|
+
|
|
43
|
+
// Add 2 of Product 1
|
|
44
|
+
const product1Increment = catalog
|
|
45
|
+
.locator('form-spinbutton')
|
|
46
|
+
.nth(0)
|
|
47
|
+
.locator('button.increment')
|
|
48
|
+
await product1Increment.click()
|
|
49
|
+
await product1Increment.click()
|
|
50
|
+
|
|
51
|
+
// Button should be enabled and show total
|
|
52
|
+
await expect(button).not.toHaveAttribute('disabled')
|
|
53
|
+
await expect(button).not.toBeDisabled()
|
|
54
|
+
await expect(badge).toHaveText('2')
|
|
55
|
+
|
|
56
|
+
// Add 3 of Product 2
|
|
57
|
+
const product2Increment = catalog
|
|
58
|
+
.locator('form-spinbutton')
|
|
59
|
+
.nth(1)
|
|
60
|
+
.locator('button.increment')
|
|
61
|
+
await product2Increment.click()
|
|
62
|
+
await product2Increment.click()
|
|
63
|
+
await product2Increment.click()
|
|
64
|
+
|
|
65
|
+
// Total should be updated
|
|
66
|
+
await expect(badge).toHaveText('5')
|
|
67
|
+
await expect(button).not.toBeDisabled()
|
|
68
|
+
|
|
69
|
+
// Add 1 of Product 3
|
|
70
|
+
const product3Increment = catalog
|
|
71
|
+
.locator('form-spinbutton')
|
|
72
|
+
.nth(2)
|
|
73
|
+
.locator('button.increment')
|
|
74
|
+
await product3Increment.click()
|
|
75
|
+
|
|
76
|
+
// Total should be 6
|
|
77
|
+
await expect(badge).toHaveText('6')
|
|
78
|
+
await expect(button).not.toBeDisabled()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('updates total when items are decremented', async ({ page }) => {
|
|
82
|
+
const catalog = page.locator('module-catalog')
|
|
83
|
+
const button = catalog.locator('basic-button button')
|
|
84
|
+
const badge = catalog.locator('basic-button .badge')
|
|
85
|
+
|
|
86
|
+
// First add some items
|
|
87
|
+
const product1Increment = catalog
|
|
88
|
+
.locator('form-spinbutton')
|
|
89
|
+
.nth(0)
|
|
90
|
+
.locator('button.increment')
|
|
91
|
+
const product2Increment = catalog
|
|
92
|
+
.locator('form-spinbutton')
|
|
93
|
+
.nth(1)
|
|
94
|
+
.locator('button.increment')
|
|
95
|
+
|
|
96
|
+
await product1Increment.click()
|
|
97
|
+
await product1Increment.click()
|
|
98
|
+
await product1Increment.click() // 3 items
|
|
99
|
+
await product2Increment.click()
|
|
100
|
+
await product2Increment.click() // 2 items
|
|
101
|
+
|
|
102
|
+
await expect(badge).toHaveText('5')
|
|
103
|
+
|
|
104
|
+
// Now decrement Product 1
|
|
105
|
+
const product1Decrement = catalog
|
|
106
|
+
.locator('form-spinbutton')
|
|
107
|
+
.nth(0)
|
|
108
|
+
.locator('button.decrement')
|
|
109
|
+
await product1Decrement.click()
|
|
110
|
+
|
|
111
|
+
// Total should be updated
|
|
112
|
+
await expect(badge).toHaveText('4')
|
|
113
|
+
await expect(button).not.toBeDisabled()
|
|
114
|
+
|
|
115
|
+
// Decrement Product 2 to 0
|
|
116
|
+
const product2Decrement = catalog
|
|
117
|
+
.locator('form-spinbutton')
|
|
118
|
+
.nth(1)
|
|
119
|
+
.locator('button.decrement')
|
|
120
|
+
await product2Decrement.click()
|
|
121
|
+
await product2Decrement.click()
|
|
122
|
+
|
|
123
|
+
// Total should be 2 (only Product 1 remaining)
|
|
124
|
+
await expect(badge).toHaveText('2')
|
|
125
|
+
await expect(button).not.toBeDisabled()
|
|
126
|
+
|
|
127
|
+
// Decrement Product 1 to 0
|
|
128
|
+
await product1Decrement.click()
|
|
129
|
+
await product1Decrement.click()
|
|
130
|
+
|
|
131
|
+
// Button should be disabled again, badge empty
|
|
132
|
+
await expect(button).toHaveAttribute('disabled')
|
|
133
|
+
await expect(button).toBeDisabled()
|
|
134
|
+
await expect(badge).toHaveText('')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('handles reaching maximum values for individual products', async ({
|
|
138
|
+
page,
|
|
139
|
+
}) => {
|
|
140
|
+
const catalog = page.locator('module-catalog')
|
|
141
|
+
const badge = catalog.locator('basic-button .badge')
|
|
142
|
+
|
|
143
|
+
// Product 1 max is 10, Product 2 max is 5, Product 3 max is 20
|
|
144
|
+
const product1Increment = catalog
|
|
145
|
+
.locator('form-spinbutton')
|
|
146
|
+
.nth(0)
|
|
147
|
+
.locator('button.increment')
|
|
148
|
+
const product2Increment = catalog
|
|
149
|
+
.locator('form-spinbutton')
|
|
150
|
+
.nth(1)
|
|
151
|
+
.locator('button.increment')
|
|
152
|
+
|
|
153
|
+
// Max out Product 1 (10 items)
|
|
154
|
+
for (let i = 0; i < 10; i++) {
|
|
155
|
+
await product1Increment.click()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Max out Product 2 (5 items)
|
|
159
|
+
for (let i = 0; i < 5; i++) {
|
|
160
|
+
await product2Increment.click()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Total should be 15
|
|
164
|
+
await expect(badge).toHaveText('15')
|
|
165
|
+
|
|
166
|
+
// Increment buttons should be disabled at max
|
|
167
|
+
await expect(product1Increment).toHaveAttribute('disabled')
|
|
168
|
+
await expect(product2Increment).toHaveAttribute('disabled')
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
test('reactive computation updates immediately', async ({ page }) => {
|
|
172
|
+
const catalog = page.locator('module-catalog')
|
|
173
|
+
const badge = catalog.locator('basic-button .badge')
|
|
174
|
+
|
|
175
|
+
const product1Increment = catalog
|
|
176
|
+
.locator('form-spinbutton')
|
|
177
|
+
.nth(0)
|
|
178
|
+
.locator('button.increment')
|
|
179
|
+
|
|
180
|
+
// Multiple rapid clicks should update total immediately
|
|
181
|
+
await product1Increment.click()
|
|
182
|
+
await expect(badge).toHaveText('1')
|
|
183
|
+
|
|
184
|
+
await product1Increment.click()
|
|
185
|
+
await expect(badge).toHaveText('2')
|
|
186
|
+
|
|
187
|
+
await product1Increment.click()
|
|
188
|
+
await expect(badge).toHaveText('3')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('total reflects component property values', async ({ page }) => {
|
|
192
|
+
const catalog = page.locator('module-catalog')
|
|
193
|
+
|
|
194
|
+
// Add items to different products
|
|
195
|
+
const product1Increment = catalog
|
|
196
|
+
.locator('form-spinbutton')
|
|
197
|
+
.nth(0)
|
|
198
|
+
.locator('button.increment')
|
|
199
|
+
const product2Increment = catalog
|
|
200
|
+
.locator('form-spinbutton')
|
|
201
|
+
.nth(1)
|
|
202
|
+
.locator('button.increment')
|
|
203
|
+
const product3Increment = catalog
|
|
204
|
+
.locator('form-spinbutton')
|
|
205
|
+
.nth(2)
|
|
206
|
+
.locator('button.increment')
|
|
207
|
+
|
|
208
|
+
await product1Increment.click() // 1
|
|
209
|
+
await product2Increment.click()
|
|
210
|
+
await product2Increment.click() // 2
|
|
211
|
+
await product3Increment.click()
|
|
212
|
+
await product3Increment.click()
|
|
213
|
+
await product3Increment.click() // 3
|
|
214
|
+
|
|
215
|
+
// Verify component properties match expected values
|
|
216
|
+
const componentValues = await page.evaluate(() => {
|
|
217
|
+
const spinbuttons = document.querySelectorAll('form-spinbutton')
|
|
218
|
+
return Array.from(spinbuttons).map((sb: any) => sb.value)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
expect(componentValues).toEqual([1, 2, 3])
|
|
222
|
+
|
|
223
|
+
// Total should be sum of all values
|
|
224
|
+
const badge = catalog.locator('basic-button .badge')
|
|
225
|
+
await expect(badge).toHaveText('6')
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('button disabled state changes correctly', async ({ page }) => {
|
|
229
|
+
const catalog = page.locator('module-catalog')
|
|
230
|
+
const button = catalog.locator('basic-button button')
|
|
231
|
+
|
|
232
|
+
const product1Increment = catalog
|
|
233
|
+
.locator('form-spinbutton')
|
|
234
|
+
.nth(0)
|
|
235
|
+
.locator('button.increment')
|
|
236
|
+
const product1Decrement = catalog
|
|
237
|
+
.locator('form-spinbutton')
|
|
238
|
+
.nth(0)
|
|
239
|
+
.locator('button.decrement')
|
|
240
|
+
|
|
241
|
+
// Initially disabled
|
|
242
|
+
await expect(button).toBeDisabled()
|
|
243
|
+
|
|
244
|
+
// Add item - becomes enabled
|
|
245
|
+
await product1Increment.click()
|
|
246
|
+
await expect(button).not.toBeDisabled()
|
|
247
|
+
|
|
248
|
+
// Remove item - becomes disabled again
|
|
249
|
+
await product1Decrement.click()
|
|
250
|
+
await expect(button).toBeDisabled()
|
|
251
|
+
|
|
252
|
+
// Add multiple items
|
|
253
|
+
await product1Increment.click()
|
|
254
|
+
await product1Increment.click()
|
|
255
|
+
await expect(button).not.toBeDisabled()
|
|
256
|
+
|
|
257
|
+
// Remove one - still enabled
|
|
258
|
+
await product1Decrement.click()
|
|
259
|
+
await expect(button).not.toBeDisabled()
|
|
260
|
+
|
|
261
|
+
// Remove last - disabled
|
|
262
|
+
await product1Decrement.click()
|
|
263
|
+
await expect(button).toBeDisabled()
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
test('handles mixed interactions across all products', async ({ page }) => {
|
|
267
|
+
const catalog = page.locator('module-catalog')
|
|
268
|
+
const badge = catalog.locator('basic-button .badge')
|
|
269
|
+
const button = catalog.locator('basic-button button')
|
|
270
|
+
|
|
271
|
+
// Get all increment buttons
|
|
272
|
+
const increments = [
|
|
273
|
+
catalog.locator('form-spinbutton').nth(0).locator('button.increment'),
|
|
274
|
+
catalog.locator('form-spinbutton').nth(1).locator('button.increment'),
|
|
275
|
+
catalog.locator('form-spinbutton').nth(2).locator('button.increment'),
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
// Get all decrement buttons
|
|
279
|
+
const decrements = [
|
|
280
|
+
catalog.locator('form-spinbutton').nth(0).locator('button.decrement'),
|
|
281
|
+
catalog.locator('form-spinbutton').nth(1).locator('button.decrement'),
|
|
282
|
+
catalog.locator('form-spinbutton').nth(2).locator('button.decrement'),
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
// Complex interaction pattern
|
|
286
|
+
await increments[0].click() // Product 1: 1, Total: 1
|
|
287
|
+
await expect(badge).toHaveText('1')
|
|
288
|
+
|
|
289
|
+
await increments[1].click() // Product 2: 1, Total: 2
|
|
290
|
+
await increments[1].click() // Product 2: 2, Total: 3
|
|
291
|
+
await expect(badge).toHaveText('3')
|
|
292
|
+
|
|
293
|
+
await increments[2].click() // Product 3: 1, Total: 4
|
|
294
|
+
await increments[0].click() // Product 1: 2, Total: 5
|
|
295
|
+
await expect(badge).toHaveText('5')
|
|
296
|
+
|
|
297
|
+
// Now some decrements
|
|
298
|
+
await decrements[1].click() // Product 2: 1, Total: 4
|
|
299
|
+
await expect(badge).toHaveText('4')
|
|
300
|
+
|
|
301
|
+
await decrements[0].click() // Product 1: 1, Total: 3
|
|
302
|
+
await decrements[0].click() // Product 1: 0, Total: 2
|
|
303
|
+
await expect(badge).toHaveText('2')
|
|
304
|
+
|
|
305
|
+
// Still enabled because other products have items
|
|
306
|
+
await expect(button).not.toBeDisabled()
|
|
307
|
+
|
|
308
|
+
// Remove remaining items
|
|
309
|
+
await decrements[1].click() // Product 2: 0, Total: 1
|
|
310
|
+
await decrements[2].click() // Product 3: 0, Total: 0
|
|
311
|
+
await expect(badge).toHaveText('')
|
|
312
|
+
await expect(button).toBeDisabled()
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
test('badge text is always string representation of total', async ({
|
|
316
|
+
page,
|
|
317
|
+
}) => {
|
|
318
|
+
const catalog = page.locator('module-catalog')
|
|
319
|
+
const badge = catalog.locator('basic-button .badge')
|
|
320
|
+
|
|
321
|
+
const product1Increment = catalog
|
|
322
|
+
.locator('form-spinbutton')
|
|
323
|
+
.nth(0)
|
|
324
|
+
.locator('button.increment')
|
|
325
|
+
|
|
326
|
+
// Test various totals are properly stringified
|
|
327
|
+
for (let i = 1; i <= 5; i++) {
|
|
328
|
+
await product1Increment.click()
|
|
329
|
+
await expect(badge).toHaveText(String(i))
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
test('component coordination works with keyboard interactions', async ({
|
|
334
|
+
page,
|
|
335
|
+
}) => {
|
|
336
|
+
const catalog = page.locator('module-catalog')
|
|
337
|
+
const badge = catalog.locator('basic-button .badge')
|
|
338
|
+
|
|
339
|
+
// Use keyboard on first product
|
|
340
|
+
const product1Increment = catalog
|
|
341
|
+
.locator('form-spinbutton')
|
|
342
|
+
.nth(0)
|
|
343
|
+
.locator('button.increment')
|
|
344
|
+
|
|
345
|
+
await product1Increment.focus()
|
|
346
|
+
await page.keyboard.press('ArrowUp')
|
|
347
|
+
await expect(badge).toHaveText('1')
|
|
348
|
+
|
|
349
|
+
await page.keyboard.press('ArrowUp')
|
|
350
|
+
await expect(badge).toHaveText('2')
|
|
351
|
+
|
|
352
|
+
await page.keyboard.press('ArrowDown')
|
|
353
|
+
await expect(badge).toHaveText('1')
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
test('all spinbuttons contribute to total calculation', async ({ page }) => {
|
|
357
|
+
const catalog = page.locator('module-catalog')
|
|
358
|
+
const badge = catalog.locator('basic-button .badge')
|
|
359
|
+
|
|
360
|
+
// Verify all 3 spinbuttons are found and contribute
|
|
361
|
+
const spinbuttonCount = await catalog.locator('form-spinbutton').count()
|
|
362
|
+
expect(spinbuttonCount).toBe(3)
|
|
363
|
+
|
|
364
|
+
// Add 1 to each spinbutton
|
|
365
|
+
for (let i = 0; i < 3; i++) {
|
|
366
|
+
const increment = catalog
|
|
367
|
+
.locator('form-spinbutton')
|
|
368
|
+
.nth(i)
|
|
369
|
+
.locator('button.increment')
|
|
370
|
+
await increment.click()
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Total should be 3
|
|
374
|
+
await expect(badge).toHaveText('3')
|
|
375
|
+
|
|
376
|
+
// Verify each spinbutton has value 1
|
|
377
|
+
const values = await page.evaluate(() => {
|
|
378
|
+
const spinbuttons = document.querySelectorAll('form-spinbutton')
|
|
379
|
+
return Array.from(spinbuttons).map((sb: any) => sb.value)
|
|
380
|
+
})
|
|
381
|
+
expect(values).toEqual([1, 1, 1])
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
test('component has no public properties exposed', async ({ page }) => {
|
|
385
|
+
// Verify the component doesn't expose any public interface
|
|
386
|
+
const hasPublicProps = await page.evaluate(() => {
|
|
387
|
+
const catalog = document.querySelector('module-catalog') as any
|
|
388
|
+
// Try to access common property names, should all be undefined or internal
|
|
389
|
+
const props = ['total', 'disabled', 'badge', 'value', 'count', 'items']
|
|
390
|
+
return props.some(prop => catalog[prop] !== undefined)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
// Component should not expose public properties
|
|
394
|
+
expect(hasPublicProps).toBe(false)
|
|
395
|
+
})
|
|
396
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Collection,
|
|
3
|
+
type Component,
|
|
4
|
+
createComputed,
|
|
5
|
+
defineComponent,
|
|
6
|
+
pass,
|
|
7
|
+
} from '../..'
|
|
8
|
+
import { BasicButtonProps } from '../basic-button/basic-button'
|
|
9
|
+
import { FormSpinbuttonProps } from '../form-spinbutton/form-spinbutton'
|
|
10
|
+
|
|
11
|
+
type ModuleCatalogUI = {
|
|
12
|
+
button: Component<BasicButtonProps>
|
|
13
|
+
spinbuttons: Collection<Component<FormSpinbuttonProps>>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default defineComponent<{}, ModuleCatalogUI>(
|
|
17
|
+
'module-catalog',
|
|
18
|
+
{},
|
|
19
|
+
({ all, first }) => ({
|
|
20
|
+
button: first('basic-button', 'Add a button to go go the Shopping Cart'),
|
|
21
|
+
spinbuttons: all(
|
|
22
|
+
'form-spinbutton',
|
|
23
|
+
'Add spinbutton components to calculate sum from.',
|
|
24
|
+
),
|
|
25
|
+
}),
|
|
26
|
+
({ spinbuttons }) => {
|
|
27
|
+
const total = createComputed(() =>
|
|
28
|
+
spinbuttons.get().reduce((sum, item) => sum + item.value, 0),
|
|
29
|
+
)
|
|
30
|
+
return {
|
|
31
|
+
button: pass({
|
|
32
|
+
disabled: () => !total.get(),
|
|
33
|
+
badge: () => (total.get() > 0 ? String(total.get()) : ''),
|
|
34
|
+
}),
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module-codeblock {
|
|
2
|
+
--color-shiki-monokai-bg: #272822;
|
|
3
|
+
|
|
4
|
+
position: relative;
|
|
5
|
+
display: block;
|
|
6
|
+
margin: 0 0 var(--space-l);
|
|
7
|
+
background: var(--color-shiki-monokai-bg);
|
|
8
|
+
border-radius: var(--space-s);
|
|
9
|
+
|
|
10
|
+
.meta {
|
|
11
|
+
display: flex;
|
|
12
|
+
margin-bottom: 0;
|
|
13
|
+
padding: var(--space-xs) var(--space-s) 0;
|
|
14
|
+
font-size: var(--font-size-s);
|
|
15
|
+
color: var(--color-neutral-20);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.language {
|
|
19
|
+
margin-left: auto;
|
|
20
|
+
text-transform: uppercase;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
& pre {
|
|
24
|
+
font-size: var(--font-size-s);
|
|
25
|
+
padding: var(--space-s);
|
|
26
|
+
border-radius: var(--space-s);
|
|
27
|
+
overflow: auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.copy {
|
|
31
|
+
position: absolute;
|
|
32
|
+
right: var(--space-s);
|
|
33
|
+
bottom: var(--space-s);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.overlay {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&[collapsed] {
|
|
41
|
+
max-height: 12rem;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
border-radius: var(--space-s) var(--space-s) 0 0;
|
|
44
|
+
|
|
45
|
+
&::after {
|
|
46
|
+
content: "";
|
|
47
|
+
display: block;
|
|
48
|
+
position: absolute;
|
|
49
|
+
bottom: 0;
|
|
50
|
+
width: 100%;
|
|
51
|
+
height: var(--space-m);
|
|
52
|
+
background:
|
|
53
|
+
linear-gradient(-135deg, var(--color-secondary) 0.5rem, transparent 0) 0
|
|
54
|
+
0.5rem,
|
|
55
|
+
linear-gradient(
|
|
56
|
+
135deg,
|
|
57
|
+
var(--color-secondary) 0.5rem,
|
|
58
|
+
var(--color-background) 0
|
|
59
|
+
)
|
|
60
|
+
0 0.5rem;
|
|
61
|
+
background-color: var(--color-secondary);
|
|
62
|
+
background-size: var(--space-m) var(--space-m);
|
|
63
|
+
background-position: bottom;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.copy {
|
|
67
|
+
display: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.overlay {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: column-reverse;
|
|
73
|
+
align-items: center;
|
|
74
|
+
position: absolute;
|
|
75
|
+
bottom: 0;
|
|
76
|
+
left: 0;
|
|
77
|
+
width: 100%;
|
|
78
|
+
height: 6rem;
|
|
79
|
+
color: var(--color-text);
|
|
80
|
+
background: linear-gradient(transparent, var(--color-secondary));
|
|
81
|
+
border: 0;
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
padding: var(--space-xs) var(--space-s);
|
|
84
|
+
margin-bottom: var(--space-m);
|
|
85
|
+
font-size: var(--font-size-s);
|
|
86
|
+
transition: background-color var(--transition-short) var(--easing-inout);
|
|
87
|
+
text-shadow: var(--color-background) 1px 0 var(--space-xs);
|
|
88
|
+
|
|
89
|
+
&:hover,
|
|
90
|
+
&:active {
|
|
91
|
+
text-shadow: var(--color-text-inverted) var(--space-xs) 0 var(--space-s);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<module-codeblock
|
|
2
|
+
id="codeblock_module-codeblock-html"
|
|
3
|
+
collapsed
|
|
4
|
+
language="html"
|
|
5
|
+
>
|
|
6
|
+
<p class="meta">
|
|
7
|
+
<span class="file">module-codeblock.html</span>
|
|
8
|
+
<span class="language">html</span>
|
|
9
|
+
</p>
|
|
10
|
+
<pre><code class="language-html"></code></pre>
|
|
11
|
+
<basic-button
|
|
12
|
+
class="copy"
|
|
13
|
+
copy-success="Copied!"
|
|
14
|
+
copy-error="Error trying to copy to clipboard!"
|
|
15
|
+
>
|
|
16
|
+
<button type="button" class="secondary small">
|
|
17
|
+
<span class="label">Copy</span>
|
|
18
|
+
</button>
|
|
19
|
+
</basic-button>
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
class="overlay"
|
|
23
|
+
aria-expanded="false"
|
|
24
|
+
aria-controls="codeblock_module-codeblock-html"
|
|
25
|
+
>
|
|
26
|
+
Expand
|
|
27
|
+
</button>
|
|
28
|
+
</module-codeblock>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asBoolean,
|
|
3
|
+
type Component,
|
|
4
|
+
defineComponent,
|
|
5
|
+
on,
|
|
6
|
+
toggleAttribute,
|
|
7
|
+
} from '../..'
|
|
8
|
+
import type { BasicButtonProps } from '../basic-button/basic-button'
|
|
9
|
+
import { copyToClipboard } from '../basic-button/copyToClipboard'
|
|
10
|
+
|
|
11
|
+
export type ModuleCodeblockProps = {
|
|
12
|
+
collapsed: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type ModuleCodeblockUI = {
|
|
16
|
+
code: HTMLElement
|
|
17
|
+
overlay?: HTMLButtonElement
|
|
18
|
+
copy?: Component<BasicButtonProps>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementTagNameMap {
|
|
23
|
+
'module-codeblock': Component<ModuleCodeblockProps>
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default defineComponent<ModuleCodeblockProps, ModuleCodeblockUI>(
|
|
28
|
+
'module-codeblock',
|
|
29
|
+
{ collapsed: asBoolean() },
|
|
30
|
+
({ first }) => ({
|
|
31
|
+
code: first('code', 'Needed as source container to copy from.'),
|
|
32
|
+
overlay: first('button.overlay'),
|
|
33
|
+
copy: first('basic-button.copy'),
|
|
34
|
+
}),
|
|
35
|
+
({ host, code, copy }) => ({
|
|
36
|
+
host: toggleAttribute('collapsed'),
|
|
37
|
+
overlay: on('click', () => {
|
|
38
|
+
host.collapsed = false
|
|
39
|
+
}),
|
|
40
|
+
copy: copyToClipboard(code, {
|
|
41
|
+
success: copy?.getAttribute('copy-success') || 'Copied!',
|
|
42
|
+
error:
|
|
43
|
+
copy?.getAttribute('copy-error') ||
|
|
44
|
+
'Error trying to copy to clipboard!',
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module-demo {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-m);
|
|
5
|
+
margin-block-end: var(--space-l);
|
|
6
|
+
|
|
7
|
+
.preview {
|
|
8
|
+
border: 1px dotted var(--color-border);
|
|
9
|
+
border-radius: var(--space-s);
|
|
10
|
+
padding: var(--space-s);
|
|
11
|
+
container: preview / inline-size;
|
|
12
|
+
}
|
|
13
|
+
}
|