├── katalog ├── gradle ├── gradle.properties ├── build.gradle.kts ├── katalog-ksp │ ├── src │ │ └── jvmMain │ │ │ ├── kotlin │ │ │ ├── util │ │ │ │ ├── Builder.kt │ │ │ │ ├── KSValueArgument.kt │ │ │ │ ├── KatalogLogger.kt │ │ │ │ └── KSFunctionDeclaration.kt │ │ │ ├── domain │ │ │ │ ├── ShowcaseName.kt │ │ │ │ └── ShowcaseData.kt │ │ │ ├── KatalogSymbolProcessorProvider.kt │ │ │ └── builder │ │ │ │ └── kotlinPoet.kt │ │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.devtools.ksp.processing.SymbolProcessorProvider │ └── build.gradle.kts ├── settings.gradle.kts └── katalog-runtime │ ├── src │ └── jsMain │ │ └── kotlin │ │ ├── KatalogConfig.kt │ │ ├── util │ │ ├── css.kt │ │ └── data.kt │ │ ├── Showcase.kt │ │ ├── domain │ │ ├── Showcase.kt │ │ └── Katalog.kt │ │ ├── UtilStyle.kt │ │ ├── layout │ │ └── Divider.kt │ │ ├── ui │ │ ├── KatalogThemeVariables.kt │ │ ├── Katalog.kt │ │ ├── Header.kt │ │ └── ShowcaseBox.kt │ │ └── theme │ │ └── KatalogTheme.kt │ └── build.gradle.kts ├── sandbox ├── gradle ├── gradle.properties ├── src │ └── jsMain │ │ ├── kotlin │ │ ├── util │ │ │ ├── general.kt │ │ │ ├── ChunkedFormFields.kt │ │ │ └── Named.kt │ │ ├── control │ │ │ ├── ActionControl.kt │ │ │ ├── FilterControl.kt │ │ │ ├── TextControl.kt │ │ │ ├── BooleanControl.kt │ │ │ ├── ChoiceControl.kt │ │ │ └── RangeControl.kt │ │ └── config.kt │ │ ├── resources │ │ ├── MDCImageList.scss │ │ ├── MDCCircularProgress.scss │ │ ├── sandbox.scss │ │ └── index.html │ │ └── showcases │ │ ├── MDCElevation.kt │ │ ├── MDCLineRipple.kt │ │ ├── MDCTouchTarget.kt │ │ ├── MDCRadio.kt │ │ ├── MDCCheckbox.kt │ │ ├── MDCFloatingLabel.kt │ │ ├── MDCSwitch.kt │ │ ├── MDCRipple.kt │ │ ├── MDCImageList.kt │ │ ├── MDCTypography.kt │ │ ├── MDCFormField.kt │ │ ├── MDCCircularProgress.kt │ │ ├── MDCLinearProgress.kt │ │ ├── MDCFab.kt │ │ ├── MDCButton.kt │ │ ├── MDCBanner.kt │ │ └── MDCNotchedOutline.kt ├── settings.gradle.kts └── build.gradle.kts ├── .github ├── CODEOWNERS ├── workflows │ └── pr.yml └── dependabot.yml ├── scripts ├── setupMingw.sh ├── setupOSX.sh ├── setupUbuntu.sh ├── setup.sh └── versionCatalogUpdate.sh ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── webpack.config.d │ └── files.js └── detekt.yml ├── kmdc ├── kmdc-drawer │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── Content.kt │ │ └── _events.kt ├── kmdc-ripple │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ └── MDCRipple.kt ├── kmdc-slider │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _events.kt │ │ ├── _module.kt │ │ ├── MDCSliderInput.kt │ │ ├── MDCSliderThumb.kt │ │ └── MDCSliderTrack.kt ├── kmdc-tab-bar │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _events.kt │ │ ├── bar │ │ ├── _module.kt │ │ ├── _events.kt │ │ └── MDCTabBar.kt │ │ ├── indicator │ │ ├── _module.kt │ │ ├── Indicator.kt │ │ └── Content.kt │ │ ├── scroller │ │ ├── _module.kt │ │ └── Scroller.kt │ │ ├── _module.kt │ │ └── Tab.kt ├── kmdc-elevation │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── MDCElevation.kt ├── kmdc-image-list │ └── build.gradle.kts ├── kmdc-typography │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── MDCTypographyStyle.kt ├── kmdc-layout-grid │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── Cell.kt ├── kmdc-line-ripple │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ └── MDCLineRipple.kt ├── kmdc-menu-surface │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── MDCMenuSurfaceAnchor.kt │ │ ├── _module.kt │ │ └── _events.kt ├── kmdc-touch-target │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── MDCTouchTarget.kt ├── kmdc-floating-label │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-linear-progress │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-notched-outline │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-base │ ├── src │ │ ├── jsMain │ │ │ └── kotlin │ │ │ │ ├── domain │ │ │ │ └── Point.kt │ │ │ │ ├── _module.kt │ │ │ │ ├── mdc.kt │ │ │ │ ├── events.kt │ │ │ │ ├── attrs.kt │ │ │ │ ├── annotations.kt │ │ │ │ └── MDCProvider.kt │ │ └── jsTest │ │ │ └── kotlin │ │ │ └── CounterTest.kt │ └── build.gradle.kts ├── kmdc-banner │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── _events.kt │ │ └── MDCBanner.kt ├── kmdc-form-field │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ └── MDCFormField.kt ├── kmdc-top-app-bar │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── _events.kt │ │ ├── Row.kt │ │ ├── Title.kt │ │ ├── Section.kt │ │ ├── Action.kt │ │ └── Navigation.kt ├── kmdc-segmented-button │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── Icon.kt │ │ ├── Label.kt │ │ ├── _module.kt │ │ ├── MDCSegmentedButton.kt │ │ └── _events.kt ├── kmdc-circular-progress │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-fab │ └── build.gradle.kts ├── kmdc-menu │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _events.kt │ │ ├── SelectionGroup.kt │ │ ├── MenuItem.kt │ │ └── _module.kt ├── kmdc-card │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── Content.kt │ │ ├── PrimaryAction.kt │ │ ├── MDCCard.kt │ │ └── Media.kt ├── kmdc-dialog │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── Title.kt │ │ ├── Content.kt │ │ ├── _events.kt │ │ └── Header.kt ├── kmdc-tooltip │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── Title.kt │ │ ├── Link.kt │ │ └── Content.kt ├── kmdc-radio │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-snackbar │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── Label.kt │ │ ├── _events.kt │ │ └── MDCSnackbar.kt ├── kmdc-switch │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-chips │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── action │ │ ├── Action.kt │ │ ├── Ripple.kt │ │ ├── Icon.kt │ │ ├── Label.kt │ │ └── Graphic.kt │ │ ├── grid │ │ ├── MDCChipsGrid.kt │ │ ├── ChipCell.kt │ │ ├── GridAction.kt │ │ └── ActionChip.kt │ │ ├── _module.kt │ │ ├── listbox │ │ └── MDCChipsListbox.kt │ │ ├── MDCChips.kt │ │ ├── _events.kt │ │ └── Chip.kt ├── kmdc-icon-button │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ └── _events.kt ├── kmdc-list │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ ├── _events.kt │ │ ├── Divider.kt │ │ └── MDCListGroup.kt ├── kmdc-checkbox │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ └── _module.kt ├── kmdc-data-table │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── _module.kt │ │ └── ProgressIndicator.kt ├── build.gradle.kts ├── kmdc-button │ ├── build.gradle.kts │ └── src │ │ ├── jsMain │ │ └── kotlin │ │ │ ├── Label.kt │ │ │ └── Icon.kt │ │ └── jsTest │ │ └── kotlin │ │ └── MDCButtonTest.kt ├── kmdc-textfield │ ├── build.gradle.kts │ └── src │ │ └── jsMain │ │ └── kotlin │ │ ├── icon │ │ ├── _module.kt │ │ └── MDCTextFieldIcon.kt │ │ └── _module.kt └── kmdc-select │ ├── build.gradle.kts │ └── src │ └── jsMain │ └── kotlin │ ├── _events.kt │ ├── _module.kt │ ├── menu │ ├── Menu.kt │ └── SelectItem.kt │ └── anchor │ └── DropDownIcon.kt ├── .gitignore ├── kmdcx ├── kmdcx-icons │ └── build.gradle.kts └── build.gradle.kts ├── .editorconfig ├── settings.gradle.kts ├── gradle.properties └── .run └── Start Sandbox.run.xml /katalog/gradle: -------------------------------------------------------------------------------- 1 | ../gradle -------------------------------------------------------------------------------- /sandbox/gradle: -------------------------------------------------------------------------------- 1 | ../gradle -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @mpetuska 2 | -------------------------------------------------------------------------------- /katalog/gradle.properties: -------------------------------------------------------------------------------- 1 | ../gradle.properties -------------------------------------------------------------------------------- /sandbox/gradle.properties: -------------------------------------------------------------------------------- 1 | ../gradle.properties -------------------------------------------------------------------------------- /scripts/setupMingw.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Not needed yet!" -------------------------------------------------------------------------------- /scripts/setupOSX.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Not needed yet!" -------------------------------------------------------------------------------- /scripts/setupUbuntu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Not needed yet!" -------------------------------------------------------------------------------- /katalog/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.versions") 3 | } 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpetuska/kmdc/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /kmdc/kmdc-drawer/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "drawer" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-ripple/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "ripple" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-slider/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "slider" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "tab-bar" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-elevation/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "elevation" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-image-list/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "image-list" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-typography/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "typography" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-layout-grid/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "layout-grid" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-line-ripple/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "line-ripple" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu-surface/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "menu-surface" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-touch-target/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "touch-target" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-floating-label/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "floating-label" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-linear-progress/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "linear-progress" 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-notched-outline/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "notched-outline" 7 | } 8 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/util/Builder.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.util 2 | 3 | public typealias Builder = T.() -> Unit 4 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/domain/Point.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.core.domain 2 | 3 | public data class Point(val x: Number, val y: Number) 4 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/util/general.kt: -------------------------------------------------------------------------------- 1 | package sandbox.util 2 | 3 | @JsName("require") 4 | external fun requireModule(module: String): dynamic 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | .vscode/ 4 | build/ 5 | out/ 6 | dist/ 7 | local.properties 8 | lint.xml 9 | *.hprof 10 | *.out 11 | *.log 12 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider: -------------------------------------------------------------------------------- 1 | dev.petuska.katalog.plugin.KatalogSymbolProcessorProvider 2 | -------------------------------------------------------------------------------- /kmdcx/kmdcx-icons/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdcx") 3 | } 4 | 5 | kmdcx { 6 | module by "material-icons" 7 | version by libs.versions.npm.material.icons 8 | } 9 | -------------------------------------------------------------------------------- /sandbox/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.gradle.enterprise") version "3.16.2" 3 | } 4 | 5 | includeBuild("../build-conventions") 6 | includeBuild("../katalog") 7 | includeBuild("..") 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{kt,kts}] 4 | indent_size = 2 5 | continuation_indent_size = 4 6 | ij_kotlin_name_count_to_use_star_import = 5 7 | disabled_rules = no-wildcard-imports, filename 8 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/domain/ShowcaseName.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.domain 2 | 3 | import com.squareup.kotlinpoet.* 4 | 5 | public typealias ShowcaseName = MemberName 6 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/resources/MDCImageList.scss: -------------------------------------------------------------------------------- 1 | @use "@material/image-list"; 2 | 3 | .kmdc-image-list { 4 | @include image-list.standard-columns(3); 5 | @include image-list.masonry-columns(3); 6 | } 7 | -------------------------------------------------------------------------------- /katalog/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.gradle.enterprise") version "3.16.2" 3 | } 4 | 5 | includeBuild("../build-conventions") 6 | 7 | include( 8 | ":katalog-ksp", 9 | ":katalog-runtime", 10 | ) 11 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/resources/MDCCircularProgress.scss: -------------------------------------------------------------------------------- 1 | @use "@material/circular-progress"; 2 | 3 | 4 | .kmdc-circular-progress { 5 | @include circular-progress.indeterminate-colors([#4285F4, #EA4335, #FBBC05, #34A853]); 6 | } 7 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/domain/ShowcaseData.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.domain 2 | 3 | public data class ShowcaseData( 4 | val id: String?, 5 | val title: String?, 6 | val description: String?, 7 | ) 8 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/KatalogConfig.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime 2 | 3 | @Target(AnnotationTarget.FUNCTION) 4 | @Retention(AnnotationRetention.SOURCE) 5 | public annotation class KatalogConfig( 6 | val priority: Int = 0 7 | ) 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-banner/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "banner" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("button")) 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsTest/kotlin/CounterTest.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.core 2 | 3 | import io.kotest.matchers.shouldBe 4 | import kotlin.test.Test 5 | 6 | class CounterTest { 7 | @Test 8 | fun keyNeverChanges() { 9 | KmdcCounterKey shouldBe "_kmdcCounter" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /kmdc/kmdc-form-field/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "form-field" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "top-app-bar" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("icon-button")) 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/util/css.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.util 2 | 3 | import org.jetbrains.compose.web.css.* 4 | 5 | internal fun StyleScope.setVariable(variable: CSSStyleVariable, value: T) { 6 | property("--" + variable.name, value) 7 | } 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-segmented-button/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "segmented-button" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /kmdc/kmdc-circular-progress/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "circular-progress" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.html.svg) 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /kmdc/kmdc-fab/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "fab" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | api(kmdc("touch-target")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "menu" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("list")) 14 | api(kmdc("menu-surface")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/Showcase.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime 2 | 3 | @Target(AnnotationTarget.FUNCTION) 4 | @Retention(AnnotationRetention.SOURCE) 5 | public annotation class Showcase( 6 | val id: String = "", 7 | val title: String = "", 8 | val description: String = "", 9 | ) 10 | -------------------------------------------------------------------------------- /kmdc/kmdc-card/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "card" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("button")) 14 | api(kmdc("icon-button")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-dialog/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "dialog" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("button")) 14 | api(kmdc("icon-button")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-tooltip/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "tooltip" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | api(kmdc("button")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdcx/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.library-js") 3 | id("convention.publishing") 4 | } 5 | 6 | description = "All KMDC extensions" 7 | 8 | kotlin { 9 | sourceSets { 10 | jsMain { 11 | dependencies { 12 | subprojects.forEach(::api) 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "base" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.runtime) 14 | api(libs.compose.html.core) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-radio/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "radio" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("form-field")) 14 | api(kmdc("touch-target")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-snackbar/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "snackbar" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("button")) 14 | api(kmdc("icon-button")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-switch/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "switch" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | api(libs.compose.html.svg) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "chips" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.html.svg) 14 | api(kmdc("touch-target")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-icon-button/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "icon-button" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.html.svg) 14 | api(kmdc("ripple")) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.library-js") 3 | } 4 | 5 | kotlin { 6 | sourceSets { 7 | jsMain { 8 | dependencies { 9 | api(libs.compose.runtime) 10 | api(libs.compose.html.core) 11 | implementation(libs.compose.routing) 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /kmdc/kmdc-list/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "list" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | api(kmdc("radio")) 15 | api(kmdc("checkbox")) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /kmdc/kmdc-checkbox/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "checkbox" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.html.svg) 14 | api(kmdc("form-field")) 15 | api(kmdc("touch-target")) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | env: 9 | GRADLE_OPTS: "-Dorg.gradle.daemon=true" 10 | 11 | concurrency: 12 | cancel-in-progress: true 13 | group: pr-${{ github.workflow }}-${{ github.head_ref || github.ref }} 14 | 15 | jobs: 16 | check: 17 | uses: ./.github/workflows/check.yml -------------------------------------------------------------------------------- /kmdc/kmdc-data-table/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "data-table" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("checkbox")) 14 | api(kmdc("linear-progress")) 15 | api(kmdc("icon-button")) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /kmdc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.library-js") 3 | id("convention.publishing") 4 | } 5 | 6 | description = "Compose Multiplatform Kotlin/JS wrappers for @material/material-components-web" 7 | 8 | kotlin { 9 | sourceSets { 10 | jsMain { 11 | dependencies { 12 | subprojects.forEach(::api) 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/top-app-bar") 2 | 3 | package dev.petuska.kmdc.top.app.bar 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCTopAppBar(element: Element) : MDCComponent 11 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/action/Action.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.action 2 | 3 | import androidx.compose.runtime.* 4 | import org.jetbrains.compose.web.dom.* 5 | import org.w3c.dom.* 6 | 7 | public interface MDCChipActionScope : ElementScope 8 | 9 | internal val MDCChipActionTypeLocal = compositionLocalOf { error("undefined") } 10 | -------------------------------------------------------------------------------- /kmdc/kmdc-button/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "button" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.html.svg) 14 | api(kmdc("ripple")) 15 | api(kmdc("touch-target")) 16 | api(kmdc("elevation")) 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/resources/sandbox.scss: -------------------------------------------------------------------------------- 1 | .sample { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | width: 100%; 7 | height: 100%; 8 | 9 | > * { 10 | width: 100%; 11 | margin-top: .25em; 12 | margin-bottom: .25em; 13 | } 14 | } 15 | 16 | .sample-container { 17 | width: 100%; 18 | height: 100%; 19 | } 20 | -------------------------------------------------------------------------------- /kmdc/kmdc-drawer/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/drawer") 2 | 3 | package dev.petuska.kmdc.drawer 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCDrawer(element: Element) : MDCComponent { 11 | public var open: Boolean 12 | } 13 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/domain/Showcase.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.domain 2 | 3 | import androidx.compose.runtime.* 4 | 5 | public typealias ShowcaseContent = @Composable () -> Unit 6 | 7 | public data class Showcase( 8 | val id: String, 9 | val title: String, 10 | val description: String?, 11 | val location: String?, 12 | val content: ShowcaseContent 13 | ) 14 | -------------------------------------------------------------------------------- /kmdc/kmdc-textfield/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "textfield" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(kmdc("ripple")) 14 | api(kmdc("line-ripple")) 15 | api(kmdc("floating-label")) 16 | api(kmdc("notched-outline")) 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/UtilStyle.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime 2 | 3 | import org.jetbrains.compose.web.css.* 4 | 5 | public object UtilStyle : StyleSheet() { 6 | public val roundedBoxShadow: String by style { 7 | borderRadius(.25.em) 8 | property("box-shadow", "rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px") 9 | padding(1.em) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/util/ChunkedFormFields.kt: -------------------------------------------------------------------------------- 1 | package sandbox.util 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.form.field.* 5 | 6 | @Composable 7 | fun ChunkedFormFields(items: Collection, size: Int = 3, render: @Composable (T) -> Unit) { 8 | items.chunked(size).forEach { chunk -> 9 | MDCFormField { 10 | chunk.forEach { 11 | render(it) 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gradle/webpack.config.d/files.js: -------------------------------------------------------------------------------- 1 | config.module.rules.push( 2 | { 3 | test: /\.(jpe?g|png|gif|svg)$/i, 4 | type: 'asset/resource', 5 | generator: { 6 | filename: "images/[hash][ext][query]", 7 | } 8 | }, 9 | { 10 | test: /\.(woff|woff2|eot|ttf|otf)$/, 11 | type: 'asset/resource', 12 | generator: { 13 | filename: "webfonts/[hash][ext][query]", 14 | } 15 | } 16 | ); 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-textfield/src/jsMain/kotlin/icon/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/textfield/icon") 2 | 3 | package dev.petuska.kmdc.textfield.icon 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCTextFieldIcon(element: Element) : MDCComponent { 11 | public val foundationForTextField: dynamic 12 | } 13 | -------------------------------------------------------------------------------- /kmdc/kmdc-select/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("convention.kmdc") 3 | } 4 | 5 | kmdc { 6 | mdc by "select" 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jsMain { 12 | dependencies { 13 | api(libs.compose.html.svg) 14 | api(kmdc("list")) 15 | api(kmdc("menu")) 16 | api(kmdc("line-ripple")) 17 | api(kmdc("floating-label")) 18 | api(kmdc("notched-outline")) 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /kmdc/kmdc-notched-outline/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/notched-outline") 2 | 3 | package dev.petuska.kmdc.notched.outline 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCNotchedOutline(element: Element) : MDCComponent { 11 | public fun closeNotch() 12 | public fun notch(notchWidth: Number) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(dirname "${BASH_SOURCE[0]}")" 4 | OS="$(uname | tr '[:upper:]' '[:lower:]')" 5 | case $OS in 6 | darwin*) echo "OSX detected" && "${ROOT}"/setupOSX.sh ;; 7 | linux*) echo "Linux detected" && "${ROOT}"/setupUbuntu.sh ;; 8 | mingw*) echo "Mingw detected" && "${ROOT}"/setupMingw.sh ;; 9 | msys*) echo "Windows detected (unsupported)" && exit 1 ;; 10 | *) echo "Unknown OS: $OS" && exit 1 ;; 11 | esac 12 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/base") 2 | 3 | package dev.petuska.kmdc.core 4 | 5 | @MDCExternalAPI 6 | public abstract external class MDCComponent { 7 | public fun destroy() 8 | public fun initialize(vararg args: Any?) 9 | public fun initialSyncWithDOM() 10 | public fun getDefaultFoundation(): F 11 | public val foundation: F 12 | } 13 | 14 | public external interface MDCLayoutComponent { 15 | public fun layout() 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/mdc.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.core 2 | 3 | import org.w3c.dom.Element 4 | 5 | @KMDCInternalAPI 6 | internal var Element.mdc: dynamic 7 | get() = asDynamic().mdc 8 | set(value) { 9 | asDynamic().mdc = value 10 | } 11 | 12 | @KMDCInternalAPI 13 | @Deprecated(message = "To be removed") 14 | public fun > Element.mdc(action: MDCAttrs? = null): MDC? = 15 | mdc.unsafeCast()?.also { action?.invoke(it) } 16 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.gradle.enterprise") version "3.16.2" 3 | } 4 | 5 | includeBuild("build-conventions") 6 | 7 | rootProject.name = "KMDC" 8 | 9 | fun includeModuleGroup(path: String) { 10 | rootDir.resolve(path).listFiles { file: File -> file.isDirectory && file.name.startsWith(path) } 11 | ?.map { ":$path:${it.name}" }?.forEach(::include)?.also { include(":$path") } 12 | } 13 | 14 | includeModuleGroup("kmdc") 15 | includeModuleGroup("kmdcx") 16 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/action/Ripple.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.action 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.* 6 | 7 | @MDCContentDsl 8 | @Composable 9 | internal fun MDCChipActionScope<*>.Ripple() { 10 | val type = MDCChipActionTypeLocal.current 11 | Span( 12 | attrs = { 13 | classes("mdc-evolution-chip__ripple", "mdc-evolution-chip__ripple--$type") 14 | } 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-line-ripple/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/line-ripple") 2 | 3 | package dev.petuska.kmdc.line.ripple 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCLineRipple(element: Element) : MDCComponent { 11 | public fun activate() 12 | public fun deactivate() 13 | public fun setRippleCenter(xCoordinate: Number) 14 | } 15 | -------------------------------------------------------------------------------- /kmdc/kmdc-icon-button/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/icon-button") 2 | 3 | package dev.petuska.kmdc.icon.button 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.ripple.MDCRipple 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCIconButtonToggle(element: Element) : MDCComponent { 12 | public val ripple: MDCRipple 13 | public var on: Boolean 14 | } 15 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/control/ActionControl.kt: -------------------------------------------------------------------------------- 1 | package sandbox.control 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.button.* 5 | import org.jetbrains.compose.web.dom.* 6 | 7 | @Composable 8 | fun ActionControl( 9 | name: String, 10 | description: String? = null, 11 | onAction: () -> Unit, 12 | ) { 13 | Div { 14 | MDCButton(text = name, type = MDCButtonType.Raised, attrs = { 15 | onClick { onAction() } 16 | description?.let(::title) 17 | }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scripts/versionCatalogUpdate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | start="$(pwd)" 4 | root="$(dirname "${BASH_SOURCE[0]}")/.." 5 | root="$(cd "$root" && pwd)" 6 | 7 | function updateVersionsCatalog() { 8 | "$root/gradlew" versionCatalogUpdate $@ 9 | } 10 | 11 | targets=("build-conventions" "" "katalog" "sandbox") 12 | for t in "${targets[@]}"; do 13 | target="$root/$t" 14 | echo ">>> Updating $target" 15 | cd "$target" && updateVersionsCatalog $@ || exit 1 16 | done 17 | 18 | cd "$start" && echo ">>> DONE!" 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab 2 | 3 | import dev.petuska.kmdc.core.* 4 | 5 | public external interface MDCTabInteractedEventDetail { 6 | public val tabId: String 7 | } 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab) 11 | */ 12 | @MDCAttrsDsl 13 | public fun MDCTabAttrsScope.onInteracted(listener: MDCEventListener) { 14 | addMdcEventListener("MDCTab:interacted", listener) 15 | } 16 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/bar/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/tab-bar") 2 | 3 | package dev.petuska.kmdc.tab.bar 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCTabBar(element: Element) : MDCComponent { 11 | public var focusOnActivate: Boolean 12 | public var useAutomaticActivation: Boolean 13 | public fun activateTab(index: Int) 14 | public fun scrollIntoView(index: Int) 15 | } 16 | -------------------------------------------------------------------------------- /kmdc/kmdc-linear-progress/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/linear-progress") 2 | 3 | package dev.petuska.kmdc.linear.progress 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCLinearProgress(element: Element) : MDCComponent { 11 | public var determinate: Boolean 12 | public var progress: Number 13 | public var buffer: Number 14 | public fun open() 15 | public fun close() 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-switch/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/switch") 2 | 3 | package dev.petuska.kmdc.switch 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.ripple.MDCRipple 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCSwitch(element: Element) : MDCComponent { 12 | public var disabled: Boolean 13 | public var processing: Boolean 14 | public var selected: Boolean 15 | public var ripple: MDCRipple 16 | } 17 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/util/KSValueArgument.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.util 2 | 3 | import com.google.devtools.ksp.symbol.* 4 | 5 | public operator fun Collection.get(name: String): KSValueArgument? = firstOrNull { 6 | it.name?.asString() == name 7 | } 8 | public operator fun Collection.get(name: String): KSValueParameter? = firstOrNull { 9 | it.name?.asString() == name 10 | } 11 | 12 | public fun KSValueArgument?.asString(): String? = (this?.value as String?)?.takeIf(String::isNotBlank) 13 | -------------------------------------------------------------------------------- /kmdc/kmdc-circular-progress/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/circular-progress") 2 | 3 | package dev.petuska.kmdc.circular.progress 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCCircularProgress(element: Element) : MDCComponent { 11 | public var determinate: Boolean 12 | public var progress: Number 13 | public val isClosed: Boolean 14 | public fun open() 15 | public fun close() 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/indicator/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/tab-indicator") 2 | 3 | package dev.petuska.kmdc.tab.indicator 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCTabIndicator(element: Element) : MDCComponent { 11 | public fun activate(previousIndicatorClientRect: dynamic = definedExternally) 12 | public fun deactivate() 13 | public fun computeContentClientRect(): dynamic 14 | } 15 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.top.app.bar 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | import kotlin.js.Json 7 | 8 | /** 9 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 10 | */ 11 | @MDCAttrsDsl 12 | public fun MDCTopAppBarAttrsScope.onNav(listener: MDCEventListener) { 13 | addMdcEventListener("MDCTopAppBar:nav", listener) 14 | } 15 | -------------------------------------------------------------------------------- /kmdc/kmdc-floating-label/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/floating-label") 2 | 3 | package dev.petuska.kmdc.floating.label 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCFloatingLabel(element: Element) : MDCComponent { 11 | public fun shake(shouldShake: Boolean) 12 | public fun float(shouldFloat: Boolean) 13 | public fun setRequired(isRequired: Boolean) 14 | public fun getWidth(): Number 15 | } 16 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.menu 2 | 3 | import dev.petuska.kmdc.core.* 4 | import org.w3c.dom.* 5 | 6 | public external interface MDCMenuSelectedEventDetail { 7 | public val item: Element 8 | public val index: Int 9 | } 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-menu) 13 | */ 14 | @MDCAttrsDsl 15 | public fun MDCMenuAttrsScope.onSelected(listener: MDCEventListener) { 16 | addMdcEventListener("MDCMenu:selected", listener) 17 | } 18 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/bar/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab.bar 2 | 3 | import dev.petuska.kmdc.core.* 4 | 5 | public external interface MDCTabBarActivatedEventDetail { 6 | public val index: Int 7 | public val tabId: String 8 | } 9 | 10 | /** 11 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-bar) 12 | */ 13 | @MDCAttrsDsl 14 | public fun MDCTabBarAttrsScope.onActivated(listener: MDCEventListener) { 15 | addMdcEventListener("MDCTabBar:activated", listener) 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-form-field/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/form-field") 2 | 3 | package dev.petuska.kmdc.form.field 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.ripple.MDCRipple 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCFormField(element: Element) : MDCComponent { 12 | public var input: MDCFormFieldInput? 13 | } 14 | 15 | @MDCExternalAPI 16 | public external interface MDCFormFieldInput { 17 | public val ripple: MDCRipple? 18 | } 19 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/layout/Divider.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.layout 2 | 3 | import androidx.compose.runtime.Composable 4 | import org.jetbrains.compose.web.css.* 5 | import org.jetbrains.compose.web.dom.Hr 6 | 7 | @Composable 8 | public fun Divider(style: (StyleScope.() -> Unit)? = null) { 9 | Hr(attrs = { 10 | style { 11 | width(100.percent) 12 | opacity(0.5) 13 | position(Position.Sticky) 14 | left(0.em) 15 | marginTop(1.em) 16 | property("border-style", "dashed") 17 | style?.invoke(this) 18 | } 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/.github/workflows/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /kmdc/kmdc-radio/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/radio") 2 | 3 | package dev.petuska.kmdc.radio 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.form.field.MDCFormFieldInput 8 | import dev.petuska.kmdc.ripple.MDCRipple 9 | import org.w3c.dom.Element 10 | 11 | @MDCExternalAPI 12 | public external class MDCRadio(element: Element) : MDCComponent, MDCFormFieldInput { 13 | public var checked: Boolean 14 | public var disabled: Boolean 15 | public var value: String 16 | override val ripple: MDCRipple? 17 | } 18 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/scroller/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/tab-scroller") 2 | 3 | package dev.petuska.kmdc.tab.scroller 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCTabScroller(element: Element) : MDCComponent { 11 | public fun getScrollPosition(): Number 12 | public fun getScrollContentWidth(): Number 13 | public fun scrollTo(scrollX: Number) 14 | public fun incrementScroll(scrollX: Number) 15 | public fun incrementScrollImmediate(scrollX: Number) 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.core 2 | 3 | import org.jetbrains.compose.web.attributes.AttrsScope 4 | import org.w3c.dom.events.Event 5 | 6 | public external class MDCEvent internal constructor() : Event { 7 | public val detail: T 8 | } 9 | 10 | public typealias MDCEventListener = @MDCAttrsDsl (event: MDCEvent) -> Unit 11 | 12 | @KMDCInternalAPI 13 | public fun , D> A.addMdcEventListener( 14 | eventName: String, 15 | listener: MDCEventListener 16 | ) { 17 | addEventListener(eventName) { 18 | listener(it.nativeEvent.unsafeCast>()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /kmdc/kmdc-typography/src/jsMain/kotlin/MDCTypographyStyle.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.typography 2 | 3 | public enum class MDCTypographyStyle(public val style: String) { 4 | Headline1(style = "headline1"), 5 | Headline2(style = "headline2"), 6 | Headline3(style = "headline3"), 7 | Headline4(style = "headline4"), 8 | Headline5(style = "headline5"), 9 | Headline6(style = "headline6"), 10 | Subtitle1(style = "subtitle1"), 11 | Subtitle2(style = "subtitle2"), 12 | Body1(style = "body1"), 13 | Body2(style = "body2"), 14 | Caption(style = "caption"), 15 | Button(style = "button"), 16 | Overline(style = "overline"), 17 | } 18 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/ui/KatalogThemeVariables.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.ui 2 | 3 | import dev.petuska.katalog.runtime.theme.* 4 | import dev.petuska.katalog.runtime.util.* 5 | import org.jetbrains.compose.web.attributes.* 6 | import org.jetbrains.compose.web.css.* 7 | import org.w3c.dom.* 8 | 9 | internal object KatalogThemeVariables { 10 | val highlightColor by variable() 11 | } 12 | 13 | internal fun AttrsScope.applyTheme(theme: KatalogTheme) = style { 14 | setVariable(KatalogThemeVariables.highlightColor, theme.highlightColor) 15 | fontFamily(value = theme.fontFamily.toTypedArray()) 16 | } 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-checkbox/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/checkbox") 2 | 3 | package dev.petuska.kmdc.checkbox 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.form.field.MDCFormFieldInput 8 | import dev.petuska.kmdc.ripple.MDCRipple 9 | import org.w3c.dom.Element 10 | 11 | @MDCExternalAPI 12 | public external class MDCCheckbox(element: Element) : MDCComponent, MDCFormFieldInput { 13 | public var checked: Boolean 14 | public var indeterminate: Boolean 15 | public var disabled: Boolean 16 | public var value: String 17 | override val ripple: MDCRipple? 18 | } 19 | -------------------------------------------------------------------------------- /kmdc/kmdc-select/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.select 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | 7 | public external interface MDCSelectChangeEventDetail { 8 | public val value: String 9 | public val index: Int 10 | } 11 | 12 | /** 13 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-select) 14 | */ 15 | @MDCAttrsDsl 16 | public fun MDCSelectAttrsScope.onChange(listener: MDCEventListener) { 17 | addMdcEventListener("MDCSelect:change", listener) 18 | } 19 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/KatalogSymbolProcessorProvider.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin 2 | 3 | import com.google.devtools.ksp.processing.* 4 | import java.io.* 5 | 6 | public class KatalogSymbolProcessorProvider : SymbolProcessorProvider { 7 | override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { 8 | require(environment.platforms.size == 1 && environment.platforms.any { it is JsPlatformInfo }) 9 | return KatalogSymbolProcessor( 10 | codeGenerator = environment.codeGenerator, 11 | logger = environment.logger, 12 | contentRoot = environment.options["katalog.contentRoot"]?.let(::File), 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /kmdc/kmdc-icon-button/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.icon.button 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | 7 | public external interface MDCIconButtonToggleChangeEventDetail { 8 | public val isOn: Boolean 9 | } 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-icon-button) 13 | */ 14 | @MDCAttrsDsl 15 | public fun MDCIconButtonAttrsScope<*>.onChange(listener: MDCEventListener) { 16 | addMdcEventListener("MDCIconButtonToggle:change", listener) 17 | } 18 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/tab") 2 | 3 | package dev.petuska.kmdc.tab 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCTab(element: Element) : MDCComponent { 11 | public var id: String 12 | public val active: Boolean 13 | public var focusOnActivate: Boolean 14 | public fun activate(previousIndicatorClientRect: dynamic = definedExternally) 15 | public fun deactivate() 16 | public fun focus() 17 | public fun computeContentClientRect(): dynamic 18 | public fun computeDimensions(): dynamic 19 | } 20 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/control/FilterControl.kt: -------------------------------------------------------------------------------- 1 | package sandbox.control 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.checkbox.* 5 | import sandbox.util.* 6 | 7 | @Composable 8 | fun FilterControl( 9 | title: String, 10 | options: Map, 11 | description: String? = null, 12 | onSelect: (label: String, selected: Boolean) -> Unit 13 | ) { 14 | NamedBlock(title, description) { 15 | ChunkedFormFields(options.entries) { (text, selected) -> 16 | MDCCheckbox( 17 | checked = selected, 18 | label = text, 19 | attrs = { 20 | onInput { onSelect(text, !selected) } 21 | } 22 | ) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/ui/Katalog.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.ui 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import org.jetbrains.compose.web.css.* 6 | import org.jetbrains.compose.web.dom.Div 7 | 8 | private object KatalogStyle : StyleSheet() { 9 | val container by style { 10 | display(DisplayStyle.Flex) 11 | flexDirection(FlexDirection.Column) 12 | } 13 | } 14 | 15 | @Composable 16 | internal fun Katalog() { 17 | val theme = katalog.theme 18 | Style(KatalogStyle) 19 | Div(attrs = { 20 | classes(KatalogStyle.container) 21 | applyTheme(theme) 22 | }) { 23 | Header() 24 | Showcases() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /kmdc/kmdc-dialog/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/dialog") 2 | 3 | package dev.petuska.kmdc.dialog 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.core.MDCLayoutComponent 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCDialog(element: Element) : MDCComponent, MDCLayoutComponent { 12 | public val isOpen: Boolean 13 | public var escapeKeyAction: String 14 | public var scrimClickAction: String 15 | public var autoStackButtons: Boolean 16 | 17 | public override fun layout() 18 | public fun open() 19 | public fun close(action: String = definedExternally) 20 | } 21 | -------------------------------------------------------------------------------- /kmdc/kmdc-card/src/jsMain/kotlin/Content.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.card 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Div 6 | import org.w3c.dom.HTMLDivElement 7 | 8 | /** 9 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-card) 10 | */ 11 | @MDCContentDsl 12 | @Composable 13 | public fun MDCCardScope.Content( 14 | attrs: MDCAttrsRaw? = null, 15 | content: MDCContentRaw? = null 16 | ) { 17 | Div( 18 | attrs = { 19 | classes("mdc-card__content") 20 | applyAttrs(attrs) 21 | }, 22 | ) { 23 | applyContent(content) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu/src/jsMain/kotlin/SelectionGroup.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.menu 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.* 6 | import org.w3c.dom.* 7 | 8 | /** 9 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-menu) 10 | */ 11 | @MDCContentDsl 12 | @Composable 13 | public fun MDCMenuScope.SelectionGroup( 14 | attrs: MDCAttrsRaw? = null, 15 | content: MDCContent? = null, 16 | ) { 17 | Li { 18 | Ul( 19 | attrs = { 20 | classes("mdc-menu__selection-group") 21 | applyAttrs(attrs) 22 | }, 23 | content = content.reinterpret() 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu-surface/src/jsMain/kotlin/MDCMenuSurfaceAnchor.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.menu.surface 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.core.applyAttrs 8 | import org.jetbrains.compose.web.dom.Div 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | @MDCContentDsl 12 | @Composable 13 | public fun MDCMenuSurfaceAnchor( 14 | attrs: MDCAttrsRaw? = null, 15 | content: MDCContentRaw? = null, 16 | ) { 17 | Div( 18 | attrs = { 19 | classes("mdc-menu-surface--anchor") 20 | applyAttrs(attrs) 21 | }, 22 | content = content 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/ui/Header.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.ui 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import org.jetbrains.compose.web.css.* 6 | import org.jetbrains.compose.web.dom.* 7 | 8 | private object HeaderStyle : StyleSheet() { 9 | val container by style { 10 | display(DisplayStyle.Flex) 11 | flexDirection(FlexDirection.Column) 12 | alignItems(AlignItems.Center) 13 | } 14 | } 15 | 16 | @Composable 17 | internal fun Header() { 18 | Style(HeaderStyle) 19 | Div(attrs = { 20 | classes(HeaderStyle.container) 21 | }) { 22 | katalog.theme.katalogTitleRender(katalog.title) 23 | katalog.subtitle?.let { katalog.theme.katalogSubtitleRender(it) } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/control/TextControl.kt: -------------------------------------------------------------------------------- 1 | package sandbox.control 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.textfield.* 5 | import sandbox.util.* 6 | import kotlin.reflect.* 7 | 8 | @Composable 9 | fun TextControl( 10 | name: String, 11 | text: String, 12 | description: String? = null, 13 | onInput: (String) -> Unit, 14 | ) { 15 | NamedBlock(name, description) { 16 | MDCTextField( 17 | value = text, 18 | attrs = { 19 | onInput { onInput(it.value) } 20 | } 21 | ) 22 | } 23 | } 24 | 25 | @Composable 26 | fun TextControl( 27 | name: String, 28 | text: KMutableProperty0, 29 | description: String? = null, 30 | ) { 31 | TextControl(name, text.get(), description, text::set) 32 | } 33 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/grid/MDCChipsGrid.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.grid 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.chips.* 5 | import dev.petuska.kmdc.core.* 6 | 7 | public interface MDCChipsGridScope : MDCChipsScope 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 11 | */ 12 | @MDCContentDsl 13 | @Composable 14 | public fun MDCChipsGrid( 15 | overflow: Boolean = false, 16 | attrs: MDCAttrs? = null, 17 | content: MDCContent? = null 18 | ) { 19 | MDCChips( 20 | overflow = overflow, 21 | attrs = { 22 | role("grid") 23 | applyAttrs(attrs) 24 | }, 25 | content = content.reinterpret() 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /kmdc/kmdc-drawer/src/jsMain/kotlin/Content.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.drawer 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.Div 8 | import org.w3c.dom.HTMLDivElement 9 | 10 | /** 11 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-drawer) 12 | */ 13 | @MDCContentDsl 14 | @Composable 15 | public fun MDCDrawerScope.Content( 16 | attrs: MDCAttrsRaw? = null, 17 | content: MDCContentRaw? = null 18 | ) { 19 | Div( 20 | attrs = { 21 | classes("mdc-drawer__content") 22 | attrs?.invoke(this) 23 | }, 24 | content = content, 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /kmdc/kmdc-ripple/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/ripple") 2 | 3 | package dev.petuska.kmdc.ripple 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.core.MDCLayoutComponent 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external interface MDCRippleAttachOpts { 12 | public var isUnbounded: Boolean? 13 | } 14 | 15 | @MDCExternalAPI 16 | public external class MDCRipple(element: Element, opts: MDCRippleAttachOpts = definedExternally) : 17 | MDCComponent, MDCLayoutComponent { 18 | public var unbounded: Boolean 19 | public var disabled: Boolean 20 | public fun activate() 21 | public fun deactivate() 22 | public fun handleFocus() 23 | public fun handleBlur() 24 | override fun layout() 25 | } 26 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/attrs.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE") 2 | 3 | package dev.petuska.kmdc.core 4 | 5 | import org.jetbrains.compose.web.attributes.AttrsScope 6 | import org.w3c.dom.Element 7 | import org.w3c.dom.HTMLElement 8 | 9 | @KMDCInternalAPI 10 | public inline fun AttrsScope.aria(key: String, value: Any) { 11 | attr("aria-$key", "$value") 12 | } 13 | 14 | @KMDCInternalAPI 15 | public inline fun AttrsScope.data(key: String, value: Any) { 16 | attr("data-$key", "$value") 17 | } 18 | 19 | @KMDCInternalAPI 20 | public inline fun AttrsScope.classes(classes: Array) { 21 | classes.forEach { classes(it) } 22 | } 23 | 24 | @KMDCInternalAPI 25 | public inline fun AttrsScope.role(value: Any) { 26 | attr("role", "$value") 27 | } 28 | -------------------------------------------------------------------------------- /kmdc/kmdc-drawer/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.drawer 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | import kotlin.js.Json 7 | 8 | /** 9 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-drawer) 10 | */ 11 | @MDCAttrsDsl 12 | public fun MDCDrawerAttrsScope.onOpened(listener: MDCEventListener) { 13 | addMdcEventListener("MDCDrawer:opened", listener) 14 | } 15 | 16 | /** 17 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-drawer) 18 | */ 19 | @MDCAttrsDsl 20 | public fun MDCDrawerAttrsScope.onClosed(listener: MDCEventListener) { 21 | addMdcEventListener("MDCDrawer:closed", listener) 22 | } 23 | -------------------------------------------------------------------------------- /kmdc/kmdc-snackbar/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/snackbar") 2 | 3 | package dev.petuska.kmdc.snackbar 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCSnackbar(element: Element) : MDCComponent { 11 | public fun initialize( 12 | segmentFactory: ( 13 | el: Element, 14 | foundation: dynamic 15 | ) -> (() -> (ariaEl: Element, labelEl: Element?) -> Unit) = definedExternally 16 | ) 17 | 18 | public fun open() 19 | public fun close(reason: String = definedExternally) 20 | public var timeoutMs: Int 21 | public var closeOnEscape: Boolean 22 | public val isOpen: Boolean 23 | public var labelText: String 24 | public var actionButtonText: String 25 | } 26 | -------------------------------------------------------------------------------- /kmdc/kmdc-card/src/jsMain/kotlin/PrimaryAction.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.card 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Div 6 | import org.jetbrains.compose.web.dom.Span 7 | import org.w3c.dom.HTMLDivElement 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-card) 11 | */ 12 | @MDCContentDsl 13 | @Composable 14 | public fun MDCCardScope.PrimaryAction( 15 | attrs: MDCAttrsRaw? = null, 16 | content: MDCContent? = null 17 | ) { 18 | Div( 19 | attrs = { 20 | classes("mdc-card__primary-action") 21 | tabIndex(0) 22 | applyAttrs(attrs) 23 | }, 24 | ) { 25 | applyContent(content) 26 | Span(attrs = { classes("mdc-card__ripple") }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /kmdc/kmdc-segmented-button/src/jsMain/kotlin/Icon.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.segmented.button 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.I 8 | import org.w3c.dom.HTMLElement 9 | 10 | /** 11 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 12 | */ 13 | @MDCContentDsl 14 | @Composable 15 | public fun MDCSegmentedButtonSegmentScope.Icon( 16 | attrs: MDCAttrsRaw? = null, 17 | content: MDCContentRaw? = null, 18 | ) { 19 | I( 20 | attrs = { 21 | classes("mdc-segmented-button__icon") 22 | attrs?.invoke(this) 23 | }, 24 | content = content 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/annotations.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.core 2 | 3 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) 4 | @DslMarker 5 | @KMDCInternalAPI 6 | public annotation class MDCContentDsl 7 | 8 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) 9 | @DslMarker 10 | @KMDCInternalAPI 11 | public annotation class MDCAttrsDsl 12 | 13 | @DslMarker 14 | @KMDCInternalAPI 15 | @RequiresOptIn( 16 | message = "This API is used internally by KMDC modules and does not provide any stability guarantees.", 17 | level = RequiresOptIn.Level.ERROR, 18 | ) 19 | public annotation class KMDCInternalAPI 20 | 21 | @DslMarker 22 | @RequiresOptIn( 23 | message = "This API is linking to the external MDC APIs and does not provide any stability guarantees.", 24 | level = RequiresOptIn.Level.WARNING, 25 | ) 26 | public annotation class MDCExternalAPI 27 | -------------------------------------------------------------------------------- /kmdc/kmdc-slider/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.slider 2 | 3 | import dev.petuska.kmdc.core.* 4 | 5 | public external interface MDCSliderChangeEventDetail { 6 | public val value: Number 7 | public val thumb: Int 8 | } 9 | 10 | /** 11 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-slider) 12 | */ 13 | @MDCAttrsDsl 14 | public fun MDCSliderAttrsScope.onChange(listener: MDCEventListener) { 15 | addMdcEventListener("MDCSlider:change", listener) 16 | } 17 | 18 | /** 19 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-slider) 20 | */ 21 | @MDCAttrsDsl 22 | public fun MDCSliderAttrsScope.onInput(listener: MDCEventListener) { 23 | addMdcEventListener("MDCSlider:input", listener) 24 | } 25 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/util/KatalogLogger.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.util 2 | 3 | import com.google.devtools.ksp.processing.KSPLogger 4 | import com.google.devtools.ksp.symbol.KSNode 5 | 6 | public interface KatalogLogger { 7 | public val logger: KSPLogger 8 | public fun error(message: String, symbol: KSNode? = null): Nothing { 9 | logger.error(message.prefix(), symbol) 10 | kotlin.error("error log") 11 | } 12 | 13 | public fun warn(message: String, symbol: KSNode? = null): Unit = logger.warn(message.prefix(), symbol) 14 | public fun info(message: String, symbol: KSNode? = null): Unit = logger.info(message.prefix(), symbol) 15 | public fun debug(message: String, symbol: KSNode? = null): Unit = logger.logging(message.prefix(), symbol) 16 | 17 | public companion object { 18 | private fun String.prefix() = "[katalog] $this" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/util/data.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.util 2 | 3 | import kotlin.random.Random 4 | 5 | public val LoremIpsum: String = """ 6 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur 7 | pretium vitae est et dapibus. Aenean sit amet felis eu lorem fermentum 8 | aliquam sit amet sit amet eros. 9 | """.trimIndent() 10 | 11 | public const val RickRollUrl: String = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" 12 | 13 | public fun randomImageUrl( 14 | seed: String = "kmdc", 15 | maxWidth: UInt = 300u, 16 | maxHeight: UInt = maxWidth, 17 | minWidth: UInt = 50u, 18 | minHeight: UInt = minWidth, 19 | ): String { 20 | val width: Int = Random.nextInt(maxWidth.toInt()) + minWidth.toInt() 21 | val height: Int = Random.nextInt(maxHeight.toInt()) + minHeight.toInt() 22 | return "https://picsum.photos/seed/$seed/$width/$height" 23 | } 24 | -------------------------------------------------------------------------------- /kmdc/kmdc-data-table/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/data-table") 2 | 3 | package dev.petuska.kmdc.data.table 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCDataTable(element: Element) : MDCComponent { 11 | public fun layout() 12 | public fun getHeaderCells(): Array 13 | public fun getRows(): Array 14 | public fun getSelectedRowIds(): Array 15 | public fun setSelectedRowIds(vararg rowIds: String) 16 | public fun showProgress() 17 | public fun hideProgress() 18 | } 19 | 20 | public external enum class SortValue { 21 | ASCENDING, 22 | DESCENDING, 23 | NONE, 24 | OTHER 25 | } 26 | 27 | public external interface ProgressIndicatorStyles { 28 | public var height: String 29 | public var top: String 30 | } 31 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/control/BooleanControl.kt: -------------------------------------------------------------------------------- 1 | package sandbox.control 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.checkbox.* 5 | import dev.petuska.kmdc.form.field.* 6 | import sandbox.util.* 7 | import kotlin.reflect.* 8 | 9 | @Composable 10 | fun BooleanControl( 11 | title: String, 12 | selected: Boolean, 13 | description: String? = null, 14 | onSelect: (selected: Boolean) -> Unit 15 | ) { 16 | NamedBlock(title, description) { 17 | MDCFormField { 18 | MDCCheckbox( 19 | checked = selected, 20 | attrs = { 21 | onInput { onSelect(!selected) } 22 | } 23 | ) 24 | } 25 | } 26 | } 27 | 28 | @Composable 29 | fun BooleanControl( 30 | title: String, 31 | property: KMutableProperty0, 32 | description: String? = null 33 | ) { 34 | BooleanControl(title, property.get(), description, onSelect = { property.set(it) }) 35 | } 36 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/theme/KatalogTheme.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.theme 2 | 3 | import androidx.compose.runtime.* 4 | import org.jetbrains.compose.web.css.* 5 | import org.jetbrains.compose.web.dom.* 6 | 7 | public data class KatalogTheme( 8 | public val highlightColor: CSSColorValue = Color("#F2BB05"), 9 | public val fontFamily: List = listOf("Roboto", "sans-serif"), 10 | 11 | public val katalogTitleRender: @Composable (title: String) -> Unit = { H3 { Text(it) } }, 12 | public val katalogSubtitleRender: @Composable (title: String) -> Unit = { H4 { Text(it) } }, 13 | public val navTitleRender: @Composable (title: String, selected: Boolean) -> Unit = { text, _ -> H4 { Text(text) } }, 14 | public val showcaseTitleRender: @Composable (title: String) -> Unit = { H3 { Text(it) } }, 15 | public val showcaseDescriptionRender: @Composable (title: String) -> Unit = { P { Text(it) } }, 16 | ) 17 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/chips") 2 | 3 | package dev.petuska.kmdc.chips 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | public external enum class MDCChipActionType { UNSPECIFIED, PRIMARY, TRAILING } 10 | 11 | @MDCExternalAPI 12 | public external class MDCChipSet(element: Element) : MDCComponent { 13 | public companion object { 14 | public fun attachTo(element: Element): MDCChipSet 15 | public fun getChipIndexByID(chipID: String): Int 16 | public fun getChipIdAtIndex(index: Int): String 17 | public fun getSelectedChipIndexes(): Set 18 | public fun setChipSelected(index: Int, action: MDCChipActionType, isSelected: Boolean) 19 | public fun isChipSelected(index: Int, action: MDCChipActionType): Boolean 20 | public fun removeChip(index: Int): Boolean 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu/src/jsMain/kotlin/MenuItem.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.menu 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.list.item.* 6 | import org.jetbrains.compose.web.attributes.* 7 | import org.w3c.dom.* 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-menu) 11 | */ 12 | @MDCContentDsl 13 | @Composable 14 | public fun MDCMenuScope.MenuItem( 15 | disabled: Boolean = false, 16 | selected: Boolean = false, 17 | activated: Boolean = false, 18 | attrs: MDCAttrs>? = null, 19 | content: MDCContent>? = null, 20 | ) { 21 | ListItem( 22 | attrs = { 23 | role("menuitem") 24 | applyAttrs(attrs) 25 | }, 26 | disabled = disabled, 27 | selected = selected, 28 | activated = activated, 29 | content = content 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #======================================== Gradle ======================================== 2 | org.gradle.vfs.watch=true 3 | org.gradle.cache=true 4 | org.gradle.parallel=true 5 | org.gradle.jvmargs=-XX:MaxMetaspaceSize=2g -Xmx2g 6 | systemProp.org.gradle.unsafe.kotlin.assignment=true 7 | #======================================== Kotlin ======================================== 8 | kotlin.code.style=official 9 | kotlin.stdlib.default.dependency=true 10 | kotlin.mpp.stability.nowarn=true 11 | kotlin.native.ignoreDisabledTargets=true 12 | kapt.includeCompileClasspath=false 13 | kotlin.incremental.js=true 14 | kotlin.incremental.js.ir=true 15 | kotlin.js.generate.externals=false 16 | kotlin.js.compiler=ir 17 | kotlin.js.browser.karma.browsers=firefox-headless 18 | #======================================= Project ======================================== 19 | group=dev.petuska 20 | version=0.1.3-SNAPSHOT 21 | project.enableSnapshots=false 22 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/listbox/MDCChipsListbox.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.listbox 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.chips.* 5 | import dev.petuska.kmdc.core.* 6 | 7 | public interface MDCChipsListboxScope : MDCChipsScope 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 11 | */ 12 | @MDCContentDsl 13 | @Composable 14 | public fun MDCChipsListbox( 15 | overflow: Boolean = false, 16 | multiselectable: Boolean = false, 17 | attrs: MDCAttrs? = null, 18 | content: MDCContent? = null 19 | ) { 20 | MDCChips( 21 | overflow = overflow, 22 | attrs = { 23 | role("listbox") 24 | aria("orientation", "horizontal") 25 | aria("multiselectable", multiselectable) 26 | applyAttrs(attrs) 27 | }, 28 | content = content.reinterpret() 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/builder/kotlinPoet.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.builder 2 | 3 | import com.squareup.kotlinpoet.CodeBlock 4 | import com.squareup.kotlinpoet.FileSpec 5 | import com.squareup.kotlinpoet.FunSpec 6 | import dev.petuska.katalog.plugin.util.Builder 7 | 8 | @Target(AnnotationTarget.TYPE) 9 | @DslMarker 10 | public annotation class KotlinPoetBuilderDSL 11 | 12 | public inline fun codeBlockOf( 13 | builder: Builder<@KotlinPoetBuilderDSL CodeBlock.Builder> 14 | ): CodeBlock = CodeBlock.builder().apply(builder).build() 15 | 16 | public inline fun funOf( 17 | name: String, 18 | builder: Builder<@KotlinPoetBuilderDSL FunSpec.Builder> 19 | ): FunSpec = FunSpec.builder(name).apply(builder).build() 20 | 21 | public inline fun fileOf( 22 | packageName: String, 23 | fileName: String, 24 | builder: Builder<@KotlinPoetBuilderDSL FileSpec.Builder> 25 | ): FileSpec = FileSpec.builder(packageName, fileName).apply(builder).build() 26 | -------------------------------------------------------------------------------- /kmdc/kmdc-list/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/list") 2 | 3 | package dev.petuska.kmdc.list 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.core.MDCLayoutComponent 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCList(element: Element) : MDCComponent, MDCLayoutComponent { 12 | public var vertical: Boolean 13 | public val listElements: Array 14 | public var wrapFocus: Boolean 15 | public val typeaheadInProgress: Boolean 16 | public var hasTypeahead: Boolean 17 | public var singleSelection: Boolean 18 | 19 | /** 20 | * type: `number | number[]` 21 | */ 22 | public var selectedIndex: dynamic 23 | 24 | public override fun layout() 25 | public fun getPrimaryText(item: Element): String 26 | public fun initializeListType() 27 | public fun setEnabled(itemIndex: Int, isEnabled: Boolean) 28 | } 29 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCElevation.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import dev.petuska.katalog.runtime.layout.* 6 | import dev.petuska.kmdc.elevation.* 7 | import dev.petuska.kmdc.typography.* 8 | import org.jetbrains.compose.web.css.* 9 | import sandbox.control.* 10 | 11 | private class MDCElevationVM { 12 | var z by mutableStateOf(12) 13 | } 14 | 15 | @Composable 16 | @Showcase(id = "MDCElevation") 17 | fun MDCElevation() = InteractiveShowcase( 18 | viewModel = { MDCElevationVM() }, 19 | controls = { 20 | RangeControl("Z", ::z, max = 24, converter = Number::toInt) 21 | }, 22 | ) { 23 | MDCElevation(z = z, attrs = { 24 | style { 25 | width(200.px) 26 | height(100.px) 27 | display(DisplayStyle.Flex) 28 | alignItems(AlignItems.Center) 29 | justifyContent(JustifyContent.Center) 30 | } 31 | }) { 32 | MDCBody1("z: ${z}dp") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kmdc/kmdc-banner/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/banner") 2 | 3 | package dev.petuska.kmdc.banner 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.core.MDCLayoutComponent 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCBanner(element: Element) : MDCComponent, MDCLayoutComponent { 12 | public val isOpen: Boolean 13 | public fun open() 14 | public fun close(closeReason: CloseReason) 15 | public fun getText(): String 16 | public fun setText(text: String) 17 | public fun getPrimaryActionText(): String 18 | public fun setPrimaryActionText(actionButtonText: String) 19 | public fun getSecondaryActionText(): String 20 | public fun setSecondaryActionText(actionButtonText: String) 21 | public override fun layout() 22 | } 23 | 24 | public external enum class CloseReason { 25 | PRIMARY, 26 | SECONDARY, 27 | UNSPECIFIED 28 | } 29 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/control/ChoiceControl.kt: -------------------------------------------------------------------------------- 1 | package sandbox.control 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.radio.* 5 | import sandbox.util.* 6 | import kotlin.reflect.* 7 | 8 | @Composable 9 | fun ChoiceControl( 10 | title: String, 11 | options: Map, 12 | selected: T?, 13 | description: String? = null, 14 | onSelect: (selected: T) -> Unit 15 | ) { 16 | NamedBlock(title, description) { 17 | ChunkedFormFields(options.entries) { (text, value) -> 18 | MDCRadio( 19 | checked = selected == value, 20 | label = text, 21 | attrs = { 22 | onInput { onSelect(value) } 23 | } 24 | ) 25 | } 26 | } 27 | } 28 | 29 | @Composable 30 | fun ChoiceControl( 31 | title: String, 32 | options: Map, 33 | selected: KMutableProperty0, 34 | description: String? = null, 35 | ) { 36 | ChoiceControl(title, options, selected.get(), description, selected::set) 37 | } 38 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCLineRipple.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import dev.petuska.katalog.runtime.layout.* 6 | import dev.petuska.kmdc.line.ripple.* 7 | import org.jetbrains.compose.web.attributes.* 8 | import org.jetbrains.compose.web.css.* 9 | import org.jetbrains.compose.web.dom.* 10 | import sandbox.control.* 11 | 12 | private class MDCLineRippleVM { 13 | var active by mutableStateOf(false) 14 | } 15 | 16 | @Composable 17 | @Showcase(id = "MDCLineRipple") 18 | fun MDCLineRipple() = InteractiveShowcase( 19 | viewModel = { MDCLineRippleVM() }, 20 | controls = { 21 | BooleanControl("Active", ::active) 22 | }, 23 | ) { 24 | Div(attrs = { 25 | style { 26 | display(DisplayStyle.LegacyInlineFlex) 27 | position(Position.Relative) 28 | } 29 | }) { 30 | TextInput("Notice the thick line bellow", attrs = { 31 | disabled() 32 | }) 33 | MDCLineRipple(active) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/src/jvmMain/kotlin/util/KSFunctionDeclaration.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.plugin.util 2 | 3 | import com.google.devtools.ksp.symbol.* 4 | import com.squareup.kotlinpoet.* 5 | import com.squareup.kotlinpoet.ksp.* 6 | 7 | public fun KSFunctionDeclaration.ref(logger: KatalogLogger): MemberName { 8 | val isExtension = extensionReceiver != null 9 | val parent = parentDeclaration 10 | val pkg = packageName.asString().takeIf(String::isNotBlank) 11 | return when { 12 | parent == null -> MemberName( 13 | pkg ?: logger.error("Root packages not supported!", this), 14 | simpleName.asString(), 15 | isExtension = isExtension, 16 | ) 17 | parent is KSClassDeclaration && parent.classKind == ClassKind.OBJECT -> MemberName( 18 | parent.toClassName(), 19 | simpleName.asString(), 20 | isExtension = isExtension, 21 | ) 22 | else -> { 23 | logger.error("Only top-level or object-scoped functions supported!", this) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /kmdc/kmdc-touch-target/src/jsMain/kotlin/MDCTouchTarget.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.touch.target 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.core.applyAttrs 8 | import org.jetbrains.compose.web.dom.Div 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | @JsModule("@material/touch-target/mdc-touch-target.scss") 12 | private external val Style: dynamic 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-touch-target) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | public fun MDCTouchTarget( 20 | attrs: MDCAttrsRaw? = null, 21 | content: MDCContentRaw? = null 22 | ) { 23 | Style 24 | Div( 25 | attrs = { 26 | classes("mdc-touch-target-wrapper") 27 | applyAttrs(attrs) 28 | }, 29 | content = content 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/Row.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.top.app.bar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContent 6 | import dev.petuska.kmdc.core.MDCContentDsl 7 | import dev.petuska.kmdc.core.reinterpret 8 | import org.jetbrains.compose.web.dom.Div 9 | import org.jetbrains.compose.web.dom.ElementScope 10 | import org.w3c.dom.HTMLDivElement 11 | 12 | public interface MDCTopAppBarRowScope : ElementScope 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | public fun MDCTopAppBarScope.Row( 20 | attrs: MDCAttrsRaw? = null, 21 | content: MDCContent? = null 22 | ) { 23 | Div( 24 | attrs = { 25 | classes("mdc-top-app-bar__row") 26 | attrs?.invoke(this) 27 | }, 28 | content = content.reinterpret() 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /.run/Start Sandbox.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | -------------------------------------------------------------------------------- /kmdc/kmdc-list/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.list 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | 7 | public external interface MDCListActionEventDetail { 8 | public val index: Int 9 | } 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-list) 13 | */ 14 | @MDCAttrsDsl 15 | public fun MDCListAttrsScope<*>.onAction(listener: MDCEventListener) { 16 | addMdcEventListener("MDCList:action", listener) 17 | } 18 | 19 | public external interface MDCListSelectionChangedEventDetail { 20 | public val changedIndices: IntArray 21 | } 22 | 23 | /** 24 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-list) 25 | */ 26 | @MDCAttrsDsl 27 | public fun MDCListAttrsScope<*>.onSelectionChanged(listener: MDCEventListener) { 28 | addMdcEventListener("MDCList:selectionChange", listener) 29 | } 30 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCTouchTarget.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.fab.MDCFab 10 | import dev.petuska.kmdc.fab.MDCFabType 11 | import dev.petuska.kmdc.touch.target.MDCTouchTarget 12 | import sandbox.control.BooleanControl 13 | 14 | private class MDCTouchTargetVM { 15 | var enabled by mutableStateOf(true) 16 | } 17 | 18 | @Composable 19 | @Showcase(id = "MDCTouchTarget") 20 | fun MDCTouchTarget() = InteractiveShowcase( 21 | viewModel = { MDCTouchTargetVM() }, 22 | controls = { 23 | BooleanControl("Enabled", ::enabled) 24 | }, 25 | ) { 26 | val render = @Composable { 27 | MDCFab(type = MDCFabType.Mini, touch = enabled) 28 | } 29 | if (enabled) { 30 | MDCTouchTarget { 31 | render() 32 | } 33 | } else { 34 | render() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /katalog/katalog-ksp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 3 | 4 | plugins { 5 | id("convention.library-jvm") 6 | alias(libs.plugins.build.config) 7 | } 8 | 9 | kotlin { 10 | sourceSets { 11 | jvmMain { 12 | dependencies { 13 | compileOnly(libs.ksp.api) 14 | implementation(libs.kotlinpoet.ksp) 15 | } 16 | } 17 | } 18 | } 19 | 20 | java { 21 | targetCompatibility = JavaVersion.VERSION_11 22 | } 23 | 24 | tasks { 25 | withType { 26 | compilerOptions { 27 | jvmTarget.set(JvmTarget.fromTarget(java.targetCompatibility.toString())) 28 | } 29 | } 30 | } 31 | 32 | buildConfig { 33 | useKotlinOutput { 34 | internalVisibility = true 35 | topLevelConstants = true 36 | } 37 | packageName("dev.petuska.katalog.plugin.config") 38 | buildConfigField("String", "GROUP", "\"${rootProject.group}\"") 39 | buildConfigField("String", "NAME", "\"${rootProject.name}\"") 40 | buildConfigField("String", "VERSION", "\"${rootProject.version}\"") 41 | } 42 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCRadio.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import dev.petuska.katalog.runtime.layout.* 6 | import dev.petuska.kmdc.form.field.* 7 | import dev.petuska.kmdc.radio.* 8 | import sandbox.control.* 9 | 10 | private class MDCRadioVM { 11 | val options = listOf("A", "B", "C", "D") 12 | var disabled by mutableStateOf(false) 13 | var touch by mutableStateOf(false) 14 | 15 | var selected by mutableStateOf(null) 16 | } 17 | 18 | @Composable 19 | @Showcase(id = "MDCRadio") 20 | fun MDCRadio() = InteractiveShowcase( 21 | viewModel = { MDCRadioVM() }, 22 | controls = { 23 | BooleanControl("Disabled", ::disabled) 24 | BooleanControl("Touch", ::touch) 25 | }, 26 | ) { 27 | MDCFormField { 28 | options.forEach { text -> 29 | MDCRadio( 30 | checked = (text == selected), 31 | label = text, 32 | disabled = disabled, 33 | touch = touch, 34 | attrs = { 35 | onInput { selected = text } 36 | } 37 | ) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/action/Icon.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.action 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.core.applyAttrs 8 | import org.jetbrains.compose.web.dom.I 9 | import org.w3c.dom.HTMLElement 10 | 11 | public interface MDCChipActionIconScope : MDCChipActionScope 12 | 13 | /** 14 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 15 | */ 16 | @MDCContentDsl 17 | @Composable 18 | public fun MDCChipActionIconScope<*>.Icon( 19 | attrs: MDCAttrsRaw? = null, 20 | content: MDCContentRaw? = null 21 | ) { 22 | val type = MDCChipActionTypeLocal.current 23 | I( 24 | attrs = { 25 | classes("mdc-evolution-chip__icon", "mdc-evolution-chip__icon--$type") 26 | if (type == "trailing") classes("mdc-chip-trailing-action") 27 | applyAttrs(attrs) 28 | }, 29 | content = content 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/domain/Katalog.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.domain 2 | 3 | import dev.petuska.katalog.runtime.theme.KatalogTheme 4 | 5 | public data class Katalog internal constructor( 6 | val id: String, 7 | val title: String, 8 | val subtitle: String?, 9 | val contentRootUrl: String?, 10 | val debug: Boolean, 11 | val showcases: List, 12 | val theme: KatalogTheme, 13 | ) { 14 | @Suppress("LongParameterList") 15 | public class Builder( 16 | public var id: String = "katalog", 17 | public var title: String = "Katalog", 18 | public var subtitle: String? = null, 19 | public var contentRootUrl: String? = null, 20 | public var debug: Boolean = false, 21 | public var theme: KatalogTheme = KatalogTheme(), 22 | public var showcases: MutableList 23 | ) { 24 | public fun build(): Katalog = Katalog( 25 | id = id, 26 | title = title, 27 | subtitle = subtitle, 28 | contentRootUrl = contentRootUrl, 29 | debug = debug, 30 | theme = theme, 31 | showcases = showcases, 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kmdc/kmdc-data-table/src/jsMain/kotlin/ProgressIndicator.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.data.table 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.applyAttrs 7 | import dev.petuska.kmdc.linear.progress.MDCLinearProgress 8 | import org.jetbrains.compose.web.dom.Div 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-data-table) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCDataTableScope.ProgressIndicator( 17 | attrs: MDCAttrsRaw? = null, 18 | ) { 19 | Div( 20 | attrs = { 21 | classes("mdc-data-table__progress-indicator") 22 | applyAttrs(attrs) 23 | } 24 | ) { 25 | Div( 26 | attrs = { 27 | classes("mdc-data-table__scrim") 28 | } 29 | ) 30 | MDCLinearProgress( 31 | determinate = false, 32 | attrs = { 33 | classes("mdc-data-table__linear-progress") 34 | }, 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kmdc/kmdc-select/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/select") 2 | 3 | package dev.petuska.kmdc.select 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | 9 | @MDCExternalAPI 10 | public external class MDCSelect(element: Element) : MDCComponent { 11 | public var value: String 12 | public var selectedIndex: String 13 | public var disabled: Boolean 14 | public var leadingIconAriaLabel: String 15 | public var helperTextContent: String 16 | public var useDefaultValidation: Boolean 17 | public var valid: Boolean 18 | public var required: Boolean 19 | 20 | public fun layout() 21 | public fun layoutOptions() 22 | public fun setValue(value: String, skipNotify: Boolean = definedExternally) 23 | public fun setSelectedIndex(selectedIndex: Int, skipNotify: Boolean = definedExternally) 24 | } 25 | 26 | @MDCExternalAPI 27 | public external class MDCSelectHelperText(element: Element) : MDCComponent 28 | 29 | @MDCExternalAPI 30 | public external class MDCSelectIcon(element: Element) : MDCComponent 31 | -------------------------------------------------------------------------------- /kmdc/kmdc-card/src/jsMain/kotlin/MDCCard.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.card 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Div 6 | import org.jetbrains.compose.web.dom.ElementScope 7 | import org.w3c.dom.HTMLDivElement 8 | 9 | @JsModule("@material/card/mdc-card.scss") 10 | private external val Style: dynamic 11 | 12 | public enum class MDCCardType(public vararg val classes: String) { 13 | Elevated, 14 | Outlined("mdc-card--outlined") 15 | } 16 | 17 | public interface MDCCardScope : ElementScope 18 | 19 | /** 20 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-card) 21 | */ 22 | @MDCContentDsl 23 | @Composable 24 | public fun MDCCard( 25 | type: MDCCardType = MDCCardType.Elevated, 26 | attrs: MDCAttrsRaw? = null, 27 | content: MDCContent? = null 28 | ) { 29 | Style 30 | Div( 31 | attrs = { 32 | classes("mdc-card") 33 | classes(type.classes) 34 | applyAttrs(attrs) 35 | }, 36 | content = content.reinterpret() 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /kmdc/kmdc-slider/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/slider") 2 | 3 | package dev.petuska.kmdc.slider 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.core.MDCLayoutComponent 8 | import org.w3c.dom.Element 9 | import org.w3c.dom.HTMLElement 10 | 11 | @MDCExternalAPI 12 | public external class MDCSlider( 13 | element: Element, 14 | options: MDCSliderOptions = definedExternally 15 | ) : MDCComponent, MDCLayoutComponent { 16 | public var root: HTMLElement 17 | public fun initialize(options: MDCSliderOptions = definedExternally): dynamic 18 | public override fun layout() 19 | public fun getValueStart(): Number 20 | public fun setValueStart(valueStart: Number) 21 | public fun getValue(): Number 22 | public fun setValue(value: Number) 23 | public fun getDisabled(): Boolean 24 | public fun setDisabled(disabled: Boolean) 25 | public fun setValueToAriaValueTextFn(mapFn: ((value: Number) -> String)?) 26 | } 27 | 28 | @MDCExternalAPI 29 | public external interface MDCSliderOptions { 30 | public var skipInitialUIUpdate: Boolean? 31 | } 32 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu-surface/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/menu-surface") 2 | 3 | package dev.petuska.kmdc.menu.surface 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | import org.w3c.dom.HTMLElement 9 | 10 | @MDCExternalAPI 11 | public external class MDCMenuSurface(element: Element) : MDCComponent { 12 | public var anchorElement: HTMLElement 13 | 14 | public fun isOpen(): Boolean 15 | public fun open() 16 | public var quickOpen: Boolean 17 | public fun close(skipRestoreFocus: Boolean? = definedExternally) 18 | public fun setIsHoisted(isHoisted: Boolean) 19 | public fun setAnchorCorner(corner: Corner) 20 | public fun setAnchorMargin(margin: dynamic) 21 | public fun setFixedPosition(isFixed: Boolean) 22 | public fun setAbsolutePosition(x: Number, y: Number) 23 | public fun setMenuSurfaceAnchorElement(element: Element) 24 | } 25 | 26 | public external enum class Corner { 27 | TOP_LEFT, 28 | TOP_RIGHT, 29 | BOTTOM_LEFT, 30 | BOTTOM_RIGHT, 31 | TOP_START, 32 | TOP_END, 33 | BOTTOM_START, 34 | BOTTOM_END, 35 | } 36 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/grid/ChipCell.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.grid 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.chips.MDCChipScope 5 | import dev.petuska.kmdc.chips.action.MDCChipActionScope 6 | import dev.petuska.kmdc.chips.action.MDCChipActionTypeLocal 7 | import dev.petuska.kmdc.core.MDCAttrsRaw 8 | import dev.petuska.kmdc.core.MDCContent 9 | import dev.petuska.kmdc.core.MDCContentDsl 10 | import dev.petuska.kmdc.core.role 11 | import org.jetbrains.compose.web.dom.Span 12 | import org.w3c.dom.HTMLButtonElement 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | internal fun MDCChipScope.ChipCell( 20 | attrs: MDCAttrsRaw? = null, 21 | content: MDCContent>? = null 22 | ) { 23 | val type = MDCChipActionTypeLocal.current 24 | Span( 25 | attrs = { 26 | classes("mdc-evolution-chip__cell", "mdc-evolution-chip__cell--$type") 27 | role("gridcell") 28 | }, 29 | ) { 30 | GridAction(attrs, content) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kmdc/kmdc-button/src/jsMain/kotlin/Label.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.button 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.Span 8 | import org.jetbrains.compose.web.dom.Text 9 | import org.w3c.dom.HTMLSpanElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-button) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCButtonScope<*>.Label( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContentRaw? = null, 19 | ) { 20 | Span( 21 | attrs = { 22 | classes("mdc-button__label") 23 | attrs?.invoke(this) 24 | }, 25 | content = content 26 | ) 27 | } 28 | 29 | /** 30 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-button) 31 | */ 32 | @MDCContentDsl 33 | @Composable 34 | public fun MDCButtonScope<*>.Label( 35 | text: String, 36 | attrs: MDCAttrsRaw? = null, 37 | ) { 38 | Label(attrs) { 39 | Text(text) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kmdc/kmdc-select/src/jsMain/kotlin/menu/Menu.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.select.menu 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.list.MDCListScope 6 | import dev.petuska.kmdc.menu.MDCMenuAttrsScope 7 | import dev.petuska.kmdc.menu.MDCMenuLayout 8 | import dev.petuska.kmdc.select.MDCSelectScope 9 | import org.w3c.dom.HTMLUListElement 10 | 11 | // TODO figure out why extending MenuScope is crashing 12 | public interface MDCSelectMenuScope : MDCListScope 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-select) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | public fun MDCSelectScope.Menu( 20 | fullWidth: Boolean = true, 21 | fixed: Boolean = false, 22 | singleSelect: Boolean = true, 23 | attrs: MDCAttrs? = null, 24 | content: MDCContent? = null, 25 | ) { 26 | MDCMenuLayout( 27 | singleSelect = singleSelect, 28 | fullWidth = fullWidth, 29 | fixed = fixed, 30 | attrs = { 31 | classes("mdc-select__menu") 32 | applyAttrs(attrs) 33 | }, 34 | content = content.reinterpret() 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /kmdc/kmdc-tooltip/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/tooltip") 2 | 3 | package dev.petuska.kmdc.tooltip 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import org.w3c.dom.Element 8 | import org.w3c.dom.events.Event 9 | 10 | @MDCExternalAPI 11 | public external class MDCTooltip(element: Element) : MDCComponent { 12 | public fun setTooltipPosition( 13 | xPos: dynamic = definedExternally, 14 | yPos: dynamic = definedExternally, 15 | withCaretPos: dynamic = definedExternally, 16 | ) 17 | 18 | public fun setAnchorBoundaryType(type: dynamic) 19 | public fun setShowDelay(delayMs: Number) 20 | public fun setHideDelay(delayMs: Number) 21 | public fun hide() 22 | public fun isShown(): Boolean 23 | public fun attachScrollHandler( 24 | addEventListenerFn: (event: dynamic, handler: (event: E) -> Unit) -> Unit 25 | ) 26 | 27 | public fun removeScrollHandler( 28 | removeEventHandlerFn: (event: dynamic, handler: (event: E) -> Unit) -> Unit 29 | ) 30 | 31 | public var checked: Boolean 32 | public var indeterminate: Boolean 33 | public var disabled: Boolean 34 | public var value: String 35 | } 36 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu-surface/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.menu.surface 2 | 3 | import dev.petuska.kmdc.core.* 4 | import org.w3c.dom.* 5 | 6 | public interface MDCMenuSurfaceEventDetail { 7 | public val item: Element 8 | } 9 | 10 | /** 11 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-menu-surface) 12 | */ 13 | @MDCAttrsDsl 14 | public fun MDCMenuSurfaceAttrsScope.onClosed(listener: MDCEventListener) { 15 | addMdcEventListener("MDCMenuSurface:closed", listener) 16 | } 17 | 18 | /** 19 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-menu-surface) 20 | */ 21 | @MDCAttrsDsl 22 | public fun MDCMenuSurfaceAttrsScope.onClosing(listener: MDCEventListener) { 23 | addMdcEventListener("MDCMenuSurface:closing", listener) 24 | } 25 | 26 | /** 27 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-menu-surface) 28 | */ 29 | @MDCAttrsDsl 30 | public fun MDCMenuSurfaceAttrsScope.onOpened(listener: MDCEventListener) { 31 | addMdcEventListener("MDCMenuSurface:opened", listener) 32 | } 33 | -------------------------------------------------------------------------------- /kmdc/kmdc-slider/src/jsMain/kotlin/MDCSliderInput.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.slider 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.* 6 | import org.jetbrains.compose.web.attributes.builders.* 7 | import org.jetbrains.compose.web.dom.* 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-slider) 11 | */ 12 | @MDCContentDsl 13 | @Composable 14 | internal fun MDCSliderInput( 15 | value: Number, 16 | min: Number, 17 | max: Number, 18 | label: String?, 19 | rangeStart: Boolean?, 20 | disabled: Boolean, 21 | step: Number, 22 | attrs: MDCAttrs>? = null, 23 | ) { 24 | Input( 25 | type = InputType.Range, 26 | attrs = { 27 | classes("mdc-slider__input") 28 | when (rangeStart) { 29 | null -> name("volume") 30 | true -> name("rangeStart") 31 | false -> name("rangeEnd") 32 | } 33 | if (label != null) aria("label", label) 34 | min("$min") 35 | max("$max") 36 | step(step) 37 | attr("value", "$value") 38 | if (disabled) disabled() 39 | attrs?.invoke(this) 40 | } 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /kmdc/kmdc-tooltip/src/jsMain/kotlin/Title.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tooltip 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.H2 8 | import org.jetbrains.compose.web.dom.Text 9 | import org.w3c.dom.HTMLHeadingElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tooltip) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCRichTooltipScope.Title( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContentRaw? = null, 19 | ) { 20 | H2( 21 | attrs = { 22 | classes("mdc-tooltip__title") 23 | attrs?.invoke(this) 24 | }, 25 | content = content 26 | ) 27 | } 28 | 29 | /** 30 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tooltip) 31 | */ 32 | @MDCContentDsl 33 | @Composable 34 | public fun MDCRichTooltipScope.Title( 35 | text: String, 36 | attrs: MDCAttrsRaw? = null, 37 | ) { 38 | Title(attrs) { 39 | Text(text) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/control/RangeControl.kt: -------------------------------------------------------------------------------- 1 | package sandbox.control 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.slider.* 5 | import org.jetbrains.compose.web.css.* 6 | import sandbox.util.* 7 | import kotlin.reflect.* 8 | 9 | @Composable 10 | fun RangeControl( 11 | name: String, 12 | value: Number, 13 | min: Number = 0, 14 | max: Number = 100, 15 | step: Number = 1, 16 | description: String? = null, 17 | onInput: (Number) -> Unit, 18 | ) { 19 | NamedBlock(name, description) { 20 | MDCSlider( 21 | value = value, 22 | label = "Size", 23 | discrete = true, 24 | min = min, 25 | max = max, 26 | step = step, 27 | attrs = { 28 | onInput { 29 | onInput(it.detail.value) 30 | } 31 | style { margin(0.em) } 32 | } 33 | ) 34 | } 35 | } 36 | 37 | @Composable 38 | fun RangeControl( 39 | name: String, 40 | value: KMutableProperty0, 41 | min: Number = 0, 42 | max: Number = 100, 43 | step: Number = 1, 44 | description: String? = null, 45 | converter: Number.() -> T 46 | ) { 47 | RangeControl(name, value.get(), min, max, step, description) { 48 | value.set(it.converter()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /kmdc/kmdc-dialog/src/jsMain/kotlin/Title.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.dialog 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.* 6 | import org.jetbrains.compose.web.dom.Text 7 | import org.w3c.dom.* 8 | 9 | public interface MDCDialogTitleScope : ElementScope 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCDialogTitleScope<*>.Title( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContentRaw? = null 19 | ) { 20 | val titleId = TitleIdLocal.current 21 | H2( 22 | attrs = { 23 | classes("mdc-dialog__title") 24 | id(titleId) 25 | attrs?.invoke(this) 26 | }, 27 | content = content.reinterpret() 28 | ) 29 | } 30 | 31 | /** 32 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 33 | */ 34 | @MDCContentDsl 35 | @Composable 36 | public fun MDCDialogTitleScope<*>.Title( 37 | title: String, 38 | attrs: MDCAttrsRaw? = null 39 | ) { 40 | Title(attrs) { Text(title) } 41 | } 42 | -------------------------------------------------------------------------------- /kmdc/kmdc-snackbar/src/jsMain/kotlin/Label.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.snackbar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.Div 8 | import org.jetbrains.compose.web.dom.Text 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCSnackbarScope.Label( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContentRaw? = null, 19 | ) { 20 | Div( 21 | attrs = { 22 | classes("mdc-snackbar__label") 23 | attr("aria-atomic", "false") 24 | attrs?.invoke(this) 25 | }, 26 | content = content 27 | ) 28 | } 29 | 30 | /** 31 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 32 | */ 33 | @MDCContentDsl 34 | @Composable 35 | public fun MDCSnackbarScope.Label( 36 | text: String, 37 | attrs: MDCAttrsRaw? = null, 38 | ) { 39 | Label(attrs) { Text(text) } 40 | } 41 | -------------------------------------------------------------------------------- /kmdc/kmdc-dialog/src/jsMain/kotlin/Content.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.dialog 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.* 6 | import org.jetbrains.compose.web.dom.* 7 | import org.w3c.dom.* 8 | 9 | public interface MDCDialogContentScope : ElementScope 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCDialogScope.Content( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContent? = null 19 | ) { 20 | val contentId = ContentIdLocal.current 21 | val open = OpenLocal.current 22 | Div( 23 | attrs = { 24 | classes("mdc-dialog__content") 25 | aria("hidden", !open) 26 | id(contentId) 27 | attrs?.invoke(this) 28 | }, 29 | content = content.reinterpret() 30 | ) 31 | } 32 | 33 | /** 34 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 35 | */ 36 | @MDCAttrsDsl 37 | public fun AttrsScope.mdcDialogInitialFocus() { 38 | tabIndex(0) 39 | data("mdc-dialog-initial-focus", "true") 40 | } 41 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/Title.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.top.app.bar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.Span 8 | import org.jetbrains.compose.web.dom.Text 9 | import org.w3c.dom.HTMLSpanElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCTopAppBarSectionScope.Title( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContentRaw? = null 19 | ) { 20 | Span( 21 | attrs = { 22 | classes("mdc-top-app-bar__title") 23 | attrs?.invoke(this) 24 | }, 25 | content = content 26 | ) 27 | } 28 | 29 | /** 30 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 31 | */ 32 | @MDCContentDsl 33 | @Composable 34 | public fun MDCTopAppBarSectionScope.Title( 35 | text: String, 36 | attrs: MDCAttrsRaw? = null, 37 | ) { 38 | Title(attrs = attrs) { 39 | Text(text) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/action/Label.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.action 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.core.applyAttrs 8 | import org.jetbrains.compose.web.dom.Span 9 | import org.jetbrains.compose.web.dom.Text 10 | import org.w3c.dom.HTMLSpanElement 11 | 12 | /** 13 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 14 | */ 15 | @MDCContentDsl 16 | @Composable 17 | public fun MDCChipActionScope<*>.Label( 18 | attrs: MDCAttrsRaw? = null, 19 | content: MDCContentRaw? = null 20 | ) { 21 | Span( 22 | attrs = { 23 | classes("mdc-evolution-chip__text-label") 24 | applyAttrs(attrs) 25 | }, 26 | content = content 27 | ) 28 | } 29 | 30 | /** 31 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 32 | */ 33 | @MDCContentDsl 34 | @Composable 35 | public fun MDCChipActionScope<*>.Label( 36 | text: String, 37 | attrs: MDCAttrsRaw? = null, 38 | ) { 39 | Label(attrs) { Text(text) } 40 | } 41 | -------------------------------------------------------------------------------- /kmdc/kmdc-slider/src/jsMain/kotlin/MDCSliderThumb.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.slider 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.aria 7 | import org.jetbrains.compose.web.dom.Div 8 | import org.jetbrains.compose.web.dom.Text 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-slider) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | internal fun MDCSliderThumb( 17 | value: Number?, 18 | discrete: Boolean, 19 | attrs: MDCAttrsRaw? = null, 20 | ) { 21 | Div(attrs = { 22 | classes("mdc-slider__thumb") 23 | attrs?.invoke(this) 24 | }) { 25 | if (discrete && value != null) { 26 | Div(attrs = { 27 | classes("mdc-slider__value-indicator-container") 28 | aria("hidden", "true") 29 | }) { 30 | Div(attrs = { classes("mdc-slider__value-indicator") }) { 31 | Div(attrs = { classes("mdc-slider__value-indicator-text") }) { 32 | Text("$value") 33 | } 34 | } 35 | } 36 | } 37 | Div(attrs = { classes("mdc-slider__thumb-knob") }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kmdc/kmdc-segmented-button/src/jsMain/kotlin/Label.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.segmented.button 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.dom.Div 8 | import org.jetbrains.compose.web.dom.Text 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCSegmentedButtonSegmentScope.Label( 17 | attrs: MDCAttrsRaw? = null, 18 | content: MDCContentRaw? = null, 19 | ) { 20 | Div( 21 | attrs = { 22 | classes("mdc-segmented-button__label") 23 | attrs?.invoke(this) 24 | }, 25 | content = content 26 | ) 27 | } 28 | 29 | /** 30 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 31 | */ 32 | @MDCContentDsl 33 | @Composable 34 | public fun MDCSegmentedButtonSegmentScope.Label( 35 | text: String, 36 | attrs: MDCAttrsRaw? = null, 37 | ) { 38 | Label(attrs) { 39 | Text(text) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCCheckbox.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import dev.petuska.katalog.runtime.layout.* 6 | import dev.petuska.kmdc.checkbox.* 7 | import dev.petuska.kmdc.form.field.* 8 | import sandbox.control.* 9 | 10 | private class MDCCheckboxVM { 11 | var disabled by mutableStateOf(false) 12 | var indeterminate by mutableStateOf(false) 13 | var touch by mutableStateOf(false) 14 | var label by mutableStateOf("My Checkbox") 15 | var checked by mutableStateOf(false) 16 | } 17 | 18 | @Composable 19 | @Showcase(id = "MDCCheckbox") 20 | fun MDCCheckbox() = InteractiveShowcase( 21 | viewModel = { MDCCheckboxVM() }, 22 | controls = { 23 | BooleanControl("Disabled", ::disabled) 24 | BooleanControl("Indeterminate", ::indeterminate) 25 | BooleanControl("Touch", ::touch) 26 | BooleanControl("Checked", ::checked) 27 | TextControl("Label", label) { label = it } 28 | }, 29 | ) { 30 | MDCFormField { 31 | MDCCheckbox( 32 | checked = checked, 33 | indeterminate = indeterminate, 34 | disabled = disabled, 35 | touch = touch, 36 | label = label, 37 | attrs = { 38 | onInput { checked = !checked } 39 | } 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/grid/GridAction.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.grid 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.chips.MDCChipDisabledLocal 5 | import dev.petuska.kmdc.chips.action.MDCChipActionScope 6 | import dev.petuska.kmdc.chips.action.MDCChipActionTypeLocal 7 | import dev.petuska.kmdc.chips.action.Ripple 8 | import dev.petuska.kmdc.core.* 9 | import org.jetbrains.compose.web.attributes.ButtonType 10 | import org.jetbrains.compose.web.attributes.disabled 11 | import org.jetbrains.compose.web.attributes.type 12 | import org.jetbrains.compose.web.dom.Button 13 | import org.w3c.dom.HTMLButtonElement 14 | 15 | @MDCContentDsl 16 | @Composable 17 | internal fun GridAction( 18 | attrs: MDCAttrsRaw?, 19 | content: MDCContent>? 20 | ) { 21 | val type = MDCChipActionTypeLocal.current 22 | val disabled = MDCChipDisabledLocal.current 23 | Button(attrs = { 24 | classes("mdc-evolution-chip__action", "mdc-evolution-chip__action--$type") 25 | type(ButtonType.Button) 26 | if (disabled) { 27 | disabled() 28 | } 29 | applyAttrs(attrs) 30 | }) { 31 | unsafeCast>().Ripple() 32 | applyContent(content) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCFloatingLabel.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.floating.label.MDCFloatingLabel 10 | import sandbox.control.BooleanControl 11 | import sandbox.control.TextControl 12 | 13 | private class MDCFloatingLabelVM { 14 | var float by mutableStateOf(false) 15 | var required by mutableStateOf(false) 16 | var shake by mutableStateOf(false) 17 | var label by mutableStateOf("My Label") 18 | } 19 | 20 | @Composable 21 | @Showcase(id = "MDCFloatingLabel") 22 | fun MDCFloatingLabel() = InteractiveShowcase( 23 | viewModel = { MDCFloatingLabelVM() }, 24 | controls = { 25 | BooleanControl("Float", ::float) 26 | BooleanControl("Required", ::required) 27 | BooleanControl("Shake", ::shake) 28 | TextControl("Label", label) { label = it } 29 | }, 30 | ) { 31 | MDCFloatingLabel( 32 | id = "kmdc-showcase-floating-label", 33 | text = label, 34 | float = float, 35 | required = required, 36 | shake = shake, 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /kmdc/kmdc-line-ripple/src/jsMain/kotlin/MDCLineRipple.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.line.ripple 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Span 6 | import org.w3c.dom.HTMLSpanElement 7 | 8 | @JsModule("@material/line-ripple/mdc-line-ripple.scss") 9 | private external val Style: dynamic 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-line-ripple) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCLineRipple( 17 | active: Boolean, 18 | attrs: MDCAttrsRaw? = null, 19 | content: MDCContentRaw? = null 20 | ) { 21 | MDCLineRippleLayout( 22 | attrs = attrs, 23 | ) { 24 | MDCProvider(::MDCLineRipple) { 25 | MDCSideEffect(active) { 26 | if (active) activate() else deactivate() 27 | } 28 | applyContent(content) 29 | } 30 | } 31 | } 32 | 33 | @Composable 34 | @KMDCInternalAPI 35 | public fun MDCLineRippleLayout( 36 | attrs: MDCAttrsRaw? = null, 37 | content: MDCContentRaw? = null 38 | ) { 39 | Style 40 | Span( 41 | attrs = { 42 | classes("mdc-line-ripple") 43 | applyAttrs(attrs) 44 | }, 45 | content = content, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /kmdc/kmdc-textfield/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/textfield") 2 | 3 | package dev.petuska.kmdc.textfield 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.core.MDCLayoutComponent 8 | import dev.petuska.kmdc.ripple.MDCRipple 9 | import org.w3c.dom.Element 10 | 11 | @MDCExternalAPI 12 | public external class MDCTextField(element: Element) : MDCComponent, MDCLayoutComponent { 13 | public var value: String 14 | public var disabled: Boolean 15 | public var valid: Boolean 16 | public var prefixText: String 17 | public var suffixText: String 18 | 19 | // Proxied from input element 20 | public var required: Boolean 21 | public var pattern: String 22 | public var minLength: Number 23 | public var maxLength: Number 24 | public var min: Number 25 | public var max: Number 26 | public var step: Number 27 | 28 | // Write-only 29 | public var useNativeValidation: Boolean 30 | public var helperTextContent: String 31 | public var ripple: MDCRipple 32 | public var leadingIconAriaLabel: String 33 | public var trailingIconAriaLabel: String 34 | public var leadingIconContent: String 35 | public var trailingIconContent: String 36 | 37 | public fun focus() 38 | public override fun layout() 39 | } 40 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCSwitch.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.form.field.MDCFormField 10 | import dev.petuska.kmdc.switch.MDCSwitch 11 | import org.jetbrains.compose.web.attributes.disabled 12 | import sandbox.control.BooleanControl 13 | import sandbox.control.TextControl 14 | 15 | private class MDCSwitchVM { 16 | var disabled by mutableStateOf(false) 17 | var selected by mutableStateOf(false) 18 | var label by mutableStateOf("on/off") 19 | } 20 | 21 | @Composable 22 | @Showcase(id = "MDCSwitch") 23 | fun MDCSwitch() = InteractiveShowcase( 24 | viewModel = { MDCSwitchVM() }, 25 | controls = { 26 | BooleanControl("Disabled", ::disabled) 27 | BooleanControl("Selected", ::selected) 28 | TextControl("Label", ::label) 29 | }, 30 | ) { 31 | MDCFormField { 32 | MDCSwitch( 33 | selected = selected, 34 | label = label.takeIf(String::isNotBlank), 35 | attrs = { 36 | if (disabled) disabled() 37 | onClick { selected = !selected } 38 | } 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kmdc/kmdc-form-field/src/jsMain/kotlin/MDCFormField.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.form.field 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Div 6 | import org.jetbrains.compose.web.dom.ElementScope 7 | import org.w3c.dom.HTMLDivElement 8 | import org.w3c.dom.HTMLElement 9 | 10 | @JsModule("@material/form-field/mdc-form-field.scss") 11 | public external val Style: dynamic 12 | 13 | public enum class MDCFormFieldAlign(public vararg val classes: String) { 14 | Start, End("mdc-form-field--align-end") 15 | } 16 | 17 | public interface MDCFormFieldScope : ElementScope 18 | 19 | /** 20 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-form-field) 21 | */ 22 | @MDCContentDsl 23 | @Composable 24 | public fun MDCFormField( 25 | align: MDCFormFieldAlign = MDCFormFieldAlign.Start, 26 | noWrap: Boolean = false, 27 | attrs: MDCAttrsRaw? = null, 28 | content: MDCContent? = null, 29 | ) { 30 | Style 31 | 32 | Div(attrs = { 33 | classes("mdc-form-field") 34 | classes(align.classes) 35 | if (noWrap) classes("mdc-form-field--nowrap") 36 | applyAttrs(attrs) 37 | }) { 38 | MDCProvider(::MDCFormField) { 39 | applyContent(content) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /kmdc/kmdc-tooltip/src/jsMain/kotlin/Link.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tooltip 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import org.jetbrains.compose.web.attributes.href 8 | import org.jetbrains.compose.web.dom.A 9 | import org.jetbrains.compose.web.dom.Text 10 | import org.w3c.dom.HTMLAnchorElement 11 | 12 | /** 13 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tooltip) 14 | */ 15 | @MDCContentDsl 16 | @Composable 17 | public fun MDCTooltipRichContentScope.Link( 18 | attrs: MDCAttrsRaw? = null, 19 | content: MDCContentRaw? = null, 20 | ) { 21 | A( 22 | attrs = { 23 | classes("mdc-tooltip__content-link") 24 | attrs?.invoke(this) 25 | }, 26 | content = content 27 | ) 28 | } 29 | 30 | /** 31 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tooltip) 32 | */ 33 | @MDCContentDsl 34 | @Composable 35 | public fun MDCTooltipRichContentScope.Link( 36 | text: String, 37 | href: String, 38 | attrs: MDCAttrsRaw? = null, 39 | ) { 40 | Link(attrs = { 41 | href(href) 42 | attrs?.invoke(this) 43 | }) { 44 | Text(text) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/MDCChips.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.AttrsScope 6 | import org.jetbrains.compose.web.dom.ElementScope 7 | import org.jetbrains.compose.web.dom.Span 8 | import org.w3c.dom.HTMLSpanElement 9 | 10 | @JsModule("@material/chips/styles.scss") 11 | private external val Style: dynamic 12 | 13 | public interface MDCChipsAttrsScope : AttrsScope 14 | 15 | public interface MDCChipsScope : ElementScope 16 | 17 | /** 18 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 19 | */ 20 | @MDCContentDsl 21 | @Composable 22 | internal fun MDCChips( 23 | overflow: Boolean = false, 24 | attrs: MDCAttrs? = null, 25 | content: MDCContent? = null 26 | ) { 27 | Style 28 | Span( 29 | attrs = { 30 | classes("mdc-evolution-chip-set") 31 | if (overflow) classes("mdc-evolution-chip-set--overflow") 32 | applyAttrs(attrs) 33 | }, 34 | ) { 35 | MDCProvider(::MDCChipSet) { 36 | Span( 37 | attrs = { 38 | classes("mdc-evolution-chip-set__chips") 39 | role("presentation") 40 | }, 41 | content = content.reinterpret() 42 | ) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kmdc/kmdc-slider/src/jsMain/kotlin/MDCSliderTrack.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.slider 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import org.jetbrains.compose.web.dom.Div 7 | import org.w3c.dom.HTMLDivElement 8 | 9 | /** 10 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-slider) 11 | */ 12 | @MDCContentDsl 13 | @Composable 14 | internal fun MDCSliderTrack( 15 | tickMarks: Boolean, 16 | attrs: MDCAttrsRaw? = null, 17 | ) { 18 | Div(attrs = { 19 | classes("mdc-slider__track") 20 | attrs?.invoke(this) 21 | }) { 22 | Div(attrs = { 23 | classes("mdc-slider__track--inactive") 24 | }) 25 | Div(attrs = { 26 | classes("mdc-slider__track--active") 27 | }) { 28 | Div(attrs = { 29 | classes("mdc-slider__track--active_fill") 30 | }) 31 | } 32 | if (tickMarks) MDCSliderTickMarks() 33 | } 34 | } 35 | 36 | /** 37 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-slider) 38 | */ 39 | @MDCContentDsl 40 | @Composable 41 | private fun MDCSliderTickMarks( 42 | attrs: MDCAttrsRaw? = null, 43 | ) { 44 | Div( 45 | attrs = { 46 | classes("mdc-slider__tick-marks") 47 | attrs?.invoke(this) 48 | }, 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/grid/ActionChip.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.grid 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.CompositionLocalProvider 5 | import dev.petuska.kmdc.chips.Chip 6 | import dev.petuska.kmdc.chips.action.MDCChipActionScope 7 | import dev.petuska.kmdc.chips.action.MDCChipActionTypeLocal 8 | import dev.petuska.kmdc.core.* 9 | import org.w3c.dom.HTMLButtonElement 10 | 11 | /** 12 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 13 | */ 14 | @MDCContentDsl 15 | @Composable 16 | public fun MDCChipsGridScope.ActionChip( 17 | id: String, 18 | disabled: Boolean = false, 19 | withPrimaryGraphic: Boolean = false, 20 | withPrimaryIcon: Boolean = false, 21 | touch: Boolean = false, 22 | attrs: AttrsBuilder? = null, 23 | content: MDCContent>? = null 24 | ) { 25 | Chip( 26 | id = id, 27 | disabled = disabled, 28 | withPrimaryGraphic = withPrimaryGraphic, 29 | withPrimaryIcon = withPrimaryIcon, 30 | touch = touch, 31 | attrs = { 32 | role("row") 33 | }, 34 | ) { 35 | CompositionLocalProvider(MDCChipActionTypeLocal provides "primary") { 36 | ChipCell( 37 | attrs = { 38 | applyAttrs(attrs) 39 | }, 40 | content = content, 41 | ) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kmdc/kmdc-segmented-button/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/segmented-button") 2 | 3 | package dev.petuska.kmdc.segmented.button 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.ripple.MDCRipple 8 | import org.w3c.dom.Element 9 | 10 | @MDCExternalAPI 11 | public external class MDCSegmentedButton(element: Element) : MDCComponent { 12 | public val segments: Array 13 | public fun initialize( 14 | segmentFactory: ( 15 | el: Element, 16 | foundation: dynamic 17 | ) -> MDCSegmentedButtonSegment = definedExternally 18 | ) 19 | 20 | public fun getSelectedSegments(): Array 21 | public fun selectSegment(indexOrSegmentId: dynamic) 22 | public fun unselectSegment(indexOrSegmentId: dynamic) 23 | public fun isSegmentSelected(indexOrSegmentId: dynamic): Boolean 24 | } 25 | 26 | @MDCExternalAPI 27 | public external class MDCSegmentedButtonSegment(element: Element) : MDCComponent { 28 | public var ripple: MDCRipple 29 | public fun initialize(rippleFactory: (el: Element, foundation: dynamic) -> MDCRipple) 30 | public fun setIndex(index: Number) 31 | public fun setIsSingleSelect(isSingleSelect: Boolean) 32 | public fun isSelected(): Boolean 33 | public fun setSelected() 34 | public fun setUnselected() 35 | public fun getSegmentId(): String? 36 | } 37 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCRipple.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.ripple.MDCRipple 10 | import dev.petuska.kmdc.typography.MDCBody1 11 | import org.jetbrains.compose.web.css.* 12 | import org.jetbrains.compose.web.dom.Div 13 | import sandbox.control.BooleanControl 14 | 15 | private class MDCRippleVM { 16 | var disabled by mutableStateOf(false) 17 | var unbounded by mutableStateOf(false) 18 | } 19 | 20 | @Composable 21 | @Showcase(id = "MDCRipple") 22 | fun MDCRipple() = InteractiveShowcase( 23 | viewModel = { MDCRippleVM() }, 24 | controls = { 25 | BooleanControl("Disabled", ::disabled) 26 | BooleanControl("Unbounded", ::unbounded) 27 | }, 28 | ) { 29 | Div(attrs = { 30 | classes("kmdc-ripple") 31 | style { 32 | padding(1.em) 33 | property("width", "fit-content") 34 | border(0.05.em, LineStyle.Solid, Color.black) 35 | cursor("pointer") 36 | overflow(if (unbounded) "visible" else "hidden") 37 | } 38 | }) { 39 | MDCRipple( 40 | unbounded = unbounded, 41 | disabled = disabled, 42 | ) 43 | MDCBody1("Click Me!") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kmdc/kmdc-tooltip/src/jsMain/kotlin/Content.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tooltip 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContent 6 | import dev.petuska.kmdc.core.MDCContentDsl 7 | import dev.petuska.kmdc.core.reinterpret 8 | import org.jetbrains.compose.web.dom.ElementScope 9 | import org.jetbrains.compose.web.dom.P 10 | import org.jetbrains.compose.web.dom.Text 11 | import org.w3c.dom.HTMLParagraphElement 12 | 13 | public interface MDCTooltipRichContentScope : ElementScope 14 | 15 | /** 16 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tooltip) 17 | */ 18 | @MDCContentDsl 19 | @Composable 20 | public fun MDCRichTooltipScope.Content( 21 | attrs: MDCAttrsRaw? = null, 22 | content: MDCContent? = null, 23 | ) { 24 | P( 25 | attrs = { 26 | classes("mdc-tooltip__content") 27 | attrs?.invoke(this) 28 | }, 29 | content = content.reinterpret() 30 | ) 31 | } 32 | 33 | /** 34 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tooltip) 35 | */ 36 | @MDCContentDsl 37 | @Composable 38 | public fun MDCRichTooltipScope.Content( 39 | text: String, 40 | attrs: MDCAttrsRaw? = null, 41 | ) { 42 | Content(attrs) { 43 | Text(text) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kmdc/kmdc-dialog/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.dialog 2 | 3 | import dev.petuska.kmdc.core.* 4 | import kotlin.js.* 5 | 6 | /** 7 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 8 | */ 9 | @MDCAttrsDsl 10 | public fun MDCDialogAttrsScope.onOpening(listener: MDCEventListener) { 11 | addMdcEventListener("MDCDialog:opening", listener) 12 | } 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 16 | */ 17 | @MDCAttrsDsl 18 | public fun MDCDialogAttrsScope.onOpened(listener: MDCEventListener) { 19 | addMdcEventListener("MDCDialog:opened", listener) 20 | } 21 | 22 | public external interface MDCDialogCloseEventDetail { 23 | public val action: String? 24 | } 25 | 26 | /** 27 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 28 | */ 29 | @MDCAttrsDsl 30 | public fun MDCDialogAttrsScope.onClosing(listener: MDCEventListener) { 31 | addMdcEventListener("MDCDialog:closing", listener) 32 | } 33 | 34 | /** 35 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 36 | */ 37 | @MDCAttrsDsl 38 | public fun MDCDialogAttrsScope.onClosed(listener: MDCEventListener) { 39 | addMdcEventListener("MDCDialog:closed", listener) 40 | } 41 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCImageList.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.katalog.runtime.util.randomImageUrl 10 | import dev.petuska.kmdc.image.list.* 11 | import sandbox.control.BooleanControl 12 | import sandbox.control.ChoiceControl 13 | import sandbox.util.requireModule 14 | 15 | private class MDCImageListVM { 16 | var type by mutableStateOf(MDCImageListType.Standard) 17 | var withTextProtection by mutableStateOf(false) 18 | } 19 | 20 | @Composable 21 | @Showcase(id = "MDCImageList") 22 | fun MDCImageList() = InteractiveShowcase( 23 | viewModel = { MDCImageListVM() }, 24 | controls = { 25 | ChoiceControl("Type", MDCImageListType.values().associateBy(MDCImageListType::name), ::type) 26 | BooleanControl("With Text Protection", ::withTextProtection) 27 | }, 28 | ) { 29 | requireModule("./MDCImageList.scss") 30 | MDCImageList( 31 | type = type, 32 | withTextProtection = withTextProtection, 33 | attrs = { 34 | classes("kmdc-image-list") 35 | } 36 | ) { 37 | repeat(7) { 38 | Item { 39 | Image(src = randomImageUrl("kmdc-$it")) 40 | Label("Image #$it") 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kmdc/kmdc-elevation/src/jsMain/kotlin/MDCElevation.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.elevation 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.core.applyAttrs 8 | import org.jetbrains.compose.web.dom.Div 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | @JsModule("@material/elevation/mdc-elevation.scss") 12 | private external val Style: dynamic 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-elevation) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | public fun MDCElevation( 20 | z: Int, 21 | attrs: MDCAttrsRaw? = null, 22 | content: MDCContentRaw? = null 23 | ) { 24 | Style 25 | Div( 26 | attrs = { 27 | classes("mdc-elevation--z$z") 28 | applyAttrs(attrs) 29 | }, 30 | content = content, 31 | ) 32 | } 33 | 34 | /** 35 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-elevation) 36 | */ 37 | @MDCContentDsl 38 | @Composable 39 | public fun MDCElevationOverlay( 40 | attrs: MDCAttrsRaw? = null, 41 | content: MDCContentRaw? = null 42 | ) { 43 | Style 44 | Div( 45 | attrs = { 46 | classes("mdc-elevation-overlay") 47 | applyAttrs(attrs) 48 | }, 49 | content = content, 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /kmdc/kmdc-snackbar/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.snackbar 2 | 3 | import dev.petuska.kmdc.core.* 4 | import kotlin.js.* 5 | 6 | /** 7 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 8 | */ 9 | @MDCAttrsDsl 10 | public fun MDCSnackbarAttrsScope.onOpening(listener: MDCEventListener) { 11 | addMdcEventListener("MDCSnackbar:opening", listener) 12 | } 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 16 | */ 17 | @MDCAttrsDsl 18 | public fun MDCSnackbarAttrsScope.onOpened(listener: MDCEventListener) { 19 | addMdcEventListener("MDCSnackbar:opened", listener) 20 | } 21 | 22 | public interface MDCSnackbarCloseEventDetail { 23 | public val reason: String? 24 | } 25 | 26 | /** 27 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 28 | */ 29 | @MDCAttrsDsl 30 | public fun MDCSnackbarAttrsScope.onClosing(listener: MDCEventListener) { 31 | addMdcEventListener("MDCSnackbar:closing", listener) 32 | } 33 | 34 | /** 35 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 36 | */ 37 | @MDCAttrsDsl 38 | public fun MDCSnackbarAttrsScope.onClosed(listener: MDCEventListener) { 39 | addMdcEventListener("MDCSnackbar:closed", listener) 40 | } 41 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/Section.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.top.app.bar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCContent 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.classes 7 | import dev.petuska.kmdc.core.reinterpret 8 | import org.jetbrains.compose.web.dom.AttrBuilderContext 9 | import org.jetbrains.compose.web.dom.ElementScope 10 | import org.w3c.dom.HTMLElement 11 | 12 | public interface MDCTopAppBarSectionScope : ElementScope 13 | 14 | public enum class MDCTopAppBarSectionAlign(public vararg val classes: String) { 15 | Start("mdc-top-app-bar__section--align-start"), 16 | End("mdc-top-app-bar__section--align-end"), 17 | } 18 | 19 | /** 20 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 21 | */ 22 | @MDCContentDsl 23 | @Composable 24 | public fun MDCTopAppBarRowScope.Section( 25 | align: MDCTopAppBarSectionAlign = MDCTopAppBarSectionAlign.Start, 26 | role: String? = null, 27 | attrs: AttrBuilderContext? = null, 28 | content: MDCContent? = null 29 | ) { 30 | org.jetbrains.compose.web.dom.Section( 31 | attrs = { 32 | classes("mdc-top-app-bar__section") 33 | classes(align.classes) 34 | role?.let { 35 | attr("role", it) 36 | } 37 | attrs?.invoke(this) 38 | }, 39 | content = content.reinterpret() 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /kmdc/kmdc-button/src/jsTest/kotlin/MDCButtonTest.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.button 2 | 3 | import androidx.compose.runtime.getValue 4 | import androidx.compose.runtime.mutableStateOf 5 | import androidx.compose.runtime.setValue 6 | import org.jetbrains.compose.web.testutils.runTest 7 | import org.w3c.dom.HTMLButtonElement 8 | import org.w3c.dom.HTMLElement 9 | import kotlin.test.Test 10 | import kotlin.test.assertEquals 11 | 12 | class MDCButtonTest { 13 | @Test 14 | fun render() = runTest { 15 | fun HTMLElement.assertHtml(count: Int, upgraded: Boolean) { 16 | assertEquals( 17 | expected = """""", 24 | actual = innerHTML 25 | ) 26 | } 27 | 28 | var count by mutableStateOf(0) 29 | composition { 30 | MDCButton("Clicked $count times", attrs = { onClick { count++ } }) 31 | } 32 | root.assertHtml(0, false) 33 | root.firstElementChild.unsafeCast().click() 34 | waitForRecompositionComplete() 35 | root.assertHtml(1, true) 36 | count = 10 37 | waitForRecompositionComplete() 38 | root.assertHtml(10, true) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/scroller/Scroller.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab.scroller 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.remember 5 | import dev.petuska.kmdc.core.* 6 | import dev.petuska.kmdc.tab.bar.MDCTabBarContext 7 | import dev.petuska.kmdc.tab.bar.MDCTabBarScope 8 | import org.jetbrains.compose.web.dom.Div 9 | import org.jetbrains.compose.web.dom.ElementScope 10 | import org.w3c.dom.HTMLDivElement 11 | 12 | @JsModule("@material/tab-scroller/mdc-tab-scroller.scss") 13 | private external val Style: dynamic 14 | 15 | public interface MDCTabScrollerScope : ElementScope 16 | 17 | /** 18 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-scroller) 19 | */ 20 | @MDCContentDsl 21 | @Composable 22 | public fun MDCTabBarScope.Scroller( 23 | attrs: MDCAttrsRaw? = null, 24 | content: MDCContent? = null 25 | ) { 26 | Style 27 | val context = remember { MDCTabBarContext() } 28 | Div( 29 | attrs = { 30 | classes("mdc-tab-scroller") 31 | applyAttrs(attrs) 32 | } 33 | ) { 34 | MDCProvider(::MDCTabScroller, context.tabs) { 35 | Div( 36 | attrs = { classes("mdc-tab-scroller__scroll-area") } 37 | ) { 38 | Div( 39 | attrs = { classes("mdc-tab-scroller__scroll-content") }, 40 | content = content.reinterpret() 41 | ) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kmdc/kmdc-base/src/jsMain/kotlin/MDCProvider.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.core 2 | 3 | import androidx.compose.runtime.* 4 | import org.jetbrains.compose.web.dom.ElementScope 5 | import org.w3c.dom.Element 6 | 7 | @KMDCInternalAPI 8 | private val MDCLocal: ProvidableCompositionLocal?> = compositionLocalOf { null } 9 | 10 | @KMDCInternalAPI 11 | public external interface MDCProviderScope> 12 | 13 | @Composable 14 | @KMDCInternalAPI 15 | public fun , E : Element> ElementScope.MDCProvider( 16 | init: (E) -> MDC, 17 | vararg keys: Any?, 18 | onDispose: (MDC.(E) -> Unit)? = null, 19 | content: @Composable Builder> 20 | ) { 21 | var mdc by rememberMutableStateOf(null) 22 | DisposableEffect(keys = keys) { 23 | mdc?.run { destroy() } 24 | mdc = init(scopeElement) 25 | if (debug) scopeElement.mdc = mdc 26 | onDispose { 27 | mdc?.run { 28 | onDispose?.invoke(this, scopeElement) 29 | destroy() 30 | } 31 | } 32 | } 33 | CompositionLocalProvider(MDCLocal provides mdc) { 34 | applyContent(content) 35 | } 36 | } 37 | 38 | @Composable 39 | @KMDCInternalAPI 40 | public fun > localMDC(): MDC? { 41 | @Suppress("UNCHECKED_CAST") 42 | return MDCLocal.current?.let { it as? MDC } 43 | } 44 | 45 | @KMDCInternalAPI 46 | public inline val > MDCProviderScope.localMDC: MDC? 47 | @Composable 48 | get() = localMDC() 49 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/indicator/Indicator.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab.indicator 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.tab.MDCTabBaseScope 6 | import org.jetbrains.compose.web.dom.ElementScope 7 | import org.jetbrains.compose.web.dom.Span 8 | import org.w3c.dom.HTMLSpanElement 9 | 10 | @JsModule("@material/tab-indicator/mdc-tab-indicator.scss") 11 | private external val Style: dynamic 12 | 13 | public enum class MDCTabIndicatorTransition(public vararg val classes: String) { 14 | Slide, Fade("mdc-tab-indicator--fade") 15 | } 16 | 17 | public interface MDCTabIndicatorScope : ElementScope 18 | 19 | /** 20 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-indicator) 21 | */ 22 | @MDCContentDsl 23 | @Composable 24 | public fun MDCTabBaseScope.Indicator( 25 | active: Boolean = false, 26 | transition: MDCTabIndicatorTransition = MDCTabIndicatorTransition.Slide, 27 | attrs: MDCAttrsRaw? = null, 28 | content: MDCContent? = null 29 | ) { 30 | Style 31 | Span( 32 | attrs = { 33 | classes("mdc-tab-indicator") 34 | if (active) classes("mdc-tab-indicator--active") 35 | classes(transition.classes) 36 | attrs?.invoke(this) 37 | }, 38 | content = { 39 | MDCProvider(::MDCTabIndicator) { 40 | applyContent(content) 41 | } 42 | } 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips 2 | 3 | import dev.petuska.kmdc.core.* 4 | 5 | public external interface MDCChipSetInteractionEventDetail { 6 | public val chipID: String 7 | public val chipIndex: Int 8 | } 9 | 10 | /** 11 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 12 | */ 13 | @MDCAttrsDsl 14 | public fun MDCChipsAttrsScope.onInteraction(listener: MDCEventListener) { 15 | addMdcEventListener("MDCChipSet:interaction", listener) 16 | } 17 | 18 | public external interface MDCChipSetRemovalEventDetail : MDCChipSetInteractionEventDetail { 19 | public val isComplete: Boolean 20 | } 21 | 22 | /** 23 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 24 | */ 25 | @MDCAttrsDsl 26 | public fun MDCChipsAttrsScope.onRemoval(listener: MDCEventListener) { 27 | addMdcEventListener("MDCChipSet:removal", listener) 28 | } 29 | 30 | public external interface MDCChipSetSelectionEventDetail : MDCChipSetInteractionEventDetail { 31 | public val isSelected: Boolean 32 | } 33 | 34 | /** 35 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 36 | */ 37 | @MDCAttrsDsl 38 | public fun MDCChipsAttrsScope.onSelection(listener: MDCEventListener) { 39 | addMdcEventListener("MDCChipSet:selection", listener) 40 | } 41 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCTypography.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.katalog.runtime.Showcase 5 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 6 | import dev.petuska.kmdc.core.ContentBuilder 7 | import dev.petuska.kmdc.typography.* 8 | import org.jetbrains.compose.web.css.* 9 | import org.jetbrains.compose.web.css.keywords.CSSAutoKeyword 10 | import org.jetbrains.compose.web.dom.Div 11 | import org.w3c.dom.HTMLDivElement 12 | 13 | @Composable 14 | @Showcase(id = "MDCTypography") 15 | fun MDCTypography() = InteractiveShowcase(viewModel = {}) { 16 | Render(first = true) { MDCH1("MDCH1") } 17 | Render { MDCH2("MDCH2") } 18 | Render { MDCH3("MDCH3") } 19 | Render { MDCH4("MDCH4") } 20 | Render { MDCH5("MDCH5") } 21 | Render { MDCH6("MDCH6") } 22 | Render { MDCButtonText("MDCButtonText") } 23 | Render { MDCOverline("MDCOverline") } 24 | Render { MDCCaption("MDCCaption") } 25 | Render { MDCSubtitle1("MDCSubtitle1") } 26 | Render { MDCSubtitle2("MDCSubtitle2") } 27 | Render { MDCBody1("MDCBody1") } 28 | Render { MDCBody2("MDCBody2") } 29 | } 30 | 31 | @Composable 32 | private fun Render(first: Boolean = false, content: ContentBuilder) { 33 | Div(attrs = { 34 | style { 35 | border(1.px, LineStyle.Dotted, Color.gray) 36 | width(CSSKeywordValue("fit-content").unsafeCast()) 37 | if (!first) marginTop(0.5.em) 38 | } 39 | }) { 40 | content() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/util/Named.kt: -------------------------------------------------------------------------------- 1 | package sandbox.util 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.katalog.runtime.layout.Divider 5 | import dev.petuska.kmdc.core.AttrsBuilder 6 | import dev.petuska.kmdc.typography.MDCButtonText 7 | import dev.petuska.kmdc.typography.MDCOverline 8 | import org.jetbrains.compose.web.css.DisplayStyle 9 | import org.jetbrains.compose.web.css.display 10 | import org.jetbrains.compose.web.css.em 11 | import org.jetbrains.compose.web.css.paddingLeft 12 | import org.jetbrains.compose.web.dom.Div 13 | import org.w3c.dom.HTMLDivElement 14 | 15 | @Composable 16 | fun NamedBlock( 17 | name: String, 18 | description: String? = null, 19 | attrs: AttrsBuilder? = null, 20 | content: @Composable () -> Unit 21 | ) { 22 | Div(attrs = attrs) { 23 | MDCOverline(name, attrs = { 24 | description?.let(::title) 25 | style { display(DisplayStyle("block ruby")) } 26 | }) 27 | content() 28 | } 29 | } 30 | 31 | @Composable 32 | fun NamedGroup( 33 | name: String, 34 | description: String? = null, 35 | topDivider: Boolean = true, 36 | attrs: AttrsBuilder? = null, 37 | content: @Composable () -> Unit 38 | ) { 39 | if (topDivider) { 40 | Divider() 41 | } 42 | Div(attrs = attrs) { 43 | MDCButtonText(name, attrs = { 44 | description?.let(::title) 45 | style { display(DisplayStyle("block ruby")) } 46 | }) 47 | Div(attrs = { style { paddingLeft(1.em) } }) { 48 | content() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/Chip.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.CompositionLocalProvider 5 | import androidx.compose.runtime.compositionLocalOf 6 | import dev.petuska.kmdc.core.* 7 | import org.jetbrains.compose.web.dom.ElementScope 8 | import org.jetbrains.compose.web.dom.Span 9 | import org.w3c.dom.HTMLSpanElement 10 | 11 | public interface MDCChipScope : ElementScope 12 | 13 | internal val MDCChipDisabledLocal = compositionLocalOf { false } 14 | 15 | /** 16 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 17 | */ 18 | @MDCContentDsl 19 | @Composable 20 | internal fun MDCChipsScope.Chip( 21 | id: String, 22 | disabled: Boolean, 23 | withPrimaryGraphic: Boolean, 24 | withPrimaryIcon: Boolean, 25 | touch: Boolean, 26 | attrs: MDCAttrsRaw?, 27 | content: MDCContent? 28 | ) { 29 | Span( 30 | attrs = { 31 | classes("mdc-evolution-chip") 32 | if (disabled) classes("mdc-evolution-chip--disabled") 33 | if (withPrimaryGraphic || withPrimaryIcon) classes("mdc-evolution-chip--with-primary-graphic") 34 | if (withPrimaryIcon) classes("mdc-evolution-chip--with-primary-icon") 35 | if (touch) classes("mdc-evolution-chip--touch") 36 | id(id) 37 | applyAttrs(attrs) 38 | }, 39 | ) { 40 | CompositionLocalProvider(MDCChipDisabledLocal provides disabled) { 41 | applyContent(content) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/bar/MDCTabBar.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab.bar 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.AttrsScope 6 | import org.jetbrains.compose.web.dom.Div 7 | import org.jetbrains.compose.web.dom.ElementScope 8 | import org.w3c.dom.HTMLDivElement 9 | 10 | @JsModule("@material/tab-bar/mdc-tab-bar.scss") 11 | private external val Style: dynamic 12 | 13 | public interface MDCTabBarAttrsScope : AttrsScope 14 | 15 | internal class MDCTabBarContext { 16 | internal var tabs: Int by mutableStateOf(0) 17 | } 18 | 19 | internal val MDCTabBarContextLocal = strictCompositionLocalOf() 20 | 21 | public interface MDCTabBarScope : ElementScope 22 | 23 | /** 24 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-bar) 25 | */ 26 | @MDCContentDsl 27 | @Composable 28 | public fun MDCTabBar( 29 | attrs: MDCAttrs? = null, 30 | content: MDCContent? = null 31 | ) { 32 | Style 33 | val context = remember { MDCTabBarContext() } 34 | Div( 35 | attrs = { 36 | classes("mdc-tab-bar") 37 | role("tablist") 38 | applyAttrs(attrs) 39 | }, 40 | content = { 41 | CompositionLocalProvider(MDCTabBarContextLocal provides context) { 42 | MDCProvider(::MDCTabBar, context.tabs) { 43 | context.tabs = 0 44 | applyContent(content) 45 | } 46 | } 47 | }, 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /kmdc/kmdc-banner/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.banner 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | import kotlin.js.Json 7 | 8 | public external interface MDCBannerCloseEventDetail { 9 | public val reason: CloseReason 10 | } 11 | 12 | /** 13 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-banner) 14 | */ 15 | @MDCAttrsDsl 16 | public fun MDCBannerAttrsScope.onClosing(listener: MDCEventListener) { 17 | addMdcEventListener("MDCBanner:closing", listener) 18 | } 19 | 20 | /** 21 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-banner) 22 | */ 23 | @MDCAttrsDsl 24 | public fun MDCBannerAttrsScope.onClosed(listener: MDCEventListener) { 25 | addMdcEventListener("MDCBanner:closed", listener) 26 | } 27 | 28 | /** 29 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-banner) 30 | */ 31 | @MDCAttrsDsl 32 | public fun MDCBannerAttrsScope.onOpening(listener: MDCEventListener) { 33 | addMdcEventListener("MDCBanner:opening", listener) 34 | } 35 | 36 | /** 37 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-banner) 38 | */ 39 | @MDCAttrsDsl 40 | public fun MDCBannerAttrsScope.onOpened(listener: MDCEventListener) { 41 | addMdcEventListener("MDCBanner:opened", listener) 42 | } 43 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/Action.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.top.app.bar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.icon.button.MDCIconButton 8 | import dev.petuska.kmdc.icon.button.MDCIconLink 9 | import org.w3c.dom.HTMLAnchorElement 10 | import org.w3c.dom.HTMLButtonElement 11 | 12 | /** 13 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 14 | */ 15 | @MDCContentDsl 16 | @Composable 17 | public fun MDCTopAppBarSectionScope.ActionButton( 18 | on: Boolean = false, 19 | attrs: MDCAttrsRaw? = null, 20 | content: MDCContentRaw? = null, 21 | ) { 22 | MDCIconButton( 23 | on = on, 24 | attrs = { 25 | classes("mdc-top-app-bar__action-icon") 26 | attrs?.invoke(this) 27 | }, 28 | content = content 29 | ) 30 | } 31 | 32 | /** 33 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 34 | */ 35 | @MDCContentDsl 36 | @Composable 37 | public fun MDCTopAppBarSectionScope.ActionLink( 38 | on: Boolean = false, 39 | attrs: MDCAttrsRaw? = null, 40 | content: MDCContentRaw? = null 41 | ) { 42 | MDCIconLink( 43 | on = on, 44 | attrs = { 45 | classes("mdc-top-app-bar__action-icon") 46 | attrs?.invoke(this) 47 | }, 48 | content = content 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /kmdc/kmdc-card/src/jsMain/kotlin/Media.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.card 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Div 6 | import org.jetbrains.compose.web.dom.ElementScope 7 | import org.w3c.dom.HTMLDivElement 8 | 9 | public enum class MDCCardMediaType(public vararg val classes: String) { 10 | Free, 11 | Square("mdc-card__media--square"), 12 | Cinema("mdc-card__media--16-9") 13 | } 14 | 15 | public interface MDCCardMediaScope : ElementScope 16 | 17 | /** 18 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-card) 19 | */ 20 | @MDCContentDsl 21 | @Composable 22 | public fun MDCCardScope.Media( 23 | type: MDCCardMediaType = MDCCardMediaType.Free, 24 | attrs: MDCAttrsRaw? = null, 25 | content: MDCContent? = null 26 | ) { 27 | Div( 28 | attrs = { 29 | classes("mdc-card__media") 30 | classes(type.classes) 31 | applyAttrs(attrs) 32 | }, 33 | content = content.reinterpret() 34 | ) 35 | } 36 | 37 | /** 38 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-card) 39 | */ 40 | @MDCContentDsl 41 | @Composable 42 | public fun MDCCardMediaScope.MediaContent( 43 | attrs: MDCAttrsRaw? = null, 44 | content: MDCContentRaw? = null 45 | ) { 46 | Div( 47 | attrs = { 48 | classes("mdc-card__media-content") 49 | applyAttrs(attrs) 50 | }, 51 | content = content.reinterpret() 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /kmdc/kmdc-button/src/jsMain/kotlin/Icon.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.button 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCContentRaw 7 | import dev.petuska.kmdc.core.applyAttrs 8 | import org.jetbrains.compose.web.ExperimentalComposeWebSvgApi 9 | import org.jetbrains.compose.web.dom.I 10 | import org.jetbrains.compose.web.svg.Svg 11 | import org.w3c.dom.HTMLElement 12 | import org.w3c.dom.svg.SVGElement 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-button#icon) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | public fun MDCButtonScope<*>.Icon( 20 | attrs: MDCAttrsRaw? = null, 21 | content: MDCContentRaw? = null, 22 | ) { 23 | I( 24 | attrs = { 25 | classes("mdc-button__icon") 26 | attr("aria-hidden", "true") 27 | applyAttrs(attrs) 28 | }, 29 | content, 30 | ) 31 | } 32 | 33 | /** 34 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-button#icon) 35 | */ 36 | @MDCContentDsl 37 | @Composable 38 | @ExperimentalComposeWebSvgApi 39 | public fun MDCButtonScope<*>.Icon( 40 | viewBox: String?, 41 | attrs: MDCAttrsRaw? = null, 42 | content: MDCContentRaw? = null, 43 | ) { 44 | Svg( 45 | viewBox = viewBox, 46 | attrs = { 47 | classes("mdc-button__icon") 48 | attr("aria-hidden", "true") 49 | applyAttrs(attrs) 50 | }, 51 | content = content, 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /kmdc/kmdc-menu/src/jsMain/kotlin/_module.kt: -------------------------------------------------------------------------------- 1 | @file:JsModule("@material/menu") 2 | 3 | package dev.petuska.kmdc.menu 4 | 5 | import dev.petuska.kmdc.core.MDCComponent 6 | import dev.petuska.kmdc.core.MDCExternalAPI 7 | import dev.petuska.kmdc.menu.surface.Corner 8 | import org.w3c.dom.Element 9 | import org.w3c.dom.HTMLElement 10 | 11 | @MDCExternalAPI 12 | public external class MDCMenu(element: Element) : MDCComponent { 13 | public var open: Boolean 14 | public var wrapFocus: Boolean 15 | public var hasTypeahead: Boolean 16 | public val typeaheadInProgress: Boolean 17 | public val items: Array 18 | public var singleSelection: Boolean 19 | 20 | /** 21 | * Int | Array 22 | */ 23 | public var selectedIndex: dynamic 24 | public var quickOpen: Boolean 25 | 26 | public fun typeaheadMatchItem(nextChar: String, startingIndex: Number = definedExternally): Int 27 | 28 | public fun setDefaultFocusState(corner: DefaultFocusState) 29 | public fun setAnchorCorner(corner: Corner) 30 | public fun setAnchorMargin(margin: dynamic) 31 | public fun setSelectedIndex(index: Int) 32 | public fun setEnabled(index: Int, isEnabled: Boolean) 33 | public fun getOptionByIndex(index: Int): Element? 34 | public fun getPrimaryTextAtIndex(index: Int): String 35 | public fun setFixedPosition(isFixed: Boolean) 36 | public fun setIsHoisted(isHoisted: Boolean) 37 | public fun setAbsolutePosition(x: Number, y: Number) 38 | public fun setAnchorElement(element: HTMLElement) 39 | } 40 | 41 | public external enum class DefaultFocusState { 42 | NONE, 43 | LIST_ROOT, 44 | FIRST_ITEM, 45 | LAST_ITEM 46 | } 47 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCFormField.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.checkbox.MDCCheckbox 10 | import dev.petuska.kmdc.form.field.MDCFormField 11 | import dev.petuska.kmdc.form.field.MDCFormFieldAlign 12 | import dev.petuska.kmdc.radio.MDCRadio 13 | import sandbox.control.BooleanControl 14 | import sandbox.control.ChoiceControl 15 | 16 | private class MDCFormFieldVM { 17 | var align by mutableStateOf(MDCFormFieldAlign.Start) 18 | var noWrap by mutableStateOf(false) 19 | 20 | var checked by mutableStateOf(false) 21 | } 22 | 23 | @Composable 24 | @Showcase(id = "MDCFormField") 25 | fun MDCFormField() = InteractiveShowcase( 26 | viewModel = { MDCFormFieldVM() }, 27 | controls = { 28 | ChoiceControl("Align", MDCFormFieldAlign.values().associateBy(MDCFormFieldAlign::name), ::align) 29 | BooleanControl("No Wrap", ::noWrap) 30 | }, 31 | ) { 32 | MDCFormField( 33 | align = align, 34 | noWrap = noWrap, 35 | ) { 36 | MDCCheckbox( 37 | checked = checked, 38 | label = "Sample Checkbox", 39 | attrs = { onInput { checked = !checked } }, 40 | ) 41 | } 42 | MDCFormField( 43 | align = align, 44 | noWrap = noWrap, 45 | ) { 46 | MDCRadio( 47 | checked = checked, 48 | label = "Sample Radio", 49 | attrs = { onInput { checked = !checked } }, 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kmdc/kmdc-select/src/jsMain/kotlin/anchor/DropDownIcon.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.select.anchor 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrsRaw 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.applyAttrs 7 | import org.jetbrains.compose.web.ExperimentalComposeWebSvgApi 8 | import org.jetbrains.compose.web.dom.Span 9 | import org.jetbrains.compose.web.svg.Polygon 10 | import org.jetbrains.compose.web.svg.Svg 11 | import org.w3c.dom.HTMLSpanElement 12 | 13 | @MDCContentDsl 14 | @Composable 15 | @OptIn(ExperimentalComposeWebSvgApi::class) 16 | internal fun DownDownIcon( 17 | attrs: MDCAttrsRaw? = null, 18 | ) { 19 | Span( 20 | attrs = { 21 | classes("mdc-select__dropdown-icon") 22 | applyAttrs(attrs) 23 | } 24 | ) { 25 | Svg( 26 | viewBox = "7 10 10 5", 27 | attrs = { 28 | classes("mdc-select__dropdown-icon-graphic") 29 | attr("focusable", "false") 30 | } 31 | ) { 32 | Polygon( 33 | 7, 34 | 10, 35 | 12, 36 | 15, 37 | 17, 38 | 10, 39 | attrs = { 40 | classes("mdc-select__dropdown-icon-inactive") 41 | attr("stroke", "none") 42 | attr("fill-rule", "evenodd") 43 | } 44 | ) 45 | Polygon( 46 | 7, 47 | 15, 48 | 12, 49 | 10, 50 | 17, 51 | 15, 52 | attrs = { 53 | classes("mdc-select__dropdown-icon-active") 54 | attr("stroke", "none") 55 | attr("fill-rule", "evenodd") 56 | } 57 | ) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /kmdc/kmdc-ripple/src/jsMain/kotlin/MDCRipple.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.ripple 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.KMDCInternalAPI 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.MDCProvider 7 | import dev.petuska.kmdc.core.MDCStateEffect 8 | import kotlinx.dom.addClass 9 | import kotlinx.dom.removeClass 10 | import org.jetbrains.compose.web.dom.ElementScope 11 | import org.w3c.dom.Element 12 | 13 | @JsModule("@material/ripple/mdc-ripple.scss") 14 | private external val Style: dynamic 15 | 16 | /** 17 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-ripple) 18 | */ 19 | @MDCContentDsl 20 | @Composable 21 | public fun ElementScope<*>.MDCRipple( 22 | unbounded: Boolean = false, 23 | disabled: Boolean = false, 24 | ) { 25 | MDCRippleLayout( 26 | unbounded = unbounded, 27 | disabled = disabled, 28 | init = { 29 | it.addClass("mdc-ripple-surface") 30 | }, 31 | onDispose = { it.removeClass("mdc-ripple-surface") } 32 | ) 33 | } 34 | 35 | @Composable 36 | @KMDCInternalAPI 37 | public fun ElementScope<*>.MDCRippleLayout( 38 | unbounded: Boolean = false, 39 | disabled: Boolean = false, 40 | vararg keys: Any?, 41 | init: (Element) -> Unit = {}, 42 | onDispose: (Element) -> Unit = {}, 43 | ) { 44 | Style 45 | MDCProvider( 46 | init = { 47 | init(it) 48 | MDCRipple(it) 49 | }, 50 | keys = keys + unbounded, 51 | onDispose = { onDispose(it) } 52 | ) { 53 | MDCStateEffect(unbounded, MDCRipple::unbounded) 54 | MDCStateEffect(disabled, MDCRipple::disabled) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /gradle/detekt.yml: -------------------------------------------------------------------------------- 1 | config: 2 | warningsAsErrors: true 3 | 4 | complexity: 5 | LongMethod: 6 | ignoreAnnotated: 7 | - Showcase 8 | TooManyFunctions: 9 | ignoreAnnotated: 10 | - MDCExternalAPI 11 | LongParameterList: 12 | ignoreAnnotated: 13 | - MDCDsl 14 | - Composable 15 | 16 | comments: 17 | active: true 18 | CommentOverPrivateFunction: 19 | active: false 20 | CommentOverPrivateProperty: 21 | active: false 22 | DeprecatedBlockTag: 23 | active: true 24 | OutdatedDocumentation: 25 | active: true 26 | allowParamOnConstructorProperties: false 27 | # UndocumentedPublicClass: 28 | # active: true 29 | # UndocumentedPublicFunction: 30 | # active: true 31 | # UndocumentedPublicProperty: 32 | # active: true 33 | 34 | naming: 35 | InvalidPackageDeclaration: 36 | active: false 37 | MatchingDeclarationName: 38 | active: false 39 | TopLevelPropertyNaming: 40 | constantPattern: '[A-Z][_a-zA-Z0-9]*' 41 | FunctionNaming: 42 | ignoreAnnotated: 43 | - Composable 44 | 45 | style: 46 | WildcardImport: 47 | active: false 48 | UnnecessaryAbstractClass: 49 | active: false 50 | MaxLineLength: 51 | active: false 52 | MagicNumber: 53 | active: false 54 | UnusedPrivateProperty: 55 | ignoreAnnotated: 56 | - dev.petuska.kmdc.core.MDCExternalAPI 57 | UnusedPrivateMember: 58 | ignoreAnnotated: 59 | - dev.petuska.kmdc.core.MDCExternalAPI 60 | 61 | formatting: 62 | AnnotationOnSeparateLine: 63 | active: false 64 | Filename: 65 | active: false 66 | NoWildcardImports: 67 | active: false 68 | Indentation: 69 | indentSize: 2 70 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | KMDC Katalog 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCCircularProgress.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.katalog.runtime.* 5 | import dev.petuska.katalog.runtime.layout.* 6 | import dev.petuska.kmdc.circular.progress.* 7 | import sandbox.control.* 8 | import sandbox.util.* 9 | 10 | private class MDCCircularProgressVM { 11 | var determinate by mutableStateOf(false) 12 | var closed by mutableStateOf(false) 13 | var fourColor by mutableStateOf(false) 14 | var label by mutableStateOf("My Label") 15 | var size by mutableStateOf(36) 16 | var progress by mutableStateOf(0.5) 17 | } 18 | 19 | @Composable 20 | @Showcase(id = "MDCCircularProgress") 21 | fun MDCCircularProgress() = InteractiveShowcase( 22 | viewModel = { MDCCircularProgressVM() }, 23 | controls = { 24 | BooleanControl("Determinate", ::determinate) 25 | BooleanControl("Closed", ::closed) 26 | BooleanControl("Four Color", ::fourColor) 27 | TextControl("Label", ::label) 28 | RangeControl( 29 | name = "Size", 30 | value = ::size, 31 | min = 24, 32 | max = 48, 33 | converter = Number::toInt 34 | ) 35 | RangeControl( 36 | name = "Progress", 37 | value = ::progress, 38 | max = 1, 39 | step = 0.05, 40 | converter = Number::toDouble 41 | ) 42 | }, 43 | ) { 44 | requireModule("./MDCCircularProgress.scss") 45 | MDCCircularProgress( 46 | determinate = determinate, 47 | closed = closed, 48 | label = label.takeIf(String::isNotBlank), 49 | progress = progress, 50 | size = size, 51 | fourColor = fourColor, 52 | attrs = { 53 | classes("kmdc-circular-progress") 54 | } 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /kmdc/kmdc-list/src/jsMain/kotlin/Divider.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.list 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.* 6 | import org.jetbrains.compose.web.dom.* 7 | import org.w3c.dom.* 8 | 9 | public enum class MDCListDividerInset(public vararg var classes: String) { 10 | None, 11 | Leading("mdc-deprecated-list-divider--inset-leading"), 12 | Trailing("mdc-deprecated-list-divider--inset-trailing"), 13 | Padding("mdc-deprecated-list-divider--inset-padding") 14 | } 15 | 16 | /** 17 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-deprecated-list) 18 | */ 19 | @MDCContentDsl 20 | @Composable 21 | public fun MDCListScope<*>.Divider( 22 | inset: MDCListDividerInset = MDCListDividerInset.None, 23 | attrs: MDCAttrs>? = null, 24 | content: MDCContentRaw? = null, 25 | ) { 26 | Li( 27 | attrs = { 28 | classes("mdc-deprecated-list-divider") 29 | classes(inset.classes) 30 | attr("role", "separator") 31 | attrs?.invoke(this) 32 | }, 33 | content = content 34 | ) 35 | } 36 | 37 | /** 38 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-deprecated-list) 39 | */ 40 | @MDCContentDsl 41 | @Composable 42 | public fun MDCListGroupScope.Divider( 43 | inset: MDCListDividerInset = MDCListDividerInset.None, 44 | attrs: MDCAttrs>? = null, 45 | ) { 46 | Hr( 47 | attrs = { 48 | classes("mdc-deprecated-list-divider") 49 | classes(inset.classes) 50 | attrs?.invoke(this) 51 | }, 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /kmdc/kmdc-segmented-button/src/jsMain/kotlin/MDCSegmentedButton.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.segmented.button 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.CompositionLocalProvider 5 | import dev.petuska.kmdc.core.* 6 | import org.jetbrains.compose.web.attributes.AttrsScope 7 | import org.jetbrains.compose.web.dom.Div 8 | import org.jetbrains.compose.web.dom.ElementScope 9 | import org.w3c.dom.HTMLDivElement 10 | 11 | @JsModule("@material/segmented-button/styles.scss") 12 | private external val Style: dynamic 13 | 14 | public interface MDCSegmentedButtonAttrsScope : AttrsScope 15 | public interface MDCSegmentedButtonScope : ElementScope 16 | 17 | internal val MDCSegmentedButtonSingleSelectLocal = strictCompositionLocalOf() 18 | 19 | /** 20 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 21 | */ 22 | @MDCContentDsl 23 | @Composable 24 | public fun MDCSegmentedButton( 25 | singleSelect: Boolean = false, 26 | attrs: MDCAttrs? = null, 27 | content: MDCContent? = null 28 | ) { 29 | Style 30 | Div( 31 | attrs = { 32 | classes("mdc-segmented-button") 33 | if (singleSelect) { 34 | classes("mdc-segmented-button--single-select") 35 | attr("role", "radiogroup") 36 | } else { 37 | attr("role", "group") 38 | } 39 | applyAttrs(attrs) 40 | }, 41 | ) { 42 | CompositionLocalProvider(MDCSegmentedButtonSingleSelectLocal provides singleSelect) { 43 | MDCProvider(::MDCSegmentedButton) { 44 | applyContent(content) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kmdc/kmdc-segmented-button/src/jsMain/kotlin/_events.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.segmented.button 2 | 3 | import dev.petuska.kmdc.core.MDCAttrsDsl 4 | import dev.petuska.kmdc.core.MDCEventListener 5 | import dev.petuska.kmdc.core.addMdcEventListener 6 | 7 | public external interface MDCSegmentedButtonEventDetail { 8 | public val index: Number 9 | public val selected: Boolean 10 | public val segmentId: String? 11 | } 12 | 13 | /** 14 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 15 | */ 16 | @MDCAttrsDsl 17 | public fun MDCSegmentedButtonAttrsScope.onSelected(listener: MDCEventListener) { 18 | addMdcEventListener("selected", listener) 19 | } 20 | 21 | /** 22 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 23 | */ 24 | @MDCAttrsDsl 25 | public fun MDCSegmentedButtonAttrsScope.onChange(listener: MDCEventListener) { 26 | addMdcEventListener("change", listener) 27 | } 28 | 29 | /** 30 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 31 | */ 32 | @MDCAttrsDsl 33 | public fun MDCSegmentedButtonSegmentAttrsScope.onSelected(listener: MDCEventListener) { 34 | addMdcEventListener("selected", listener) 35 | } 36 | 37 | /** 38 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-segmented-button) 39 | */ 40 | @MDCAttrsDsl 41 | public fun MDCSegmentedButtonSegmentAttrsScope.onClicked(listener: MDCEventListener) { 42 | addMdcEventListener("click", listener) 43 | } 44 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCLinearProgress.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.linear.progress.MDCLinearProgress 10 | import sandbox.control.BooleanControl 11 | import sandbox.control.RangeControl 12 | import sandbox.control.TextControl 13 | 14 | private class MDCLinearProgressVM { 15 | var progress by mutableStateOf(0.5) 16 | var buffer by mutableStateOf(0.5) 17 | var determinate by mutableStateOf(false) 18 | var closed by mutableStateOf(false) 19 | var label by mutableStateOf("My Label") 20 | } 21 | 22 | @Composable 23 | @Showcase(id = "MDCLinearProgress") 24 | fun MDCLinearProgress() = InteractiveShowcase( 25 | viewModel = { MDCLinearProgressVM() }, 26 | controls = { 27 | BooleanControl("Determinate", ::determinate) 28 | BooleanControl("Closed", ::closed) 29 | TextControl("Label", ::label) 30 | RangeControl( 31 | name = "Progress", 32 | value = ::progress, 33 | max = 1, 34 | step = 0.05, 35 | converter = Number::toDouble 36 | ) 37 | RangeControl( 38 | name = "Buffer", 39 | value = ::buffer, 40 | min = progress, 41 | max = 1, 42 | step = 0.05, 43 | converter = Number::toDouble 44 | ) 45 | }, 46 | ) { 47 | MDCLinearProgress( 48 | progress = progress, 49 | buffer = buffer, 50 | determinate = determinate, 51 | closed = closed, 52 | label = label.takeIf(String::isNotBlank), 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCFab.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.fab.Icon 10 | import dev.petuska.kmdc.fab.Label 11 | import dev.petuska.kmdc.fab.MDCFab 12 | import dev.petuska.kmdc.fab.MDCFabType 13 | import dev.petuska.kmdc.touch.target.MDCTouchTarget 14 | import dev.petuska.kmdcx.icons.MDCIcon 15 | import dev.petuska.kmdcx.icons.mdcIcon 16 | import org.jetbrains.compose.web.dom.Text 17 | import sandbox.control.BooleanControl 18 | import sandbox.control.ChoiceControl 19 | 20 | private class MDCFabVM { 21 | var exited by mutableStateOf(false) 22 | var type by mutableStateOf(MDCFabType.Regular) 23 | var touch by mutableStateOf(false) 24 | } 25 | 26 | @Composable 27 | @Showcase(id = "MDCFab") 28 | fun MDCFab() = InteractiveShowcase( 29 | viewModel = { MDCFabVM() }, 30 | controls = { 31 | ChoiceControl("Type", MDCFabType.values().associateBy(MDCFabType::name), type) { type = it } 32 | BooleanControl("Exited", ::exited) 33 | BooleanControl("Touch", ::touch) 34 | }, 35 | ) { 36 | val content = @Composable { 37 | MDCFab(touch = touch, exited = exited, type = type, attrs = { 38 | attr("aria-label", "Favorite") 39 | }) { 40 | if (type == MDCFabType.Extended) Label("Favorite") 41 | Icon(attrs = { mdcIcon() }) { Text(MDCIcon.Favorite.type) } 42 | } 43 | } 44 | if (touch) { 45 | MDCTouchTarget { 46 | content() 47 | } 48 | } else { 49 | content() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /katalog/katalog-runtime/src/jsMain/kotlin/ui/ShowcaseBox.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.katalog.runtime.ui 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.LaunchedEffect 5 | import dev.petuska.katalog.runtime.domain.Showcase 6 | import dev.petuska.katalog.runtime.katalog 7 | import dev.petuska.katalog.runtime.layout.Divider 8 | import org.jetbrains.compose.web.attributes.ATarget 9 | import org.jetbrains.compose.web.attributes.target 10 | import org.jetbrains.compose.web.css.StyleSheet 11 | import org.jetbrains.compose.web.css.percent 12 | import org.jetbrains.compose.web.css.width 13 | import org.jetbrains.compose.web.dom.A 14 | import org.jetbrains.compose.web.dom.Div 15 | 16 | private object ShowcaseBoxStyle : StyleSheet() { 17 | val header by style { 18 | } 19 | val content by style { 20 | width(100.percent) 21 | } 22 | } 23 | 24 | @Composable 25 | internal fun ShowcaseBox(showcase: Showcase) = with(showcase) { 26 | val debug = katalog.debug 27 | LaunchedEffect(showcase) { 28 | if (!debug) console.asDynamic().clear() 29 | } 30 | val contentRootUrl = katalog.contentRootUrl 31 | val location = contentRootUrl?.let { 32 | showcase.location?.let { 33 | "${contentRootUrl.removeSuffix("/")}/${showcase.location}" 34 | } 35 | } 36 | val theme = katalog.theme 37 | Div(attrs = { 38 | id("${id}__header") 39 | classes(ShowcaseBoxStyle.header) 40 | }) { 41 | A(location, attrs = { 42 | target(ATarget.Blank) 43 | }) { 44 | theme.showcaseTitleRender(title) 45 | } 46 | description?.let { theme.showcaseDescriptionRender(it) } 47 | } 48 | Divider() 49 | Div(attrs = { 50 | id("${id}__content") 51 | classes(ShowcaseBoxStyle.content) 52 | }) { 53 | content() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /kmdc/kmdc-top-app-bar/src/jsMain/kotlin/Navigation.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.top.app.bar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCAttrs 5 | import dev.petuska.kmdc.core.MDCContent 6 | import dev.petuska.kmdc.core.MDCContentDsl 7 | import dev.petuska.kmdc.icon.button.MDCIconButton 8 | import dev.petuska.kmdc.icon.button.MDCIconButtonAttrsScope 9 | import dev.petuska.kmdc.icon.button.MDCIconButtonScope 10 | import dev.petuska.kmdc.icon.button.MDCIconLink 11 | import org.w3c.dom.HTMLAnchorElement 12 | import org.w3c.dom.HTMLButtonElement 13 | 14 | /** 15 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 16 | */ 17 | @MDCContentDsl 18 | @Composable 19 | public fun MDCTopAppBarSectionScope.NavButton( 20 | touch: Boolean = false, 21 | attrs: MDCAttrs>? = null, 22 | content: MDCContent>? = null 23 | ) { 24 | MDCIconButton( 25 | touch = touch, 26 | attrs = { 27 | classes("mdc-top-app-bar__navigation-icon") 28 | attrs?.invoke(this) 29 | }, 30 | content = content 31 | ) 32 | } 33 | 34 | /** 35 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-top-app-bar) 36 | */ 37 | @MDCContentDsl 38 | @Composable 39 | public fun MDCTopAppBarSectionScope.NavLink( 40 | touch: Boolean = false, 41 | attrs: MDCAttrs>? = null, 42 | content: MDCContent>? = null 43 | ) { 44 | MDCIconLink( 45 | touch = touch, 46 | attrs = { 47 | classes("mdc-top-app-bar__navigation-icon") 48 | attrs?.invoke(this) 49 | }, 50 | content = content 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/Tab.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.tab.bar.MDCTabBarContextLocal 6 | import dev.petuska.kmdc.tab.scroller.MDCTabScrollerScope 7 | import org.jetbrains.compose.web.attributes.AttrsScope 8 | import org.jetbrains.compose.web.dom.Button 9 | import org.jetbrains.compose.web.dom.ElementScope 10 | import org.jetbrains.compose.web.dom.Span 11 | import org.w3c.dom.HTMLButtonElement 12 | 13 | @JsModule("@material/tab/mdc-tab.scss") 14 | private external val Style: dynamic 15 | 16 | public interface MDCTabAttrsScope : AttrsScope 17 | public interface MDCTabBaseScope 18 | public interface MDCTabScope : ElementScope, MDCTabBaseScope 19 | 20 | /** 21 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab) 22 | */ 23 | @MDCContentDsl 24 | @Composable 25 | public fun MDCTabScrollerScope.Tab( 26 | active: Boolean = false, 27 | stacked: Boolean = false, 28 | minWidth: Boolean = false, 29 | attrs: MDCAttrs? = null, 30 | content: MDCContent? = null 31 | ) { 32 | Style 33 | MDCTabBarContextLocal.current.tabs++ 34 | Button( 35 | attrs = { 36 | classes("mdc-tab") 37 | role("tab") 38 | aria("selected", active) 39 | if (active) classes("mdc-tab--active") 40 | tabIndex(if (active) 0 else -1) 41 | if (stacked) classes("mdc-tab--stacked") 42 | if (minWidth) classes("mdc-tab--min-width") 43 | applyAttrs(attrs) 44 | }, 45 | content = { 46 | MDCProvider(::MDCTab, stacked, minWidth) { 47 | applyContent(content) 48 | Span(attrs = { classes("mdc-tab__ripple") }) 49 | } 50 | } 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /kmdc/kmdc-textfield/src/jsMain/kotlin/icon/MDCTextFieldIcon.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.textfield.icon 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.textfield.MDCTextFieldScope 6 | import org.jetbrains.compose.web.dom.I 7 | import org.w3c.dom.HTMLElement 8 | 9 | @Composable 10 | @KMDCInternalAPI 11 | internal fun MDCTextFieldScope.MDCTextFieldIcon( 12 | leading: Boolean, 13 | clickable: Boolean = false, 14 | attrs: MDCAttrsRaw? = null, 15 | content: MDCContentRaw? = null, 16 | ) { 17 | I( 18 | attrs = { 19 | classes("mdc-text-field__icon") 20 | if (leading) { 21 | classes("mdc-text-field__icon--leading") 22 | } else { 23 | classes("mdc-text-field__icon--trailing") 24 | } 25 | if (clickable) { 26 | tabIndex(0) 27 | role("button") 28 | } 29 | attrs?.invoke(this) 30 | }, 31 | content = { 32 | MDCProvider(::MDCTextFieldIcon) { 33 | applyContent(content) 34 | } 35 | } 36 | ) 37 | } 38 | 39 | @MDCContentDsl 40 | @Composable 41 | public fun MDCTextFieldScope.MDCTextFieldLeadingIcon( 42 | clickable: Boolean = false, 43 | attrs: MDCAttrsRaw? = null, 44 | content: MDCContentRaw? = null, 45 | ) { 46 | MDCTextFieldIcon( 47 | leading = true, 48 | clickable = clickable, 49 | attrs = attrs, 50 | content = content, 51 | ) 52 | } 53 | 54 | @MDCContentDsl 55 | @Composable 56 | public fun MDCTextFieldScope.MDCTextFieldTrailingIcon( 57 | clickable: Boolean = false, 58 | attrs: MDCAttrsRaw? = null, 59 | content: MDCContentRaw? = null, 60 | ) { 61 | MDCTextFieldIcon( 62 | leading = false, 63 | clickable = clickable, 64 | attrs = attrs, 65 | content = content, 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /sandbox/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.kotlin.multiplatform) 3 | id("convention.common") 4 | id("convention.ksp") 5 | id("convention.compose") 6 | id("convention.versions") 7 | } 8 | 9 | kotlin { 10 | js { 11 | binaries.executable() 12 | useCommonJs() 13 | browser { 14 | commonWebpackConfig { 15 | cssSupport { enabled.set(true) } 16 | scssSupport { enabled.set(true) } 17 | devServer = devServer?.copy( 18 | open = false, 19 | port = 3000, 20 | ) 21 | } 22 | runTask { 23 | sourceMaps = true 24 | } 25 | testTask { useKarma() } 26 | } 27 | } 28 | sourceSets { 29 | jsMain { 30 | kotlin.srcDir("src/jsMain/showcases") 31 | dependencies { 32 | implementation("dev.petuska:kmdc") 33 | implementation("dev.petuska:kmdcx") 34 | implementation("dev.petuska:katalog-runtime") 35 | implementation(libs.compose.routing) 36 | } 37 | } 38 | configureEach { 39 | languageSettings { 40 | optIn("kotlin.RequiresOptIn") 41 | optIn("kotlin.ExperimentalStdlibApi") 42 | } 43 | } 44 | } 45 | } 46 | 47 | ksp { 48 | arg("katalog.contentRoot", rootDir.parentFile.absolutePath) 49 | } 50 | 51 | dependencies { 52 | "kspJs"("dev.petuska:katalog-ksp") 53 | } 54 | 55 | gradleEnterprise { 56 | buildScan { 57 | termsOfServiceUrl = "https://gradle.com/terms-of-service" 58 | termsOfServiceAgree = "yes" 59 | } 60 | } 61 | 62 | /* 63 | Horrifying workaround for https://github.com/google/ksp/issues/367 64 | See also https://kotlinlang.slack.com/archives/C013BA8EQSE/p1674737612683639 65 | */ 66 | afterEvaluate { 67 | configurations.filter { it.name.startsWith("generatedByKspKotlinJs") && it.name.endsWith("DependenciesMetadata") } 68 | .forEach { configurations.remove(it) } 69 | } 70 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCButton.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.button.* 10 | import dev.petuska.kmdcx.icons.MDCIcon 11 | import dev.petuska.kmdcx.icons.mdcIcon 12 | import org.jetbrains.compose.web.attributes.disabled 13 | import org.jetbrains.compose.web.dom.Text 14 | import sandbox.control.BooleanControl 15 | import sandbox.control.ChoiceControl 16 | import sandbox.control.TextControl 17 | 18 | private class MDCButtonVM { 19 | var type by mutableStateOf(MDCButtonType.Text) 20 | var icon by mutableStateOf(MDCButtonIconType.None) 21 | var touch by mutableStateOf(false) 22 | var disabled by mutableStateOf(false) 23 | var label by mutableStateOf("My Label") 24 | } 25 | 26 | @Composable 27 | @Showcase(id = "MDCButton") 28 | fun MDCButton() = InteractiveShowcase( 29 | viewModel = { MDCButtonVM() }, 30 | controls = { 31 | ChoiceControl("Type", MDCButtonType.values().associateBy(MDCButtonType::name), ::type) 32 | ChoiceControl("Icon", MDCButtonIconType.values().associateBy(MDCButtonIconType::name), ::icon) 33 | BooleanControl("Disabled", ::disabled) 34 | BooleanControl("Touch", ::touch) 35 | TextControl("Label", label) { label = it } 36 | }, 37 | ) { 38 | MDCButton(type = type, icon = icon, touch = touch, attrs = { 39 | if (disabled) disabled() 40 | }) { 41 | val renderIcon = @Composable { 42 | Icon(attrs = { mdcIcon() }) { Text(MDCIcon.Star.type) } 43 | } 44 | if (icon == MDCButtonIconType.Leading) renderIcon() 45 | Label(label) 46 | if (icon == MDCButtonIconType.Trailing) renderIcon() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kmdc/kmdc-dialog/src/jsMain/kotlin/Header.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.dialog 2 | 3 | import androidx.compose.runtime.* 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.icon.button.* 6 | import org.jetbrains.compose.web.dom.* 7 | import org.jetbrains.compose.web.dom.Text 8 | import org.w3c.dom.* 9 | 10 | public interface MDCDialogHeaderScope : ElementScope, MDCDialogTitleScope 11 | 12 | /** 13 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 14 | */ 15 | @MDCContentDsl 16 | @Composable 17 | public fun MDCDialogScope.Header( 18 | attrs: MDCAttrsRaw? = null, 19 | content: MDCContent? = null 20 | ) { 21 | Div( 22 | attrs = { 23 | classes("mdc-dialog__header") 24 | attrs?.invoke(this) 25 | }, 26 | content = content.reinterpret() 27 | ) 28 | } 29 | 30 | /** 31 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 32 | */ 33 | @Suppress("unused") 34 | @MDCContentDsl 35 | @Composable 36 | public fun MDCDialogHeaderScope.CloseButton( 37 | attrs: MDCAttrsRaw? = null, 38 | content: MDCContent>? = null 39 | ) { 40 | MDCIconButton( 41 | attrs = { 42 | classes("mdc-dialog__close") 43 | data("mdc-dialog-action", "close") 44 | applyAttrs(attrs) 45 | }, 46 | content = content, 47 | ) 48 | } 49 | 50 | /** 51 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-dialog) 52 | */ 53 | @Suppress("unused") 54 | @MDCContentDsl 55 | @Composable 56 | public fun MDCDialogHeaderScope.CloseButton( 57 | text: String, 58 | attrs: MDCAttrsRaw? = null, 59 | ) { 60 | CloseButton(attrs = attrs) { 61 | Text(text) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /kmdc/kmdc-layout-grid/src/jsMain/kotlin/Cell.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.layout.grid 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.MDCContent 5 | import dev.petuska.kmdc.core.MDCContentDsl 6 | import dev.petuska.kmdc.core.classes 7 | import dev.petuska.kmdc.core.reinterpret 8 | import org.jetbrains.compose.web.dom.AttrBuilderContext 9 | import org.jetbrains.compose.web.dom.Div 10 | import org.w3c.dom.HTMLDivElement 11 | 12 | public data class SpanMap(val desktop: UInt, val tablet: UInt, val phone: UInt) 13 | 14 | public enum class MDCLayoutGridCellAlign(public vararg val classes: String) { 15 | Stretch, 16 | Top("mdc-layout-grid__cell--align-top"), 17 | Middle("mdc-layout-grid__cell--align-middle"), 18 | Bottom("mdc-layout-grid__cell--align-bottom") 19 | } 20 | 21 | /** 22 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-layout-grid) 23 | */ 24 | @MDCContentDsl 25 | @Composable 26 | public fun MDCLayoutGridCellsScope.Cell( 27 | span: UInt? = null, 28 | spanMap: SpanMap? = null, 29 | order: UInt? = null, 30 | align: MDCLayoutGridCellAlign = MDCLayoutGridCellAlign.Stretch, 31 | attrs: AttrBuilderContext? = null, 32 | content: MDCContent? = null 33 | ) { 34 | Div( 35 | attrs = { 36 | classes("mdc-layout-grid__cell") 37 | classes(align.classes) 38 | order?.let { classes("mdc-layout-grid__cell--order-$it") } 39 | span?.let { classes("mdc-layout-grid__cell--span-$it") } 40 | spanMap?.let { 41 | classes( 42 | "mdc-layout-grid__cell--span-${it.desktop}-desktop", 43 | "mdc-layout-grid__cell--span-${it.tablet}-tablet", 44 | "mdc-layout-grid__cell--span-${it.phone}-phone", 45 | ) 46 | } 47 | attrs?.invoke(this) 48 | }, 49 | content = content.reinterpret() 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /kmdc/kmdc-chips/src/jsMain/kotlin/action/Graphic.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.chips.action 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.ExperimentalComposeWebSvgApi 6 | import org.jetbrains.compose.web.dom.Span 7 | import org.jetbrains.compose.web.svg.Path 8 | import org.jetbrains.compose.web.svg.Svg 9 | import org.w3c.dom.HTMLSpanElement 10 | 11 | public interface MDCFilterChipGraphicScope : MDCChipActionIconScope 12 | 13 | /** 14 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 15 | */ 16 | @MDCContentDsl 17 | @Composable 18 | public fun MDCChipActionScope<*>.Graphic( 19 | attrs: MDCAttrsRaw? = null, 20 | content: MDCContent? = null 21 | ) { 22 | Span( 23 | attrs = { 24 | classes("mdc-evolution-chip__graphic") 25 | applyAttrs(attrs) 26 | }, 27 | content = content.reinterpret(), 28 | ) 29 | } 30 | 31 | /** 32 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-chips) 33 | */ 34 | @OptIn(ExperimentalComposeWebSvgApi::class) 35 | @MDCContentDsl 36 | @Composable 37 | public fun MDCFilterChipGraphicScope.Checkmark( 38 | attrs: MDCAttrsRaw? = null, 39 | ) { 40 | Span( 41 | attrs = { 42 | classes("mdc-evolution-chip__checkmark") 43 | applyAttrs(attrs) 44 | }, 45 | ) { 46 | Svg( 47 | viewBox = "-2 -3 30 30", 48 | attrs = { 49 | classes("mdc-evolution-chip__checkmark-svg") 50 | }, 51 | ) { 52 | Path( 53 | d = "M1.73,12.91 8.1,19.28 22.79,4.59", 54 | attrs = { 55 | attr("fill", "none") 56 | attr("stroke", "black") 57 | classes("mdc-evolution-chip__checkmark-path") 58 | } 59 | ) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kmdc/kmdc-tab-bar/src/jsMain/kotlin/indicator/Content.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.tab.indicator 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.dom.Span 6 | import org.w3c.dom.HTMLSpanElement 7 | 8 | public enum class MDCTabIndicatorType(public vararg val classes: String) { 9 | Underline("mdc-tab-indicator__content--underline"), 10 | Icon("mdc-tab-indicator__content--icon") 11 | } 12 | 13 | /** 14 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-indicator) 15 | */ 16 | @MDCContentDsl 17 | @Composable 18 | public fun MDCTabIndicatorScope.Content( 19 | indicatorType: MDCTabIndicatorType, 20 | attrs: MDCAttrsRaw? = null, 21 | content: MDCContentRaw? = null 22 | ) { 23 | Span( 24 | attrs = { 25 | classes("mdc-tab-indicator__content") 26 | classes(indicatorType.classes) 27 | if (indicatorType == MDCTabIndicatorType.Icon) aria("hidden", true) 28 | attrs?.invoke(this) 29 | }, 30 | content = content 31 | ) 32 | } 33 | 34 | /** 35 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-indicator) 36 | */ 37 | @MDCContentDsl 38 | @Composable 39 | public fun MDCTabIndicatorScope.Icon( 40 | attrs: MDCAttrsRaw? = null, 41 | content: MDCContentRaw? = null 42 | ) { 43 | Content(MDCTabIndicatorType.Icon, attrs, content) 44 | } 45 | 46 | /** 47 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-tab-indicator) 48 | */ 49 | @MDCContentDsl 50 | @Composable 51 | public fun MDCTabIndicatorScope.Underline( 52 | attrs: MDCAttrsRaw? = null, 53 | content: MDCContentRaw? = null 54 | ) { 55 | Content(MDCTabIndicatorType.Underline, attrs, content) 56 | } 57 | -------------------------------------------------------------------------------- /kmdc/kmdc-list/src/jsMain/kotlin/MDCListGroup.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.list 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.AttrsScope 6 | import org.jetbrains.compose.web.dom.Div 7 | import org.jetbrains.compose.web.dom.ElementScope 8 | import org.jetbrains.compose.web.dom.H3 9 | import org.jetbrains.compose.web.dom.Text 10 | import org.w3c.dom.HTMLDivElement 11 | import org.w3c.dom.HTMLHeadingElement 12 | 13 | public interface MDCListGroupScope : ElementScope 14 | 15 | /** 16 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-deprecated-list) 17 | */ 18 | @MDCContentDsl 19 | @Composable 20 | public fun MDCListGroup( 21 | attrs: MDCAttrs>? = null, 22 | content: MDCContent? = null, 23 | ) { 24 | Div( 25 | attrs = { 26 | classes("mdc-deprecated-list-group") 27 | attrs?.invoke(this) 28 | }, 29 | content = content.reinterpret() 30 | ) 31 | } 32 | 33 | /** 34 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-deprecated-list) 35 | */ 36 | @MDCContentDsl 37 | @Composable 38 | public fun MDCListGroupScope.Subheader( 39 | attrs: MDCAttrs>? = null, 40 | content: MDCContentRaw? = null, 41 | ) { 42 | H3(attrs = { 43 | classes("mdc-deprecated-list-group__subheader") 44 | attrs?.invoke(this) 45 | }, content = content) 46 | } 47 | 48 | /** 49 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-deprecated-list) 50 | */ 51 | @MDCContentDsl 52 | @Composable 53 | public fun MDCListGroupScope.Subheader( 54 | text: String, 55 | attrs: MDCAttrs>? = null, 56 | ) { 57 | Subheader(attrs) { Text(text) } 58 | } 59 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCBanner.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.banner.* 10 | import dev.petuska.kmdcx.icons.MDCIcon 11 | import dev.petuska.kmdcx.icons.mdcIcon 12 | import sandbox.control.BooleanControl 13 | import org.jetbrains.compose.web.dom.Text as ComposeText 14 | 15 | private class MDCBannerVM { 16 | var open by mutableStateOf(true) 17 | var centered by mutableStateOf(false) 18 | var mobileStacked by mutableStateOf(false) 19 | } 20 | 21 | @Composable 22 | @Showcase(id = "MDCBanner") 23 | fun MDCBanner() = InteractiveShowcase( 24 | viewModel = { MDCBannerVM() }, 25 | controls = { 26 | BooleanControl("Centered", ::centered) 27 | BooleanControl("Mobile Stacked", ::mobileStacked) 28 | BooleanControl("Open", ::open) 29 | }, 30 | ) { 31 | MDCBanner(open = open, centered = centered, mobileStacked = mobileStacked, attrs = { 32 | registerEvents() 33 | onOpening { open = true } 34 | onClosing { open = false } 35 | }) { 36 | Content { 37 | Graphic { 38 | Icon(attrs = { mdcIcon() }) { ComposeText(MDCIcon.ErrorOutline.type) } 39 | } 40 | Text("There was a problem processing a transaction on your credit card.") 41 | } 42 | Actions { 43 | PrimaryAction("Learn more") 44 | SecondaryAction("Fix it") 45 | } 46 | } 47 | } 48 | 49 | private fun MDCBannerAttrsScope.registerEvents() { 50 | onClosing { console.log("MDCBanner#onClosing", it.detail) } 51 | onClosed { console.log("MDCBanner#onClosed", it.detail) } 52 | onOpening { console.log("MDCBanner#onOpening", it.detail) } 53 | onOpened { console.log("MDCBanner#onOpened", it.detail) } 54 | } 55 | -------------------------------------------------------------------------------- /kmdc/kmdc-snackbar/src/jsMain/kotlin/MDCSnackbar.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.snackbar 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.AttrsScope 6 | import org.jetbrains.compose.web.dom.Aside 7 | import org.jetbrains.compose.web.dom.Div 8 | import org.jetbrains.compose.web.dom.ElementScope 9 | import org.w3c.dom.HTMLElement 10 | 11 | @JsModule("@material/snackbar/mdc-snackbar.scss") 12 | private external val MDCStyle: dynamic 13 | 14 | public enum class MDCSnackbarType(public vararg val classes: String) { 15 | Default, 16 | Stacked("mdc-snackbar--stacked"), 17 | Leading("mdc-snackbar--leading"), 18 | } 19 | 20 | public interface MDCSnackbarAttrsScope : AttrsScope 21 | public interface MDCSnackbarScope : ElementScope 22 | 23 | /** 24 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-snackbar) 25 | */ 26 | @MDCContentDsl 27 | @Composable 28 | public fun MDCSnackbar( 29 | type: MDCSnackbarType = MDCSnackbarType.Default, 30 | open: Boolean = false, 31 | timeoutMs: Int? = 5000, 32 | closeOnEscape: Boolean = true, 33 | attrs: MDCAttrs? = null, 34 | content: MDCContent? = null, 35 | ) { 36 | MDCStyle 37 | Aside(attrs = { 38 | classes("mdc-snackbar") 39 | classes(type.classes) 40 | applyAttrs(attrs) 41 | }) { 42 | MDCProvider(::MDCSnackbar, type) { 43 | MDCStateEffect(timeoutMs?.coerceIn(4000, 10000) ?: -1, MDCSnackbar::timeoutMs) 44 | MDCStateEffect(closeOnEscape, MDCSnackbar::closeOnEscape) 45 | MDCSideEffect(open) { 46 | if (open) open() else close() 47 | } 48 | } 49 | Div( 50 | attrs = { 51 | classes("mdc-snackbar__surface") 52 | attr("role", "status") 53 | attr("aria-relevant", "additions") 54 | }, 55 | content = content.reinterpret() 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/kotlin/config.kt: -------------------------------------------------------------------------------- 1 | package sandbox 2 | 3 | import dev.petuska.katalog.runtime.KatalogConfig 4 | import dev.petuska.katalog.runtime.domain.Katalog 5 | import dev.petuska.katalog.runtime.theme.KatalogTheme 6 | import dev.petuska.kmdc.typography.MDCH4 7 | import dev.petuska.kmdc.typography.MDCH5 8 | import dev.petuska.kmdc.typography.MDCH6 9 | import dev.petuska.kmdc.typography.MDCSubtitle2 10 | import kotlinx.browser.document 11 | import kotlinx.dom.addClass 12 | import org.jetbrains.compose.web.attributes.ATarget 13 | import org.jetbrains.compose.web.attributes.target 14 | import org.jetbrains.compose.web.css.Color 15 | import org.jetbrains.compose.web.css.color 16 | import org.jetbrains.compose.web.css.em 17 | import org.jetbrains.compose.web.css.marginBottom 18 | import org.jetbrains.compose.web.dom.A 19 | import sandbox.util.requireModule 20 | 21 | private external val process: dynamic 22 | 23 | @KatalogConfig 24 | fun Katalog.Builder.config() { 25 | requireModule("./sandbox.scss") 26 | document.body?.addClass("mdc-typography") 27 | 28 | debug = process.env.NODE_ENV == "development" 29 | title = "KMDC Katalog" 30 | subtitle = "Play around with various KMDC components" 31 | contentRootUrl = "https://github.com/mpetuska/kmdc/blob/master" 32 | theme = KatalogTheme( 33 | highlightColor = Color("#6200ee"), 34 | katalogTitleRender = { 35 | A(href = "https://github.com/mpetuska/kmdc", attrs = { 36 | target(ATarget.Blank) 37 | }) { 38 | MDCH4(it, attrs = { 39 | style { 40 | marginBottom(0.em) 41 | } 42 | }) 43 | } 44 | }, 45 | katalogSubtitleRender = { MDCSubtitle2(it) }, 46 | navTitleRender = { text, selected -> 47 | MDCH6(text, attrs = { 48 | style { 49 | if (selected) color(Color.white) 50 | } 51 | }) 52 | }, 53 | showcaseTitleRender = { MDCH5(it) }, 54 | showcaseDescriptionRender = { MDCSubtitle2(it) }, 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /kmdc/kmdc-banner/src/jsMain/kotlin/MDCBanner.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.banner 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import org.jetbrains.compose.web.attributes.AttrsScope 6 | import org.jetbrains.compose.web.dom.Div 7 | import org.jetbrains.compose.web.dom.ElementScope 8 | import org.w3c.dom.HTMLDivElement 9 | 10 | @JsModule("@material/banner/styles.scss") 11 | private external val Style: dynamic 12 | 13 | public interface MDCBannerAttrsScope : AttrsScope 14 | public interface MDCBannerScope : ElementScope 15 | 16 | /** 17 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-banner) 18 | */ 19 | @MDCContentDsl 20 | @Composable 21 | public fun MDCBanner( 22 | open: Boolean, 23 | centered: Boolean = false, 24 | fixed: Boolean = false, 25 | mobileStacked: Boolean = false, 26 | attrs: MDCAttrs? = null, 27 | content: MDCContent? = null, 28 | ) { 29 | Style 30 | Div(attrs = { 31 | classes("mdc-banner") 32 | role("banner") 33 | if (centered) classes("mdc-banner--centered") 34 | if (mobileStacked) classes("mdc-banner--mobile-stacked") 35 | applyAttrs(attrs) 36 | }) { 37 | MDCProvider(::MDCBanner) { 38 | MDCSideEffect(open, centered, mobileStacked) { 39 | if (open) open() else close(CloseReason.UNSPECIFIED) 40 | } 41 | if (fixed) { 42 | Div(attrs = { 43 | classes("mdc-banner__fixed") 44 | }, content = { InnerContent(content) }) 45 | } else { 46 | InnerContent(content) 47 | } 48 | } 49 | } 50 | } 51 | 52 | @Composable 53 | private fun InnerContent( 54 | content: MDCContent? = null, 55 | ) { 56 | Div( 57 | attrs = { 58 | classes("mdc-banner__content") 59 | role("alertdialog") 60 | aria("live", "assertive") 61 | }, 62 | content = content.reinterpret() 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /sandbox/src/jsMain/showcases/MDCNotchedOutline.kt: -------------------------------------------------------------------------------- 1 | package showcases 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import dev.petuska.katalog.runtime.Showcase 8 | import dev.petuska.katalog.runtime.layout.InteractiveShowcase 9 | import dev.petuska.kmdc.floating.label.MDCFloatingLabel 10 | import dev.petuska.kmdc.notched.outline.Leading 11 | import dev.petuska.kmdc.notched.outline.MDCNotchedOutline 12 | import dev.petuska.kmdc.notched.outline.Notch 13 | import dev.petuska.kmdc.notched.outline.Trailing 14 | import org.jetbrains.compose.web.css.* 15 | import org.jetbrains.compose.web.dom.Div 16 | import sandbox.control.BooleanControl 17 | import sandbox.control.TextControl 18 | 19 | private class MDCNotchedOutlineVM { 20 | var notched by mutableStateOf(false) 21 | var noLabel by mutableStateOf(false) 22 | var label by mutableStateOf("My Label") 23 | } 24 | 25 | @Composable 26 | @Showcase(id = "MDCNotchedOutline") 27 | fun MDCNotchedOutline() = InteractiveShowcase( 28 | viewModel = { MDCNotchedOutlineVM() }, 29 | controls = { 30 | BooleanControl("Notched", ::notched) 31 | BooleanControl("No Label", ::noLabel) 32 | TextControl("Label", ::label) 33 | }, 34 | ) { 35 | Div(attrs = { 36 | style { 37 | height(43.px) 38 | } 39 | }) { 40 | MDCNotchedOutline( 41 | noLabel = noLabel, 42 | notched = notched, 43 | attrs = { 44 | style { 45 | width(200.px) 46 | } 47 | } 48 | ) { 49 | Leading() 50 | Notch { 51 | MDCFloatingLabel( 52 | text = label, 53 | float = notched, 54 | id = "kmdc-notched-outline-label", 55 | attrs = { 56 | style { 57 | marginTop(0.75.em) 58 | paddingLeft(0.5.em) 59 | } 60 | } 61 | ) 62 | } 63 | Trailing() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /kmdc/kmdc-select/src/jsMain/kotlin/menu/SelectItem.kt: -------------------------------------------------------------------------------- 1 | package dev.petuska.kmdc.select.menu 2 | 3 | import androidx.compose.runtime.Composable 4 | import dev.petuska.kmdc.core.* 5 | import dev.petuska.kmdc.list.item.Graphic 6 | import dev.petuska.kmdc.list.item.ListItem 7 | import dev.petuska.kmdc.list.item.MDCListItemScope 8 | import dev.petuska.kmdc.list.item.Text 9 | import dev.petuska.kmdc.select.MDCSelect 10 | import dev.petuska.kmdc.select.MDCSelectLeadingIconLocal 11 | import org.w3c.dom.HTMLLIElement 12 | 13 | /** 14 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-select) 15 | */ 16 | @MDCContentDsl 17 | @Composable 18 | public fun MDCSelectMenuScope.SelectItem( 19 | value: String, 20 | disabled: Boolean = false, 21 | selected: Boolean = false, 22 | activated: Boolean = false, 23 | attrs: MDCAttrsRaw? = null, 24 | content: MDCContent>, 25 | ) { 26 | ListItem( 27 | disabled = disabled, 28 | selected = selected, 29 | activated = activated, 30 | attrs = { 31 | data("value", value) 32 | applyAttrs(attrs) 33 | }, 34 | content = content, 35 | ) 36 | } 37 | 38 | /** 39 | * [JS API](https://github.com/material-components/material-components-web/tree/v14.0.0/packages/mdc-select) 40 | */ 41 | @MDCContentDsl 42 | @Composable 43 | public fun MDCSelectMenuScope.SelectItem( 44 | text: String, 45 | value: String = text, 46 | disabled: Boolean = false, 47 | selected: Boolean = false, 48 | activated: Boolean = false, 49 | attrs: MDCAttrsRaw? = null, 50 | ) { 51 | val leadingIcon = MDCSelectLeadingIconLocal.current 52 | SelectItem( 53 | value = value, 54 | disabled = disabled, 55 | selected = selected, 56 | activated = activated, 57 | attrs = attrs, 58 | ) { 59 | if (leadingIcon) Graphic() 60 | Text(text) 61 | MDCSideEffect(onDispose = MDCSelect::layoutOptions) { 62 | layoutOptions() 63 | } 64 | } 65 | } 66 | --------------------------------------------------------------------------------