From 22564bca700f286c0f39e91e17757cdce56f9fb6 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Wed, 19 Jun 2024 10:18:07 +0200 Subject: [PATCH 01/21] Initial multiplatform commit --- .gitignore | 1 + build.gradle.kts | 8 +- compose-multiplatform/lib/.gitignore | 1 + compose-multiplatform/lib/build.gradle.kts | 99 +++++ .../lib/src/androidMain/AndroidManifest.xml | 2 + .../androidMain/kotlin/Platform.android.kt | 7 + .../lib/src/commonMain/kotlin/Platform.kt | 5 + .../src/desktopMain/kotlin/Platform.jvm.kt | 5 + .../lib/src/iosMain/kotlin/Platform.ios.kt | 7 + .../src/wasmJsMain/kotlin/Platform.wasmJs.kt | 5 + .../Configuration/Config.xcconfig | 3 + .../iosApp.xcodeproj/project.pbxproj | 403 ++++++++++++++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/app-icon-1024.png | Bin 0 -> 67285 bytes .../iosApp/Assets.xcassets/Contents.json | 6 + .../sample-iosApp/iosApp/ContentView.swift | 21 + .../sample-iosApp/iosApp/Info.plist | 50 +++ .../Preview Assets.xcassets/Contents.json | 6 + .../sample-iosApp/iosApp/iOSApp.swift | 10 + compose-multiplatform/sample/.gitignore | 1 + compose-multiplatform/sample/build.gradle.kts | 123 ++++++ .../src/androidMain/AndroidManifest.xml | 23 + .../androidMain/kotlin/Platform.android.kt | 7 + .../com/kizitonwose/calendarx/MainActivity.kt | 24 ++ .../drawable-v24/ic_launcher_foreground.xml | 30 ++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3593 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5339 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2636 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3388 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4926 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7472 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7909 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 11873 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10652 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16570 bytes .../src/androidMain/res/values/strings.xml | 3 + .../drawable/compose-multiplatform.xml | 36 ++ .../sample/src/commonMain/kotlin/App.kt | 38 ++ .../sample/src/commonMain/kotlin/Greeting.kt | 7 + .../sample/src/commonMain/kotlin/Platform.kt | 5 + .../src/desktopMain/kotlin/Platform.jvm.kt | 5 + .../sample/src/desktopMain/kotlin/main.kt | 11 + .../src/iosMain/kotlin/MainViewController.kt | 3 + .../sample/src/iosMain/kotlin/Platform.ios.kt | 7 + .../src/wasmJsMain/kotlin/Platform.wasmJs.kt | 5 + .../sample/src/wasmJsMain/kotlin/main.kt | 10 + .../src/wasmJsMain/resources/index.html | 12 + .../src/wasmJsMain/resources/styles.css | 7 + gradle/libs.versions.toml | 4 + settings.gradle.kts | 2 + 54 files changed, 1204 insertions(+), 3 deletions(-) create mode 100644 compose-multiplatform/lib/.gitignore create mode 100644 compose-multiplatform/lib/build.gradle.kts create mode 100644 compose-multiplatform/lib/src/androidMain/AndroidManifest.xml create mode 100644 compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt create mode 100644 compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt create mode 100644 compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt create mode 100644 compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt create mode 100644 compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig create mode 100644 compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png create mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json create mode 100644 compose-multiplatform/sample-iosApp/iosApp/ContentView.swift create mode 100644 compose-multiplatform/sample-iosApp/iosApp/Info.plist create mode 100644 compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift create mode 100644 compose-multiplatform/sample/.gitignore create mode 100644 compose-multiplatform/sample/build.gradle.kts create mode 100644 compose-multiplatform/sample/src/androidMain/AndroidManifest.xml create mode 100644 compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt create mode 100644 compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt create mode 100644 compose-multiplatform/sample/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 compose-multiplatform/sample/src/androidMain/res/drawable/ic_launcher_background.xml create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-hdpi/ic_launcher.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-mdpi/ic_launcher.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-xhdpi/ic_launcher.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 compose-multiplatform/sample/src/androidMain/res/values/strings.xml create mode 100644 compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/App.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt create mode 100644 compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt create mode 100644 compose-multiplatform/sample/src/desktopMain/kotlin/main.kt create mode 100644 compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt create mode 100644 compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt create mode 100644 compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt create mode 100644 compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt create mode 100644 compose-multiplatform/sample/src/wasmJsMain/resources/index.html create mode 100644 compose-multiplatform/sample/src/wasmJsMain/resources/styles.css diff --git a/.gitignore b/.gitignore index 1a4f120c..16547d40 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /captures .externalNativeBuild .idea +/.kotlin diff --git a/build.gradle.kts b/build.gradle.kts index 39482b65..86219a5d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,8 @@ plugins { alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.kotlinter) apply false alias(libs.plugins.mavenPublish) apply false + alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.jetbrainsCompose) apply false alias(libs.plugins.versionCheck) } @@ -13,6 +15,6 @@ allprojects { apply(plugin = rootProject.libs.plugins.kotlinter.get().pluginId) } -tasks.register("clean").configure { - delete(rootProject.layout.buildDirectory) -} +// tasks.register("clean").configure { +// delete(rootProject.layout.buildDirectory) +// } diff --git a/compose-multiplatform/lib/.gitignore b/compose-multiplatform/lib/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/compose-multiplatform/lib/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/compose-multiplatform/lib/build.gradle.kts b/compose-multiplatform/lib/build.gradle.kts new file mode 100644 index 00000000..e022ab95 --- /dev/null +++ b/compose-multiplatform/lib/build.gradle.kts @@ -0,0 +1,99 @@ + +import com.kizitonwose.calendar.buildsrc.Android +import com.kizitonwose.calendar.buildsrc.Config +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.composeCompiler) + alias(libs.plugins.mavenPublish) +} + +kotlin { + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + moduleName = "composeApp" + browser { + commonWebpackConfig { + outputFileName = "composeApp.js" + devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { + static = (static ?: mutableListOf()).apply { + // Serve sources to debug inside browser + add(project.projectDir.path) + } + } + } + } + binaries.executable() + } + + androidTarget {} + + jvm("desktop") + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64(), + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "ComposeApp" + isStatic = true + } + } + + sourceSets { + val desktopMain by getting + + androidMain.dependencies { + implementation(compose.preview) + } + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + } + } +} + +android { + namespace = "com.kizitonwose.calendarx" + compileSdk = Android.compileSdk + + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") + + defaultConfig { + minSdk = Android.minSdkComposeLibrary + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + java { + toolchain { + languageVersion.set(Config.compatibleJavaLanguageVersion) + } + } + kotlin { + jvmToolchain { + languageVersion.set(Config.compatibleJavaLanguageVersion) + } + } + buildFeatures { + compose = true + } + dependencies { + debugImplementation(compose.uiTooling) + } +} diff --git a/compose-multiplatform/lib/src/androidMain/AndroidManifest.xml b/compose-multiplatform/lib/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..568741e5 --- /dev/null +++ b/compose-multiplatform/lib/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt b/compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt new file mode 100644 index 00000000..1e282d1e --- /dev/null +++ b/compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt @@ -0,0 +1,7 @@ +import android.os.Build + +class AndroidPlatform : Platform { + override val name: String = "Android ${Build.VERSION.SDK_INT}" +} + +actual fun getPlatform(): Platform = AndroidPlatform() diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt b/compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt new file mode 100644 index 00000000..fc48e12e --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt @@ -0,0 +1,5 @@ +interface Platform { + val name: String +} + +expect fun getPlatform(): Platform diff --git a/compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt b/compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt new file mode 100644 index 00000000..5bf6fade --- /dev/null +++ b/compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt @@ -0,0 +1,5 @@ +class JVMPlatform : Platform { + override val name: String = "Java ${System.getProperty("java.version")}" +} + +actual fun getPlatform(): Platform = JVMPlatform() diff --git a/compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt b/compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt new file mode 100644 index 00000000..9bb8535a --- /dev/null +++ b/compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt @@ -0,0 +1,7 @@ +import platform.UIKit.UIDevice + +class IOSPlatform : Platform { + override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion +} + +actual fun getPlatform(): Platform = IOSPlatform() diff --git a/compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt b/compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt new file mode 100644 index 00000000..28dcfdea --- /dev/null +++ b/compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt @@ -0,0 +1,5 @@ +class WasmPlatform : Platform { + override val name: String = "Web with Kotlin/Wasm" +} + +actual fun getPlatform(): Platform = WasmPlatform() diff --git a/compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig b/compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig new file mode 100644 index 00000000..8e809e32 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID= +BUNDLE_ID=com.kizitonwose.calendarx.KotlinProject +APP_NAME=KotlinProject \ No newline at end of file diff --git a/compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj b/compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 00000000..24eb3b48 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,403 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* KotlinProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KotlinProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B92378962B6B1156000C7307 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* KotlinProject.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, + 7555FF77242A565900829871 /* Sources */, + B92378962B6B1156000C7307 /* Frameworks */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* KotlinProject.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1540; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + ComposeApp, + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + ComposeApp, + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} \ No newline at end of file diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..0afb3cf0 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors": [ + { + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..bdf2efb2 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images": [ + { + "filename": "app-icon-1024.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..53fc536fb9ac5c1dbb27c7e1da13db3760070a11 GIT binary patch literal 67285 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 BXNCX( literal 0 HcmV?d00001 diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 00000000..74d6a722 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/compose-multiplatform/sample-iosApp/iosApp/ContentView.swift b/compose-multiplatform/sample-iosApp/iosApp/ContentView.swift new file mode 100644 index 00000000..3cd5c325 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/ContentView.swift @@ -0,0 +1,21 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.MainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea(.keyboard) // Compose has own keyboard handler + } +} + + + diff --git a/compose-multiplatform/sample-iosApp/iosApp/Info.plist b/compose-multiplatform/sample-iosApp/iosApp/Info.plist new file mode 100644 index 00000000..412e3781 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..74d6a722 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift b/compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift new file mode 100644 index 00000000..0648e860 --- /dev/null +++ b/compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} \ No newline at end of file diff --git a/compose-multiplatform/sample/.gitignore b/compose-multiplatform/sample/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/compose-multiplatform/sample/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts new file mode 100644 index 00000000..fa348fd0 --- /dev/null +++ b/compose-multiplatform/sample/build.gradle.kts @@ -0,0 +1,123 @@ + +import com.kizitonwose.calendar.buildsrc.Android +import com.kizitonwose.calendar.buildsrc.Config +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.composeCompiler) +} + +kotlin { + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + moduleName = "composeApp" + browser { + commonWebpackConfig { + outputFileName = "composeApp.js" + devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { + static = (static ?: mutableListOf()).apply { + // Serve sources to debug inside browser + add(project.projectDir.path) + } + } + } + } + binaries.executable() + } + + androidTarget {} + + jvm("desktop") + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64(), + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "ComposeApp" + isStatic = true + } + } + + sourceSets { + val desktopMain by getting + + androidMain.dependencies { + implementation(compose.preview) + implementation(libs.compose.activity) + } + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(project(":compose-multiplatform:lib")) + } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + } + } +} + +android { + namespace = "com.kizitonwose.calendarx" + compileSdk = Android.compileSdk + + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") + + defaultConfig { + applicationId = "com.kizitonwose.calendarx" + minSdk = Android.minSdkSampleApp + targetSdk = Android.targetSdk + versionCode = 1 + versionName = "1.0" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } + java { + toolchain { + languageVersion.set(Config.compatibleJavaLanguageVersion) + } + } + kotlin { + jvmToolchain { + languageVersion.set(Config.compatibleJavaLanguageVersion) + } + } + buildFeatures { + compose = true + } + dependencies { + debugImplementation(compose.uiTooling) + } +} + +compose.desktop { + application { + mainClass = "MainKt" + + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "com.kizitonwose.calendarx" + packageVersion = "1.0.0" + } + } +} diff --git a/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml b/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..c5db0b15 --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt b/compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt new file mode 100644 index 00000000..4f3ea051 --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt @@ -0,0 +1,7 @@ +import android.os.Build + +class AndroidPlatform : Platform { + override val name: String = "Android ${Build.VERSION.SDK_INT}" +} + +actual fun getPlatform(): Platform = AndroidPlatform() \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt b/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt new file mode 100644 index 00000000..c750bbc5 --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt @@ -0,0 +1,24 @@ +package com.kizitonwose.calendarx + +import App +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + App() + } + } +} + +@Preview +@Composable +fun AppAndroidPreview() { + App() +} \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/compose-multiplatform/sample/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/res/drawable/ic_launcher_background.xml b/compose-multiplatform/sample/src/androidMain/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..e93e11ad --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/compose-multiplatform/sample/src/androidMain/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a571e60098c92c2baca8a5df62f2929cbff01b52 GIT binary patch literal 3593 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 P00000NkvXXu0mjfP=x?Y literal 0 HcmV?d00001 diff --git a/compose-multiplatform/sample/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/compose-multiplatform/sample/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..61da551c5594a1f9d26193983d2cd69189014603 GIT binary patch literal 5339 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 literal 0 HcmV?d00001 diff --git a/compose-multiplatform/sample/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/compose-multiplatform/sample/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b216f2d313cc673d8b8c4da591c174ebed52795c GIT binary patch literal 11873 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 literal 0 HcmV?d00001 diff --git a/compose-multiplatform/sample/src/androidMain/res/values/strings.xml b/compose-multiplatform/sample/src/androidMain/res/values/strings.xml new file mode 100644 index 00000000..3385b162 --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/res/values/strings.xml @@ -0,0 +1,3 @@ + + KotlinProject + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml b/compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml new file mode 100644 index 00000000..b7d4281f --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml @@ -0,0 +1,36 @@ + + + + + + + + diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt new file mode 100644 index 00000000..589e1e01 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -0,0 +1,38 @@ +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import oguafo.compose_multiplatform.sample.generated.resources.Res +import oguafo.compose_multiplatform.sample.generated.resources.compose_multiplatform +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun App() { + MaterialTheme { + var showContent by remember { mutableStateOf(false) } + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Button(onClick = { showContent = !showContent }) { + Text("Click me!") + } + AnimatedVisibility(showContent) { + val greeting = remember { Greeting().greet() } + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Image(painterResource(Res.drawable.compose_multiplatform), null) + Text("Compose: $greeting") + } + } + } + } +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt new file mode 100644 index 00000000..887d8350 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt @@ -0,0 +1,7 @@ +class Greeting { + private val platform = getPlatform() + + fun greet(): String { + return "Hello, ${platform.name}!" + } +} \ No newline at end of file diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt new file mode 100644 index 00000000..87ca3fff --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt @@ -0,0 +1,5 @@ +interface Platform { + val name: String +} + +expect fun getPlatform(): Platform \ No newline at end of file diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt new file mode 100644 index 00000000..5bf6fade --- /dev/null +++ b/compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt @@ -0,0 +1,5 @@ +class JVMPlatform : Platform { + override val name: String = "Java ${System.getProperty("java.version")}" +} + +actual fun getPlatform(): Platform = JVMPlatform() diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt new file mode 100644 index 00000000..239593a3 --- /dev/null +++ b/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt @@ -0,0 +1,11 @@ +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "KotlinProject", + ) { + App() + } +} \ No newline at end of file diff --git a/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt b/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt new file mode 100644 index 00000000..fa143d45 --- /dev/null +++ b/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt @@ -0,0 +1,3 @@ +import androidx.compose.ui.window.ComposeUIViewController + +fun MainViewController() = ComposeUIViewController { App() } \ No newline at end of file diff --git a/compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt b/compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt new file mode 100644 index 00000000..9bb8535a --- /dev/null +++ b/compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt @@ -0,0 +1,7 @@ +import platform.UIKit.UIDevice + +class IOSPlatform : Platform { + override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion +} + +actual fun getPlatform(): Platform = IOSPlatform() diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt new file mode 100644 index 00000000..28dcfdea --- /dev/null +++ b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt @@ -0,0 +1,5 @@ +class WasmPlatform : Platform { + override val name: String = "Web with Kotlin/Wasm" +} + +actual fun getPlatform(): Platform = WasmPlatform() diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt new file mode 100644 index 00000000..d2fd60c1 --- /dev/null +++ b/compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt @@ -0,0 +1,10 @@ +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.ComposeViewport +import kotlinx.browser.document + +@OptIn(ExperimentalComposeUiApi::class) +fun main() { + ComposeViewport(document.body!!) { + App() + } +} \ No newline at end of file diff --git a/compose-multiplatform/sample/src/wasmJsMain/resources/index.html b/compose-multiplatform/sample/src/wasmJsMain/resources/index.html new file mode 100644 index 00000000..0be283fe --- /dev/null +++ b/compose-multiplatform/sample/src/wasmJsMain/resources/index.html @@ -0,0 +1,12 @@ + + + + + + KotlinProject + + + + + + \ No newline at end of file diff --git a/compose-multiplatform/sample/src/wasmJsMain/resources/styles.css b/compose-multiplatform/sample/src/wasmJsMain/resources/styles.css new file mode 100644 index 00000000..0549b10f --- /dev/null +++ b/compose-multiplatform/sample/src/wasmJsMain/resources/styles.css @@ -0,0 +1,7 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 302f2cfb..bfe68e18 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,3 +42,7 @@ kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlinter = { id = "org.jmailen.kotlinter", version = "4.3.0" } mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.28.0" } versionCheck = { id = "com.github.ben-manes.versions", version = "0.51.0" } + +# KMM +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +jetbrainsCompose = { id = "org.jetbrains.compose", version = "1.6.10" } diff --git a/settings.gradle.kts b/settings.gradle.kts index eb300931..42f445bd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,3 +18,5 @@ include(":data") include(":view") include(":compose") include(":sample") +include(":compose-multiplatform:lib") +include(":compose-multiplatform:sample") From 561c22148fb2913c8eb4a845b6a36e9a382014a9 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sat, 22 Jun 2024 21:56:51 +0200 Subject: [PATCH 02/21] Implement multiplatform data logic --- compose-multiplatform/lib/build.gradle.kts | 2 + .../kotlin/calendar/core/CalendarDay.kt | 13 +++ .../kotlin/calendar/core/CalendarMonth.kt | 42 +++++++++ .../kotlin/calendar/core/DayPosition.kt | 26 ++++++ .../kotlin/calendar/core/OutDateStyle.kt | 22 +++++ .../commonMain/kotlin/calendar/core/Week.kt | 36 ++++++++ .../kotlin/calendar/core/WeekDay.kt | 13 +++ .../kotlin/calendar/core/WeekDayPosition.kt | 25 ++++++ .../kotlin/calendar/core/YearMonth.kt | 7 ++ .../kotlin/calendar/data/Extensions.kt | 63 ++++++++++++++ .../kotlin/calendar/data/MonthData.kt | 87 +++++++++++++++++++ gradle/libs.versions.toml | 1 + 12 files changed, 337 insertions(+) create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt diff --git a/compose-multiplatform/lib/build.gradle.kts b/compose-multiplatform/lib/build.gradle.kts index e022ab95..5843799d 100644 --- a/compose-multiplatform/lib/build.gradle.kts +++ b/compose-multiplatform/lib/build.gradle.kts @@ -57,6 +57,8 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) + implementation(compose.components.uiToolingPreview) + implementation(libs.kotlinx.datetime) } desktopMain.dependencies { implementation(compose.desktop.currentOs) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt new file mode 100644 index 00000000..290362b3 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt @@ -0,0 +1,13 @@ +package calendar.core + +import androidx.compose.runtime.Immutable +import kotlinx.datetime.LocalDate + +/** + * Represents a day on the calendar. + * + * @param date the date for this day. + * @param position the [DayPosition] for this day. + */ +@Immutable +data class CalendarDay(val date: LocalDate, val position: DayPosition) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt new file mode 100644 index 00000000..582945e5 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt @@ -0,0 +1,42 @@ +package calendar.core + +import androidx.compose.runtime.Immutable + +/** + * Represents a month on the calendar. + * + * @param yearMonth the calendar month value. + * @param weekDays the weeks in this month. + */ +@Immutable +data class CalendarMonth( + val yearMonth: YearMonth, + val weekDays: List>, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as CalendarMonth + + if (yearMonth != other.yearMonth) return false + if (weekDays.first().first() != other.weekDays.first().first()) return false + if (weekDays.last().last() != other.weekDays.last().last()) return false + + return true + } + + override fun hashCode(): Int { + var result = yearMonth.hashCode() + result = 31 * result + weekDays.first().first().hashCode() + result = 31 * result + weekDays.last().last().hashCode() + return result + } + + override fun toString(): String { + return "CalendarMonth { " + + "first = ${weekDays.first().first()}, " + + "last = ${weekDays.last().last()} " + + "} " + } +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt new file mode 100644 index 00000000..eed49fa3 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt @@ -0,0 +1,26 @@ +package calendar.core + +/** + * Describes the position of a [CalendarDay] in the month. + */ +enum class DayPosition { + /** + * The day is positioned at the start of the month to + * ensure proper alignment of the first day of the week. + * The day belongs to the previous month on the calendar. + */ + InDate, + + /** + * The day belongs to the current month on the calendar. + */ + MonthDate, + + /** + * The day is positioned at the end of the month to + * to fill the remaining days after the days in the month. + * The day belongs to the next month on the calendar. + * @see [OutDateStyle] + */ + OutDate, +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt new file mode 100644 index 00000000..b1069c65 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt @@ -0,0 +1,22 @@ +package calendar.core + +/** + * Determines how [DayPosition.OutDate] are + * generated for each month on the calendar. + */ +enum class OutDateStyle { + /** + * The calendar will generate outDates until it reaches + * the end of the month row. This means that if a month + * has 5 rows, it will display 5 rows and if a month + * has 6 rows, it will display 6 rows. + */ + EndOfRow, + + /** + * The calendar will generate outDates until it + * reaches the end of a 6 x 7 grid on each month. + * This means that all months will have 6 rows. + */ + EndOfGrid, +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt new file mode 100644 index 00000000..dfd350ea --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt @@ -0,0 +1,36 @@ +package calendar.core + +import androidx.compose.runtime.Immutable + +/** + * Represents a week on the week-based calendar. + * + * @param days the days in this week. + */ +@Immutable +data class Week(val days: List) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Week + + if (days.first() != other.days.first()) return false + if (days.last() != other.days.last()) return false + + return true + } + + override fun hashCode(): Int { + var result = days.first().hashCode() + result = 31 * result + days.last().hashCode() + return result + } + + override fun toString(): String { + return "Week { " + + "first = ${days.first()}, " + + "last = ${days.last()} " + + "} " + } +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt new file mode 100644 index 00000000..39d4ae4d --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt @@ -0,0 +1,13 @@ +package calendar.core + +import androidx.compose.runtime.Immutable +import kotlinx.datetime.LocalDate + +/** + * Represents a day on the week calendar. + * + * @param date the date for this day. + * @param position the [WeekDayPosition] for this day. + */ +@Immutable +data class WeekDay(val date: LocalDate, val position: WeekDayPosition) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt new file mode 100644 index 00000000..63ee47bc --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt @@ -0,0 +1,25 @@ +package calendar.core + +/** + * Describes the position of a [WeekDay] on the calendar. + */ +enum class WeekDayPosition { + /** + * The day is positioned at the start of the calendar to + * ensure proper alignment of the first day of the week. + * The day is before the provided start date. + */ + InDate, + + /** + * The day is in the range of the provided start and end dates. + */ + RangeDate, + + /** + * The day is positioned at the end of the calendar to to fill the + * remaining days in the last week after the provided end date. + * The day is after the provided end date. + */ + OutDate, +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt new file mode 100644 index 00000000..c95e79a6 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt @@ -0,0 +1,7 @@ +package calendar.core + +import androidx.compose.runtime.Immutable +import kotlinx.datetime.Month + +@Immutable +data class YearMonth(val year: Int, val month: Month) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt new file mode 100644 index 00000000..80387082 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt @@ -0,0 +1,63 @@ +package calendar.data + +import calendar.core.YearMonth +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate +import kotlinx.datetime.daysUntil +import kotlinx.datetime.isoDayNumber +import kotlinx.datetime.minus +import kotlinx.datetime.monthsUntil +import kotlinx.datetime.plus + +/** + * Returns the days of week values such that the desired + * [firstDayOfWeek] property is at the start position. + */ +fun daysOfWeek(firstDayOfWeek: DayOfWeek): List { + val pivot = 7 - firstDayOfWeek.ordinal + val daysOfWeek = DayOfWeek.entries + // Order `daysOfWeek` array so that firstDayOfWeek is at the start position. + return (daysOfWeek.takeLast(pivot) + daysOfWeek.dropLast(pivot)) +} + +// /** +// * Returns the first day of the week from the provided locale. +// */ +// fun firstDayOfWeekFromLocale(locale: Locale = Locale.current): DayOfWeek = ?? + +fun YearMonth.atStartOfMonth(): LocalDate = LocalDate(year, month, 1) + +fun YearMonth.atEndOfMonth(): LocalDate = LocalDate(year, month, lengthOfMonth()) + +val LocalDate.yearMonth: YearMonth + get() = YearMonth(year, month) + +val YearMonth.nextMonth: YearMonth + get() = this.plusMonths(1) + +val YearMonth.previousMonth: YearMonth + get() = this.minusMonths(1) + +internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) + +internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) + +internal fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeUnit.MONTH) + +internal fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) + +internal fun YearMonth.plusMonths(value: Int): YearMonth = atStartOfMonth().plusMonths(value).yearMonth + +internal fun YearMonth.minusMonths(value: Int): YearMonth = atStartOfMonth().minusMonths(value).yearMonth + +internal fun YearMonth.lengthOfMonth(): Int { + val thisMonthStart = atStartOfMonth() + val nextMonthStart = thisMonthStart.plusMonths(1) + return thisMonthStart.daysUntil(nextMonthStart) +} + +internal fun YearMonth.monthsUntil(other: YearMonth): Int = atStartOfMonth().monthsUntil(other.atStartOfMonth()) + +// E.g DayOfWeek.SATURDAY.daysUntil(DayOfWeek.TUESDAY) = 3 +internal fun DayOfWeek.daysUntil(other: DayOfWeek) = (7 + (other.isoDayNumber - isoDayNumber)) % 7 diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt new file mode 100644 index 00000000..afad256c --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt @@ -0,0 +1,87 @@ +package calendar.data + +import calendar.core.CalendarDay +import calendar.core.CalendarMonth +import calendar.core.DayPosition +import calendar.core.OutDateStyle +import calendar.core.YearMonth +import kotlinx.datetime.DayOfWeek + +internal data class MonthData internal constructor( + private val month: YearMonth, + private val inDays: Int, + private val outDays: Int, +) { + private val totalDays = inDays + month.lengthOfMonth() + outDays + + private val firstDay = month.atStartOfMonth().minusDays(inDays) + + private val rows = (0 until totalDays).chunked(7) + + private val previousMonth = month.previousMonth + + private val nextMonth = month.nextMonth + + val calendarMonth = + CalendarMonth(month, rows.map { week -> week.map { dayOffset -> getDay(dayOffset) } }) + + private fun getDay(dayOffset: Int): CalendarDay { + val date = firstDay.plusDays(dayOffset) + val position = when (date.yearMonth) { + month -> DayPosition.MonthDate + previousMonth -> DayPosition.InDate + nextMonth -> DayPosition.OutDate + else -> throw IllegalArgumentException("Invalid date: $date in month: $month") + } + return CalendarDay(date, position) + } +} + +internal fun getCalendarMonthData( + startMonth: YearMonth, + offset: Int, + firstDayOfWeek: DayOfWeek, + outDateStyle: OutDateStyle, +): MonthData { + val month = startMonth.plusMonths(offset) + val firstDay = month.atStartOfMonth() + val inDays = firstDayOfWeek.daysUntil(firstDay.dayOfWeek) + val outDays = (inDays + month.lengthOfMonth()).let { inAndMonthDays -> + val endOfRowDays = if (inAndMonthDays % 7 != 0) 7 - (inAndMonthDays % 7) else 0 + val endOfGridDays = if (outDateStyle == OutDateStyle.EndOfRow) { + 0 + } else { + val weeksInMonth = (inAndMonthDays + endOfRowDays) / 7 + (6 - weeksInMonth) * 7 + } + return@let endOfRowDays + endOfGridDays + } + return MonthData(month, inDays, outDays) +} + +internal fun getHeatMapCalendarMonthData( + startMonth: YearMonth, + offset: Int, + firstDayOfWeek: DayOfWeek, +): MonthData { + val month = startMonth.plusMonths(offset) + val firstDay = month.atStartOfMonth() + val inDays = if (offset == 0) { + firstDayOfWeek.daysUntil(firstDay.dayOfWeek) + } else { + -firstDay.dayOfWeek.daysUntil(firstDayOfWeek) + } + val outDays = (inDays + month.lengthOfMonth()).let { inAndMonthDays -> + if (inAndMonthDays % 7 != 0) 7 - (inAndMonthDays % 7) else 0 + } + return MonthData(month, inDays, outDays) +} + +internal fun getMonthIndex(startMonth: YearMonth, targetMonth: YearMonth): Int { + return startMonth.monthsUntil(targetMonth) +} + +internal fun getMonthIndicesCount(startMonth: YearMonth, endMonth: YearMonth): Int { + // Add one to include the start month itself! + return getMonthIndex(startMonth, endMonth) + 1 +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bfe68e18..bf647667 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,7 @@ espresso = "3.5.1" [libraries] kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.0" } desugar = { module = "com.android.tools:desugar_jdk_libs", version = "2.0.4" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" } From 7909f248e7fae40aef35eb6fe30d544932d41a6e Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sun, 23 Jun 2024 19:26:47 +0200 Subject: [PATCH 03/21] Multiplatform example --- compose-multiplatform/lib/build.gradle.kts | 17 +- .../kotlin/calendar/core/CalendarMonth.kt | 2 +- .../commonMain/kotlin/calendar/core/Week.kt | 2 +- .../kotlin/calendar/core/YearMonth.kt | 16 +- .../kotlin/calendar/data/DataStore.kt | 21 ++ .../kotlin/calendar/data/Extensions.kt | 28 +- .../commonMain/kotlin/calendar/data/Utils.kt | 16 + .../kotlin/calendar/data/VisibleItemState.kt | 9 + .../commonMain/kotlin/calendar/ui/Calendar.kt | 199 ++++++++++++ .../kotlin/calendar/ui/CalendarDefaults.kt | 54 +++ .../kotlin/calendar/ui/CalendarInfo.kt | 12 + .../kotlin/calendar/ui/CalendarLayoutInfo.kt | 33 ++ .../kotlin/calendar/ui/CalendarMonths.kt | 90 +++++ .../kotlin/calendar/ui/CalendarState.kt | 307 ++++++++++++++++++ .../kotlin/calendar/ui/ContentHeightMode.kt | 28 ++ compose-multiplatform/sample/build.gradle.kts | 4 +- .../androidMain/kotlin/Platform.android.kt | 7 - .../sample/src/commonMain/kotlin/App.kt | 214 ++++++++++-- .../sample/src/commonMain/kotlin/Platform.kt | 5 - .../commonMain/kotlin/SimpleCalendarTitle.kt | 83 +++++ .../src/desktopMain/kotlin/Platform.jvm.kt | 5 - .../sample/src/desktopMain/kotlin/main.kt | 4 +- .../sample/src/iosMain/kotlin/Platform.ios.kt | 7 - .../src/wasmJsMain/kotlin/Platform.wasmJs.kt | 5 - compose/build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- sample/build.gradle.kts | 2 +- settings.gradle.kts | 2 +- 28 files changed, 1094 insertions(+), 82 deletions(-) create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt create mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt delete mode 100644 compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt delete mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt delete mode 100644 compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt delete mode 100644 compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt delete mode 100644 compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt diff --git a/compose-multiplatform/lib/build.gradle.kts b/compose-multiplatform/lib/build.gradle.kts index 5843799d..f46243c6 100644 --- a/compose-multiplatform/lib/build.gradle.kts +++ b/compose-multiplatform/lib/build.gradle.kts @@ -2,7 +2,6 @@ import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl -import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig plugins { alias(libs.plugins.kotlinMultiplatform) @@ -15,19 +14,7 @@ plugins { kotlin { @OptIn(ExperimentalWasmDsl::class) wasmJs { - moduleName = "composeApp" - browser { - commonWebpackConfig { - outputFileName = "composeApp.js" - devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { - static = (static ?: mutableListOf()).apply { - // Serve sources to debug inside browser - add(project.projectDir.path) - } - } - } - } - binaries.executable() + moduleName = "calendar" } androidTarget {} @@ -58,7 +45,7 @@ kotlin { implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) implementation(compose.components.uiToolingPreview) - implementation(libs.kotlinx.datetime) + api(libs.kotlinx.datetime) } desktopMain.dependencies { implementation(compose.desktop.currentOs) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt index 582945e5..8ad547bf 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.Immutable * @param weekDays the weeks in this month. */ @Immutable -data class CalendarMonth( +data class CalendarMonth internal constructor( val yearMonth: YearMonth, val weekDays: List>, ) { diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt index dfd350ea..64095837 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.Immutable * @param days the days in this week. */ @Immutable -data class Week(val days: List) { +data class Week internal constructor(val days: List) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt index c95e79a6..04f02f5c 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt @@ -2,6 +2,20 @@ package calendar.core import androidx.compose.runtime.Immutable import kotlinx.datetime.Month +import kotlinx.datetime.number @Immutable -data class YearMonth(val year: Int, val month: Month) +data class YearMonth(val year: Int, val month: Month) : Comparable { + /** + * Same as java.time.YearMonth.compareTo() + */ + override fun compareTo(other: YearMonth): Int { + var cmp = (year - other.year) + if (cmp == 0) { + cmp = (month.number - other.month.number) + } + return cmp + } + + companion object +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt new file mode 100644 index 00000000..b512c88c --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt @@ -0,0 +1,21 @@ +package calendar.data + +/** + * Basically [MutableMap.getOrPut] but allows us read the map + * in multiple places without calling `getOrPut` everywhere. + */ +class DataStore( + private val store: MutableMap = HashMap(), + private val create: (offset: Int) -> V, +) : MutableMap by store { + override fun get(key: Int): V { + val value = store[key] + return if (value == null) { + val data = create(key) + put(key, data) + data + } else { + value + } + } +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt index 80387082..eff7521c 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt @@ -1,14 +1,17 @@ package calendar.data import calendar.core.YearMonth +import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.DayOfWeek import kotlinx.datetime.LocalDate +import kotlinx.datetime.TimeZone import kotlinx.datetime.daysUntil import kotlinx.datetime.isoDayNumber import kotlinx.datetime.minus import kotlinx.datetime.monthsUntil import kotlinx.datetime.plus +import kotlinx.datetime.toLocalDateTime /** * Returns the days of week values such that the desired @@ -39,25 +42,34 @@ val YearMonth.nextMonth: YearMonth val YearMonth.previousMonth: YearMonth get() = this.minusMonths(1) -internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) +fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) -internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) +fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) -internal fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeUnit.MONTH) +fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeUnit.MONTH) -internal fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) +fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) -internal fun YearMonth.plusMonths(value: Int): YearMonth = atStartOfMonth().plusMonths(value).yearMonth +fun YearMonth.plusMonths(value: Int): YearMonth = + atStartOfMonth().plusMonths(value).yearMonth -internal fun YearMonth.minusMonths(value: Int): YearMonth = atStartOfMonth().minusMonths(value).yearMonth +fun YearMonth.minusMonths(value: Int): YearMonth = + atStartOfMonth().minusMonths(value).yearMonth -internal fun YearMonth.lengthOfMonth(): Int { +fun YearMonth.lengthOfMonth(): Int { val thisMonthStart = atStartOfMonth() val nextMonthStart = thisMonthStart.plusMonths(1) return thisMonthStart.daysUntil(nextMonthStart) } -internal fun YearMonth.monthsUntil(other: YearMonth): Int = atStartOfMonth().monthsUntil(other.atStartOfMonth()) +// TODO KMP Maybe restrict to group +val YearMonth.Companion.current: YearMonth + get() = Clock.System.now() + .toLocalDateTime(TimeZone.currentSystemDefault()) + .date.yearMonth + +internal fun YearMonth.monthsUntil(other: YearMonth): Int = + atStartOfMonth().monthsUntil(other.atStartOfMonth()) // E.g DayOfWeek.SATURDAY.daysUntil(DayOfWeek.TUESDAY) = 3 internal fun DayOfWeek.daysUntil(other: DayOfWeek) = (7 + (other.isoDayNumber - isoDayNumber)) % 7 diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt new file mode 100644 index 00000000..844d4709 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt @@ -0,0 +1,16 @@ +package calendar.data + +import calendar.core.YearMonth +import kotlinx.datetime.LocalDate + +fun checkDateRange(startMonth: YearMonth, endMonth: YearMonth) { + check(endMonth >= startMonth) { + "startMonth: $startMonth is greater than endMonth: $endMonth" + } +} + +fun checkDateRange(startDate: LocalDate, endDate: LocalDate) { + check(endDate >= startDate) { + "startDate: $startDate is greater than endDate: $endDate" + } +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt new file mode 100644 index 00000000..e648dc24 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt @@ -0,0 +1,9 @@ +package calendar.data + +import androidx.compose.runtime.Immutable + +@Immutable +internal class VisibleItemState( + val firstVisibleItemIndex: Int = 0, + val firstVisibleItemScrollOffset: Int = 0, +) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt new file mode 100644 index 00000000..cb64736b --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt @@ -0,0 +1,199 @@ +package calendar.ui + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import calendar.core.CalendarDay +import calendar.core.CalendarMonth +import calendar.ui.CalendarDefaults.flingBehavior + +/** + * A horizontally scrolling calendar. + * + * @param modifier the modifier to apply to this calendar. + * @param state the state object to be used to control or observe the calendar's properties. + * Examples: `startMonth`, `endMonth`, `firstDayOfWeek`, `firstVisibleMonth`, `outDateStyle`. + * @param calendarScrollPaged the scrolling behavior of the calendar. When `true`, the calendar will + * snap to the nearest month after a scroll or swipe action. When `false`, the calendar scrolls normally. + * @param userScrollEnabled whether the scrolling via the user gestures or accessibility actions + * is allowed. You can still scroll programmatically using the state even when it is disabled. + * @param reverseLayout reverse the direction of scrolling and layout. When `true`, months will be + * composed from the end to the start and [CalendarState.startMonth] will be located at the end. + * @param contentPadding a padding around the whole calendar. This will add padding for the + * content after it has been clipped, which is not possible via [modifier] param. You can use it + * to add a padding before the first month or after the last one. If you want to add a spacing + * between each month use the [monthContainer] composable. + * @param contentHeightMode Determines how the height of the day content is calculated. + * @param dayContent a composable block which describes the day content. + * @param monthHeader a composable block which describes the month header content. The header is + * placed above each month on the calendar. + * @param monthBody a composable block which describes the month body content. This is the container + * where all the month days are placed, excluding the header and footer. This is useful if you + * want to customize the day container, for example, with a background color or other effects. + * The actual body content is provided in the block and must be called after your desired + * customisations are rendered. + * @param monthFooter a composable block which describes the month footer content. The footer is + * placed below each month on the calendar. + * @param monthContainer a composable block which describes the entire month content. This is the + * container where all the month contents are placed (header => days => footer). This is useful if + * you want to customize the month container, for example, with a background color or other effects. + * The actual container content is provided in the block and must be called after your desired + * customisations are rendered. + */ +@Composable +fun HorizontalCalendar( + modifier: Modifier = Modifier, + state: CalendarState = rememberCalendarState(), + calendarScrollPaged: Boolean = true, + userScrollEnabled: Boolean = true, + reverseLayout: Boolean = false, + contentPadding: PaddingValues = PaddingValues(0.dp), + contentHeightMode: ContentHeightMode = ContentHeightMode.Wrap, + dayContent: @Composable BoxScope.(CalendarDay) -> Unit, + monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, + monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)? = null, + monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, + monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)? = null, +) = Calendar( + modifier = modifier, + state = state, + calendarScrollPaged = calendarScrollPaged, + userScrollEnabled = userScrollEnabled, + isHorizontal = true, + reverseLayout = reverseLayout, + contentHeightMode = contentHeightMode, + dayContent = dayContent, + monthHeader = monthHeader, + monthBody = monthBody, + monthFooter = monthFooter, + monthContainer = monthContainer, + contentPadding = contentPadding, +) + +/** + * A vertically scrolling calendar. + * + * @param modifier the modifier to apply to this calendar. + * @param state the state object to be used to control or observe the calendar's properties. + * Examples: `startMonth`, `endMonth`, `firstDayOfWeek`, `firstVisibleMonth`, `outDateStyle`. + * @param calendarScrollPaged the scrolling behavior of the calendar. When `true`, the calendar will + * snap to the nearest month after a scroll or swipe action. When `false`, the calendar scrolls normally. + * @param userScrollEnabled whether the scrolling via the user gestures or accessibility actions + * is allowed. You can still scroll programmatically using the state even when it is disabled. + * @param reverseLayout reverse the direction of scrolling and layout. When `true`, months will be + * composed from the end to the start and [CalendarState.startMonth] will be located at the end. + * @param contentPadding a padding around the whole calendar. This will add padding for the + * content after it has been clipped, which is not possible via [modifier] param. You can use it + * to add a padding before the first month or after the last one. If you want to add a spacing + * between each month use the [monthContainer] composable. + * @param contentHeightMode Determines how the height of the day content is calculated. + * @param dayContent a composable block which describes the day content. + * @param monthHeader a composable block which describes the month header content. The header is + * placed above each month on the calendar. + * @param monthBody a composable block which describes the month body content. This is the container + * where all the month days are placed, excluding the header and footer. This is useful if you + * want to customize the day container, for example, with a background color or other effects. + * The actual body content is provided in the block and must be called after your desired + * customisations are rendered. + * @param monthFooter a composable block which describes the month footer content. The footer is + * placed below each month on the calendar. + * @param monthContainer a composable block which describes the entire month content. This is the + * container where all the month contents are placed (header => days => footer). This is useful if + * you want to customize the month container, for example, with a background color or other effects. + * The actual container content is provided in the block and must be called after your desired + * customisations are rendered. + */ +@Composable +fun VerticalCalendar( + modifier: Modifier = Modifier, + state: CalendarState = rememberCalendarState(), + calendarScrollPaged: Boolean = false, + userScrollEnabled: Boolean = true, + reverseLayout: Boolean = false, + contentPadding: PaddingValues = PaddingValues(0.dp), + contentHeightMode: ContentHeightMode = ContentHeightMode.Wrap, + dayContent: @Composable BoxScope.(CalendarDay) -> Unit, + monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, + monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)? = null, + monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, + monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)? = null, +) = Calendar( + modifier = modifier, + state = state, + calendarScrollPaged = calendarScrollPaged, + userScrollEnabled = userScrollEnabled, + isHorizontal = false, + reverseLayout = reverseLayout, + contentHeightMode = contentHeightMode, + dayContent = dayContent, + monthHeader = monthHeader, + monthBody = monthBody, + monthFooter = monthFooter, + monthContainer = monthContainer, + contentPadding = contentPadding, +) + +@Composable +private fun Calendar( + modifier: Modifier, + state: CalendarState, + calendarScrollPaged: Boolean, + userScrollEnabled: Boolean, + isHorizontal: Boolean, + reverseLayout: Boolean, + contentPadding: PaddingValues, + contentHeightMode: ContentHeightMode, + dayContent: @Composable BoxScope.(CalendarDay) -> Unit, + monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)?, + monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)?, + monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)?, + monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)?, +) { + if (isHorizontal) { + LazyRow( + modifier = modifier, + state = state.listState, + flingBehavior = flingBehavior(calendarScrollPaged, state.listState), + userScrollEnabled = userScrollEnabled, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + ) { + CalendarMonths( + monthCount = state.calendarInfo.indexCount, + monthData = { offset -> state.store[offset] }, + contentHeightMode = contentHeightMode, + dayContent = dayContent, + monthHeader = monthHeader, + monthBody = monthBody, + monthFooter = monthFooter, + monthContainer = monthContainer, + ) + } + } else { + LazyColumn( + modifier = modifier, + state = state.listState, + flingBehavior = flingBehavior(calendarScrollPaged, state.listState), + userScrollEnabled = userScrollEnabled, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + ) { + CalendarMonths( + monthCount = state.calendarInfo.indexCount, + monthData = { offset -> state.store[offset] }, + contentHeightMode = contentHeightMode, + dayContent = dayContent, + monthHeader = monthHeader, + monthBody = monthBody, + monthFooter = monthFooter, + monthContainer = monthContainer, + ) + } + } +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt new file mode 100644 index 00000000..64fb4d5a --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt @@ -0,0 +1,54 @@ +package calendar.ui + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollableDefaults +import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider +import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout +import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember + +internal object CalendarDefaults { + /** + * The default implementation in [rememberSnapFlingBehavior] snaps to the center of the layout + * but we want to snap to the start. For example, in a vertical calendar, when the layout size + * is larger than the item size(e.g two or more visible months), we don't want the item's + * center to be at the center of the layout when it snaps, instead we want the item's top + * to be at the top of the layout. + */ + @OptIn(ExperimentalFoundationApi::class) + @Composable + private fun pagedFlingBehavior(state: LazyListState): FlingBehavior { + val snappingLayout = remember(state) { + val provider = SnapLayoutInfoProvider(state, CalendarSnapPositionInLayout()) + CalendarSnapLayoutInfoProvider(provider) + } + return rememberSnapFlingBehavior(snappingLayout) + } + + @Composable + private fun continuousFlingBehavior(): FlingBehavior = ScrollableDefaults.flingBehavior() + + @Composable + fun flingBehavior(isPaged: Boolean, state: LazyListState): FlingBehavior { + return if (isPaged) pagedFlingBehavior(state) else continuousFlingBehavior() + } +} + +@ExperimentalFoundationApi +@Suppress("FunctionName") +private fun CalendarSnapLayoutInfoProvider( + snapLayoutInfoProvider: SnapLayoutInfoProvider, +): SnapLayoutInfoProvider = object : SnapLayoutInfoProvider by snapLayoutInfoProvider { + /** + * In compose 1.3, the default was single page snapping (zero), but this changed + * in compose 1.4 to decayed page snapping which is not great for calendar usage. + */ + override fun calculateApproachOffset(initialVelocity: Float): Float = 0f +} + +@OptIn(ExperimentalFoundationApi::class) +@Suppress("FunctionName") +private fun CalendarSnapPositionInLayout() = SnapPositionInLayout { _, _, _, _, _ -> 0 } diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt new file mode 100644 index 00000000..7e5bf209 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt @@ -0,0 +1,12 @@ +package calendar.ui + +import androidx.compose.runtime.Immutable +import calendar.core.OutDateStyle +import kotlinx.datetime.DayOfWeek + +@Immutable +internal data class CalendarInfo( + val indexCount: Int, + private val firstDayOfWeek: DayOfWeek? = null, + private val outDateStyle: OutDateStyle? = null, +) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt new file mode 100644 index 00000000..8e8c70b3 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt @@ -0,0 +1,33 @@ +package calendar.ui + +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListLayoutInfo +import calendar.core.CalendarMonth + +/** + * Contains useful information about the currently displayed layout state of the calendar. + * For example you can get the list of currently displayed months. + * + * Use [CalendarState.layoutInfo] to retrieve this. + * @see LazyListLayoutInfo + */ +class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> CalendarMonth) : + LazyListLayoutInfo by info { + /** + * The list of [CalendarItemInfo] representing all the currently visible months. + */ + val visibleMonthsInfo: List + get() = visibleItemsInfo.map { + CalendarItemInfo(it, month(it.index)) + } +} + +/** + * Contains useful information about an individual [CalendarMonth] on the calendar. + * + * @param month The month in the list. + * + * @see CalendarLayoutInfo + * @see LazyListItemInfo + */ +class CalendarItemInfo(info: LazyListItemInfo, val month: CalendarMonth) : LazyListItemInfo by info diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt new file mode 100644 index 00000000..7d7189ea --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt @@ -0,0 +1,90 @@ +package calendar.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import calendar.core.CalendarDay +import calendar.core.CalendarMonth + +@Suppress("FunctionName") +internal fun LazyListScope.CalendarMonths( + monthCount: Int, + monthData: (offset: Int) -> CalendarMonth, + contentHeightMode: ContentHeightMode, + dayContent: @Composable BoxScope.(CalendarDay) -> Unit, + monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)?, + monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)?, + monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)?, + monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)?, +) { + items( + count = monthCount, + key = { offset -> monthData(offset).yearMonth }, + ) { offset -> + val month = monthData(offset) + val fillHeight = when (contentHeightMode) { + ContentHeightMode.Wrap -> false + ContentHeightMode.Fill -> true + } + val hasContainer = monthContainer != null + monthContainer.or(defaultMonthContainer)(month) { + Column( + modifier = Modifier + .then(if (hasContainer) Modifier.fillMaxWidth() else Modifier.fillParentMaxWidth()) + .then( + if (fillHeight) { + if (hasContainer) Modifier.fillMaxHeight() else Modifier.fillParentMaxHeight() + } else { + Modifier.wrapContentHeight() + }, + ), + ) { + monthHeader?.invoke(this, month) + monthBody.or(defaultMonthBody)(month) { + Column( + modifier = Modifier + .fillMaxWidth() + .then(if (fillHeight) Modifier.weight(1f) else Modifier.wrapContentHeight()), + ) { + for (week in month.weekDays) { + Row( + modifier = Modifier + .fillMaxWidth() + .then(if (fillHeight) Modifier.weight(1f) else Modifier.wrapContentHeight()), + ) { + for (day in week) { + Box( + modifier = Modifier + .weight(1f) + .clipToBounds(), + ) { + dayContent(day) + } + } + } + } + } + } + monthFooter?.invoke(this, month) + } + } + } +} + +private val defaultMonthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit) = + { _, container -> container() } + +private val defaultMonthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit) = + { _, content -> content() } + +private fun T?.or(default: T) = this ?: default diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt new file mode 100644 index 00000000..4f2bc700 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt @@ -0,0 +1,307 @@ +package calendar.ui + +import androidx.compose.foundation.MutatePriority +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.foundation.lazy.LazyListLayoutInfo +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.setValue +import calendar.core.CalendarMonth +import calendar.core.OutDateStyle +import calendar.core.YearMonth +import calendar.data.DataStore +import calendar.data.VisibleItemState +import calendar.data.checkDateRange +import calendar.data.current +import calendar.data.getCalendarMonthData +import calendar.data.getMonthIndex +import calendar.data.getMonthIndicesCount +import kotlinx.datetime.DayOfWeek + +/** + * Creates a [CalendarState] that is remembered across compositions. + * + * @param startMonth the initial value for [CalendarState.startMonth] + * @param endMonth the initial value for [CalendarState.endMonth] + * @param firstDayOfWeek the initial value for [CalendarState.firstDayOfWeek] + * @param firstVisibleMonth the initial value for [CalendarState.firstVisibleMonth] + * @param outDateStyle the initial value for [CalendarState.outDateStyle] + */ +@Composable +fun rememberCalendarState( + startMonth: YearMonth = YearMonth.current, + endMonth: YearMonth = startMonth, + firstVisibleMonth: YearMonth = startMonth, + // TODO KMP + firstDayOfWeek: DayOfWeek = DayOfWeek.MONDAY, + outDateStyle: OutDateStyle = OutDateStyle.EndOfRow, +): CalendarState { + // TODO KMP +// return rememberSaveable( +// inputs = arrayOf( +// startMonth, +// endMonth, +// firstVisibleMonth, +// firstDayOfWeek, +// outDateStyle, +// ), +// saver = CalendarState.Saver, +// ) { +// CalendarState( +// startMonth = startMonth, +// endMonth = endMonth, +// firstDayOfWeek = firstDayOfWeek, +// firstVisibleMonth = firstVisibleMonth, +// outDateStyle = outDateStyle, +// visibleItemState = null, +// ) +// } + return remember { + CalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstDayOfWeek = firstDayOfWeek, + firstVisibleMonth = firstVisibleMonth, + outDateStyle = outDateStyle, + visibleItemState = null, + ) + } +} + +/** + * A state object that can be hoisted to control and observe calendar properties. + * + * This should be created via [rememberCalendarState]. + * + * @param startMonth the first month on the calendar. + * @param endMonth the last month on the calendar. + * @param firstDayOfWeek the first day of week on the calendar. + * @param firstVisibleMonth the initial value for [CalendarState.firstVisibleMonth] + * @param outDateStyle the preferred style for out date generation. + */ +@Stable +class CalendarState internal constructor( + startMonth: YearMonth, + endMonth: YearMonth, + firstDayOfWeek: DayOfWeek, + firstVisibleMonth: YearMonth, + outDateStyle: OutDateStyle, + visibleItemState: VisibleItemState?, +) : ScrollableState { + /** Backing state for [startMonth] */ + private var _startMonth by mutableStateOf(startMonth) + + /** The first month on the calendar. */ + var startMonth: YearMonth + get() = _startMonth + set(value) { + if (value != startMonth) { + _startMonth = value + monthDataChanged() + } + } + + /** Backing state for [endMonth] */ + private var _endMonth by mutableStateOf(endMonth) + + /** The last month on the calendar. */ + var endMonth: YearMonth + get() = _endMonth + set(value) { + if (value != endMonth) { + _endMonth = value + monthDataChanged() + } + } + + /** Backing state for [firstDayOfWeek] */ + private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) + + /** The first day of week on the calendar. */ + var firstDayOfWeek: DayOfWeek + get() = _firstDayOfWeek + set(value) { + if (value != firstDayOfWeek) { + _firstDayOfWeek = value + monthDataChanged() + } + } + + /** Backing state for [outDateStyle] */ + private var _outDateStyle by mutableStateOf(outDateStyle) + + /** The preferred style for out date generation. */ + var outDateStyle: OutDateStyle + get() = _outDateStyle + set(value) { + if (value != outDateStyle) { + _outDateStyle = value + monthDataChanged() + } + } + + /** + * The first month that is visible. + * + * @see [lastVisibleMonth] + */ + val firstVisibleMonth: CalendarMonth by derivedStateOf { + store[listState.firstVisibleItemIndex] + } + + /** + * The last month that is visible. + * + * @see [firstVisibleMonth] + */ + val lastVisibleMonth: CalendarMonth by derivedStateOf { + store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] + } + + /** + * The object of [CalendarLayoutInfo] calculated during the last layout pass. For example, + * you can use it to calculate what items are currently visible. + * + * Note that this property is observable and is updated after every scroll or remeasure. + * If you use it in the composable function it will be recomposed on every change causing + * potential performance issues including infinity recomposition loop. + * Therefore, avoid using it in the composition. + * + * If you need to use it in the composition then consider wrapping the calculation into a + * derived state in order to only have recompositions when the derived value changes. + * See Example6Page in the sample app for usage. + * + * If you want to run some side effects like sending an analytics event or updating a state + * based on this value consider using "snapshotFlow". + * + * see [LazyListLayoutInfo] + */ + val layoutInfo: CalendarLayoutInfo + get() = CalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } + + /** + * [InteractionSource] that will be used to dispatch drag events when this + * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in + * progress, use [isScrollInProgress]. + */ + val interactionSource: InteractionSource + get() = listState.interactionSource + + internal val listState = LazyListState( + firstVisibleItemIndex = visibleItemState?.firstVisibleItemIndex + ?: getScrollIndex(firstVisibleMonth) ?: 0, + firstVisibleItemScrollOffset = visibleItemState?.firstVisibleItemScrollOffset ?: 0, + ) + + internal var calendarInfo by mutableStateOf(CalendarInfo(indexCount = 0)) + + internal val store = DataStore { offset -> + getCalendarMonthData( + startMonth = this.startMonth, + offset = offset, + firstDayOfWeek = this.firstDayOfWeek, + outDateStyle = this.outDateStyle, + ).calendarMonth + } + + init { + monthDataChanged() // Update monthIndexCount initially. + } + + private fun monthDataChanged() { + store.clear() + checkDateRange(startMonth, endMonth) + // Read the firstDayOfWeek and outDateStyle properties to ensure recomposition + // even though they are unused in the CalendarInfo. Alternatively, we could use + // mutableStateMapOf() as the backing store for DataStore() to ensure recomposition + // but not sure how compose handles recomposition of a lazy list that reads from + // such map when an entry unrelated to the visible indices changes. + calendarInfo = CalendarInfo( + indexCount = getMonthIndicesCount(startMonth, endMonth), + firstDayOfWeek = firstDayOfWeek, + outDateStyle = outDateStyle, + ) + } + + /** + * Instantly brings the [month] to the top of the viewport. + * + * @param month the month to which to scroll. Must be within the + * range of [startMonth] and [endMonth]. + * + * @see [animateScrollToMonth] + */ + suspend fun scrollToMonth(month: YearMonth) { + listState.scrollToItem(getScrollIndex(month) ?: return) + } + + /** + * Animate (smooth scroll) to the given [month]. + * + * @param month the month to which to scroll. Must be within the + * range of [startMonth] and [endMonth]. + */ + suspend fun animateScrollToMonth(month: YearMonth) { + listState.animateScrollToItem(getScrollIndex(month) ?: return) + } + + private fun getScrollIndex(month: YearMonth): Int? { + if (month !in startMonth..endMonth) { + // TODO KMP +// Log.d("CalendarState", "Attempting to scroll out of range: $month") + return null + } + return getMonthIndex(startMonth, month) + } + + /** + * Whether this [ScrollableState] is currently scrolling by gesture, fling or programmatically. + */ + override val isScrollInProgress: Boolean + get() = listState.isScrollInProgress + + override fun dispatchRawDelta(delta: Float): Float = listState.dispatchRawDelta(delta) + + override suspend fun scroll( + scrollPriority: MutatePriority, + block: suspend ScrollScope.() -> Unit, + ) = listState.scroll(scrollPriority, block) + + companion object { + internal val Saver: Saver = listSaver( + save = { + val visibleItemState = VisibleItemState( + firstVisibleItemIndex = it.listState.firstVisibleItemIndex, + firstVisibleItemScrollOffset = it.listState.firstVisibleItemScrollOffset, + ) + listOf( + it.startMonth, + it.endMonth, + it.firstVisibleMonth.yearMonth, + it.firstDayOfWeek, + it.outDateStyle, + visibleItemState, + ) + }, + restore = { + CalendarState( + startMonth = it[0] as YearMonth, + endMonth = it[1] as YearMonth, + firstVisibleMonth = it[2] as YearMonth, + firstDayOfWeek = it[3] as DayOfWeek, + outDateStyle = it[4] as OutDateStyle, + visibleItemState = it[5] as VisibleItemState, + ) + }, + ) + } +} diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt new file mode 100644 index 00000000..76f6bfd4 --- /dev/null +++ b/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt @@ -0,0 +1,28 @@ +package calendar.ui + +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.ui.Modifier + +/** + * Determines how the height of the day content is calculated. + */ +enum class ContentHeightMode { + /** + * The day container will wrap its height. This allows you to + * use [Modifier.aspectRatio] if you want square day content + * or [Modifier.height] if you want a specific height value + * for the day content. + */ + Wrap, + + /** + * The days in each month will spread to fill the parent's height after + * any available header and footer content height has been accounted for. + * This allows you to use [Modifier.fillMaxHeight] for the day content + * height. With this option, your Calendar composable should also + * be created with [Modifier.fillMaxHeight] or [Modifier.height]. + */ + Fill, +} diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts index fa348fd0..65b5082e 100644 --- a/compose-multiplatform/sample/build.gradle.kts +++ b/compose-multiplatform/sample/build.gradle.kts @@ -112,11 +112,11 @@ android { compose.desktop { application { - mainClass = "MainKt" + mainClass = "Calendar Sample" nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) - packageName = "com.kizitonwose.calendarx" + packageName = "com.kizitonwose.calendar" packageVersion = "1.0.0" } } diff --git a/compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt b/compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt deleted file mode 100644 index 4f3ea051..00000000 --- a/compose-multiplatform/sample/src/androidMain/kotlin/Platform.android.kt +++ /dev/null @@ -1,7 +0,0 @@ -import android.os.Build - -class AndroidPlatform : Platform { - override val name: String = "Android ${Build.VERSION.SDK_INT}" -} - -actual fun getPlatform(): Platform = AndroidPlatform() \ No newline at end of file diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt index 589e1e01..8c387b8d 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -1,38 +1,214 @@ -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material3.Button +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import oguafo.compose_multiplatform.sample.generated.resources.Res -import oguafo.compose_multiplatform.sample.generated.resources.compose_multiplatform -import org.jetbrains.compose.resources.painterResource +import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.capitalize +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import calendar.core.CalendarDay +import calendar.core.CalendarMonth +import calendar.core.DayPosition +import calendar.core.YearMonth +import calendar.data.current +import calendar.data.daysOfWeek +import calendar.data.minusMonths +import calendar.data.nextMonth +import calendar.data.plusMonths +import calendar.data.previousMonth +import calendar.ui.CalendarLayoutInfo +import calendar.ui.CalendarState +import calendar.ui.HorizontalCalendar +import calendar.ui.rememberCalendarState +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.launch +import kotlinx.datetime.DayOfWeek import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview fun App() { MaterialTheme { - var showContent by remember { mutableStateOf(false) } - Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - Button(onClick = { showContent = !showContent }) { - Text("Click me!") - } - AnimatedVisibility(showContent) { - val greeting = remember { Greeting().greet() } - Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - Image(painterResource(Res.drawable.compose_multiplatform), null) - Text("Compose: $greeting") + Example1Page() + } +} + +@Composable +fun Example1Page(adjacentMonths: Int = 500) { + val currentMonth = remember { YearMonth.current } + val startMonth = remember { currentMonth.minusMonths(adjacentMonths) } + val endMonth = remember { currentMonth.plusMonths(adjacentMonths) } + val selections = remember { mutableStateListOf() } + val daysOfWeek = remember { daysOfWeek(DayOfWeek.SUNDAY) } + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first(), + ) + val coroutineScope = rememberCoroutineScope() + val visibleMonth = rememberFirstMostVisibleMonth(state, viewportPercent = 90f) + SimpleCalendarTitle( + modifier = Modifier.padding(vertical = 10.dp, horizontal = 8.dp), + currentMonth = visibleMonth.yearMonth, + goToPrevious = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) } - } + }, + goToNext = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) + } + }, + ) + HorizontalCalendar( + modifier = Modifier.testTag("Calendar"), + state = state, + dayContent = { day -> + Day(day, isSelected = selections.contains(day)) { clicked -> + if (selections.contains(clicked)) { + selections.remove(clicked) + } else { + selections.add(clicked) + } + } + }, + monthHeader = { + MonthHeader(daysOfWeek = daysOfWeek) + }, + ) + } +} + +@Composable +private fun MonthHeader(daysOfWeek: List) { + Row( + modifier = Modifier + .fillMaxWidth() + .testTag("MonthHeader"), + ) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + fontSize = 15.sp, + text = dayOfWeek.name.lowercase().capitalize(Locale.current).take(3), + fontWeight = FontWeight.Medium, + ) } } } + +@Composable +private fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { + Box( + modifier = Modifier + .aspectRatio(1f) // This is important for square-sizing! + .testTag("MonthDay") + .padding(6.dp) + .clip(CircleShape) + .background(color = if (isSelected) Color(0xFFFCCA3E) else Color.Transparent) + // Disable clicks on inDates/outDates + .clickable( + enabled = day.position == DayPosition.MonthDate, + showRipple = !isSelected, + onClick = { onClick(day) }, + ), + contentAlignment = Alignment.Center, + ) { + val textColor = when (day.position) { + // Color.Unspecified will use the default text color from the current theme + DayPosition.MonthDate -> if (isSelected) Color.White else Color.Unspecified + DayPosition.InDate, DayPosition.OutDate -> Color(0xFFBEBEBE) + } + Text( + text = day.date.dayOfMonth.toString(), + color = textColor, + fontSize = 14.sp, + ) + } +} + +@Preview +@Composable +private fun Example1Preview() { + Example1Page() +} + +@Composable +fun rememberFirstMostVisibleMonth( + state: CalendarState, + viewportPercent: Float = 50f, +): CalendarMonth { + val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } + LaunchedEffect(state) { + snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } + .filterNotNull() + .collect { month -> visibleMonth.value = month } + } + return visibleMonth.value +} + +private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { + return if (visibleMonthsInfo.isEmpty()) { + null + } else { + val viewportSize = (viewportEndOffset + viewportStartOffset) * viewportPercent / 100f + visibleMonthsInfo.firstOrNull { itemInfo -> + if (itemInfo.offset < 0) { + itemInfo.offset + itemInfo.size >= viewportSize + } else { + itemInfo.size - itemInfo.offset >= viewportSize + } + }?.month + } +} + +fun Modifier.clickable( + enabled: Boolean = true, + showRipple: Boolean = true, + onClickLabel: String? = null, + role: Role? = null, + onClick: () -> Unit, +): Modifier = composed { + clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = if (showRipple) LocalIndication.current else null, + enabled = enabled, + onClickLabel = onClickLabel, + role = role, + onClick = onClick, + ) +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt deleted file mode 100644 index 87ca3fff..00000000 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Platform.kt +++ /dev/null @@ -1,5 +0,0 @@ -interface Platform { - val name: String -} - -expect fun getPlatform(): Platform \ No newline at end of file diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt b/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt new file mode 100644 index 00000000..6adcbde3 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt @@ -0,0 +1,83 @@ +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.capitalize +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import calendar.core.YearMonth + +@Composable +fun SimpleCalendarTitle( + modifier: Modifier, + currentMonth: YearMonth, + goToPrevious: () -> Unit, + goToNext: () -> Unit, +) { + Row( + modifier = modifier.height(40.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + CalendarNavigationIcon( + icon = Icons.AutoMirrored.Filled.KeyboardArrowLeft, + contentDescription = "Previous", + onClick = goToPrevious, + ) + Text( + modifier = Modifier + .weight(1f) + .testTag("MonthTitle"), + text = "${currentMonth.month.name.lowercase().capitalize(Locale.current)} ${currentMonth.year}", + fontSize = 22.sp, + textAlign = TextAlign.Center, + fontWeight = FontWeight.Medium, + ) + CalendarNavigationIcon( + icon = Icons.AutoMirrored.Filled.KeyboardArrowRight, + contentDescription = "Next", + onClick = goToNext, + ) + } +} + +@Composable +private fun CalendarNavigationIcon( + icon: ImageVector, + contentDescription: String, + onClick: () -> Unit, +) = Box( + modifier = Modifier + .fillMaxHeight() + .aspectRatio(1f) + .clip(shape = CircleShape) + .clickable(role = Role.Button, onClick = onClick), +) { + Icon( + modifier = Modifier + .fillMaxSize() + .padding(4.dp) + .align(Alignment.Center), + imageVector = icon, + contentDescription = contentDescription, + ) +} diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt deleted file mode 100644 index 5bf6fade..00000000 --- a/compose-multiplatform/sample/src/desktopMain/kotlin/Platform.jvm.kt +++ /dev/null @@ -1,5 +0,0 @@ -class JVMPlatform : Platform { - override val name: String = "Java ${System.getProperty("java.version")}" -} - -actual fun getPlatform(): Platform = JVMPlatform() diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt index 239593a3..263a957c 100644 --- a/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt +++ b/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt @@ -4,8 +4,8 @@ import androidx.compose.ui.window.application fun main() = application { Window( onCloseRequest = ::exitApplication, - title = "KotlinProject", + title = "Calendar Sample", ) { App() } -} \ No newline at end of file +} diff --git a/compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt b/compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt deleted file mode 100644 index 9bb8535a..00000000 --- a/compose-multiplatform/sample/src/iosMain/kotlin/Platform.ios.kt +++ /dev/null @@ -1,7 +0,0 @@ -import platform.UIKit.UIDevice - -class IOSPlatform : Platform { - override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion -} - -actual fun getPlatform(): Platform = IOSPlatform() diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt deleted file mode 100644 index 28dcfdea..00000000 --- a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Platform.wasmJs.kt +++ /dev/null @@ -1,5 +0,0 @@ -class WasmPlatform : Platform { - override val name: String = "Web with Kotlin/Wasm" -} - -actual fun getPlatform(): Platform = WasmPlatform() diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index 29967417..fe2a6465 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { implementation(project(":data")) implementation(libs.kotlin.stdlib) - implementation(libs.compose.ui) + implementation(libs.compose.ui.ui) implementation(libs.compose.ui.tooling) implementation(libs.compose.foundation) implementation(libs.compose.runtime) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bf647667..95bb8d50 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ androidx-test-rules = { module = "androidx.test:rules", version = "1.5.0" } androidx-test-junit = { module = "androidx.test.ext:junit", version = "1.1.5" } test-junit = { module = "junit:junit", version = "4.13.2" } -compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } +compose-ui-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "compose" } diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index c2f2c8e2..ee357143 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -55,7 +55,7 @@ dependencies { implementation(libs.androidx.cardview) implementation(libs.material.view) - implementation(libs.compose.ui) + implementation(libs.compose.ui.ui) implementation(libs.compose.ui.tooling) implementation(libs.compose.foundation) implementation(libs.compose.runtime) diff --git a/settings.gradle.kts b/settings.gradle.kts index 42f445bd..96852789 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,7 +7,7 @@ pluginManagement { } @Suppress("UnstableApiUsage") dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) +// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() From 90eb22c0304e35f86e281e14331b063fa584b839 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sat, 29 Jun 2024 07:40:43 +0200 Subject: [PATCH 04/21] Implement month calendar --- .../androidMain/kotlin/Platform.android.kt | 7 - .../lib/src/commonMain/kotlin/Platform.kt | 5 - .../src/desktopMain/kotlin/Platform.jvm.kt | 5 - .../lib/src/iosMain/kotlin/Platform.ios.kt | 7 - .../src/wasmJsMain/kotlin/Platform.wasmJs.kt | 5 - .../{lib => library}/.gitignore | 0 .../{lib => library}/build.gradle.kts | 28 +- .../src/androidMain/AndroidManifest.xml | 0 .../kizitonwose/calendar/compose}/Calendar.kt | 8 +- .../calendar/compose}/CalendarDefaults.kt | 2 +- .../calendar/compose}/CalendarInfo.kt | 4 +- .../calendar/compose}/CalendarLayoutInfo.kt | 4 +- .../calendar/compose}/CalendarMonths.kt | 7 +- .../calendar/compose}/CalendarState.kt | 73 +- .../calendar/compose}/ContentHeightMode.kt | 2 +- .../kizitonwose}/calendar/core/CalendarDay.kt | 4 +- .../calendar/core/CalendarMonth.kt | 4 +- .../kizitonwose}/calendar/core/DayPosition.kt | 2 +- .../kizitonwose/calendar/core}/Extensions.kt | 15 +- .../calendar/core/JvmSerializable.kt | 3 + .../calendar/core/OutDateStyle.kt | 2 +- .../com/kizitonwose}/calendar/core/Week.kt | 4 +- .../com/kizitonwose}/calendar/core/WeekDay.kt | 4 +- .../calendar/core/WeekDayPosition.kt | 2 +- .../kizitonwose}/calendar/core/YearMonth.kt | 4 +- .../kizitonwose}/calendar/data/DataStore.kt | 2 +- .../kizitonwose}/calendar/data/MonthData.kt | 28 +- .../com/kizitonwose}/calendar/data/Utils.kt | 4 +- .../calendar/data/VisibleItemState.kt | 2 +- .../kizitonwose/calendar/core/Converters.kt | 5 + .../calendar/core/JvmSerializable.jvm.kt | 3 + .../calendar/core/JvmSerializable.nonJvm.kt | 3 + compose-multiplatform/sample/build.gradle.kts | 2 +- .../sample/src/commonMain/kotlin/App.kt | 37 +- .../sample/src/commonMain/kotlin/Greeting.kt | 7 - .../commonMain/kotlin/SimpleCalendarTitle.kt | 2 +- .../calendar/compose/CalendarState.kt | 12 +- kotlin-js-store/yarn.lock | 3050 +++++++++++++++++ settings.gradle.kts | 2 +- 39 files changed, 3199 insertions(+), 161 deletions(-) delete mode 100644 compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt delete mode 100644 compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt delete mode 100644 compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt delete mode 100644 compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt delete mode 100644 compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt rename compose-multiplatform/{lib => library}/.gitignore (100%) rename compose-multiplatform/{lib => library}/build.gradle.kts (72%) rename compose-multiplatform/{lib => library}/src/androidMain/AndroidManifest.xml (100%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/Calendar.kt (97%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/CalendarDefaults.kt (98%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/CalendarInfo.kt (73%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/CalendarLayoutInfo.kt (91%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/CalendarMonths.kt (95%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/CalendarState.kt (85%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/ui => library/src/commonMain/kotlin/com/kizitonwose/calendar/compose}/ContentHeightMode.kt (95%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/CalendarDay.kt (82%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/CalendarMonth.kt (95%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/DayPosition.kt (94%) rename compose-multiplatform/{lib/src/commonMain/kotlin/calendar/data => library/src/commonMain/kotlin/com/kizitonwose/calendar/core}/Extensions.kt (88%) create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.kt rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/OutDateStyle.kt (93%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/Week.kt (87%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/WeekDay.kt (82%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/WeekDayPosition.kt (94%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/core/YearMonth.kt (86%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/data/DataStore.kt (93%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/data/MonthData.kt (71%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/data/Utils.kt (81%) rename compose-multiplatform/{lib/src/commonMain/kotlin => library/src/commonMain/kotlin/com/kizitonwose}/calendar/data/VisibleItemState.kt (82%) create mode 100644 compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt create mode 100644 compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt create mode 100644 compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt delete mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt create mode 100644 kotlin-js-store/yarn.lock diff --git a/compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt b/compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt deleted file mode 100644 index 1e282d1e..00000000 --- a/compose-multiplatform/lib/src/androidMain/kotlin/Platform.android.kt +++ /dev/null @@ -1,7 +0,0 @@ -import android.os.Build - -class AndroidPlatform : Platform { - override val name: String = "Android ${Build.VERSION.SDK_INT}" -} - -actual fun getPlatform(): Platform = AndroidPlatform() diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt b/compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt deleted file mode 100644 index fc48e12e..00000000 --- a/compose-multiplatform/lib/src/commonMain/kotlin/Platform.kt +++ /dev/null @@ -1,5 +0,0 @@ -interface Platform { - val name: String -} - -expect fun getPlatform(): Platform diff --git a/compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt b/compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt deleted file mode 100644 index 5bf6fade..00000000 --- a/compose-multiplatform/lib/src/desktopMain/kotlin/Platform.jvm.kt +++ /dev/null @@ -1,5 +0,0 @@ -class JVMPlatform : Platform { - override val name: String = "Java ${System.getProperty("java.version")}" -} - -actual fun getPlatform(): Platform = JVMPlatform() diff --git a/compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt b/compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt deleted file mode 100644 index 9bb8535a..00000000 --- a/compose-multiplatform/lib/src/iosMain/kotlin/Platform.ios.kt +++ /dev/null @@ -1,7 +0,0 @@ -import platform.UIKit.UIDevice - -class IOSPlatform : Platform { - override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion -} - -actual fun getPlatform(): Platform = IOSPlatform() diff --git a/compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt b/compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt deleted file mode 100644 index 28dcfdea..00000000 --- a/compose-multiplatform/lib/src/wasmJsMain/kotlin/Platform.wasmJs.kt +++ /dev/null @@ -1,5 +0,0 @@ -class WasmPlatform : Platform { - override val name: String = "Web with Kotlin/Wasm" -} - -actual fun getPlatform(): Platform = WasmPlatform() diff --git a/compose-multiplatform/lib/.gitignore b/compose-multiplatform/library/.gitignore similarity index 100% rename from compose-multiplatform/lib/.gitignore rename to compose-multiplatform/library/.gitignore diff --git a/compose-multiplatform/lib/build.gradle.kts b/compose-multiplatform/library/build.gradle.kts similarity index 72% rename from compose-multiplatform/lib/build.gradle.kts rename to compose-multiplatform/library/build.gradle.kts index f46243c6..c499aa32 100644 --- a/compose-multiplatform/lib/build.gradle.kts +++ b/compose-multiplatform/library/build.gradle.kts @@ -1,6 +1,7 @@ import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { @@ -15,11 +16,13 @@ kotlin { @OptIn(ExperimentalWasmDsl::class) wasmJs { moduleName = "calendar" + browser {} + binaries.library() } androidTarget {} - jvm("desktop") + jvm("jvm") // jvm("desktop") listOf( iosX64(), @@ -32,9 +35,15 @@ kotlin { } } + applyDefaultHierarchyTemplate() + sourceSets { - val desktopMain by getting + val jvmMain by getting + val commonMain by getting + val wasmJsMain by getting + val nativeMain by getting + androidMain.get().dependsOn(jvmMain) androidMain.dependencies { implementation(compose.preview) } @@ -44,12 +53,21 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) - implementation(compose.components.uiToolingPreview) api(libs.kotlinx.datetime) } - desktopMain.dependencies { - implementation(compose.desktop.currentOs) + val nonJvmMain by creating { + dependsOn(commonMain) + nativeMain.dependsOn(this) + wasmJsMain.dependsOn(this) + dependencies {} } + jvmMain.dependencies { +// implementation(compose.desktop.currentOs) + } + } + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") } } diff --git a/compose-multiplatform/lib/src/androidMain/AndroidManifest.xml b/compose-multiplatform/library/src/androidMain/AndroidManifest.xml similarity index 100% rename from compose-multiplatform/lib/src/androidMain/AndroidManifest.xml rename to compose-multiplatform/library/src/androidMain/AndroidManifest.xml diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt similarity index 97% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt index cb64736b..cddad97e 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/Calendar.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt @@ -1,4 +1,4 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.ColumnScope @@ -9,9 +9,9 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import calendar.core.CalendarDay -import calendar.core.CalendarMonth -import calendar.ui.CalendarDefaults.flingBehavior +import com.kizitonwose.calendar.compose.CalendarDefaults.flingBehavior +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth /** * A horizontally scrolling calendar. diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt similarity index 98% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt index 64fb4d5a..3856d891 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarDefaults.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt @@ -1,4 +1,4 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.FlingBehavior diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarInfo.kt similarity index 73% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarInfo.kt index 7e5bf209..52107343 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarInfo.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarInfo.kt @@ -1,7 +1,7 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.runtime.Immutable -import calendar.core.OutDateStyle +import com.kizitonwose.calendar.core.OutDateStyle import kotlinx.datetime.DayOfWeek @Immutable diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt similarity index 91% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt index 8e8c70b3..d78948da 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarLayoutInfo.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt @@ -1,8 +1,8 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.foundation.lazy.LazyListItemInfo import androidx.compose.foundation.lazy.LazyListLayoutInfo -import calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.CalendarMonth /** * Contains useful information about the currently displayed layout state of the calendar. diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarMonths.kt similarity index 95% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarMonths.kt index 7d7189ea..1b246f67 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarMonths.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarMonths.kt @@ -1,4 +1,4 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -13,15 +13,14 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds -import calendar.core.CalendarDay -import calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.CalendarMonth @Suppress("FunctionName") internal fun LazyListScope.CalendarMonths( monthCount: Int, monthData: (offset: Int) -> CalendarMonth, contentHeightMode: ContentHeightMode, - dayContent: @Composable BoxScope.(CalendarDay) -> Unit, + dayContent: @Composable BoxScope.(com.kizitonwose.calendar.core.CalendarDay) -> Unit, monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)?, monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)?, monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)?, diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt similarity index 85% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt index 4f2bc700..8d46100f 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/CalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt @@ -1,4 +1,4 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.foundation.MutatePriority import androidx.compose.foundation.gestures.ScrollScope @@ -11,20 +11,19 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import calendar.core.CalendarMonth -import calendar.core.OutDateStyle -import calendar.core.YearMonth -import calendar.data.DataStore -import calendar.data.VisibleItemState -import calendar.data.checkDateRange -import calendar.data.current -import calendar.data.getCalendarMonthData -import calendar.data.getMonthIndex -import calendar.data.getMonthIndicesCount +import com.kizitonwose.calendar.core.OutDateStyle +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.data.DataStore +import com.kizitonwose.calendar.data.VisibleItemState +import com.kizitonwose.calendar.data.checkDateRange +import com.kizitonwose.calendar.data.getCalendarMonthData +import com.kizitonwose.calendar.data.getMonthIndex +import com.kizitonwose.calendar.data.getMonthIndicesCount import kotlinx.datetime.DayOfWeek /** @@ -38,34 +37,22 @@ import kotlinx.datetime.DayOfWeek */ @Composable fun rememberCalendarState( - startMonth: YearMonth = YearMonth.current, + startMonth: YearMonth = YearMonth.now(), endMonth: YearMonth = startMonth, firstVisibleMonth: YearMonth = startMonth, - // TODO KMP firstDayOfWeek: DayOfWeek = DayOfWeek.MONDAY, outDateStyle: OutDateStyle = OutDateStyle.EndOfRow, ): CalendarState { - // TODO KMP -// return rememberSaveable( -// inputs = arrayOf( -// startMonth, -// endMonth, -// firstVisibleMonth, -// firstDayOfWeek, -// outDateStyle, -// ), -// saver = CalendarState.Saver, -// ) { -// CalendarState( -// startMonth = startMonth, -// endMonth = endMonth, -// firstDayOfWeek = firstDayOfWeek, -// firstVisibleMonth = firstVisibleMonth, -// outDateStyle = outDateStyle, -// visibleItemState = null, -// ) -// } - return remember { + return rememberSaveable( + inputs = arrayOf( + startMonth, + endMonth, + firstVisibleMonth, + firstDayOfWeek, + outDateStyle, + ), + saver = CalendarState.Saver, + ) { CalendarState( startMonth = startMonth, endMonth = endMonth, @@ -154,7 +141,7 @@ class CalendarState internal constructor( * * @see [lastVisibleMonth] */ - val firstVisibleMonth: CalendarMonth by derivedStateOf { + val firstVisibleMonth: com.kizitonwose.calendar.core.CalendarMonth by derivedStateOf { store[listState.firstVisibleItemIndex] } @@ -163,7 +150,7 @@ class CalendarState internal constructor( * * @see [firstVisibleMonth] */ - val lastVisibleMonth: CalendarMonth by derivedStateOf { + val lastVisibleMonth: com.kizitonwose.calendar.core.CalendarMonth by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -279,17 +266,14 @@ class CalendarState internal constructor( companion object { internal val Saver: Saver = listSaver( save = { - val visibleItemState = VisibleItemState( - firstVisibleItemIndex = it.listState.firstVisibleItemIndex, - firstVisibleItemScrollOffset = it.listState.firstVisibleItemScrollOffset, - ) listOf( it.startMonth, it.endMonth, it.firstVisibleMonth.yearMonth, it.firstDayOfWeek, it.outDateStyle, - visibleItemState, + it.listState.firstVisibleItemIndex, + it.listState.firstVisibleItemScrollOffset, ) }, restore = { @@ -299,7 +283,10 @@ class CalendarState internal constructor( firstVisibleMonth = it[2] as YearMonth, firstDayOfWeek = it[3] as DayOfWeek, outDateStyle = it[4] as OutDateStyle, - visibleItemState = it[5] as VisibleItemState, + visibleItemState = VisibleItemState( + firstVisibleItemIndex = it[5] as Int, + firstVisibleItemScrollOffset = it[6] as Int, + ), ) }, ) diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt similarity index 95% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt index 76f6bfd4..370751fe 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/ui/ContentHeightMode.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt @@ -1,4 +1,4 @@ -package calendar.ui +package com.kizitonwose.calendar.compose import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt similarity index 82% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt index 290362b3..b8ee8d41 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarDay.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core import androidx.compose.runtime.Immutable import kotlinx.datetime.LocalDate @@ -10,4 +10,4 @@ import kotlinx.datetime.LocalDate * @param position the [DayPosition] for this day. */ @Immutable -data class CalendarDay(val date: LocalDate, val position: DayPosition) +data class CalendarDay(val date: LocalDate, val position: DayPosition) : JvmSerializable diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt similarity index 95% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt index 8ad547bf..98bb35c4 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/CalendarMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core import androidx.compose.runtime.Immutable @@ -12,7 +12,7 @@ import androidx.compose.runtime.Immutable data class CalendarMonth internal constructor( val yearMonth: YearMonth, val weekDays: List>, -) { +) : JvmSerializable { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt similarity index 94% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt index eed49fa3..116e5458 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/DayPosition.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core /** * Describes the position of a [CalendarDay] in the month. diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt similarity index 88% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index eff7521c..0538c374 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -1,6 +1,5 @@ -package calendar.data +package com.kizitonwose.calendar.core -import calendar.core.YearMonth import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.DayOfWeek @@ -62,13 +61,13 @@ fun YearMonth.lengthOfMonth(): Int { return thisMonthStart.daysUntil(nextMonthStart) } -// TODO KMP Maybe restrict to group -val YearMonth.Companion.current: YearMonth - get() = Clock.System.now() - .toLocalDateTime(TimeZone.currentSystemDefault()) - .date.yearMonth +fun LocalDate.Companion.now(): LocalDate = Clock.System.now() + .toLocalDateTime(TimeZone.currentSystemDefault()) + .date -internal fun YearMonth.monthsUntil(other: YearMonth): Int = +fun YearMonth.Companion.now(): YearMonth = LocalDate.now().yearMonth + +fun YearMonth.monthsUntil(other: YearMonth): Int = atStartOfMonth().monthsUntil(other.atStartOfMonth()) // E.g DayOfWeek.SATURDAY.daysUntil(DayOfWeek.TUESDAY) = 3 diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.kt new file mode 100644 index 00000000..4fd2e04e --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.kt @@ -0,0 +1,3 @@ +package com.kizitonwose.calendar.core + +expect interface JvmSerializable diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/OutDateStyle.kt similarity index 93% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/OutDateStyle.kt index b1069c65..cd4f688b 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/OutDateStyle.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/OutDateStyle.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core /** * Determines how [DayPosition.OutDate] are diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt similarity index 87% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt index 64095837..90c38706 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/Week.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core import androidx.compose.runtime.Immutable @@ -8,7 +8,7 @@ import androidx.compose.runtime.Immutable * @param days the days in this week. */ @Immutable -data class Week internal constructor(val days: List) { +data class Week internal constructor(val days: List) : JvmSerializable { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt similarity index 82% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt index 39d4ae4d..b9840869 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDay.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core import androidx.compose.runtime.Immutable import kotlinx.datetime.LocalDate @@ -10,4 +10,4 @@ import kotlinx.datetime.LocalDate * @param position the [WeekDayPosition] for this day. */ @Immutable -data class WeekDay(val date: LocalDate, val position: WeekDayPosition) +data class WeekDay(val date: LocalDate, val position: WeekDayPosition) : JvmSerializable diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt similarity index 94% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt index 63ee47bc..8dfc6809 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/WeekDayPosition.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt @@ -1,4 +1,4 @@ -package calendar.core +package com.kizitonwose.calendar.core /** * Describes the position of a [WeekDay] on the calendar. diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt similarity index 86% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt index 04f02f5c..a457606b 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/core/YearMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt @@ -1,11 +1,11 @@ -package calendar.core +package com.kizitonwose.calendar.core import androidx.compose.runtime.Immutable import kotlinx.datetime.Month import kotlinx.datetime.number @Immutable -data class YearMonth(val year: Int, val month: Month) : Comparable { +data class YearMonth(val year: Int, val month: Month) : Comparable, JvmSerializable { /** * Same as java.time.YearMonth.compareTo() */ diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt similarity index 93% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt index b512c88c..7445bd89 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/DataStore.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt @@ -1,4 +1,4 @@ -package calendar.data +package com.kizitonwose.calendar.data /** * Basically [MutableMap.getOrPut] but allows us read the map diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt similarity index 71% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt index afad256c..d880d4f6 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/MonthData.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt @@ -1,10 +1,19 @@ -package calendar.data +package com.kizitonwose.calendar.data -import calendar.core.CalendarDay -import calendar.core.CalendarMonth -import calendar.core.DayPosition -import calendar.core.OutDateStyle -import calendar.core.YearMonth +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.OutDateStyle +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.atStartOfMonth +import com.kizitonwose.calendar.core.daysUntil +import com.kizitonwose.calendar.core.lengthOfMonth +import com.kizitonwose.calendar.core.minusDays +import com.kizitonwose.calendar.core.monthsUntil +import com.kizitonwose.calendar.core.nextMonth +import com.kizitonwose.calendar.core.plusDays +import com.kizitonwose.calendar.core.plusMonths +import com.kizitonwose.calendar.core.previousMonth +import com.kizitonwose.calendar.core.yearMonth import kotlinx.datetime.DayOfWeek internal data class MonthData internal constructor( @@ -22,10 +31,9 @@ internal data class MonthData internal constructor( private val nextMonth = month.nextMonth - val calendarMonth = - CalendarMonth(month, rows.map { week -> week.map { dayOffset -> getDay(dayOffset) } }) + val calendarMonth = CalendarMonth(month, rows.map { week -> week.map { dayOffset -> getDay(dayOffset) } }) - private fun getDay(dayOffset: Int): CalendarDay { + private fun getDay(dayOffset: Int): com.kizitonwose.calendar.core.CalendarDay { val date = firstDay.plusDays(dayOffset) val position = when (date.yearMonth) { month -> DayPosition.MonthDate @@ -33,7 +41,7 @@ internal data class MonthData internal constructor( nextMonth -> DayPosition.OutDate else -> throw IllegalArgumentException("Invalid date: $date in month: $month") } - return CalendarDay(date, position) + return com.kizitonwose.calendar.core.CalendarDay(date, position) } } diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt similarity index 81% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt index 844d4709..b147f63d 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/Utils.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt @@ -1,6 +1,6 @@ -package calendar.data +package com.kizitonwose.calendar.data -import calendar.core.YearMonth +import com.kizitonwose.calendar.core.YearMonth import kotlinx.datetime.LocalDate fun checkDateRange(startMonth: YearMonth, endMonth: YearMonth) { diff --git a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/VisibleItemState.kt similarity index 82% rename from compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt rename to compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/VisibleItemState.kt index e648dc24..497afc3a 100644 --- a/compose-multiplatform/lib/src/commonMain/kotlin/calendar/data/VisibleItemState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/VisibleItemState.kt @@ -1,4 +1,4 @@ -package calendar.data +package com.kizitonwose.calendar.data import androidx.compose.runtime.Immutable diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt new file mode 100644 index 00000000..9fa240cf --- /dev/null +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt @@ -0,0 +1,5 @@ +package com.kizitonwose.calendar.core + +import java.time.YearMonth as jtYearMonth + +fun YearMonth.toJavaYearMonth(): jtYearMonth = jtYearMonth.of(year, month) diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt new file mode 100644 index 00000000..2b9ed644 --- /dev/null +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt @@ -0,0 +1,3 @@ +package com.kizitonwose.calendar.core + +actual typealias JvmSerializable = java.io.Serializable diff --git a/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt b/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt new file mode 100644 index 00000000..bbce3a20 --- /dev/null +++ b/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt @@ -0,0 +1,3 @@ +package com.kizitonwose.calendar.core + +actual interface JvmSerializable diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts index 65b5082e..50e6cac4 100644 --- a/compose-multiplatform/sample/build.gradle.kts +++ b/compose-multiplatform/sample/build.gradle.kts @@ -59,7 +59,7 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) - implementation(project(":compose-multiplatform:lib")) + implementation(project(":compose-multiplatform:library")) } desktopMain.dependencies { implementation(compose.desktop.currentOs) diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt index 8c387b8d..e7d194df 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -1,3 +1,4 @@ + import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -32,20 +33,18 @@ import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import calendar.core.CalendarDay -import calendar.core.CalendarMonth -import calendar.core.DayPosition -import calendar.core.YearMonth -import calendar.data.current -import calendar.data.daysOfWeek -import calendar.data.minusMonths -import calendar.data.nextMonth -import calendar.data.plusMonths -import calendar.data.previousMonth -import calendar.ui.CalendarLayoutInfo -import calendar.ui.CalendarState -import calendar.ui.HorizontalCalendar -import calendar.ui.rememberCalendarState +import com.kizitonwose.calendar.compose.CalendarLayoutInfo +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.minusMonths +import com.kizitonwose.calendar.core.nextMonth +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import com.kizitonwose.calendar.core.previousMonth import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch import kotlinx.datetime.DayOfWeek @@ -61,10 +60,10 @@ fun App() { @Composable fun Example1Page(adjacentMonths: Int = 500) { - val currentMonth = remember { YearMonth.current } + val currentMonth = remember { YearMonth.now() } val startMonth = remember { currentMonth.minusMonths(adjacentMonths) } val endMonth = remember { currentMonth.plusMonths(adjacentMonths) } - val selections = remember { mutableStateListOf() } + val selections = remember { mutableStateListOf() } val daysOfWeek = remember { daysOfWeek(DayOfWeek.SUNDAY) } Column( modifier = Modifier @@ -132,7 +131,7 @@ private fun MonthHeader(daysOfWeek: List) { } @Composable -private fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { +private fun Day(day: com.kizitonwose.calendar.core.CalendarDay, isSelected: Boolean, onClick: (com.kizitonwose.calendar.core.CalendarDay) -> Unit) { Box( modifier = Modifier .aspectRatio(1f) // This is important for square-sizing! @@ -171,7 +170,7 @@ private fun Example1Preview() { fun rememberFirstMostVisibleMonth( state: CalendarState, viewportPercent: Float = 50f, -): CalendarMonth { +): com.kizitonwose.calendar.core.CalendarMonth { val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } LaunchedEffect(state) { snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } @@ -181,7 +180,7 @@ fun rememberFirstMostVisibleMonth( return visibleMonth.value } -private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { +private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): com.kizitonwose.calendar.core.CalendarMonth? { return if (visibleMonthsInfo.isEmpty()) { null } else { diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt deleted file mode 100644 index 887d8350..00000000 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Greeting.kt +++ /dev/null @@ -1,7 +0,0 @@ -class Greeting { - private val platform = getPlatform() - - fun greet(): String { - return "Hello, ${platform.name}!" - } -} \ No newline at end of file diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt b/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt index 6adcbde3..a6e0cfe8 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import calendar.core.YearMonth +import com.kizitonwose.calendar.core.YearMonth @Composable fun SimpleCalendarTitle( diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt index 17adf85f..a66be734 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt @@ -266,17 +266,14 @@ class CalendarState internal constructor( companion object { internal val Saver: Saver = listSaver( save = { - val visibleItemState = VisibleItemState( - firstVisibleItemIndex = it.listState.firstVisibleItemIndex, - firstVisibleItemScrollOffset = it.listState.firstVisibleItemScrollOffset, - ) listOf( it.startMonth, it.endMonth, it.firstVisibleMonth.yearMonth, it.firstDayOfWeek, it.outDateStyle, - visibleItemState, + it.listState.firstVisibleItemIndex, + it.listState.firstVisibleItemScrollOffset, ) }, restore = { @@ -286,7 +283,10 @@ class CalendarState internal constructor( firstVisibleMonth = it[2] as YearMonth, firstDayOfWeek = it[3] as DayOfWeek, outDateStyle = it[4] as OutDateStyle, - visibleItemState = it[5] as VisibleItemState, + visibleItemState = VisibleItemState( + firstVisibleItemIndex = it[5] as Int, + firstVisibleItemScrollOffset = it[6] as Int, + ), ) }, ) diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock new file mode 100644 index 00000000..014de6a7 --- /dev/null +++ b/kotlin-js-store/yarn.lock @@ -0,0 +1,3050 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + +"@jsonjoy.com/base64@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== + +"@jsonjoy.com/json-pack@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz#ab59c642a2e5368e8bcfd815d817143d4f3035d0" + integrity sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg== + dependencies: + "@jsonjoy.com/base64" "^1.1.1" + "@jsonjoy.com/util" "^1.1.2" + hyperdyperid "^1.2.0" + thingies "^1.20.0" + +"@jsonjoy.com/util@^1.1.2": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.2.0.tgz#0fe9a92de72308c566ebcebe8b5a3f01d3149df2" + integrity sha512-4B8B+3vFsY4eo33DMKyJPlQ3sBMpPFUZK2dr3O3rXrOGKKbYG44J0XSFkDo1VOQiri5HFEhIeVvItjR2xcazmg== + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" + integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" + integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.19.5" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz#218064e321126fcf9048d1ca25dd2465da55d9c6" + integrity sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^4.17.21": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/http-proxy@^1.17.8": + version "1.17.14" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" + integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + +"@types/node@*", "@types/node@>=10.0.0": + version "20.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" + integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== + dependencies: + undici-types "~5.26.4" + +"@types/qs@*": + version "6.9.15" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.15.5": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/sockjs@^0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/ws@^8.5.10": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.12.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" + integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.16.0.tgz#22e2a92b94f005f7e0f9c9d39652ef0b8f6f0cb4" + integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +body-parser@1.20.2, body-parser@^1.19.0: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.21.10: + version "4.23.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" + integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== + dependencies: + caniuse-lite "^1.0.30001629" + electron-to-chromium "^1.4.796" + node-releases "^2.0.14" + update-browserslist-db "^1.0.16" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001629: + version "1.0.30001638" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001638.tgz#598e1f0c2ac36f37ebc3f5b8887a32ca558e5d56" + integrity sha512-5SuJUJ7cZnhPpeLHaH0c/HPAnAHZvS6ElWyHK9GSIbVOQABLzowiI2pjmpvZ1WEbkyz46iFd4UXlOHR5SqgfMQ== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^3.5.1, chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.10, colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.0, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^4.1.0, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.796: + version "1.4.814" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.814.tgz#176535a0b899c9c473464502ab77576aa8bb1cbe" + integrity sha512-GVulpHjFu1Y9ZvikvbArHmAhZXtm3wHlpjTMcXNGKl4IQ4jMQjlnz8yMQYYqdLHKi/jEL2+CBC2akWVCoIGUdw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +engine.io-parser@~5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49" + integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw== + +engine.io@~6.5.2: + version "6.5.5" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.5.tgz#430b80d8840caab91a50e9e23cb551455195fc93" + integrity sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.17.1" + +enhanced-resolve@^5.16.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" + integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.1.tgz#68dc99a002f115792c26239baedaaea9e70c0ca2" + integrity sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A== + dependencies: + punycode "^1.4.1" + +envinfo@^7.7.3: + version "7.13.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" + integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + +escalade@^3.1.1, escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.7: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.0.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +foreground-child@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7" + integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +format-util@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +glob@^10.3.7: + version "10.4.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" + integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.4.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" + integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + dependencies: + hasown "^2.0.2" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-network-error@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jackspeak@^3.1.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" + integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== + dependencies: + glob "^7.1.3" + minimatch "^9.0.3" + webpack-merge "^4.1.5" + +karma@6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.3.tgz#763e500f99597218bbb536de1a14acc4ceea7ce8" + integrity sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.7.2" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +launch-editor@^2.6.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.8.0.tgz#7255d90bdba414448e2138faa770a74f28451305" + integrity sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log4js@^6.4.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +lru-cache@^10.2.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b" + integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^4.6.0: + version "4.9.3" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.9.3.tgz#41a3218065fe3911d9eba836250c8f4e43f816bc" + integrity sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA== + dependencies: + "@jsonjoy.com/json-pack" "^1.0.3" + "@jsonjoy.com/util" "^1.1.2" + tree-dump "^1.0.1" + tslib "^2.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.3, minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.3.0.tgz#0e185c49e6dccf582035c05fa91084a4ff6e3fe9" + integrity sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "8.1.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1, on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^10.0.3: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-retry@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" + integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== + dependencies: + "@types/retry" "0.12.2" + is-network-error "^1.0.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@^5.0.5: + version "5.0.7" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74" + integrity sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg== + dependencies: + glob "^10.3.7" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0, schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +socket.io-adapter@~2.5.2: + version "2.5.5" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz#c7a1f9c703d7756844751b6ff9abfc1780664082" + integrity sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg== + dependencies: + debug "~4.3.4" + ws "~8.17.1" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.7.2: + version "4.7.5" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.5.tgz#56eb2d976aef9d1445f373a62d781a41c7add8f8" + integrity sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.3.2" + engine.io "~6.5.2" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-map-js@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== + dependencies: + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.31.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.1.tgz#735de3c987dd671e95190e6b98cfe2f07f3cf0d4" + integrity sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +thingies@^1.20.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" + integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmp@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tree-dump@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.0.2.tgz#c460d5921caeb197bde71d0e9a7b479848c5b8ac" + integrity sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ== + +tslib@^2.0.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" + integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== + +ua-parser-js@^0.7.30: + version "0.7.38" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.38.tgz#f497d8a4dc1fec6e854e5caa4b2f9913422ef054" + integrity sha512-fYmIy7fKTSFAhG3fuPlubeGaMoAd6r0rSnfEsO5nEY55i26KSLt9EH7PLQiiqPUhNqYIJvSkTy1oArIcXAbPbA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" + integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2, uri-js@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webpack-cli@5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^7.1.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz#2af00538b6e4eda05f5afdd5d711dbebc05958f7" + integrity sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA== + dependencies: + colorette "^2.0.10" + memfs "^4.6.0" + mime-types "^2.1.31" + on-finished "^2.4.1" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz#cb6ea47ff796b9251ec49a94f24a425e12e3c9b8" + integrity sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" + ansi-html-community "^0.0.8" + bonjour-service "^1.2.1" + chokidar "^3.6.0" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.4.0" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + rimraf "^5.0.5" + schema-utils "^4.2.0" + selfsigned "^2.4.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^7.1.0" + ws "^8.16.0" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.91.0: + version "5.91.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" + integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.16.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.16.0, ws@~8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0, yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/settings.gradle.kts b/settings.gradle.kts index 96852789..5514c86a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,5 +18,5 @@ include(":data") include(":view") include(":compose") include(":sample") -include(":compose-multiplatform:lib") +include(":compose-multiplatform:library") include(":compose-multiplatform:sample") From 99b7f119a3cf7ef36be87491f871003684bb6864 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sat, 29 Jun 2024 11:24:35 +0200 Subject: [PATCH 05/21] Implement firstDayOfWeekFromLocale --- .../calendar/compose/CalendarState.kt | 3 +- .../kizitonwose/calendar/core/Extensions.kt | 45 ++--- .../kizitonwose/calendar/data/DataStore.kt | 2 +- .../com/kizitonwose/calendar/data/Utils.kt | 4 +- .../calendar/core/Extensions.apple.kt | 20 +++ .../calendar/core/Extensions.jvm.kt | 9 + .../calendar/core/Extensions.wasmJs.kt | 28 +++ .../calendar/core/FirstDayFromMap.kt | 168 ++++++++++++++++++ .../sample/src/commonMain/kotlin/App.kt | 12 +- 9 files changed, 260 insertions(+), 31 deletions(-) create mode 100644 compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.apple.kt create mode 100644 compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt create mode 100644 compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt create mode 100644 compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt index 8d46100f..bed2735e 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.kizitonwose.calendar.core.OutDateStyle import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale import com.kizitonwose.calendar.core.now import com.kizitonwose.calendar.data.DataStore import com.kizitonwose.calendar.data.VisibleItemState @@ -40,7 +41,7 @@ fun rememberCalendarState( startMonth: YearMonth = YearMonth.now(), endMonth: YearMonth = startMonth, firstVisibleMonth: YearMonth = startMonth, - firstDayOfWeek: DayOfWeek = DayOfWeek.MONDAY, + firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale(), outDateStyle: OutDateStyle = OutDateStyle.EndOfRow, ): CalendarState { return rememberSaveable( diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index 0538c374..5d6fbe59 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -1,5 +1,6 @@ package com.kizitonwose.calendar.core +import androidx.compose.ui.text.intl.Locale import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.DayOfWeek @@ -16,38 +17,33 @@ import kotlinx.datetime.toLocalDateTime * Returns the days of week values such that the desired * [firstDayOfWeek] property is at the start position. */ -fun daysOfWeek(firstDayOfWeek: DayOfWeek): List { +fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List { val pivot = 7 - firstDayOfWeek.ordinal val daysOfWeek = DayOfWeek.entries // Order `daysOfWeek` array so that firstDayOfWeek is at the start position. return (daysOfWeek.takeLast(pivot) + daysOfWeek.dropLast(pivot)) } -// /** -// * Returns the first day of the week from the provided locale. -// */ -// fun firstDayOfWeekFromLocale(locale: Locale = Locale.current): DayOfWeek = ?? +/** + * Returns the first day of the week from the provided locale. + */ +expect fun firstDayOfWeekFromLocale(locale: Locale = Locale.current): DayOfWeek fun YearMonth.atStartOfMonth(): LocalDate = LocalDate(year, month, 1) fun YearMonth.atEndOfMonth(): LocalDate = LocalDate(year, month, lengthOfMonth()) -val LocalDate.yearMonth: YearMonth - get() = YearMonth(year, month) - val YearMonth.nextMonth: YearMonth get() = this.plusMonths(1) val YearMonth.previousMonth: YearMonth get() = this.minusMonths(1) -fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) - -fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) - -fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeUnit.MONTH) +fun LocalDate.Companion.now(): LocalDate = Clock.System.now() + .toLocalDateTime(TimeZone.currentSystemDefault()) + .date -fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) +fun YearMonth.Companion.now(): YearMonth = LocalDate.now().yearMonth fun YearMonth.plusMonths(value: Int): YearMonth = atStartOfMonth().plusMonths(value).yearMonth @@ -55,19 +51,24 @@ fun YearMonth.plusMonths(value: Int): YearMonth = fun YearMonth.minusMonths(value: Int): YearMonth = atStartOfMonth().minusMonths(value).yearMonth -fun YearMonth.lengthOfMonth(): Int { +val LocalDate.yearMonth: YearMonth + get() = YearMonth(year, month) + +internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) + +internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) + +internal fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeUnit.MONTH) + +internal fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) + +internal fun YearMonth.lengthOfMonth(): Int { val thisMonthStart = atStartOfMonth() val nextMonthStart = thisMonthStart.plusMonths(1) return thisMonthStart.daysUntil(nextMonthStart) } -fun LocalDate.Companion.now(): LocalDate = Clock.System.now() - .toLocalDateTime(TimeZone.currentSystemDefault()) - .date - -fun YearMonth.Companion.now(): YearMonth = LocalDate.now().yearMonth - -fun YearMonth.monthsUntil(other: YearMonth): Int = +internal fun YearMonth.monthsUntil(other: YearMonth): Int = atStartOfMonth().monthsUntil(other.atStartOfMonth()) // E.g DayOfWeek.SATURDAY.daysUntil(DayOfWeek.TUESDAY) = 3 diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt index 7445bd89..365460d2 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/DataStore.kt @@ -4,7 +4,7 @@ package com.kizitonwose.calendar.data * Basically [MutableMap.getOrPut] but allows us read the map * in multiple places without calling `getOrPut` everywhere. */ -class DataStore( +internal class DataStore( private val store: MutableMap = HashMap(), private val create: (offset: Int) -> V, ) : MutableMap by store { diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt index b147f63d..0b36ece9 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/Utils.kt @@ -3,13 +3,13 @@ package com.kizitonwose.calendar.data import com.kizitonwose.calendar.core.YearMonth import kotlinx.datetime.LocalDate -fun checkDateRange(startMonth: YearMonth, endMonth: YearMonth) { +internal fun checkDateRange(startMonth: YearMonth, endMonth: YearMonth) { check(endMonth >= startMonth) { "startMonth: $startMonth is greater than endMonth: $endMonth" } } -fun checkDateRange(startDate: LocalDate, endDate: LocalDate) { +internal fun checkDateRange(startDate: LocalDate, endDate: LocalDate) { check(endDate >= startDate) { "startDate: $startDate is greater than endDate: $endDate" } diff --git a/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.apple.kt b/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.apple.kt new file mode 100644 index 00000000..f0a164db --- /dev/null +++ b/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.apple.kt @@ -0,0 +1,20 @@ +package com.kizitonwose.calendar.core + +import androidx.compose.ui.text.intl.Locale +import kotlinx.datetime.DayOfWeek +import platform.Foundation.NSCalendar +import platform.Foundation.NSLocale + +/** + * Returns the first day of the week from the provided locale. + */ +actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { + val firstWeekday = NSCalendar.currentCalendar.let { + it.setLocale(NSLocale(locale.toLanguageTag())) + // https://developer.apple.com/documentation/foundation/calendar/2293656-firstweekday + // Value is one-based, starting from sunday + it.firstWeekday.toInt() + } + // Get the index value from a sunday-based array. + return daysOfWeek(firstDayOfWeek = DayOfWeek.SUNDAY)[firstWeekday - 1] +} diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt new file mode 100644 index 00000000..1282928e --- /dev/null +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt @@ -0,0 +1,9 @@ +package com.kizitonwose.calendar.core + +import androidx.compose.ui.text.intl.Locale +import java.time.DayOfWeek +import java.time.temporal.WeekFields +import java.util.Locale as JavaLocale + +actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek = + WeekFields.of(JavaLocale.forLanguageTag(locale.toLanguageTag())).firstDayOfWeek diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt new file mode 100644 index 00000000..9fd45963 --- /dev/null +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt @@ -0,0 +1,28 @@ +package com.kizitonwose.calendar.core + +import androidx.compose.ui.text.intl.Locale +import kotlinx.datetime.DayOfWeek + +/** + * Returns the first day of the week from the provided locale. + */ +actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { + return try { + val firstDay = jsFirstDayFromTag(locale.toLanguageTag()) + daysOfWeek(firstDayOfWeek = DayOfWeek.MONDAY)[firstDay - 1] + } catch (e: Exception) { // Unavailable on Firefox + firstDayFromMap(locale) + } +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo#firstday +private fun jsFirstDayFromTag(languageTag: String): Int = js("new Intl.Locale(languageTag).weekInfo?.firstDay") + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale +//internal external interface IntlLocale { +// val language: String +// val script: String? +// val region: String? +// val baseName: String +//} +//internal fun parseLanguageTagToIntlLocale(languageTag: String): IntlLocale = js("new Intl.Locale(languageTag)") diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt new file mode 100644 index 00000000..d5469ceb --- /dev/null +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt @@ -0,0 +1,168 @@ +package com.kizitonwose.calendar.core + +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toLowerCase +import kotlinx.datetime.DayOfWeek + +internal fun firstDayFromMap(locale: Locale): DayOfWeek = + firstDays[locale.region]?.let { firstDayValues[it] } ?: DayOfWeek.SUNDAY + +private val USLocale = Locale("en-US") +private var firstDayValues = DayOfWeek.entries.associateBy { + it.name.toLowerCase(USLocale).take(3) +} + +// https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-core/supplemental/weekData.json#L7 +private val firstDays = mapOf( + "001" to "mon", + "AD" to "mon", + "AE" to "sat", + "AF" to "sat", + "AG" to "sun", + "AI" to "mon", + "AL" to "mon", + "AM" to "mon", + "AN" to "mon", + "AR" to "mon", + "AS" to "sun", + "AT" to "mon", + "AU" to "mon", + "AX" to "mon", + "AZ" to "mon", + "BA" to "mon", + "BD" to "sun", + "BE" to "mon", + "BG" to "mon", + "BH" to "sat", + "BM" to "mon", + "BN" to "mon", + "BR" to "sun", + "BS" to "sun", + "BT" to "sun", + "BW" to "sun", + "BY" to "mon", + "BZ" to "sun", + "CA" to "sun", + "CH" to "mon", + "CL" to "mon", + "CM" to "mon", + "CN" to "mon", + "CO" to "sun", + "CR" to "mon", + "CY" to "mon", + "CZ" to "mon", + "DE" to "mon", + "DJ" to "sat", + "DK" to "mon", + "DM" to "sun", + "DO" to "sun", + "DZ" to "sat", + "EC" to "mon", + "EE" to "mon", + "EG" to "sat", + "ES" to "mon", + "ET" to "sun", + "FI" to "mon", + "FJ" to "mon", + "FO" to "mon", + "FR" to "mon", + "GB" to "mon", + "GB-alt-variant" to "sun", + "GE" to "mon", + "GF" to "mon", + "GP" to "mon", + "GR" to "mon", + "GT" to "sun", + "GU" to "sun", + "HK" to "sun", + "HN" to "sun", + "HR" to "mon", + "HU" to "mon", + "ID" to "sun", + "IE" to "mon", + "IL" to "sun", + "IN" to "sun", + "IQ" to "sat", + "IR" to "sat", + "IS" to "mon", + "IT" to "mon", + "JM" to "sun", + "JO" to "sat", + "JP" to "sun", + "KE" to "sun", + "KG" to "mon", + "KH" to "sun", + "KR" to "sun", + "KW" to "sat", + "KZ" to "mon", + "LA" to "sun", + "LB" to "mon", + "LI" to "mon", + "LK" to "mon", + "LT" to "mon", + "LU" to "mon", + "LV" to "mon", + "LY" to "sat", + "MC" to "mon", + "MD" to "mon", + "ME" to "mon", + "MH" to "sun", + "MK" to "mon", + "MM" to "sun", + "MN" to "mon", + "MO" to "sun", + "MQ" to "mon", + "MT" to "sun", + "MV" to "fri", + "MX" to "sun", + "MY" to "mon", + "MZ" to "sun", + "NI" to "sun", + "NL" to "mon", + "NO" to "mon", + "NP" to "sun", + "NZ" to "mon", + "OM" to "sat", + "PA" to "sun", + "PE" to "sun", + "PH" to "sun", + "PK" to "sun", + "PL" to "mon", + "PR" to "sun", + "PT" to "sun", + "PY" to "sun", + "QA" to "sat", + "RE" to "mon", + "RO" to "mon", + "RS" to "mon", + "RU" to "mon", + "SA" to "sun", + "SD" to "sat", + "SE" to "mon", + "SG" to "sun", + "SI" to "mon", + "SK" to "mon", + "SM" to "mon", + "SV" to "sun", + "SY" to "sat", + "TH" to "sun", + "TJ" to "mon", + "TM" to "mon", + "TR" to "mon", + "TT" to "sun", + "TW" to "sun", + "UA" to "mon", + "UM" to "sun", + "US" to "sun", + "UY" to "mon", + "UZ" to "mon", + "VA" to "mon", + "VE" to "sun", + "VI" to "sun", + "VN" to "mon", + "WS" to "sun", + "XK" to "mon", + "YE" to "sun", + "ZA" to "sun", + "ZW" to "sun", +) diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt index e7d194df..ccdfba5d 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -37,6 +37,8 @@ import com.kizitonwose.calendar.compose.CalendarLayoutInfo import com.kizitonwose.calendar.compose.CalendarState import com.kizitonwose.calendar.compose.HorizontalCalendar import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth import com.kizitonwose.calendar.core.DayPosition import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.daysOfWeek @@ -63,8 +65,8 @@ fun Example1Page(adjacentMonths: Int = 500) { val currentMonth = remember { YearMonth.now() } val startMonth = remember { currentMonth.minusMonths(adjacentMonths) } val endMonth = remember { currentMonth.plusMonths(adjacentMonths) } - val selections = remember { mutableStateListOf() } - val daysOfWeek = remember { daysOfWeek(DayOfWeek.SUNDAY) } + val selections = remember { mutableStateListOf() } + val daysOfWeek = remember { daysOfWeek() } Column( modifier = Modifier .fillMaxSize() @@ -131,7 +133,7 @@ private fun MonthHeader(daysOfWeek: List) { } @Composable -private fun Day(day: com.kizitonwose.calendar.core.CalendarDay, isSelected: Boolean, onClick: (com.kizitonwose.calendar.core.CalendarDay) -> Unit) { +private fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { Box( modifier = Modifier .aspectRatio(1f) // This is important for square-sizing! @@ -170,7 +172,7 @@ private fun Example1Preview() { fun rememberFirstMostVisibleMonth( state: CalendarState, viewportPercent: Float = 50f, -): com.kizitonwose.calendar.core.CalendarMonth { +): CalendarMonth { val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } LaunchedEffect(state) { snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } @@ -180,7 +182,7 @@ fun rememberFirstMostVisibleMonth( return visibleMonth.value } -private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): com.kizitonwose.calendar.core.CalendarMonth? { +private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { return if (visibleMonthsInfo.isEmpty()) { null } else { From e7cde340a8d79f1a50254eafe465dcd03de5ab81 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sat, 29 Jun 2024 19:05:39 +0200 Subject: [PATCH 06/21] Add multiplatform week and heatmap calendars --- .../kizitonwose/calendar/compose/Calendar.kt | 96 ++++++ .../calendar/compose/CalendarState.kt | 3 +- .../heatmapcalendar/HeatMapCalendar.kt | 91 ++++++ .../heatmapcalendar/HeatMapCalendarState.kt | 265 ++++++++++++++++ .../compose/heatmapcalendar/HeatMapWeek.kt | 19 ++ .../HeatMapWeekHeaderPosition.kt | 19 ++ .../compose/weekcalendar/WeekCalendar.kt | 70 +++++ .../weekcalendar/WeekCalendarLayoutInfo.kt | 37 +++ .../compose/weekcalendar/WeekCalendarState.kt | 290 ++++++++++++++++++ .../kizitonwose/calendar/core/Extensions.kt | 8 + .../kizitonwose/calendar/data/MonthData.kt | 5 +- .../com/kizitonwose/calendar/data/WeekData.kt | 66 ++++ ...{Extensions.apple.kt => Extensions.ios.kt} | 0 .../heatmapcalendar/HeatMapCalendarState.kt | 12 +- .../compose/weekcalendar/WeekCalendarState.kt | 12 +- 15 files changed, 977 insertions(+), 16 deletions(-) create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendar.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt rename compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/{Extensions.apple.kt => Extensions.ios.kt} (100%) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt index cddad97e..0c5ad92f 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt @@ -10,8 +10,19 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.kizitonwose.calendar.compose.CalendarDefaults.flingBehavior +import com.kizitonwose.calendar.compose.heatmapcalendar.HeatMapCalendarImpl +import com.kizitonwose.calendar.compose.heatmapcalendar.HeatMapCalendarState +import com.kizitonwose.calendar.compose.heatmapcalendar.HeatMapWeek +import com.kizitonwose.calendar.compose.heatmapcalendar.HeatMapWeekHeaderPosition +import com.kizitonwose.calendar.compose.heatmapcalendar.rememberHeatMapCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarImpl +import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState import com.kizitonwose.calendar.core.CalendarDay import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.Week +import com.kizitonwose.calendar.core.WeekDay +import kotlinx.datetime.DayOfWeek /** * A horizontally scrolling calendar. @@ -197,3 +208,88 @@ private fun Calendar( } } } + +/** + * A horizontally scrolling week calendar. + * + * @param modifier the modifier to apply to this calendar. + * @param state the state object to be used to control or observe the calendar's properties. + * Examples: `startDate`, `endDate`, `firstDayOfWeek`, `firstVisibleWeek`. + * @param calendarScrollPaged the scrolling behavior of the calendar. When `true`, the calendar will + * snap to the nearest week after a scroll or swipe action. When `false`, the calendar scrolls normally. + * Note that when `false`, you should set the desired day width on the [dayContent] composable. + * @param userScrollEnabled whether the scrolling via the user gestures or accessibility actions + * is allowed. You can still scroll programmatically using the state even when it is disabled. + * @param reverseLayout reverse the direction of scrolling and layout. When `true`, weeks will be + * composed from the end to the start and [WeekCalendarState.startDate] will be located at the end. + * @param contentPadding a padding around the whole calendar. This will add padding for the + * content after it has been clipped, which is not possible via [modifier] param. You can use it + * to add a padding before the first week or after the last one. + * @param dayContent a composable block which describes the day content. + * @param weekHeader a composable block which describes the week header content. The header is + * placed above each week on the calendar. + * @param weekFooter a composable block which describes the week footer content. The footer is + * placed below each week on the calendar. + */ +@Composable +fun WeekCalendar( + modifier: Modifier = Modifier, + state: WeekCalendarState = rememberWeekCalendarState(), + calendarScrollPaged: Boolean = true, + userScrollEnabled: Boolean = true, + reverseLayout: Boolean = false, + contentPadding: PaddingValues = PaddingValues(0.dp), + dayContent: @Composable BoxScope.(WeekDay) -> Unit, + weekHeader: (@Composable ColumnScope.(Week) -> Unit)? = null, + weekFooter: (@Composable ColumnScope.(Week) -> Unit)? = null, +) = WeekCalendarImpl( + modifier = modifier, + state = state, + calendarScrollPaged = calendarScrollPaged, + userScrollEnabled = userScrollEnabled, + reverseLayout = reverseLayout, + dayContent = dayContent, + weekHeader = weekHeader, + weekFooter = weekFooter, + contentPadding = contentPadding, +) + +/** + * A horizontal scrolling heatmap calendar, useful for showing how data changes over time. + * A popular example is the user contribution chart on GitHub. + * + * @param modifier the modifier to apply to this calendar. + * @param state the state object to be used to control or observe the calendar's properties. + * Examples: `startMonth`, `endMonth`, `firstDayOfWeek`, `firstVisibleMonth`. + * @param weekHeaderPosition Determines the position for the [weekHeader] composable. + * @param userScrollEnabled whether the scrolling via the user gestures or accessibility actions + * is allowed. You can still scroll programmatically using the state even when it is disabled. + * @param contentPadding a padding around the whole calendar. This will add padding for the + * content after it has been clipped, which is not possible via [modifier] param. You can use it + * to add a padding before the first month or after the last one. + * @param dayContent a composable block which describes the day content. + * @param weekHeader a composable block which describes the day of week (Mon, Tue, Wed...) on the + * horizontal axis of the calendar. The position is determined by the [weekHeaderPosition] property. + * @param monthHeader a composable block which describes the month header content. The header is + * placed above each month on the calendar. + */ +@Composable +fun HeatMapCalendar( + modifier: Modifier = Modifier, + state: HeatMapCalendarState = rememberHeatMapCalendarState(), + weekHeaderPosition: HeatMapWeekHeaderPosition = HeatMapWeekHeaderPosition.Start, + userScrollEnabled: Boolean = true, + contentPadding: PaddingValues = PaddingValues(0.dp), + dayContent: @Composable ColumnScope.(day: CalendarDay, week: HeatMapWeek) -> Unit, + weekHeader: (@Composable ColumnScope.(DayOfWeek) -> Unit)? = null, + monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, +) = HeatMapCalendarImpl( + modifier = modifier, + state = state, + weekHeaderPosition = weekHeaderPosition, + userScrollEnabled = userScrollEnabled, + dayContent = dayContent, + weekHeader = weekHeader, + monthHeader = monthHeader, + contentPadding = contentPadding, +) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt index bed2735e..323930a4 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt @@ -244,8 +244,7 @@ class CalendarState internal constructor( private fun getScrollIndex(month: YearMonth): Int? { if (month !in startMonth..endMonth) { - // TODO KMP -// Log.d("CalendarState", "Attempting to scroll out of range: $month") + println("CalendarState - Attempting to scroll out of range: $month") return null } return getMonthIndex(startMonth, month) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendar.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendar.kt new file mode 100644 index 00000000..875aeb46 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendar.kt @@ -0,0 +1,91 @@ +package com.kizitonwose.calendar.compose.heatmapcalendar + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.daysOfWeek +import kotlinx.datetime.DayOfWeek + +@Composable +internal fun HeatMapCalendarImpl( + modifier: Modifier, + state: HeatMapCalendarState, + userScrollEnabled: Boolean, + weekHeaderPosition: HeatMapWeekHeaderPosition, + contentPadding: PaddingValues, + dayContent: @Composable ColumnScope.(day: CalendarDay, week: HeatMapWeek) -> Unit, + weekHeader: (@Composable ColumnScope.(DayOfWeek) -> Unit)? = null, + monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.Bottom, + ) { + if (weekHeaderPosition == HeatMapWeekHeaderPosition.Start && weekHeader != null) { + WeekHeaderColumn( + horizontalAlignment = Alignment.End, + firstDayOfWeek = state.firstDayOfWeek, + weekHeader = weekHeader, + ) + } + LazyRow( + modifier = Modifier.weight(1f), + state = state.listState, + userScrollEnabled = userScrollEnabled, + contentPadding = contentPadding, + ) { + items( + count = state.calendarInfo.indexCount, + key = { offset -> state.store[offset].yearMonth }, + ) { offset -> + val calendarMonth = state.store[offset] + Column(modifier = Modifier.width(IntrinsicSize.Max)) { + monthHeader?.invoke(this, calendarMonth) + Row { + for (week in calendarMonth.weekDays) { + Column { + for (day in week) { + dayContent(day, HeatMapWeek(week)) + } + } + } + } + } + } + } + if (weekHeaderPosition == HeatMapWeekHeaderPosition.End && weekHeader != null) { + WeekHeaderColumn( + horizontalAlignment = Alignment.Start, + firstDayOfWeek = state.firstDayOfWeek, + weekHeader = weekHeader, + ) + } + } +} + +@Composable +private fun WeekHeaderColumn( + horizontalAlignment: Alignment.Horizontal, + firstDayOfWeek: DayOfWeek, + weekHeader: @Composable (ColumnScope.(DayOfWeek) -> Unit), +) { + Column( + modifier = Modifier.width(IntrinsicSize.Max), + verticalArrangement = Arrangement.SpaceEvenly, + horizontalAlignment = horizontalAlignment, + ) { + for (dayOfWeek in daysOfWeek(firstDayOfWeek)) { + weekHeader(dayOfWeek) + } + } +} diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt new file mode 100644 index 00000000..774a13c3 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt @@ -0,0 +1,265 @@ +package com.kizitonwose.calendar.compose.heatmapcalendar + +import androidx.compose.foundation.MutatePriority +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import com.kizitonwose.calendar.compose.CalendarInfo +import com.kizitonwose.calendar.compose.CalendarLayoutInfo +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.data.DataStore +import com.kizitonwose.calendar.data.VisibleItemState +import com.kizitonwose.calendar.data.checkDateRange +import com.kizitonwose.calendar.data.getHeatMapCalendarMonthData +import com.kizitonwose.calendar.data.getMonthIndex +import com.kizitonwose.calendar.data.getMonthIndicesCount +import kotlinx.datetime.DayOfWeek + +/** + * Creates a [HeatMapCalendarState] that is remembered across compositions. + * + * @param startMonth the initial value for [HeatMapCalendarState.startMonth] + * @param endMonth the initial value for [HeatMapCalendarState.endMonth] + * @param firstDayOfWeek the initial value for [HeatMapCalendarState.firstDayOfWeek] + * @param firstVisibleMonth the initial value for [HeatMapCalendarState.firstVisibleMonth] + */ +@Composable +fun rememberHeatMapCalendarState( + startMonth: YearMonth = YearMonth.now(), + endMonth: YearMonth = startMonth, + firstVisibleMonth: YearMonth = startMonth, + firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale(), +): HeatMapCalendarState { + return rememberSaveable( + inputs = arrayOf( + startMonth, + endMonth, + firstVisibleMonth, + firstDayOfWeek, + ), + saver = HeatMapCalendarState.Saver, + ) { + HeatMapCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstDayOfWeek = firstDayOfWeek, + firstVisibleMonth = firstVisibleMonth, + visibleItemState = null, + ) + } +} + +/** + * A state object that can be hoisted to control and observe calendar properties. + * + * This should be created via [rememberHeatMapCalendarState]. + * + * @param startMonth the first month on the calendar. + * @param endMonth the last month on the calendar. + * @param firstDayOfWeek the first day of week on the calendar. + * @param firstVisibleMonth the initial value for [HeatMapCalendarState.firstVisibleMonth] + */ +@Stable +class HeatMapCalendarState internal constructor( + startMonth: YearMonth, + endMonth: YearMonth, + firstVisibleMonth: YearMonth, + firstDayOfWeek: DayOfWeek, + visibleItemState: VisibleItemState?, +) : ScrollableState { + /** Backing state for [startMonth] */ + private var _startMonth by mutableStateOf(startMonth) + + /** The first month on the calendar. */ + var startMonth: YearMonth + get() = _startMonth + set(value) { + if (value != startMonth) { + _startMonth = value + monthDataChanged() + } + } + + /** Backing state for [endMonth] */ + private var _endMonth by mutableStateOf(endMonth) + + /** The last month on the calendar. */ + var endMonth: YearMonth + get() = _endMonth + set(value) { + if (value != endMonth) { + _endMonth = value + monthDataChanged() + } + } + + /** Backing state for [firstDayOfWeek] */ + private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) + + /** The first day of week on the calendar. */ + var firstDayOfWeek: DayOfWeek + get() = _firstDayOfWeek + set(value) { + if (value != firstDayOfWeek) { + _firstDayOfWeek = value + monthDataChanged() + } + } + + /** + * The first month that is visible. + * + * @see [lastVisibleMonth] + */ + val firstVisibleMonth: CalendarMonth by derivedStateOf { + store[listState.firstVisibleItemIndex] + } + + /** + * The last month that is visible. + * + * @see [firstVisibleMonth] + */ + val lastVisibleMonth: CalendarMonth by derivedStateOf { + store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] + } + + /** + * The object of [CalendarLayoutInfo] calculated during the last layout pass. For example, + * you can use it to calculate what items are currently visible. + * + * Note that this property is observable and is updated after every scroll or remeasure. + * If you use it in the composable function it will be recomposed on every change causing + * potential performance issues including infinity recomposition loop. + * Therefore, avoid using it in the composition. + * + * If you need to use it in the composition then consider wrapping the calculation into a + * derived state in order to only have recompositions when the derived value changes. + * See Example6Page in the sample app for usage. + * + * If you want to run some side effects like sending an analytics event or updating a state + * based on this value consider using "snapshotFlow". + */ + val layoutInfo: CalendarLayoutInfo + get() = CalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } + + /** + * [InteractionSource] that will be used to dispatch drag events when this + * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in + * progress, use [isScrollInProgress]. + */ + val interactionSource: InteractionSource + get() = listState.interactionSource + + internal val listState = LazyListState( + firstVisibleItemIndex = visibleItemState?.firstVisibleItemIndex + ?: getScrollIndex(firstVisibleMonth) ?: 0, + firstVisibleItemScrollOffset = visibleItemState?.firstVisibleItemScrollOffset ?: 0, + ) + + internal var calendarInfo by mutableStateOf(CalendarInfo(indexCount = 0)) + + internal val store = DataStore { offset -> + getHeatMapCalendarMonthData( + startMonth = this.startMonth, + offset = offset, + firstDayOfWeek = this.firstDayOfWeek, + ).calendarMonth + } + + init { + monthDataChanged() // Update monthIndexCount initially. + } + + private fun monthDataChanged() { + store.clear() + checkDateRange(startMonth, endMonth) + calendarInfo = CalendarInfo( + indexCount = getMonthIndicesCount(startMonth, endMonth), + firstDayOfWeek = firstDayOfWeek, + ) + } + + /** + * Instantly brings the [month] to the top of the viewport. + * + * @param month the month to which to scroll. Must be within the + * range of [startMonth] and [endMonth]. + * + * @see [animateScrollToMonth] + */ + suspend fun scrollToMonth(month: YearMonth) { + listState.scrollToItem(getScrollIndex(month) ?: return) + } + + /** + * Animate (smooth scroll) to the given [month]. + * + * @param month the month to which to scroll. Must be within the + * range of [startMonth] and [endMonth]. + */ + suspend fun animateScrollToMonth(month: YearMonth) { + listState.animateScrollToItem(getScrollIndex(month) ?: return) + } + + private fun getScrollIndex(month: YearMonth): Int? { + if (month !in startMonth..endMonth) { + println("CalendarState - Attempting to scroll out of range: $month") + return null + } + return getMonthIndex(startMonth, month) + } + + /** + * Whether this [ScrollableState] is currently scrolling by gesture, fling or programmatically. + */ + override val isScrollInProgress: Boolean + get() = listState.isScrollInProgress + + override fun dispatchRawDelta(delta: Float): Float = listState.dispatchRawDelta(delta) + + override suspend fun scroll( + scrollPriority: MutatePriority, + block: suspend ScrollScope.() -> Unit, + ) = listState.scroll(scrollPriority, block) + + companion object { + internal val Saver: Saver = listSaver( + save = { + listOf( + it.startMonth, + it.endMonth, + it.firstVisibleMonth.yearMonth, + it.firstDayOfWeek, + it.listState.firstVisibleItemIndex, + it.listState.firstVisibleItemScrollOffset, + ) + }, + restore = { + HeatMapCalendarState( + startMonth = it[0] as YearMonth, + endMonth = it[1] as YearMonth, + firstVisibleMonth = it[2] as YearMonth, + firstDayOfWeek = it[3] as DayOfWeek, + visibleItemState = VisibleItemState( + firstVisibleItemIndex = it[4] as Int, + firstVisibleItemScrollOffset = it[5] as Int, + ), + ) + }, + ) + } +} diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt new file mode 100644 index 00000000..611f53a9 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt @@ -0,0 +1,19 @@ +package com.kizitonwose.calendar.compose.heatmapcalendar + +import androidx.compose.runtime.Immutable +import com.kizitonwose.calendar.compose.HeatMapCalendar +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.JvmSerializable + +/** + * Represents a week on the heatmap calendar. + * + * This model exists only as a wrapper class with the [Immutable] annotation for compose. + * The alternative would be to use the `kotlinx.ImmutableList` type for the `days` value + * which is used ONLY in the dayContent parameter of the [HeatMapCalendar] but then we + * would force that dependency on the library consumers. + * + * @param days the days in this week. + */ +@Immutable +data class HeatMapWeek(val days: List) : JvmSerializable diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt new file mode 100644 index 00000000..ef077405 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt @@ -0,0 +1,19 @@ +package com.kizitonwose.calendar.compose.heatmapcalendar + +import com.kizitonwose.calendar.compose.HeatMapCalendar + +/** + * Determines the position of the week header + * composable (Mon, Tue, Wed...) in the [HeatMapCalendar] + */ +enum class HeatMapWeekHeaderPosition { + /** + * The header is positioned at the start of the calendar. + */ + Start, + + /** + * The header is positioned at the end of the calendar. + */ + End, +} diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt new file mode 100644 index 00000000..2d2cfc9c --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt @@ -0,0 +1,70 @@ +package com.kizitonwose.calendar.compose.weekcalendar + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import com.kizitonwose.calendar.compose.CalendarDefaults.flingBehavior +import com.kizitonwose.calendar.core.Week +import com.kizitonwose.calendar.core.WeekDay + +@Composable +internal fun WeekCalendarImpl( + modifier: Modifier, + state: WeekCalendarState, + calendarScrollPaged: Boolean, + userScrollEnabled: Boolean, + reverseLayout: Boolean, + contentPadding: PaddingValues, + dayContent: @Composable BoxScope.(WeekDay) -> Unit, + weekHeader: (@Composable ColumnScope.(Week) -> Unit)? = null, + weekFooter: (@Composable ColumnScope.(Week) -> Unit)? = null, +) { + LazyRow( + modifier = modifier, + state = state.listState, + flingBehavior = flingBehavior(calendarScrollPaged, state.listState), + userScrollEnabled = userScrollEnabled, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + ) { + items( + count = state.weekIndexCount, + key = { offset -> state.store[offset].days.first().date }, + ) { offset -> + val week = state.store[offset] + Column( + modifier = Modifier + .then( + if (calendarScrollPaged) { + Modifier.fillParentMaxWidth() + } else { + Modifier.width(IntrinsicSize.Max) + }, + ), + ) { + weekHeader?.invoke(this, week) + Row { + for (date in week.days) { + Box( + modifier = Modifier + .then(if (calendarScrollPaged) Modifier.weight(1f) else Modifier) + .clipToBounds(), + ) { + dayContent(date) + } + } + } + weekFooter?.invoke(this, week) + } + } + } +} diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt new file mode 100644 index 00000000..b8c7fbac --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt @@ -0,0 +1,37 @@ +package com.kizitonwose.calendar.compose.weekcalendar + +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListLayoutInfo +import com.kizitonwose.calendar.core.Week + +/** + * Contains useful information about the currently displayed layout state of the calendar. + * For example you can get the list of currently displayed months. + * + * Use [WeekCalendarState.layoutInfo] to retrieve this. + * + * @see LazyListLayoutInfo + */ +class WeekCalendarLayoutInfo( + info: LazyListLayoutInfo, + private val getIndexData: (Int) -> Week, +) : LazyListLayoutInfo by info { + /** + * The list of [WeekCalendarItemInfo] representing all the currently visible weeks. + */ + val visibleWeeksInfo: List + get() = visibleItemsInfo.map { info -> + WeekCalendarItemInfo(info, getIndexData(info.index)) + } +} + +/** + * Contains useful information about an individual week on the calendar. + * + * @param week The week in the list. + + * @see WeekCalendarLayoutInfo + * @see LazyListItemInfo + */ +class WeekCalendarItemInfo(info: LazyListItemInfo, val week: Week) : + LazyListItemInfo by info diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt new file mode 100644 index 00000000..a37f3a25 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -0,0 +1,290 @@ +package com.kizitonwose.calendar.compose.weekcalendar + +import androidx.compose.foundation.MutatePriority +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import com.kizitonwose.calendar.core.Week +import com.kizitonwose.calendar.core.WeekDayPosition +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.atEndOfMonth +import com.kizitonwose.calendar.core.atStartOfMonth +import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.data.DataStore +import com.kizitonwose.calendar.data.VisibleItemState +import com.kizitonwose.calendar.data.getWeekCalendarAdjustedRange +import com.kizitonwose.calendar.data.getWeekCalendarData +import com.kizitonwose.calendar.data.getWeekIndex +import com.kizitonwose.calendar.data.getWeekIndicesCount +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate + +/** + * Creates a [WeekCalendarState] that is remembered across compositions. + * + * @param startDate the initial value for [WeekCalendarState.startDate] + * @param endDate the initial value for [WeekCalendarState.endDate] + * @param firstDayOfWeek the initial value for [WeekCalendarState.firstDayOfWeek] + * @param firstVisibleWeekDate the date which will have its week visible initially. + */ +@Composable +fun rememberWeekCalendarState( + startDate: LocalDate = YearMonth.now().atStartOfMonth(), + endDate: LocalDate = YearMonth.now().atEndOfMonth(), + firstVisibleWeekDate: LocalDate = LocalDate.now(), + firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale(), +): WeekCalendarState { + return rememberSaveable( + inputs = arrayOf( + startDate, + endDate, + firstVisibleWeekDate, + firstDayOfWeek, + ), + saver = WeekCalendarState.Saver, + ) { + WeekCalendarState( + startDate = startDate, + endDate = endDate, + firstVisibleWeekDate = firstVisibleWeekDate, + firstDayOfWeek = firstDayOfWeek, + visibleItemState = null, + ) + } +} + +/** + * A state object that can be hoisted to control and observe calendar properties. + * + * This should be created via [rememberWeekCalendarState]. + * + * @param startDate the desired first date on the calendar. The actual first date will be the + * first day in the week to which this date belongs, depending on the provided [firstDayOfWeek]. + * Such days will have their [WeekDayPosition] set to [WeekDayPosition.InDate]. + * @param endDate the desired last date on the calendar. The actual last date will be the last + * day in the week to which this date belongs. Such days will have their [WeekDayPosition] set + * to [WeekDayPosition.OutDate]. + * @param firstDayOfWeek the first day of week on the calendar. + * @param firstVisibleWeekDate the date which will have its week visible initially. + */ +@Stable +class WeekCalendarState internal constructor( + startDate: LocalDate, + endDate: LocalDate, + firstVisibleWeekDate: LocalDate, + firstDayOfWeek: DayOfWeek, + visibleItemState: VisibleItemState?, +) : ScrollableState { + /** + * The adjusted first date on the calendar to ensure proper alignment + * of the provided [firstDayOfWeek]. + */ + private var startDateAdjusted by mutableStateOf(startDate) + + /** + * The adjusted last date on the calendar to fill the remaining days in the + * last week after the provided end date. + */ + private var endDateAdjusted by mutableStateOf(endDate) + + /** Backing state for [startDate] */ + private var _startDate by mutableStateOf(startDate) + + /** + * The desired first date on the calendar. The actual first date will be the first day + * in the week to which this date belongs, depending on the provided [firstDayOfWeek]. + * Such days will have their [WeekDayPosition] set to [WeekDayPosition.InDate] + */ + var startDate: LocalDate + get() = _startDate + set(value) { + if (value != _startDate) { + _startDate = value + adjustDateRange() + } + } + + /** Backing state for [endDate] */ + private var _endDate by mutableStateOf(endDate) + + /** + * The desired last date on the calendar. The actual last date will be the last day + * in the week to which this date belongs. Such days will have their [WeekDayPosition] + * set to [WeekDayPosition.OutDate] + */ + var endDate: LocalDate + get() = _endDate + set(value) { + if (value != _endDate) { + _endDate = value + adjustDateRange() + } + } + + /** Backing state for [firstDayOfWeek] */ + private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) + + /** The first day of week on the calendar. */ + var firstDayOfWeek: DayOfWeek + get() = _firstDayOfWeek + set(value) { + if (value != _firstDayOfWeek) { + _firstDayOfWeek = value + adjustDateRange() + } + } + + /** + * The first week that is visible. + */ + val firstVisibleWeek: Week by derivedStateOf { + store[listState.firstVisibleItemIndex] + } + + /** + * The last week that is visible. + */ + val lastVisibleWeek: Week by derivedStateOf { + store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] + } + + /** + * The object of [WeekCalendarLayoutInfo] calculated during the last layout pass. For example, + * you can use it to calculate what items are currently visible. + * + * Note that this property is observable and is updated after every scroll or remeasure. + * If you use it in the composable function it will be recomposed on every change causing + * potential performance issues including infinity recomposition loop. + * Therefore, avoid using it in the composition. + * + * If you need to use it in the composition then consider wrapping the calculation into a + * derived state in order to only have recompositions when the derived value changes. + * See Example5Page in the sample app for usage. + * + * If you want to run some side effects like sending an analytics event or updating a state + * based on this value consider using "snapshotFlow". + */ + val layoutInfo: WeekCalendarLayoutInfo + get() = WeekCalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } + + internal val store = DataStore { offset -> + getWeekCalendarData( + startDateAdjusted = this.startDateAdjusted, + offset = offset, + desiredStartDate = this.startDate, + desiredEndDate = this.endDate, + ).week + } + + internal var weekIndexCount by mutableIntStateOf(0) + + internal val listState = run { + // Update date range and weekIndexCount initially. + // Since getScrollIndex requires the adjusted start date, it is necessary to do this + // before finding the first visible index. + adjustDateRange() + val item = visibleItemState ?: run { + VisibleItemState(firstVisibleItemIndex = getScrollIndex(firstVisibleWeekDate) ?: 0) + } + LazyListState( + firstVisibleItemIndex = item.firstVisibleItemIndex, + firstVisibleItemScrollOffset = item.firstVisibleItemScrollOffset, + ) + } + + private fun adjustDateRange() { + val data = getWeekCalendarAdjustedRange(startDate, endDate, firstDayOfWeek) + startDateAdjusted = data.startDateAdjusted + endDateAdjusted = data.endDateAdjusted + store.clear() + weekIndexCount = getWeekIndicesCount(startDateAdjusted, endDateAdjusted) + } + + /** + * Instantly brings the week containing the given [date] to the top of the viewport. + * + * @param date the week to which to scroll. + * + * @see [animateScrollToWeek] + */ + suspend fun scrollToWeek(date: LocalDate) { + listState.scrollToItem(getScrollIndex(date) ?: return) + } + + /** + * Animate (smooth scroll) to the week containing the given [date]. + * + * @param date the week to which to scroll. + */ + suspend fun animateScrollToWeek(date: LocalDate) { + listState.animateScrollToItem(getScrollIndex(date) ?: return) + } + + /** + * [InteractionSource] that will be used to dispatch drag events when this + * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in + * progress, use [isScrollInProgress]. + */ + val interactionSource: InteractionSource + get() = listState.interactionSource + + /** + * Whether this [ScrollableState] is currently scrolling by gesture, fling or programmatically. + */ + override val isScrollInProgress: Boolean + get() = listState.isScrollInProgress + + override fun dispatchRawDelta(delta: Float): Float = listState.dispatchRawDelta(delta) + + override suspend fun scroll( + scrollPriority: MutatePriority, + block: suspend ScrollScope.() -> Unit, + ) = listState.scroll(scrollPriority, block) + + private fun getScrollIndex(date: LocalDate): Int? { + if (date !in startDateAdjusted..endDateAdjusted) { + println("WeekCalendarState - Attempting to scroll out of range; $date") + return null + } + return getWeekIndex(startDateAdjusted, date) + } + + companion object { + val Saver: Saver = listSaver( + save = { + listOf( + it.startDate, + it.endDate, + it.firstVisibleWeek.days.first().date, + it.firstDayOfWeek, + it.listState.firstVisibleItemIndex, + it.listState.firstVisibleItemScrollOffset, + ) + }, + restore = { + WeekCalendarState( + startDate = it[0] as LocalDate, + endDate = it[1] as LocalDate, + firstVisibleWeekDate = it[2] as LocalDate, + firstDayOfWeek = it[3] as DayOfWeek, + visibleItemState = VisibleItemState( + firstVisibleItemIndex = it[4] as Int, + firstVisibleItemScrollOffset = it[5] as Int, + ), + ) + }, + ) + } +} diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index 5d6fbe59..e64cc0ff 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -12,6 +12,7 @@ import kotlinx.datetime.minus import kotlinx.datetime.monthsUntil import kotlinx.datetime.plus import kotlinx.datetime.toLocalDateTime +import kotlinx.datetime.until /** * Returns the days of week values such that the desired @@ -58,6 +59,10 @@ internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUni internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) +internal fun LocalDate.plusWeeks(value: Int): LocalDate = plus(value, DateTimeUnit.WEEK) + +internal fun LocalDate.minusWeeks(value: Int): LocalDate = minus(value, DateTimeUnit.WEEK) + internal fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeUnit.MONTH) internal fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) @@ -71,5 +76,8 @@ internal fun YearMonth.lengthOfMonth(): Int { internal fun YearMonth.monthsUntil(other: YearMonth): Int = atStartOfMonth().monthsUntil(other.atStartOfMonth()) +internal fun LocalDate.weeksUntil(other: LocalDate): Int = + until(other, DateTimeUnit.WEEK) + // E.g DayOfWeek.SATURDAY.daysUntil(DayOfWeek.TUESDAY) = 3 internal fun DayOfWeek.daysUntil(other: DayOfWeek) = (7 + (other.isoDayNumber - isoDayNumber)) % 7 diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt index d880d4f6..14bb6e69 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/MonthData.kt @@ -1,5 +1,6 @@ package com.kizitonwose.calendar.data +import com.kizitonwose.calendar.core.CalendarDay import com.kizitonwose.calendar.core.CalendarMonth import com.kizitonwose.calendar.core.DayPosition import com.kizitonwose.calendar.core.OutDateStyle @@ -33,7 +34,7 @@ internal data class MonthData internal constructor( val calendarMonth = CalendarMonth(month, rows.map { week -> week.map { dayOffset -> getDay(dayOffset) } }) - private fun getDay(dayOffset: Int): com.kizitonwose.calendar.core.CalendarDay { + private fun getDay(dayOffset: Int): CalendarDay { val date = firstDay.plusDays(dayOffset) val position = when (date.yearMonth) { month -> DayPosition.MonthDate @@ -41,7 +42,7 @@ internal data class MonthData internal constructor( nextMonth -> DayPosition.OutDate else -> throw IllegalArgumentException("Invalid date: $date in month: $month") } - return com.kizitonwose.calendar.core.CalendarDay(date, position) + return CalendarDay(date, position) } } diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt new file mode 100644 index 00000000..1015a741 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt @@ -0,0 +1,66 @@ +package com.kizitonwose.calendar.data + +import com.kizitonwose.calendar.core.Week +import com.kizitonwose.calendar.core.WeekDay +import com.kizitonwose.calendar.core.WeekDayPosition +import com.kizitonwose.calendar.core.daysUntil +import com.kizitonwose.calendar.core.minusDays +import com.kizitonwose.calendar.core.plusDays +import com.kizitonwose.calendar.core.plusWeeks +import com.kizitonwose.calendar.core.weeksUntil +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate + +data class WeekDateRange( + val startDateAdjusted: LocalDate, + val endDateAdjusted: LocalDate, +) + +fun getWeekCalendarAdjustedRange( + startDate: LocalDate, + endDate: LocalDate, + firstDayOfWeek: DayOfWeek, +): WeekDateRange { + val inDays = firstDayOfWeek.daysUntil(startDate.dayOfWeek) + val startDateAdjusted = startDate.minusDays(inDays) + val weeksBetween = startDateAdjusted.weeksUntil(endDate) + val endDateAdjusted = startDateAdjusted.plusWeeks(weeksBetween).plusDays(6) + return WeekDateRange(startDateAdjusted = startDateAdjusted, endDateAdjusted = endDateAdjusted) +} + +fun getWeekCalendarData( + startDateAdjusted: LocalDate, + offset: Int, + desiredStartDate: LocalDate, + desiredEndDate: LocalDate, +): WeekData { + val firstDayInWeek = startDateAdjusted.plusWeeks(offset) + return WeekData(firstDayInWeek, desiredStartDate, desiredEndDate) +} + +data class WeekData internal constructor( + private val firstDayInWeek: LocalDate, + private val desiredStartDate: LocalDate, + private val desiredEndDate: LocalDate, +) { + val week = Week((0 until 7).map { dayOffset -> getDay(dayOffset) }) + + private fun getDay(dayOffset: Int): WeekDay { + val date = firstDayInWeek.plusDays(dayOffset) + val position = when { + date < desiredStartDate -> WeekDayPosition.InDate + date > desiredEndDate -> WeekDayPosition.OutDate + else -> WeekDayPosition.RangeDate + } + return WeekDay(date, position) + } +} + +fun getWeekIndex(startDateAdjusted: LocalDate, date: LocalDate): Int { + return startDateAdjusted.weeksUntil(date) +} + +fun getWeekIndicesCount(startDateAdjusted: LocalDate, endDateAdjusted: LocalDate): Int { + // Add one to include the start week itself! + return getWeekIndex(startDateAdjusted, endDateAdjusted) + 1 +} diff --git a/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.apple.kt b/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.ios.kt similarity index 100% rename from compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.apple.kt rename to compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.ios.kt diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt index 5cc52091..9fbcd240 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt @@ -239,16 +239,13 @@ class HeatMapCalendarState internal constructor( companion object { internal val Saver: Saver = listSaver( save = { - val visibleItemState = VisibleItemState( - firstVisibleItemIndex = it.listState.firstVisibleItemIndex, - firstVisibleItemScrollOffset = it.listState.firstVisibleItemScrollOffset, - ) listOf( it.startMonth, it.endMonth, it.firstVisibleMonth.yearMonth, it.firstDayOfWeek, - visibleItemState, + it.listState.firstVisibleItemIndex, + it.listState.firstVisibleItemScrollOffset, ) }, restore = { @@ -257,7 +254,10 @@ class HeatMapCalendarState internal constructor( endMonth = it[1] as YearMonth, firstVisibleMonth = it[2] as YearMonth, firstDayOfWeek = it[3] as DayOfWeek, - visibleItemState = it[4] as VisibleItemState, + visibleItemState = VisibleItemState( + firstVisibleItemIndex = it[4] as Int, + firstVisibleItemScrollOffset = it[5] as Int, + ), ) }, ) diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt index 91b82239..9a271885 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -263,16 +263,13 @@ class WeekCalendarState internal constructor( companion object { val Saver: Saver = listSaver( save = { - val visibleItemState = VisibleItemState( - firstVisibleItemIndex = it.listState.firstVisibleItemIndex, - firstVisibleItemScrollOffset = it.listState.firstVisibleItemScrollOffset, - ) listOf( it.startDate, it.endDate, it.firstVisibleWeek.days.first().date, it.firstDayOfWeek, - visibleItemState, + it.listState.firstVisibleItemIndex, + it.listState.firstVisibleItemScrollOffset, ) }, restore = { @@ -281,7 +278,10 @@ class WeekCalendarState internal constructor( endDate = it[1] as LocalDate, firstVisibleWeekDate = it[2] as LocalDate, firstDayOfWeek = it[3] as DayOfWeek, - visibleItemState = it[4] as VisibleItemState, + visibleItemState = VisibleItemState( + firstVisibleItemIndex = it[4] as Int, + firstVisibleItemScrollOffset = it[5] as Int, + ), ) }, ) From 1641d8593eca672de3b81a84299c8891eef9de4c Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sun, 30 Jun 2024 21:17:55 +0200 Subject: [PATCH 07/21] Add multiplatform samples --- .../library/build.gradle.kts | 2 +- .../kizitonwose/calendar/core/Extensions.kt | 11 +- .../kizitonwose/calendar/core/YearMonth.kt | 8 + .../calendar/core/FirstDayFromMap.kt | 4 +- compose-multiplatform/sample/build.gradle.kts | 28 +- .../sample/src/commonMain/kotlin/App.kt | 279 ++++++--------- .../sample/src/commonMain/kotlin/AppIcons.kt | 115 ++++++ .../sample/src/commonMain/kotlin/Colors.kt | 19 + .../kotlin/ContinuousSelectionHelper.kt | 65 ++++ .../src/commonMain/kotlin/Example1Page.kt | 145 ++++++++ .../src/commonMain/kotlin/Example2Page.kt | 283 +++++++++++++++ .../kotlin/Example2PageHighlight.kt | 233 +++++++++++++ .../src/commonMain/kotlin/Example3Page.kt | 323 +++++++++++++++++ .../src/commonMain/kotlin/Example4Page.kt | 158 +++++++++ .../src/commonMain/kotlin/Example5Page.kt | 117 +++++++ .../src/commonMain/kotlin/Example6Page.kt | 292 ++++++++++++++++ .../src/commonMain/kotlin/Example7Page.kt | 136 ++++++++ .../src/commonMain/kotlin/Example9Page.kt | 327 ++++++++++++++++++ .../kotlin/Example9PageAnimatedVisibility.kt | 170 +++++++++ .../sample/src/commonMain/kotlin/Flight.kt | 131 +++++++ .../sample/src/commonMain/kotlin/Format.kt | 45 +++ .../sample/src/commonMain/kotlin/ListPage.kt | 103 ++++++ .../src/commonMain/kotlin/PageAnimation.kt | 73 ++++ .../commonMain/kotlin/SimpleCalendarTitle.kt | 13 +- .../sample/src/commonMain/kotlin/Utils.kt | 178 ++++++++++ .../sample/src/iosMain/kotlin/Format.ios.kt | 20 ++ .../sample/src/jvmMain/kotlin/Format.jvm.kt | 14 + .../{desktopMain => jvmMain}/kotlin/main.kt | 0 .../src/wasmJsMain/kotlin/Format.wasmJs.kt | 15 + .../kizitonwose/calendar/core/Extensions.kt | 2 +- gradle/libs.versions.toml | 1 + .../sample/compose/SimpleCalendarTitle.kt | 15 +- .../main/res/drawable/ic_airplane_takeoff.xml | 2 +- sample/src/main/res/values/colors.xml | 4 +- 34 files changed, 3119 insertions(+), 212 deletions(-) create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Colors.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example2PageHighlight.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example6Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Format.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/PageAnimation.kt create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt create mode 100644 compose-multiplatform/sample/src/iosMain/kotlin/Format.ios.kt create mode 100644 compose-multiplatform/sample/src/jvmMain/kotlin/Format.jvm.kt rename compose-multiplatform/sample/src/{desktopMain => jvmMain}/kotlin/main.kt (100%) create mode 100644 compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt diff --git a/compose-multiplatform/library/build.gradle.kts b/compose-multiplatform/library/build.gradle.kts index c499aa32..c6636023 100644 --- a/compose-multiplatform/library/build.gradle.kts +++ b/compose-multiplatform/library/build.gradle.kts @@ -62,7 +62,7 @@ kotlin { dependencies {} } jvmMain.dependencies { -// implementation(compose.desktop.currentOs) + implementation(compose.desktop.currentOs) } } @OptIn(ExperimentalKotlinGradlePluginApi::class) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index e64cc0ff..fabeb03c 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -17,12 +17,14 @@ import kotlinx.datetime.until /** * Returns the days of week values such that the desired * [firstDayOfWeek] property is at the start position. + * + * @see [firstDayOfWeekFromLocale] */ fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List { val pivot = 7 - firstDayOfWeek.ordinal val daysOfWeek = DayOfWeek.entries // Order `daysOfWeek` array so that firstDayOfWeek is at the start position. - return (daysOfWeek.takeLast(pivot) + daysOfWeek.dropLast(pivot)) + return daysOfWeek.takeLast(pivot) + daysOfWeek.dropLast(pivot) } /** @@ -30,9 +32,11 @@ fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List, JvmSerializable { + + init { + try { + atStartOfMonth() + } catch (e: IllegalArgumentException) { + throw IllegalArgumentException("Year value $year is out of range", e) + } + } /** * Same as java.time.YearMonth.compareTo() */ diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt index d5469ceb..3e6189a3 100644 --- a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt @@ -7,9 +7,9 @@ import kotlinx.datetime.DayOfWeek internal fun firstDayFromMap(locale: Locale): DayOfWeek = firstDays[locale.region]?.let { firstDayValues[it] } ?: DayOfWeek.SUNDAY -private val USLocale = Locale("en-US") +private val EnLocale = Locale("en-US") private var firstDayValues = DayOfWeek.entries.associateBy { - it.name.toLowerCase(USLocale).take(3) + it.name.toLowerCase(EnLocale).take(3) } // https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-core/supplemental/weekData.json#L7 diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts index 50e6cac4..379a213b 100644 --- a/compose-multiplatform/sample/build.gradle.kts +++ b/compose-multiplatform/sample/build.gradle.kts @@ -1,7 +1,7 @@ - import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig @@ -32,7 +32,7 @@ kotlin { androidTarget {} - jvm("desktop") + jvm("jvm") // jvm("desktop") listOf( iosX64(), @@ -45,12 +45,17 @@ kotlin { } } + applyDefaultHierarchyTemplate() + sourceSets { - val desktopMain by getting + val jvmMain by getting + val commonMain by getting + val wasmJsMain by getting + val nativeMain by getting + androidMain.get().dependsOn(jvmMain) androidMain.dependencies { implementation(compose.preview) - implementation(libs.compose.activity) } commonMain.dependencies { implementation(compose.runtime) @@ -60,10 +65,21 @@ kotlin { implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) implementation(project(":compose-multiplatform:library")) + implementation(libs.jetbrains.compose.navigation) } - desktopMain.dependencies { - implementation(compose.desktop.currentOs) + val nonJvmMain by creating { + dependsOn(commonMain) + nativeMain.dependsOn(this) + wasmJsMain.dependsOn(this) + dependencies {} } + jvmMain.dependencies { +// implementation(compose.desktop.currentOs) + } + } + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") } } diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt index ccdfba5d..a8bb12c6 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -1,215 +1,130 @@ - -import androidx.compose.foundation.LocalIndication -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.snapshotFlow +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.capitalize -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.intl.Locale -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.kizitonwose.calendar.compose.CalendarLayoutInfo -import com.kizitonwose.calendar.compose.CalendarState -import com.kizitonwose.calendar.compose.HorizontalCalendar -import com.kizitonwose.calendar.compose.rememberCalendarState -import com.kizitonwose.calendar.core.CalendarDay -import com.kizitonwose.calendar.core.CalendarMonth -import com.kizitonwose.calendar.core.DayPosition -import com.kizitonwose.calendar.core.YearMonth -import com.kizitonwose.calendar.core.daysOfWeek -import com.kizitonwose.calendar.core.minusMonths -import com.kizitonwose.calendar.core.nextMonth -import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths -import com.kizitonwose.calendar.core.previousMonth -import kotlinx.coroutines.flow.filterNotNull +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController import kotlinx.coroutines.launch -import kotlinx.datetime.DayOfWeek import org.jetbrains.compose.ui.tooling.preview.Preview @Composable @Preview fun App() { MaterialTheme { - Example1Page() + Box( + Modifier.fillMaxSize(), + contentAlignment = Alignment.TopCenter, + ) { + val primaryColor = Colors.primary + var toolBarTitle by remember { mutableStateOf("") } + var toolBarVisible by remember { mutableStateOf(true) } + val navController = rememberNavController() + val coroutineScope = rememberCoroutineScope() + val snackbarHostState = remember { SnackbarHostState() } + LaunchedEffect(navController) { + navController.currentBackStackEntryFlow.collect { backStackEntry -> + val page = Page.valueOf(backStackEntry.destination.route ?: return@collect) + toolBarTitle = page.title + toolBarVisible = page.showToolBar + } + } + MaterialTheme(colorScheme = MaterialTheme.colorScheme.copy(primary = primaryColor)) { + Scaffold( + modifier = Modifier.widthIn(max = 600.dp), + topBar = { + if (toolBarVisible) { + AppToolBar(title = toolBarTitle, navController) + } + }, + snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + }, + content = { + AppNavHost( + modifier = Modifier.padding(it), + navController = navController, + showSnack = { message -> + coroutineScope.launch { + snackbarHostState.showSnackbar(message) + } + }, + ) + }, + ) + } + } } } +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun Example1Page(adjacentMonths: Int = 500) { - val currentMonth = remember { YearMonth.now() } - val startMonth = remember { currentMonth.minusMonths(adjacentMonths) } - val endMonth = remember { currentMonth.plusMonths(adjacentMonths) } - val selections = remember { mutableStateListOf() } - val daysOfWeek = remember { daysOfWeek() } - Column( - modifier = Modifier - .fillMaxSize() - .background(Color.White), - ) { - val state = rememberCalendarState( - startMonth = startMonth, - endMonth = endMonth, - firstVisibleMonth = currentMonth, - firstDayOfWeek = daysOfWeek.first(), - ) - val coroutineScope = rememberCoroutineScope() - val visibleMonth = rememberFirstMostVisibleMonth(state, viewportPercent = 90f) - SimpleCalendarTitle( - modifier = Modifier.padding(vertical = 10.dp, horizontal = 8.dp), - currentMonth = visibleMonth.yearMonth, - goToPrevious = { - coroutineScope.launch { - state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) - } - }, - goToNext = { - coroutineScope.launch { - state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) - } - }, - ) - HorizontalCalendar( - modifier = Modifier.testTag("Calendar"), - state = state, - dayContent = { day -> - Day(day, isSelected = selections.contains(day)) { clicked -> - if (selections.contains(clicked)) { - selections.remove(clicked) - } else { - selections.add(clicked) - } +private fun AppToolBar(title: String, navController: NavHostController) { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors(), + title = { Text(text = title) }, + navigationIcon = navIcon@{ + val destination = navController.currentBackStackEntry?.destination + val page = Page.valueOf(destination?.route ?: return@navIcon) + if (page == Page.List) { + Unit + } else { + NavigationIcon { + navController.popBackStack() } - }, - monthHeader = { - MonthHeader(daysOfWeek = daysOfWeek) - }, - ) - } + } + }, + ) } @Composable -private fun MonthHeader(daysOfWeek: List) { - Row( - modifier = Modifier - .fillMaxWidth() - .testTag("MonthHeader"), +private fun AppNavHost( + modifier: Modifier = Modifier, + navController: NavHostController = rememberNavController(), + showSnack: (String) -> Unit = {}, +) { + NavHost( + modifier = modifier, + navController = navController, + startDestination = Page.List.name, ) { - for (dayOfWeek in daysOfWeek) { - Text( - modifier = Modifier.weight(1f), - textAlign = TextAlign.Center, - fontSize = 15.sp, - text = dayOfWeek.name.lowercase().capitalize(Locale.current).take(3), - fontWeight = FontWeight.Medium, - ) + composable(Page.List.name) { + ListPage { page -> navController.navigate(page.name) } } - } -} - -@Composable -private fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { - Box( - modifier = Modifier - .aspectRatio(1f) // This is important for square-sizing! - .testTag("MonthDay") - .padding(6.dp) - .clip(CircleShape) - .background(color = if (isSelected) Color(0xFFFCCA3E) else Color.Transparent) - // Disable clicks on inDates/outDates - .clickable( - enabled = day.position == DayPosition.MonthDate, - showRipple = !isSelected, - onClick = { onClick(day) }, - ), - contentAlignment = Alignment.Center, - ) { - val textColor = when (day.position) { - // Color.Unspecified will use the default text color from the current theme - DayPosition.MonthDate -> if (isSelected) Color.White else Color.Unspecified - DayPosition.InDate, DayPosition.OutDate -> Color(0xFFBEBEBE) + horizontallyAnimatedComposable(Page.Example1.name) { Example1Page() } + verticallyAnimatedComposable(Page.Example2.name) { + Example2Page( + close = { navController.popBackStack() }, + dateSelected = { startDate, endDate -> + navController.popBackStack() + showSnack(dateRangeDisplayText(startDate, endDate)) + }, + ) } - Text( - text = day.date.dayOfMonth.toString(), - color = textColor, - fontSize = 14.sp, - ) - } -} - -@Preview -@Composable -private fun Example1Preview() { - Example1Page() -} - -@Composable -fun rememberFirstMostVisibleMonth( - state: CalendarState, - viewportPercent: Float = 50f, -): CalendarMonth { - val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } - LaunchedEffect(state) { - snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } - .filterNotNull() - .collect { month -> visibleMonth.value = month } + verticallyAnimatedComposable(Page.Example3.name) { Example3Page() } + horizontallyAnimatedComposable(Page.Example4.name) { Example4Page() } + horizontallyAnimatedComposable(Page.Example5.name) { Example5Page { navController.popBackStack() } } + horizontallyAnimatedComposable(Page.Example6.name) { Example6Page() } + horizontallyAnimatedComposable(Page.Example7.name) { Example7Page() } + horizontallyAnimatedComposable(Page.Example9.name) { Example9Page() } } - return visibleMonth.value -} - -private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { - return if (visibleMonthsInfo.isEmpty()) { - null - } else { - val viewportSize = (viewportEndOffset + viewportStartOffset) * viewportPercent / 100f - visibleMonthsInfo.firstOrNull { itemInfo -> - if (itemInfo.offset < 0) { - itemInfo.offset + itemInfo.size >= viewportSize - } else { - itemInfo.size - itemInfo.offset >= viewportSize - } - }?.month - } -} - -fun Modifier.clickable( - enabled: Boolean = true, - showRipple: Boolean = true, - onClickLabel: String? = null, - role: Role? = null, - onClick: () -> Unit, -): Modifier = composed { - clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = if (showRipple) LocalIndication.current else null, - enabled = enabled, - onClickLabel = onClickLabel, - role = role, - onClick = onClick, - ) } diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt b/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt new file mode 100644 index 00000000..6fc067b6 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt @@ -0,0 +1,115 @@ +import androidx.compose.foundation.Image +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import org.jetbrains.compose.ui.tooling.preview.Preview + +val Icons.AirplaneTakeoff: ImageVector + get() { + if (_airplaneTakeoff != null) { + return _airplaneTakeoff!! + } + _airplaneTakeoff = materialIcon(name = "Plane") { + path( + fill = SolidColor(Color(0xFFDCDCDC)), + fillAlpha = 1.0F, + strokeAlpha = 1.0F, + strokeLineWidth = 0.0F, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 4.0F, + pathFillType = PathFillType.NonZero, + ) { + moveTo(2.5F, 19.0F) + horizontalLineTo(21.5F) + verticalLineTo(21.0F) + horizontalLineTo(2.5F) + verticalLineTo(19.0F) + moveTo(22.07F, 9.64F) + curveTo(21.86F, 8.84F, 21.03F, 8.36F, 20.23F, 8.58F) + lineTo(14.92F, 10.0F) + lineTo(8.0F, 3.57F) + lineTo(6.09F, 4.08F) + lineTo(10.23F, 11.25F) + lineTo(5.26F, 12.58F) + lineTo(3.29F, 11.04F) + lineTo(1.84F, 11.43F) + lineTo(3.66F, 14.59F) + lineTo(4.43F, 15.92F) + lineTo(6.03F, 15.5F) + lineTo(11.34F, 14.07F) + lineTo(15.69F, 12.91F) + lineTo(21.0F, 11.5F) + curveTo(21.81F, 11.26F, 22.28F, 10.44F, 22.07F, 9.64F) + close() + } + } + return _airplaneTakeoff!! + } + +private var _airplaneTakeoff: ImageVector? = null + +@Preview +@Composable +@Suppress("UnusedPrivateMember") +private fun IconAirplaneTakeoffPreview() { + Image(imageVector = Icons.AirplaneTakeoff, contentDescription = null) +} + +val Icons.AirplaneLanding: ImageVector + get() { + if (_airplaneLanding != null) { + return _airplaneLanding!! + } + _airplaneLanding = materialIcon(name = "AirplaneLanding") { + path( + fill = SolidColor(Color(0xFFDCDCDC)), + fillAlpha = 1.0F, + strokeAlpha = 1.0F, + strokeLineWidth = 0.0F, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 4.0F, + pathFillType = PathFillType.NonZero, + ) { + moveTo(2.5F, 19.0F) + horizontalLineTo(21.5F) + verticalLineTo(21.0F) + horizontalLineTo(2.5F) + verticalLineTo(19.0F) + moveTo(9.68F, 13.27F) + lineTo(14.03F, 14.43F) + lineTo(19.34F, 15.85F) + curveTo(20.14F, 16.06F, 20.96F, 15.59F, 21.18F, 14.79F) + curveTo(21.39F, 14.0F, 20.92F, 13.17F, 20.12F, 12.95F) + lineTo(14.81F, 11.53F) + lineTo(12.05F, 2.5F) + lineTo(10.12F, 2.0F) + verticalLineTo(10.28F) + lineTo(5.15F, 8.95F) + lineTo(4.22F, 6.63F) + lineTo(2.77F, 6.24F) + verticalLineTo(11.41F) + lineTo(4.37F, 11.84F) + lineTo(9.68F, 13.27F) + close() + } + } + return _airplaneLanding!! + } + +private var _airplaneLanding: ImageVector? = null + +@Preview +@Composable +@Suppress("UnusedPrivateMember") +private fun IconAirplaneLandingPreview() { + Image(imageVector = Icons.AirplaneLanding, contentDescription = null) +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Colors.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Colors.kt new file mode 100644 index 00000000..fcb1535a --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Colors.kt @@ -0,0 +1,19 @@ +import androidx.compose.ui.graphics.Color + +object Colors { + val example1Selection = Color(0xFFFCCA3E) + val inactiveText = Color(0xFFBEBEBE) + val example4Gray = Color(0xFF474747) + val example4GrayPast = Color(0xFFBEBEBE) + val example5PageBgColor = Color(0xFF0E0E0E) + val example5ItemViewBgColor = Color(0xFF1B1B1B) + val example5ToolbarColor = Color(0xFF282828) + val example5TextGrey = Color(0xFFDCDCDC) + val example5TextGreyLight = Color(0xFF616161) + val example6MonthBgColor = Color(0xFFB2EBF2) + val example6MonthBgColor2 = Color(0xFFF2C4B2) + val example6MonthBgColor3 = Color(0xFFB2B8F2) + val example7Yellow = Color(0xFFFFEB3B) + val primary = Color(0xFF3F51B5) + val accent = Color(0xFFFF4081) +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt b/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt new file mode 100644 index 00000000..d75d2cc2 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt @@ -0,0 +1,65 @@ +import com.kizitonwose.calendar.core.atEndOfMonth +import com.kizitonwose.calendar.core.atStartOfMonth +import com.kizitonwose.calendar.core.nextMonth +import com.kizitonwose.calendar.core.previousMonth +import com.kizitonwose.calendar.core.yearMonth +import kotlinx.datetime.LocalDate +import kotlinx.datetime.daysUntil +import kotlin.LazyThreadSafetyMode.NONE + +data class DateSelection(val startDate: LocalDate? = null, val endDate: LocalDate? = null) { + val daysBetween by lazy(NONE) { + if (startDate == null || endDate == null) { + null + } else { + startDate.daysUntil(endDate) + } + } +} + +private val rangeFormatter = LocalDate.Formats.ISO +fun dateRangeDisplayText(startDate: LocalDate, endDate: LocalDate): String { + return "Selected: ${rangeFormatter.format(startDate)} - ${rangeFormatter.format(endDate)}" +} + +object ContinuousSelectionHelper { + fun getSelection( + clickedDate: LocalDate, + dateSelection: DateSelection, + ): DateSelection { + val (selectionStartDate, selectionEndDate) = dateSelection + return if (selectionStartDate != null) { + if (clickedDate < selectionStartDate || selectionEndDate != null) { + DateSelection(startDate = clickedDate, endDate = null) + } else if (clickedDate != selectionStartDate) { + DateSelection(startDate = selectionStartDate, endDate = clickedDate) + } else { + DateSelection(startDate = clickedDate, endDate = null) + } + } else { + DateSelection(startDate = clickedDate, endDate = null) + } + } + + fun isInDateBetweenSelection( + inDate: LocalDate, + startDate: LocalDate, + endDate: LocalDate, + ): Boolean { + if (startDate.yearMonth == endDate.yearMonth) return false + if (inDate.yearMonth == startDate.yearMonth) return true + val firstDateInThisMonth = inDate.yearMonth.nextMonth.atStartOfMonth() + return firstDateInThisMonth in startDate..endDate && startDate != firstDateInThisMonth + } + + fun isOutDateBetweenSelection( + outDate: LocalDate, + startDate: LocalDate, + endDate: LocalDate, + ): Boolean { + if (startDate.yearMonth == endDate.yearMonth) return false + if (outDate.yearMonth == endDate.yearMonth) return true + val lastDateInThisMonth = outDate.yearMonth.previousMonth.atEndOfMonth() + return lastDateInThisMonth in startDate..endDate && endDate != lastDateInThisMonth + } +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt new file mode 100644 index 00000000..f6e147ca --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt @@ -0,0 +1,145 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.minusMonths +import com.kizitonwose.calendar.core.nextMonth +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import com.kizitonwose.calendar.core.previousMonth +import kotlinx.coroutines.launch +import kotlinx.datetime.DayOfWeek +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun Example1Page(adjacentMonths: Int = 500) { + val currentMonth = remember { YearMonth.now() } + val startMonth = remember { currentMonth.minusMonths(adjacentMonths) } + val endMonth = remember { currentMonth.plusMonths(adjacentMonths) } + val selections = remember { mutableStateListOf() } + val daysOfWeek = remember { daysOfWeek() } + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first(), + ) + val coroutineScope = rememberCoroutineScope() + val visibleMonth = rememberFirstMostVisibleMonth(state, viewportPercent = 90f) + SimpleCalendarTitle( + modifier = Modifier.padding(vertical = 10.dp, horizontal = 8.dp), + currentMonth = visibleMonth.yearMonth, + goToPrevious = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) + } + }, + goToNext = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) + } + }, + ) + HorizontalCalendar( + modifier = Modifier.testTag("Calendar"), + state = state, + dayContent = { day -> + Day(day, isSelected = selections.contains(day)) { clicked -> + if (selections.contains(clicked)) { + selections.remove(clicked) + } else { + selections.add(clicked) + } + } + }, + monthHeader = { + MonthHeader(daysOfWeek = daysOfWeek) + }, + ) + } +} + +@Composable +private fun MonthHeader(daysOfWeek: List) { + Row( + modifier = Modifier + .fillMaxWidth() + .testTag("MonthHeader"), + ) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + fontSize = 15.sp, + text = dayOfWeek.displayText(), + fontWeight = FontWeight.Medium, + ) + } + } +} + +@Composable +private fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { + Box( + modifier = Modifier + .aspectRatio(1f) // This is important for square-sizing! + .testTag("MonthDay") + .padding(6.dp) + .clip(CircleShape) + .background(color = if (isSelected) Colors.example1Selection else Color.Transparent) + // Disable clicks on inDates/outDates + .clickable( + enabled = day.position == DayPosition.MonthDate, + showRipple = !isSelected, + onClick = { onClick(day) }, + ), + contentAlignment = Alignment.Center, + ) { + val textColor = when (day.position) { + // Color.Unspecified will use the default text color from the current theme + DayPosition.MonthDate -> if (isSelected) Color.White else Color.Unspecified + DayPosition.InDate, DayPosition.OutDate -> Colors.inactiveText + } + Text( + text = day.date.dayOfMonth.toString(), + color = textColor, + fontSize = 14.sp, + ) + } +} + +@Preview +@Composable +private fun Example1Preview() { + Example1Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt new file mode 100644 index 00000000..a050dd56 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt @@ -0,0 +1,283 @@ +import ContinuousSelectionHelper.getSelection +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Button +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.VerticalCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate +import org.jetbrains.compose.ui.tooling.preview.Preview + +private val primaryColor = Color.Black.copy(alpha = 0.9f) +private val selectionColor = primaryColor +private val continuousSelectionColor = Color.LightGray.copy(alpha = 0.3f) + +@Composable +fun Example2Page( + close: () -> Unit = {}, + dateSelected: (startDate: LocalDate, endDate: LocalDate) -> Unit = { _, _ -> }, +) { + val currentMonth = remember { YearMonth.now() } + val startMonth = remember { currentMonth } + val endMonth = remember { currentMonth.plusMonths(12) } + val today = remember { LocalDate.now() } + var selection by remember { mutableStateOf(DateSelection()) } + val daysOfWeek = remember { daysOfWeek() } + MaterialTheme(colorScheme = MaterialTheme.colorScheme.copy(primary = primaryColor)) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + Column { + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first(), + ) + CalendarTop( + daysOfWeek = daysOfWeek, + selection = selection, + close = close, + clearDates = { selection = DateSelection() }, + ) + VerticalCalendar( + state = state, + contentPadding = PaddingValues(bottom = 100.dp), + dayContent = { value -> + Day( + value, + today = today, + selection = selection, + ) { day -> + if (day.position == DayPosition.MonthDate && day.date >= today) { + selection = getSelection( + clickedDate = day.date, + dateSelection = selection, + ) + } + } + }, + monthHeader = { month -> MonthHeader(month) }, + ) + } + CalendarBottom( + modifier = Modifier + .wrapContentHeight() + .fillMaxWidth() + .background(Color.White) + .align(Alignment.BottomCenter), + selection = selection, + save = { + val (startDate, endDate) = selection + if (startDate != null && endDate != null) { + dateSelected(startDate, endDate) + } + }, + ) + } + } +} + +@Composable +private fun Day( + day: CalendarDay, + today: LocalDate, + selection: DateSelection, + onClick: (CalendarDay) -> Unit, +) { + var textColor = Color.Transparent + Box( + modifier = Modifier + .aspectRatio(1f) // This is important for square-sizing! + .clickable( + enabled = day.position == DayPosition.MonthDate && day.date >= today, + showRipple = false, + onClick = { onClick(day) }, + ) + .backgroundHighlight( + day = day, + today = today, + selection = selection, + selectionColor = selectionColor, + continuousSelectionColor = continuousSelectionColor, + ) { textColor = it }, + contentAlignment = Alignment.Center, + ) { + Text( + text = day.date.dayOfMonth.toString(), + color = textColor, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + ) + } +} + +@Composable +private fun MonthHeader(calendarMonth: CalendarMonth) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp, bottom = 8.dp, start = 16.dp, end = 16.dp), + ) { + Text( + textAlign = TextAlign.Center, + text = calendarMonth.yearMonth.displayText(), + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + ) + } +} + +@Composable +private fun CalendarTop( + modifier: Modifier = Modifier, + daysOfWeek: List, + selection: DateSelection, + close: () -> Unit, + clearDates: () -> Unit, +) { + Column(modifier.fillMaxWidth()) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 6.dp, bottom = 10.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + Row( + modifier = Modifier.height(IntrinsicSize.Max), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier + .fillMaxHeight() + .aspectRatio(1f) + .clip(CircleShape) + .clickable(onClick = close) + .padding(12.dp), + imageVector = Icons.Default.Close, + contentDescription = "Close", + ) + Spacer(modifier = Modifier.weight(1f)) + Text( + modifier = Modifier + .clip(RoundedCornerShape(percent = 50)) + .clickable(onClick = clearDates) + .padding(horizontal = 16.dp, vertical = 8.dp), + text = "Clear", + fontWeight = FontWeight.Medium, + textAlign = TextAlign.End, + ) + } + val daysBetween = selection.daysBetween + val text = if (daysBetween == null) { + "Select dates" + } else { + // Ideally you'd do this using the strings.xml file + "$daysBetween ${if (daysBetween == 1) "night" else "nights"} in Munich" + } + Text( + modifier = Modifier.padding(horizontal = 14.dp), + text = text, + fontWeight = FontWeight.Bold, + fontSize = 24.sp, + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp), + ) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + color = Color.DarkGray, + text = dayOfWeek.displayText(), + fontSize = 15.sp, + ) + } + } + } + HorizontalDivider() + } +} + +@Composable +private fun CalendarBottom( + modifier: Modifier = Modifier, + selection: DateSelection, + save: () -> Unit, +) { + Column(modifier.fillMaxWidth()) { + HorizontalDivider() + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = "€75 night", + fontWeight = FontWeight.Bold, + ) + Spacer(modifier = Modifier.weight(1f)) + Button( + modifier = Modifier + .height(40.dp) + .width(100.dp), + onClick = save, + enabled = selection.daysBetween != null, + ) { + Text(text = "Save") + } + } + } +} + +@Preview +@Composable +private fun Example2Preview() { + Example2Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example2PageHighlight.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example2PageHighlight.kt new file mode 100644 index 00000000..82c1d7cf --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example2PageHighlight.kt @@ -0,0 +1,233 @@ +import ContinuousSelectionHelper.isInDateBetweenSelection +import ContinuousSelectionHelper.isOutDateBetweenSelection +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.DayPosition +import kotlinx.datetime.LocalDate + +private class HalfSizeShape(private val clipStart: Boolean) : Shape { + override fun createOutline( + size: Size, + layoutDirection: LayoutDirection, + density: Density, + ): Outline { + val half = size.width / 2f + val offset = if (layoutDirection == LayoutDirection.Ltr) { + if (clipStart) Offset(half, 0f) else Offset.Zero + } else { + if (clipStart) Offset.Zero else Offset(half, 0f) + } + return Outline.Rectangle(Rect(offset, Size(half, size.height))) + } +} + +/** + * Modern Airbnb highlight style, as seen in the app. + * See also [backgroundHighlightLegacy]. + */ +fun Modifier.backgroundHighlight( + day: CalendarDay, + today: LocalDate, + selection: DateSelection, + selectionColor: Color, + continuousSelectionColor: Color, + textColor: (Color) -> Unit, +): Modifier = composed { + val (startDate, endDate) = selection + val padding = 4.dp + when (day.position) { + DayPosition.MonthDate -> { + when { + day.date < today -> { + textColor(Colors.inactiveText) + this + } + + startDate == day.date && endDate == null -> { + textColor(Color.White) + padding(padding) + .background(color = selectionColor, shape = CircleShape) + } + + day.date == startDate -> { + textColor(Color.White) + padding(vertical = padding) + .background( + color = continuousSelectionColor, + shape = HalfSizeShape(clipStart = true), + ) + .padding(horizontal = padding) + .background(color = selectionColor, shape = CircleShape) + } + + startDate != null && endDate != null && (day.date > startDate && day.date < endDate) -> { + textColor(Colors.example4Gray) + padding(vertical = padding) + .background(color = continuousSelectionColor) + } + + day.date == endDate -> { + textColor(Color.White) + padding(vertical = padding) + .background( + color = continuousSelectionColor, + shape = HalfSizeShape(clipStart = false), + ) + .padding(horizontal = padding) + .background(color = selectionColor, shape = CircleShape) + } + + day.date == today -> { + textColor(Colors.example4Gray) + padding(padding) + .border( + width = 1.dp, + shape = CircleShape, + color = Colors.inactiveText, + ) + } + + else -> { + textColor(Colors.example4Gray) + this + } + } + } + + DayPosition.InDate -> { + textColor(Color.Transparent) + if (startDate != null && endDate != null && + isInDateBetweenSelection(day.date, startDate, endDate) + ) { + padding(vertical = padding) + .background(color = continuousSelectionColor) + } else { + this + } + } + + DayPosition.OutDate -> { + textColor(Color.Transparent) + if (startDate != null && endDate != null && + isOutDateBetweenSelection(day.date, startDate, endDate) + ) { + padding(vertical = padding) + .background(color = continuousSelectionColor) + } else { + this + } + } + } +} + +/** + * Old Airbnb highlight style. + * See also [backgroundHighlight]. + */ +fun Modifier.backgroundHighlightLegacy( + day: CalendarDay, + today: LocalDate, + selection: DateSelection, + selectionColor: Color, + textColor: (Color) -> Unit, +): Modifier = composed { + val (startDate, endDate) = selection + val padding = 4.dp + when (day.position) { + DayPosition.MonthDate -> { + when { + day.date < today -> { + textColor(Colors.inactiveText) + this + } + + startDate == day.date && endDate == null -> { + textColor(Color.White) + padding(padding) + .background(color = selectionColor, shape = CircleShape) + } + + day.date == startDate -> { + textColor(Color.White) + padding(vertical = padding) + .background( + color = selectionColor, + shape = RoundedCornerShape( + topStartPercent = 50, + bottomStartPercent = 50, + ), + ) + } + + startDate != null && endDate != null && (day.date > startDate && day.date < endDate) -> { + textColor(Color.White) + padding(vertical = padding) + .background(color = selectionColor) + } + + day.date == endDate -> { + textColor(Color.White) + padding(vertical = padding) + .background( + color = selectionColor, + shape = RoundedCornerShape(topEndPercent = 50, bottomEndPercent = 50), + ) + } + + day.date == today -> { + textColor(Colors.example4Gray) + padding(padding) + .border( + width = 1.dp, + shape = CircleShape, + color = Colors.inactiveText, + ) + } + + else -> { + textColor(Colors.example4Gray) + this + } + } + } + + DayPosition.InDate -> { + textColor(Color.Transparent) + if (startDate != null && endDate != null && + isInDateBetweenSelection(day.date, startDate, endDate) + ) { + padding(vertical = padding) + .background(color = selectionColor) + } else { + this + } + } + + DayPosition.OutDate -> { + textColor(Color.Transparent) + if (startDate != null && endDate != null && + isOutDateBetweenSelection(day.date, startDate, endDate) + ) { + padding(vertical = padding) + .background(color = selectionColor) + } else { + this + } + } + } +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt new file mode 100644 index 00000000..ea4ff919 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt @@ -0,0 +1,323 @@ +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.Text +import androidx.compose.material3.darkColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.OutDateStyle +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.minusMonths +import com.kizitonwose.calendar.core.nextMonth +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import com.kizitonwose.calendar.core.previousMonth +import kotlinx.coroutines.launch +import kotlinx.datetime.DayOfWeek +import org.jetbrains.compose.ui.tooling.preview.Preview + +private val flights = generateFlights().groupBy { it.time.date } + +private val pageBackgroundColor: Color = Colors.example5PageBgColor +private val itemBackgroundColor: Color = Colors.example5ItemViewBgColor +private val toolbarColor: Color = Colors.example5ToolbarColor +private val selectedItemColor: Color = Colors.example5TextGrey +private val inActiveTextColor: Color = Colors.example5TextGreyLight + +@Composable +fun Example3Page() { + val currentMonth = remember { YearMonth.now() } + val startMonth = remember { currentMonth.minusMonths(500) } + val endMonth = remember { currentMonth.plusMonths(500) } + var selection by remember { mutableStateOf(null) } + val daysOfWeek = remember { daysOfWeek() } + val flightsInSelectedDate = remember { + derivedStateOf { + val date = selection?.date + if (date == null) emptyList() else flights[date].orEmpty() + } + } + Column( + modifier = Modifier + .fillMaxSize() + .background(pageBackgroundColor), + ) { + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first(), + outDateStyle = OutDateStyle.EndOfGrid, + ) + val coroutineScope = rememberCoroutineScope() + val visibleMonth = rememberFirstCompletelyVisibleMonth(state) + LaunchedEffect(visibleMonth) { + // Clear selection if we scroll to a new month. + selection = null + } + + // Draw light content on dark background. + CompositionLocalProvider(LocalContentColor provides darkColorScheme().onSurface) { + SimpleCalendarTitle( + modifier = Modifier + .background(toolbarColor) + .padding(horizontal = 8.dp, vertical = 12.dp), + currentMonth = visibleMonth.yearMonth, + goToPrevious = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) + } + }, + goToNext = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) + } + }, + ) + HorizontalCalendar( + modifier = Modifier.wrapContentWidth(), + state = state, + dayContent = { day -> +// CompositionLocalProvider(LocalRippleConfiguration provides Example3RippleConfiguration) { + val colors = if (day.position == DayPosition.MonthDate) { + flights[day.date].orEmpty().map { it.color } + } else { + emptyList() + } + Day( + day = day, + isSelected = selection == day, + colors = colors, + ) { clicked -> + selection = clicked + } +// } + }, + monthHeader = { + MonthHeader( + modifier = Modifier.padding(vertical = 8.dp), + daysOfWeek = daysOfWeek, + ) + }, + ) + HorizontalDivider(color = pageBackgroundColor) + LazyColumn(modifier = Modifier.fillMaxWidth()) { + items(items = flightsInSelectedDate.value) { flight -> + FlightInformation(flight) + } + } + } + } +} + +@Composable +private fun Day( + day: CalendarDay, + isSelected: Boolean = false, + colors: List = emptyList(), + onClick: (CalendarDay) -> Unit = {}, +) { + Box( + modifier = Modifier + .aspectRatio(1f) // This is important for square-sizing! + .border( + width = if (isSelected) 1.dp else 0.dp, + color = if (isSelected) selectedItemColor else Color.Transparent, + ) + .padding(1.dp) + .background(color = itemBackgroundColor) + // Disable clicks on inDates/outDates + .clickable( + enabled = day.position == DayPosition.MonthDate, + onClick = { onClick(day) }, + ), + ) { + val textColor = when (day.position) { + DayPosition.MonthDate -> Color.Unspecified + DayPosition.InDate, DayPosition.OutDate -> inActiveTextColor + } + Text( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(top = 3.dp, end = 4.dp), + text = day.date.dayOfMonth.toString(), + color = textColor, + fontSize = 12.sp, + ) + Column( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .padding(bottom = 8.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + for (color in colors) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(5.dp) + .background(color), + ) + } + } + } +} + +@Composable +private fun MonthHeader( + modifier: Modifier = Modifier, + daysOfWeek: List = emptyList(), +) { + Row(modifier.fillMaxWidth()) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + fontSize = 12.sp, + color = Color.White, + text = dayOfWeek.displayText(uppercase = true), + fontWeight = FontWeight.Light, + ) + } + } +} + +@Composable +private fun LazyItemScope.FlightInformation(flight: Flight) { + Row( + modifier = Modifier + .fillParentMaxWidth() + .height(IntrinsicSize.Max), + horizontalArrangement = Arrangement.spacedBy(2.dp), + ) { + Box( + modifier = Modifier + .background(color = flight.color) + .fillParentMaxWidth(1 / 7f) + .aspectRatio(1f), + contentAlignment = Alignment.Center, + ) { + Text( + text = flightDateTimeFormatter.format(flight.time).uppercase(), + textAlign = TextAlign.Center, + lineHeight = 17.sp, + fontSize = 12.sp, + ) + } + Box( + modifier = Modifier + .background(color = itemBackgroundColor) + .weight(1f) + .fillMaxHeight(), + ) { + AirportInformation(flight.departure, isDeparture = true) + } + Box( + modifier = Modifier + .background(color = itemBackgroundColor) + .weight(1f) + .fillMaxHeight(), + ) { + AirportInformation(flight.destination, isDeparture = false) + } + } + HorizontalDivider(thickness = 2.dp, color = pageBackgroundColor) +} + +@Composable +private fun AirportInformation(airport: Flight.Airport, isDeparture: Boolean) { + Row( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + ) { + val vector = if (isDeparture) Icons.AirplaneTakeoff else Icons.AirplaneLanding + Box( + modifier = Modifier + .weight(0.3f) + .fillMaxHeight() + .fillMaxHeight(), + contentAlignment = Alignment.CenterEnd, + ) { + Image(imageVector = vector, contentDescription = null) + } + Column( + modifier = Modifier + .weight(0.7f) + .fillMaxHeight() + .fillMaxWidth(), + verticalArrangement = Arrangement.Center, + ) { + Text( + modifier = Modifier.fillMaxWidth(), + text = airport.code, + textAlign = TextAlign.Center, + fontSize = 16.sp, + fontWeight = FontWeight.Black, + ) + Text( + modifier = Modifier.fillMaxWidth(), + text = airport.city, + textAlign = TextAlign.Center, + fontSize = 16.sp, + fontWeight = FontWeight.Light, + ) + } + } +} + +// The default dark them ripple is too bright so we tone it down. +//private val Example3RippleConfiguration = RippleConfiguration( +// color = Color.Gray, +// // Copied from RippleTheme#DarkThemeRippleAlpha +// rippleAlpha = RippleAlpha( +// pressedAlpha = 0.10f, +// focusedAlpha = 0.12f, +// draggedAlpha = 0.08f, +// hoveredAlpha = 0.04f, +// ), +//) + +@Preview +@Composable +private fun Example3Preview() { + Example3Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt new file mode 100644 index 00000000..ed5d475e --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt @@ -0,0 +1,158 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun Example4Page() { + val currentMonth = remember { YearMonth.now() } + val startMonth = remember { currentMonth } + val endMonth = remember { currentMonth.plusMonths(500) } + val widthPx = remember { mutableStateOf(0) } + Column( + modifier = Modifier + .fillMaxSize() + .onSizeChanged { widthPx.value = it.width } + .background(Color.White), + ) { + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = firstDayOfWeekFromLocale(), + ) + HorizontalCalendar( + state = state, + dayContent = { day -> Day(day) }, + monthHeader = { month -> MonthHeader(month) }, + calendarScrollPaged = false, + contentPadding = PaddingValues(4.dp), + monthContainer = { _, container -> + Box( + modifier = Modifier + .width( + with(LocalDensity.current) { + (widthPx.value * 0.73f).toDp() + }, + ) + .padding(8.dp) + .clip(shape = RoundedCornerShape(8.dp)) + .border( + color = Color.Black, + width = 1.dp, + shape = RoundedCornerShape(8.dp), + ), + ) { + container() + } + }, + monthBody = { _, content -> + Box( + modifier = Modifier.background( + brush = Brush.verticalGradient( + colors = listOf( + Colors.example6MonthBgColor, + Colors.example6MonthBgColor3, + ), + ), + ), + ) { + content() + } + }, + ) + } +} + +@Composable +private fun MonthHeader(calendarMonth: CalendarMonth) { + val daysOfWeek = calendarMonth.weekDays.first().map { it.date.dayOfWeek } + Column( + modifier = Modifier + .wrapContentHeight() + .background(color = Colors.example6MonthBgColor2) + .padding(top = 6.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + modifier = Modifier.fillMaxWidth(), + text = calendarMonth.yearMonth.displayText(short = true), + fontSize = 18.sp, + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + ) + Row(modifier = Modifier.fillMaxWidth()) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + fontSize = 15.sp, + text = dayOfWeek.name.first().toString(), + fontWeight = FontWeight.Medium, + ) + } + } + HorizontalDivider(color = Color.Black) + } +} + +@Composable +private fun Day(day: CalendarDay) { + Box( + Modifier + .fillMaxWidth() + .height(70.dp), + contentAlignment = Alignment.Center, + ) { + if (day.position == DayPosition.MonthDate) { + Text( + modifier = Modifier.align(Alignment.Center), + text = day.date.dayOfMonth.toString(), + color = Color.Black, + fontSize = 14.sp, + ) + } + } +} + +@Preview +@Composable +private fun Example4Preview() { + Example4Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt new file mode 100644 index 00000000..30d11d76 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt @@ -0,0 +1,117 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.WeekCalendar +import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState +import com.kizitonwose.calendar.core.now +import kotlinx.datetime.LocalDate +import kotlinx.datetime.format.Padding +import org.jetbrains.compose.ui.tooling.preview.Preview + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Example5Page(close: () -> Unit = {}) { + val currentDate = remember { LocalDate.now() } + val startDate = remember { currentDate.minusDays(500) } + val endDate = remember { currentDate.plusDays(500) } + var selection by remember { mutableStateOf(currentDate) } + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + val state = rememberWeekCalendarState( + startDate = startDate, + endDate = endDate, + firstVisibleWeekDate = currentDate, + ) + val visibleWeek = rememberFirstVisibleWeekAfterScroll(state) + TopAppBar( + title = { Text(text = getWeekPageTitle(visibleWeek)) }, + navigationIcon = { NavigationIcon(onBackClick = close) }, + ) + WeekCalendar( + modifier = Modifier.background(color = Colors.primary), + state = state, + dayContent = { day -> + Day(day.date, isSelected = selection == day.date) { clicked -> + if (selection != clicked) { + selection = clicked + } + } + }, + ) + } +} + +private val dateFormatter by lazy { + LocalDate.Format { + dayOfMonth(Padding.ZERO) + } +} + +@Composable +private fun Day(date: LocalDate, isSelected: Boolean, onClick: (LocalDate) -> Unit) { + Box( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .clickable { onClick(date) }, + contentAlignment = Alignment.Center, + ) { + Column( + modifier = Modifier.padding(vertical = 10.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + text = date.dayOfWeek.displayText(), + fontSize = 12.sp, + color = Color.White, + fontWeight = FontWeight.Light, + ) + Text( + text = dateFormatter.format(date), + fontSize = 14.sp, + color = if (isSelected) Colors.example7Yellow else Color.White, + fontWeight = FontWeight.Bold, + ) + } + if (isSelected) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(5.dp) + .background(Colors.example7Yellow) + .align(Alignment.BottomCenter), + ) + } + } +} + +@Preview +@Composable +private fun Example5Preview() { + Example5Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example6Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example6Page.kt new file mode 100644 index 00000000..0350e8aa --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example6Page.kt @@ -0,0 +1,292 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.CalendarLayoutInfo +import com.kizitonwose.calendar.compose.HeatMapCalendar +import com.kizitonwose.calendar.compose.heatmapcalendar.HeatMapCalendarState +import com.kizitonwose.calendar.compose.heatmapcalendar.HeatMapWeek +import com.kizitonwose.calendar.compose.heatmapcalendar.rememberHeatMapCalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.yearMonth +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate +import kotlinx.datetime.daysUntil +import kotlinx.datetime.minus +import org.jetbrains.compose.ui.tooling.preview.Preview + +private enum class Level(val color: Color) { + Zero(Color(0xFFEBEDF0)), + One(Color(0xFF9BE9A8)), + Two(Color(0xFF40C463)), + Three(Color(0xFF30A14E)), + Four(Color(0xFF216E3A)), +} + +private fun generateRandomData(startDate: LocalDate, endDate: LocalDate): Map { + val levels = Level.entries + return (0..startDate.daysUntil(endDate)) + .associateTo(hashMapOf()) { count -> + startDate.plusDays(count) to levels.random() + } +} + +@Composable +fun Example6Page() { + var refreshKey by remember { mutableIntStateOf(1) } + val endDate = remember { LocalDate.now() } + // GitHub only shows contributions for the past 12 months + val startDate = remember { endDate.minus(12, DateTimeUnit.MONTH) } + val data = remember { mutableStateOf>(emptyMap()) } + var selection by remember { mutableStateOf?>(null) } + LaunchedEffect(startDate, endDate, refreshKey) { + selection = null + data.value = withContext(Dispatchers.Default) { + generateRandomData(startDate, endDate) + } + } + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + val state = rememberHeatMapCalendarState( + startMonth = startDate.yearMonth, + endMonth = endDate.yearMonth, + firstVisibleMonth = endDate.yearMonth, + firstDayOfWeek = firstDayOfWeekFromLocale(), + ) + HeatMapCalendar( + modifier = Modifier.padding(vertical = 10.dp), + state = state, + contentPadding = PaddingValues(end = 6.dp), + dayContent = { day, week -> + Day( + day = day, + startDate = startDate, + endDate = endDate, + week = week, + level = data.value[day.date] ?: Level.Zero, + ) { clicked -> + selection = Pair(clicked, data.value[clicked] ?: Level.Zero) + } + }, + weekHeader = { WeekHeader(it) }, + monthHeader = { MonthHeader(it, endDate, state) }, + ) + CalendarInfo( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 44.dp), + ) + Box(modifier = Modifier.weight(1f)) { + BottomContent( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp) + .align(Alignment.BottomCenter), + selection = selection, + ) { refreshKey += 1 } + } + } +} + +private val formatter = LocalDate.Formats.ISO + +@Composable +private fun BottomContent( + modifier: Modifier = Modifier, + selection: Pair? = null, + refresh: (() -> Unit), +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(20.dp), + ) { + if (selection != null) { + Row( + modifier = Modifier.align(Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text(text = "Clicked: ${formatter.format(selection.first)}") + LevelBox(color = selection.second.color) + } + } + Button( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + onClick = refresh, + ) { + Text( + text = "Generate random data", + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + ) + } + } +} + +@Composable +private fun CalendarInfo(modifier: Modifier = Modifier) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.Bottom, + ) { + Text(text = "Less", fontSize = 10.sp) + Level.entries.forEach { level -> + LevelBox(level.color) + } + Text(text = "More", fontSize = 10.sp) + } +} + +private val daySize = 18.dp + +@Composable +private fun Day( + day: CalendarDay, + startDate: LocalDate, + endDate: LocalDate, + week: HeatMapWeek, + level: Level, + onClick: (LocalDate) -> Unit, +) { + // We only want to draw boxes on the days that are in the + // past 12 months. Since the calendar is month-based, we ignore + // the future dates in the current month and those in the start + // month that are older than 12 months from today. + // We draw a transparent box on the empty spaces in the first week + // so the items are laid out properly as the column is top to bottom. + val weekDates = week.days.map { it.date } + if (day.date in startDate..endDate) { + LevelBox(level.color) { onClick(day.date) } + } else if (weekDates.contains(startDate)) { + LevelBox(Color.Transparent) + } +} + +@Composable +private fun LevelBox(color: Color, onClick: (() -> Unit)? = null) { + Box( + modifier = Modifier + .size(daySize) // Must set a size on the day. + .padding(2.dp) + .clip(RoundedCornerShape(2.dp)) + .background(color = color) + .clickable(enabled = onClick != null) { onClick?.invoke() }, + ) +} + +@Composable +private fun WeekHeader(dayOfWeek: DayOfWeek) { + Box( + modifier = Modifier + .height(daySize) // Must set a height on the day of week so it aligns with the day. + .padding(horizontal = 4.dp), + ) { + Text( + text = dayOfWeek.displayText(), + modifier = Modifier.align(Alignment.Center), + fontSize = 10.sp, + ) + } +} + +@Composable +private fun MonthHeader( + calendarMonth: CalendarMonth, + endDate: LocalDate, + state: HeatMapCalendarState, +) { + val density = LocalDensity.current + val firstFullyVisibleMonth by remember { + // Find the first index with at most one box out of bounds. + derivedStateOf { getMonthWithYear(state.layoutInfo, daySize, density) } + } + if (calendarMonth.weekDays.first().first().date <= endDate) { + val month = calendarMonth.yearMonth + val title = if (month == firstFullyVisibleMonth) { + month.displayText(short = true) + } else { + month.month.displayText() + } + Box( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 1.dp, start = 2.dp), + ) { + Text(text = title, fontSize = 10.sp) + } + } +} + +// Find the first index with at most one box out of bounds. +private fun getMonthWithYear( + layoutInfo: CalendarLayoutInfo, + daySize: Dp, + density: Density, +): YearMonth? { + val visibleItemsInfo = layoutInfo.visibleMonthsInfo + return when { + visibleItemsInfo.isEmpty() -> null + visibleItemsInfo.count() == 1 -> visibleItemsInfo.first().month.yearMonth + else -> { + val firstItem = visibleItemsInfo.first() + val daySizePx = with(density) { daySize.toPx() } + if ( + firstItem.size < daySizePx * 3 || // Ensure the Month + Year text can fit. + firstItem.offset < layoutInfo.viewportStartOffset && // Ensure the week row size - 1 is visible. + (layoutInfo.viewportStartOffset - firstItem.offset > daySizePx) + ) { + visibleItemsInfo[1].month.yearMonth + } else { + firstItem.month.yearMonth + } + } + } +} + +@Preview +@Composable +private fun Example6Preview() { + Example6Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt new file mode 100644 index 00000000..aa0db9e3 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt @@ -0,0 +1,136 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.Text +import androidx.compose.material3.darkColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kizitonwose.calendar.compose.WeekCalendar +import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState +import com.kizitonwose.calendar.core.now +import kotlinx.datetime.LocalDate +import kotlinx.datetime.format.Padding +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun Example7Page() { + val currentDate = remember { LocalDate.now() } + val startDate = remember { currentDate.minusDays(500) } + val endDate = remember { currentDate.plusDays(500) } + var selection by remember { mutableStateOf(null) } + val widthPx = remember { mutableStateOf(0) } + Column( + modifier = Modifier + .fillMaxSize() + .onSizeChanged { widthPx.value = it.width } + .background(Color.White), + ) { + val state = rememberWeekCalendarState( + startDate = startDate, + endDate = endDate, + firstVisibleWeekDate = currentDate, + ) + // Draw light content on dark background. + CompositionLocalProvider(LocalContentColor provides darkColorScheme().onSurface) { + WeekCalendar( + modifier = Modifier.padding(vertical = 4.dp), + state = state, + calendarScrollPaged = false, + dayContent = { day -> + Day( + // If paged scrolling is disabled (calendarScrollPaged = false), + // you must set the day width on the WeekCalendar! + modifier = Modifier.width( + with(LocalDensity.current) { + (widthPx.value / 9f).toDp() + }, + ), + date = day.date, + selected = selection == day.date, + ) { + selection = it + } + }, + ) + } + } +} + +private val dateFormatter by lazy { + LocalDate.Format { + dayOfMonth(Padding.ZERO) + } +} + +@Composable +private fun Day( + date: LocalDate, + selected: Boolean = false, + modifier: Modifier = Modifier, + onClick: (LocalDate) -> Unit = {}, +) { + Box( + modifier = modifier + .padding(2.dp) + .clip(RoundedCornerShape(8.dp)) + .background(color = Colors.example5ToolbarColor) + .border( + shape = RoundedCornerShape(8.dp), + width = if (selected) 2.dp else 0.dp, + color = if (selected) Colors.accent else Color.Transparent, + ) + .wrapContentHeight() + .clickable { onClick(date) }, + contentAlignment = Alignment.Center, + ) { + Column( + modifier = Modifier.padding(vertical = 10.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + text = date.month.displayText(), + fontSize = 10.sp, + fontWeight = FontWeight.Normal, + ) + Text( + text = dateFormatter.format(date), + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + ) + Text( + text = date.dayOfWeek.displayText(), + fontSize = 10.sp, + fontWeight = FontWeight.Normal, + ) + } + } +} + +@Preview +@Composable +private fun Example7Preview() { + Example7Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt new file mode 100644 index 00000000..054321dd --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt @@ -0,0 +1,327 @@ +import Example9PageSharedComponents.CalendarHeader +import Example9PageSharedComponents.Day +import Example9PageSharedComponents.MonthAndWeekCalendarTitle +import Example9PageSharedComponents.WeekModeToggle +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Checkbox +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.zIndex +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.WeekCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.WeekDayPosition +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.atEndOfMonth +import com.kizitonwose.calendar.core.atStartOfMonth +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.minusMonths +import com.kizitonwose.calendar.core.nextMonth +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import com.kizitonwose.calendar.core.previousMonth +import com.kizitonwose.calendar.core.yearMonth +import kotlinx.coroutines.launch +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate +import org.jetbrains.compose.ui.tooling.preview.Preview + +/** + * Go to [Example9PageAnimatedVisibility] to see how toggling between week and + * month modes can be done using [AnimatedVisibility] if that interests you. + */ +@Composable +fun Example9Page(adjacentMonths: Int = 500) { + val currentDate = remember { LocalDate.now() } + val currentMonth = remember(currentDate) { currentDate.yearMonth } + val startMonth = remember(currentDate) { currentMonth.minusMonths(adjacentMonths) } + val endMonth = remember(currentDate) { currentMonth.plusMonths(adjacentMonths) } + val selections = remember { mutableStateListOf() } + val daysOfWeek = remember { daysOfWeek() } + + var isWeekMode by remember { mutableStateOf(false) } + val coroutineScope = rememberCoroutineScope() + + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + val monthState = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first(), + ) + val weekState = rememberWeekCalendarState( + startDate = startMonth.atStartOfMonth(), + endDate = endMonth.atEndOfMonth(), + firstVisibleWeekDate = currentDate, + firstDayOfWeek = daysOfWeek.first(), + ) + CalendarTitle( + isWeekMode = isWeekMode, + monthState = monthState, + weekState = weekState, + ) + CalendarHeader(daysOfWeek = daysOfWeek) + val monthCalendarAlpha by animateFloatAsState(if (isWeekMode) 0f else 1f) + val weekCalendarAlpha by animateFloatAsState(if (isWeekMode) 1f else 0f) + var weekCalendarSize by remember { mutableStateOf(DpSize.Zero) } + val visibleMonth = rememberFirstVisibleMonthAfterScroll(monthState) + val weeksInVisibleMonth = visibleMonth.weekDays.count() + val monthCalendarHeight by animateDpAsState( + if (isWeekMode) { + weekCalendarSize.height + } else { + weekCalendarSize.height * weeksInVisibleMonth + }, + tween(durationMillis = 250), + ) + val density = LocalDensity.current + Box { + HorizontalCalendar( + modifier = Modifier + .height(monthCalendarHeight) + .alpha(monthCalendarAlpha) + .zIndex(if (isWeekMode) 0f else 1f), + state = monthState, + dayContent = { day -> + val isSelectable = day.position == DayPosition.MonthDate + Day( + day.date, + isSelected = isSelectable && selections.contains(day.date), + isSelectable = isSelectable, + ) { clicked -> + if (selections.contains(clicked)) { + selections.remove(clicked) + } else { + selections.add(clicked) + } + } + }, + ) + WeekCalendar( + modifier = Modifier + .wrapContentHeight() + .onSizeChanged { + val size = density.run { DpSize(it.width.toDp(), it.height.toDp()) } + if (weekCalendarSize != size) { + weekCalendarSize = size + } + } + .alpha(weekCalendarAlpha) + .zIndex(if (isWeekMode) 1f else 0f), + state = weekState, + dayContent = { day -> + val isSelectable = day.position == WeekDayPosition.RangeDate + Day( + day.date, + isSelected = isSelectable && selections.contains(day.date), + isSelectable = isSelectable, + ) { clicked -> + if (selections.contains(clicked)) { + selections.remove(clicked) + } else { + selections.add(clicked) + } + } + }, + ) + } + Spacer(modifier = Modifier.weight(1f)) + WeekModeToggle( + modifier = Modifier.align(Alignment.CenterHorizontally), + isWeekMode = isWeekMode, + ) { weekMode -> + coroutineScope.launch { + if (weekMode) { + val targetDate = monthState.firstVisibleMonth.weekDays.last().last().date + weekState.scrollToWeek(targetDate) + weekState.animateScrollToWeek(targetDate) // Trigger a layout pass for title update + } else { + val targetMonth = weekState.firstVisibleWeek.days.first().date.yearMonth + monthState.scrollToMonth(targetMonth) + monthState.animateScrollToMonth(targetMonth) // Trigger a layout pass for title update + } + isWeekMode = weekMode + } + } + } +} + +@Composable +private fun CalendarTitle( + isWeekMode: Boolean, + monthState: CalendarState, + weekState: WeekCalendarState, +) { + val visibleMonth = rememberFirstVisibleMonthAfterScroll(monthState) + val visibleWeek = rememberFirstVisibleWeekAfterScroll(weekState) + MonthAndWeekCalendarTitle( + isWeekMode = isWeekMode, + currentMonth = if (isWeekMode) visibleWeek.days.first().date.yearMonth else visibleMonth.yearMonth, + monthState = monthState, + weekState = weekState, + ) +} + +object Example9PageSharedComponents { + @Composable + fun MonthAndWeekCalendarTitle( + isWeekMode: Boolean, + currentMonth: YearMonth, + monthState: CalendarState, + weekState: WeekCalendarState, + ) { + val coroutineScope = rememberCoroutineScope() + SimpleCalendarTitle( + modifier = Modifier.padding(vertical = 10.dp, horizontal = 8.dp), + currentMonth = currentMonth, + goToPrevious = { + coroutineScope.launch { + if (isWeekMode) { + val targetDate = weekState.firstVisibleWeek.days.first().date.minusDays(1) + weekState.animateScrollToWeek(targetDate) + } else { + val targetMonth = monthState.firstVisibleMonth.yearMonth.previousMonth + monthState.animateScrollToMonth(targetMonth) + } + } + }, + goToNext = { + coroutineScope.launch { + if (isWeekMode) { + val targetDate = weekState.firstVisibleWeek.days.last().date.plusDays(1) + weekState.animateScrollToWeek(targetDate) + } else { + val targetMonth = monthState.firstVisibleMonth.yearMonth.nextMonth + monthState.animateScrollToMonth(targetMonth) + } + } + }, + ) + } + + @Composable + fun CalendarHeader(daysOfWeek: List) { + Row( + modifier = Modifier + .fillMaxWidth(), + ) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + fontSize = 15.sp, + text = dayOfWeek.displayText(), + fontWeight = FontWeight.Medium, + ) + } + } + } + + @Composable + fun Day( + day: LocalDate, + isSelected: Boolean, + isSelectable: Boolean, + onClick: (LocalDate) -> Unit, + ) { + Box( + modifier = Modifier + .aspectRatio(1f) // This is important for square-sizing! + .padding(6.dp) + .clip(CircleShape) + .background(color = if (isSelected) Colors.example1Selection else Color.Transparent) + .clickable( + enabled = isSelectable, + showRipple = !isSelected, + onClick = { onClick(day) }, + ), + contentAlignment = Alignment.Center, + ) { + val textColor = when { + isSelected -> Color.White + isSelectable -> Color.Unspecified + else -> Colors.inactiveText + } + Text( + text = day.dayOfMonth.toString(), + color = textColor, + fontSize = 14.sp, + ) + } + } + + @Composable + fun WeekModeToggle( + modifier: Modifier, + isWeekMode: Boolean, + weekModeToggled: (isWeekMode: Boolean) -> Unit, + ) { + // We want the entire content to be clickable, not just the checkbox. + Row( + modifier = modifier + .padding(10.dp) + .clip(MaterialTheme.shapes.small) + .clickable { weekModeToggled(!isWeekMode) } + .padding(10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp, Alignment.CenterHorizontally), + ) { + Checkbox( + checked = isWeekMode, + onCheckedChange = null, // Check is handled by parent. + colors = CheckboxDefaults.colors(checkedColor = Colors.example1Selection), + ) + Text(text = "Week mode") + } + } +} + +@Preview +@Composable +private fun Example9Preview() { + Example9Page() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt new file mode 100644 index 00000000..16a36949 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt @@ -0,0 +1,170 @@ +import Example9PageSharedComponents.CalendarHeader +import Example9PageSharedComponents.Day +import Example9PageSharedComponents.MonthAndWeekCalendarTitle +import Example9PageSharedComponents.WeekModeToggle +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.WeekCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.WeekDayPosition +import com.kizitonwose.calendar.core.atEndOfMonth +import com.kizitonwose.calendar.core.atStartOfMonth +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.minusMonths +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import com.kizitonwose.calendar.core.yearMonth +import kotlinx.coroutines.launch +import kotlinx.datetime.LocalDate +import org.jetbrains.compose.ui.tooling.preview.Preview + +/** + * Go to [Example9Page] to see how toggling between week and month + * modes can be done using the [Modifier] if that interests you. + */ +@Composable +fun Example9PageAnimatedVisibility(adjacentMonths: Int = 500) { + val currentDate = remember { LocalDate.now() } + val currentMonth = remember(currentDate) { currentDate.yearMonth } + val startMonth = remember(currentDate) { currentMonth.minusMonths(adjacentMonths) } + val endMonth = remember(currentDate) { currentMonth.plusMonths(adjacentMonths) } + val selections = remember { mutableStateListOf() } + val daysOfWeek = remember { daysOfWeek() } + + var isWeekMode by remember { mutableStateOf(false) } + var isAnimating by remember { mutableStateOf(false) } + val coroutineScope = rememberCoroutineScope() + + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + val monthState = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first(), + ) + val weekState = rememberWeekCalendarState( + startDate = startMonth.atStartOfMonth(), + endDate = endMonth.atEndOfMonth(), + firstVisibleWeekDate = currentDate, + firstDayOfWeek = daysOfWeek.first(), + ) + CalendarTitle( + isWeekMode = isWeekMode, + monthState = monthState, + weekState = weekState, + isAnimating = isAnimating, + ) + CalendarHeader(daysOfWeek = daysOfWeek) + AnimatedVisibility(visible = !isWeekMode) { + HorizontalCalendar( + state = monthState, + dayContent = { day -> + val isSelectable = day.position == DayPosition.MonthDate + Day( + day.date, + isSelected = isSelectable && selections.contains(day.date), + isSelectable = isSelectable, + ) { clicked -> + if (selections.contains(clicked)) { + selections.remove(clicked) + } else { + selections.add(clicked) + } + } + }, + ) + } + AnimatedVisibility(visible = isWeekMode) { + WeekCalendar( + state = weekState, + dayContent = { day -> + val isSelectable = day.position == WeekDayPosition.RangeDate + Day( + day.date, + isSelected = isSelectable && selections.contains(day.date), + isSelectable = isSelectable, + ) { clicked -> + if (selections.contains(clicked)) { + selections.remove(clicked) + } else { + selections.add(clicked) + } + } + }, + ) + } + Spacer(modifier = Modifier.weight(1f)) + WeekModeToggle( + modifier = Modifier.align(Alignment.CenterHorizontally), + isWeekMode = isWeekMode, + ) { weekMode -> + isAnimating = true + isWeekMode = weekMode + coroutineScope.launch { + if (weekMode) { + val targetDate = monthState.firstVisibleMonth.weekDays.last().last().date + weekState.scrollToWeek(targetDate) + weekState.animateScrollToWeek(targetDate) // Trigger a layout pass for title update + } else { + val targetMonth = weekState.firstVisibleWeek.days.first().date.yearMonth + monthState.scrollToMonth(targetMonth) + monthState.animateScrollToMonth(targetMonth) // Trigger a layout pass for title update + } + isAnimating = false + } + } + } +} + +@Composable +private fun CalendarTitle( + isWeekMode: Boolean, + monthState: CalendarState, + weekState: WeekCalendarState, + isAnimating: Boolean, +) { + val visibleMonth = rememberFirstVisibleMonthAfterScroll(monthState) + val visibleWeek = rememberFirstVisibleWeekAfterScroll(weekState) + val visibleWeekMonth = visibleWeek.days.first().date.yearMonth + // Track animation state to prevent updating the title too early before + // the correct value is available (after the animation). + val currentMonth = if (isWeekMode) { + if (isAnimating) visibleMonth.yearMonth else visibleWeekMonth + } else { + if (isAnimating) visibleWeekMonth else visibleMonth.yearMonth + } + MonthAndWeekCalendarTitle( + isWeekMode = isWeekMode, + currentMonth = currentMonth, + monthState = monthState, + weekState = weekState, + ) +} + +@Preview +@Composable +private fun Example9PageAnimatedVisibilityPreview() { + Example9PageAnimatedVisibility() +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt new file mode 100644 index 00000000..ec603ea5 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt @@ -0,0 +1,131 @@ +import androidx.compose.ui.graphics.Color +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.atDay +import com.kizitonwose.calendar.core.minusMonths +import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.plusMonths +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.atTime +import kotlinx.datetime.format.DayOfWeekNames +import kotlinx.datetime.format.MonthNames +import kotlinx.datetime.format.Padding +import kotlinx.datetime.format.char + +private typealias Airport = Flight.Airport + +data class Flight( + val time: LocalDateTime, + val departure: Airport, + val destination: Airport, + val color: Color, +) { + data class Airport(val city: String, val code: String) +} + +fun generateFlights(): List = buildList { + val currentMonth = YearMonth.now() + + currentMonth.atDay(17).also { date -> + add( + Flight( + date.atTime(14, 0), + Airport("Lagos", "LOS"), + Airport("Abuja", "ABV"), + Color(0xFF1565C0), + ), + ) + add( + Flight( + date.atTime(21, 30), + Airport("Enugu", "ENU"), + Airport("Owerri", "QOW"), + Color(0xFFC62828), + ), + ) + } + + currentMonth.atDay(22).also { date -> + add( + Flight( + date.atTime(13, 20), + Airport("Ibadan", "IBA"), + Airport("Benin", "BNI"), + Color(0xFF5D4037), + ), + ) + add( + Flight( + date.atTime(17, 40), + Airport("Sokoto", "SKO"), + Airport("Ilorin", "ILR"), + Color(0xFF455A64), + ), + ) + } + + currentMonth.atDay(3).also { date -> + add( + Flight( + date.atTime(20, 0), + Airport("Makurdi", "MDI"), + Airport("Calabar", "CBQ"), + Color(0xFF00796B), + ), + ) + } + + currentMonth.atDay(12).also { date -> + add( + Flight( + date.atTime(18, 15), + Airport("Kaduna", "KAD"), + Airport("Jos", "JOS"), + Color(0xFF0097A7), + ), + ) + } + + currentMonth.plusMonths(1).atDay(13).also { date -> + add( + Flight( + date.atTime(7, 30), + Airport("Kano", "KAN"), + Airport("Akure", "AKR"), + Color(0xFFC2185B), + ), + ) + add( + Flight( + date.atTime(10, 50), + Airport("Minna", "MXJ"), + Airport("Zaria", "ZAR"), + Color(0xFFEF6C00), + ), + ) + } + + currentMonth.minusMonths(1).atDay(9).also { date -> + add( + Flight( + date.atTime(20, 15), + Airport("Asaba", "ABB"), + Airport("Port Harcourt", "PHC"), + Color(0xFFEF6C00), + ), + ) + } +} + +val flightDateTimeFormatter by lazy { + LocalDateTime.Format { + dayOfWeek(DayOfWeekNames(DayOfWeekNames.ENGLISH_ABBREVIATED.names.map { it.uppercase() })) + char('\n') + dayOfMonth(Padding.ZERO) + char(' ') + monthName(MonthNames(MonthNames.ENGLISH_ABBREVIATED.names.map { it.uppercase() })) + char('\n') + hour() + char(':') + minute() + } +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt new file mode 100644 index 00000000..acfc7e99 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt @@ -0,0 +1,45 @@ +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toUpperCase +import com.kizitonwose.calendar.core.Week +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.yearMonth +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.Month + +fun YearMonth.displayText(short: Boolean = false): String { + return "${month.displayText(short = short)} $year" +} + +fun Month.displayText(short: Boolean = true): String { + return getDisplayName(short, EnLocale) +} + +fun DayOfWeek.displayText(uppercase: Boolean = false): String { + return getShortDisplayName(EnLocale).let { value -> + if (uppercase) value.toUpperCase(EnLocale) else value + } +} + +expect fun Month.getDisplayName(short: Boolean, locale: Locale): String + +expect fun DayOfWeek.getShortDisplayName(locale: Locale): String + +private val EnLocale = Locale("en-US") + +fun getWeekPageTitle(week: Week): String { + val firstDate = week.days.first().date + val lastDate = week.days.last().date + return when { + firstDate.yearMonth == lastDate.yearMonth -> { + firstDate.yearMonth.displayText() + } + + firstDate.year == lastDate.year -> { + "${firstDate.month.displayText(short = false)} - ${lastDate.yearMonth.displayText()}" + } + + else -> { + "${firstDate.yearMonth.displayText()} - ${lastDate.yearMonth.displayText()}" + } + } +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt b/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt new file mode 100644 index 00000000..7c778331 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt @@ -0,0 +1,103 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp + +enum class Page(val title: String, val subtitle: String, val showToolBar: Boolean) { + List( + title = "Calendar Compose Sample", + subtitle = "", + showToolBar = true, + ), + Example1( + title = "Example 1", + subtitle = "Horizontal Calendar - Month header, paged scroll style, programmatic scrolling, multiple selection.", + showToolBar = true, + ), + Example2( + title = "Example 2", + subtitle = "Vertical Calendar - Sticky header, continuous selection within one month and across multiple months, " + + "dates older than the current day are disabled. Similar to what is in the Airbnb app.", + showToolBar = false, + ), + Example3( + title = "Example 3", + subtitle = "Horizontal Calendar - Single selection, shows the \"EndOfGrid\" implementation of \"OutDateStyle\" property. A flight schedule calendar.", + showToolBar = false, + ), + Example4( + title = "Example 4", + subtitle = "Horizontal Calendar - Custom date width and height, custom month container and content backgrounds, continuous horizontal scroll style.", + showToolBar = true, + ), + Example5( + title = "Example 5", + subtitle = "Week Calendar - Single selection, paged scroll and visible item observation.", + showToolBar = false, + ), + Example6( + title = "Example 6", + subtitle = "HeatMap Calendar - Dynamic month header, continuous scroll. Similar to GitHub's contributions chart.", + showToolBar = true, + ), + Example7( + title = "Example 7", + subtitle = "Week Calendar - Continuous scroll, custom day content width, single selection.", + showToolBar = true, + ), + + // Example8( +// title = "Example 8", +// subtitle = "Fullscreen Horizontal Calendar - Month header and footer, paged horizontal scrolling. Shows the \"Fill\" option of ContentHeightMode property.", +// showToolBar = false, +// ), + Example9( + title = "Example 8", + subtitle = "Month and week calendar toggle with animations.", + showToolBar = true, + ), +} + +@Composable +fun ListPage(click: (Page) -> Unit) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + ) { + items(Page.entries.drop(1)) { item -> + Column( + modifier = Modifier + .fillParentMaxWidth() + .clickable { click(item) } + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + val titleStyle = MaterialTheme.typography.titleMedium + Text( + text = item.title, + fontWeight = FontWeight.Medium, + style = titleStyle, + ) + val subtitleStyle = MaterialTheme.typography.bodyMedium + Text( + text = item.subtitle, + style = subtitleStyle, + ) + } + HorizontalDivider() + } + } +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/PageAnimation.kt b/compose-multiplatform/sample/src/commonMain/kotlin/PageAnimation.kt new file mode 100644 index 00000000..91608655 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/PageAnimation.kt @@ -0,0 +1,73 @@ +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable + +private const val ANIM_DURATION_MILLIS = 400 + +fun NavGraphBuilder.horizontallyAnimatedComposable( + route: String, + content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit, +) { + composable( + route = route, + content = content, + enterTransition = { + slideIntoContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Left, + animationSpec = tween(ANIM_DURATION_MILLIS), + ) + }, + exitTransition = { + slideOutOfContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Left, + animationSpec = tween(ANIM_DURATION_MILLIS), + ) + }, + popEnterTransition = { + slideIntoContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Right, + animationSpec = tween(ANIM_DURATION_MILLIS), + ) + }, + popExitTransition = { + slideOutOfContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Right, + animationSpec = tween(ANIM_DURATION_MILLIS), + ) + }, + ) +} + +fun NavGraphBuilder.verticallyAnimatedComposable( + route: String, + content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit, +) { + composable( + route = route, + content = content, + enterTransition = { + slideIntoContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Up, + animationSpec = tween(ANIM_DURATION_MILLIS), + ) + }, + exitTransition = { + fadeOut(animationSpec = tween(ANIM_DURATION_MILLIS)) + }, + popEnterTransition = { + fadeIn(animationSpec = tween(ANIM_DURATION_MILLIS)) + }, + popExitTransition = { + slideOutOfContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Down, + animationSpec = tween(ANIM_DURATION_MILLIS), + ) + }, + ) +} diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt b/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt index a6e0cfe8..0fe2edbf 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/SimpleCalendarTitle.kt @@ -1,3 +1,4 @@ + import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -19,9 +20,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -39,7 +38,7 @@ fun SimpleCalendarTitle( verticalAlignment = Alignment.CenterVertically, ) { CalendarNavigationIcon( - icon = Icons.AutoMirrored.Filled.KeyboardArrowLeft, + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft, contentDescription = "Previous", onClick = goToPrevious, ) @@ -47,13 +46,13 @@ fun SimpleCalendarTitle( modifier = Modifier .weight(1f) .testTag("MonthTitle"), - text = "${currentMonth.month.name.lowercase().capitalize(Locale.current)} ${currentMonth.year}", + text = currentMonth.displayText(), fontSize = 22.sp, textAlign = TextAlign.Center, fontWeight = FontWeight.Medium, ) CalendarNavigationIcon( - icon = Icons.AutoMirrored.Filled.KeyboardArrowRight, + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = "Next", onClick = goToNext, ) @@ -62,7 +61,7 @@ fun SimpleCalendarTitle( @Composable private fun CalendarNavigationIcon( - icon: ImageVector, + imageVector: ImageVector, contentDescription: String, onClick: () -> Unit, ) = Box( @@ -77,7 +76,7 @@ private fun CalendarNavigationIcon( .fillMaxSize() .padding(4.dp) .align(Alignment.Center), - imageVector = icon, + imageVector = imageVector, contentDescription = contentDescription, ) } diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt new file mode 100644 index 00000000..24b61eeb --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt @@ -0,0 +1,178 @@ +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import com.kizitonwose.calendar.compose.CalendarLayoutInfo +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarState +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.Week +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.LocalDate +import kotlinx.datetime.minus +import kotlinx.datetime.plus + +fun Modifier.clickable( + enabled: Boolean = true, + showRipple: Boolean = true, + onClickLabel: String? = null, + role: Role? = null, + onClick: () -> Unit, +): Modifier = composed { + clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = if (showRipple) LocalIndication.current else null, + enabled = enabled, + onClickLabel = onClickLabel, + role = role, + onClick = onClick, + ) +} + +@Composable +fun NavigationIcon(onBackClick: () -> Unit) { + Box( + modifier = Modifier + .fillMaxHeight() + .aspectRatio(1f) + .padding(8.dp) + .clip(shape = CircleShape) + .clickable(role = Role.Button, onClick = onBackClick), + ) { + Icon( + tint = Color.White, + modifier = Modifier.align(Alignment.Center), + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + ) + } +} + +/** + * Alternative way to find the first fully visible month in the layout. + * + * @see [rememberFirstVisibleMonthAfterScroll] + * @see [rememberFirstMostVisibleMonth] + */ +@Composable +fun rememberFirstCompletelyVisibleMonth(state: CalendarState): CalendarMonth { + val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } + // Only take non-null values as null will be produced when the + // list is mid-scroll as no index will be completely visible. + LaunchedEffect(state) { + snapshotFlow { state.layoutInfo.completelyVisibleMonths.firstOrNull() } + .filterNotNull() + .collect { month -> visibleMonth.value = month } + } + return visibleMonth.value +} + +/** + * Returns the first visible month in a paged calendar **after** scrolling stops. + * + * @see [rememberFirstCompletelyVisibleMonth] + * @see [rememberFirstMostVisibleMonth] + */ +@Composable +fun rememberFirstVisibleMonthAfterScroll(state: CalendarState): CalendarMonth { + val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } + LaunchedEffect(state) { + snapshotFlow { state.isScrollInProgress } + .filter { scrolling -> !scrolling } + .collect { visibleMonth.value = state.firstVisibleMonth } + } + return visibleMonth.value +} + +/** + * Find first visible week in a paged week calendar **after** scrolling stops. + */ +@Composable +fun rememberFirstVisibleWeekAfterScroll(state: WeekCalendarState): Week { + val visibleWeek = remember(state) { mutableStateOf(state.firstVisibleWeek) } + LaunchedEffect(state) { + snapshotFlow { state.isScrollInProgress } + .filter { scrolling -> !scrolling } + .collect { visibleWeek.value = state.firstVisibleWeek } + } + return visibleWeek.value +} + +/** + * Find the first month on the calendar visible up to the given [viewportPercent] size. + * + * @see [rememberFirstCompletelyVisibleMonth] + * @see [rememberFirstVisibleMonthAfterScroll] + */ +@Composable +fun rememberFirstMostVisibleMonth( + state: CalendarState, + viewportPercent: Float = 50f, +): CalendarMonth { + val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } + LaunchedEffect(state) { + snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } + .filterNotNull() + .collect { month -> visibleMonth.value = month } + } + return visibleMonth.value +} + +private val CalendarLayoutInfo.completelyVisibleMonths: List + get() { + val visibleItemsInfo = this.visibleMonthsInfo.toMutableList() + return if (visibleItemsInfo.isEmpty()) { + emptyList() + } else { + val lastItem = visibleItemsInfo.last() + val viewportSize = this.viewportEndOffset + this.viewportStartOffset + if (lastItem.offset + lastItem.size > viewportSize) { + visibleItemsInfo.removeLast() + } + val firstItem = visibleItemsInfo.firstOrNull() + if (firstItem != null && firstItem.offset < this.viewportStartOffset) { + visibleItemsInfo.removeFirst() + } + visibleItemsInfo.map { it.month } + } + } + +private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { + return if (visibleMonthsInfo.isEmpty()) { + null + } else { + val viewportSize = (viewportEndOffset + viewportStartOffset) * viewportPercent / 100f + visibleMonthsInfo.firstOrNull { itemInfo -> + if (itemInfo.offset < 0) { + itemInfo.offset + itemInfo.size >= viewportSize + } else { + itemInfo.size - itemInfo.offset >= viewportSize + } + }?.month + } +} + +internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) + +internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) diff --git a/compose-multiplatform/sample/src/iosMain/kotlin/Format.ios.kt b/compose-multiplatform/sample/src/iosMain/kotlin/Format.ios.kt new file mode 100644 index 00000000..5291f3fa --- /dev/null +++ b/compose-multiplatform/sample/src/iosMain/kotlin/Format.ios.kt @@ -0,0 +1,20 @@ +import androidx.compose.ui.text.intl.Locale +import com.kizitonwose.calendar.core.daysOfWeek +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.Month +import platform.Foundation.NSCalendar +import platform.Foundation.NSLocale + +actual fun Month.getDisplayName(short: Boolean, locale: Locale): String = + NSCalendar.currentCalendar.let { + it.setLocale(NSLocale(locale.toLanguageTag())) + it.monthSymbols[Month.entries.indexOf(this)] as String + } + +actual fun DayOfWeek.getShortDisplayName(locale: Locale): String = + NSCalendar.currentCalendar.let { + it.setLocale(NSLocale(locale.toLanguageTag())) + it.shortWeekdaySymbols[sundayBasedWeek.indexOf(this)] as String + } + +private val sundayBasedWeek = daysOfWeek(firstDayOfWeek = DayOfWeek.SUNDAY) diff --git a/compose-multiplatform/sample/src/jvmMain/kotlin/Format.jvm.kt b/compose-multiplatform/sample/src/jvmMain/kotlin/Format.jvm.kt new file mode 100644 index 00000000..13b90094 --- /dev/null +++ b/compose-multiplatform/sample/src/jvmMain/kotlin/Format.jvm.kt @@ -0,0 +1,14 @@ +import androidx.compose.ui.text.intl.Locale +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.Month +import java.time.format.TextStyle +import java.util.Locale as JavaLocale + +actual fun Month.getDisplayName(short: Boolean, locale: Locale): String { + val style = if (short) TextStyle.SHORT else TextStyle.FULL + return getDisplayName(style, JavaLocale.forLanguageTag(locale.toLanguageTag())) +} + +actual fun DayOfWeek.getShortDisplayName(locale: Locale): String { + return getDisplayName(TextStyle.SHORT, JavaLocale.forLanguageTag(locale.toLanguageTag())) +} diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt b/compose-multiplatform/sample/src/jvmMain/kotlin/main.kt similarity index 100% rename from compose-multiplatform/sample/src/desktopMain/kotlin/main.kt rename to compose-multiplatform/sample/src/jvmMain/kotlin/main.kt diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt new file mode 100644 index 00000000..46e06b1e --- /dev/null +++ b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt @@ -0,0 +1,15 @@ +import androidx.compose.ui.text.capitalize +import androidx.compose.ui.text.intl.Locale +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.Month + +actual fun Month.getDisplayName(short: Boolean, locale: Locale): String { + val name = name.capitalize(EnLocale) + return if (short) name.take(3) else name +} + +actual fun DayOfWeek.getShortDisplayName(locale: Locale): String { + return name.capitalize(EnLocale).first().toString() +} + +private val EnLocale = Locale("en-US") diff --git a/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt b/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt index 8ee5e512..7df1adfb 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt @@ -17,7 +17,7 @@ fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List Unit, ) = Box( @@ -77,7 +78,7 @@ private fun CalendarNavigationIcon( .fillMaxSize() .padding(4.dp) .align(Alignment.Center), - painter = icon, + imageVector = imageVector, contentDescription = contentDescription, ) } diff --git a/sample/src/main/res/drawable/ic_airplane_takeoff.xml b/sample/src/main/res/drawable/ic_airplane_takeoff.xml index c0f531a1..10ffb020 100644 --- a/sample/src/main/res/drawable/ic_airplane_takeoff.xml +++ b/sample/src/main/res/drawable/ic_airplane_takeoff.xml @@ -6,4 +6,4 @@ - \ No newline at end of file + diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml index a012e7f6..19ec2b53 100644 --- a/sample/src/main/res/values/colors.xml +++ b/sample/src/main/res/values/colors.xml @@ -28,7 +28,7 @@ #282828 #DCDCDC #616161 - #b2ebf2 + #B2EBF2 #F2C4B2 #B2B8F2 #5D4037 @@ -37,7 +37,7 @@ #388E3C #00796B #C2185B - #c62828 + #C62828 #1565C0 #0097A7 #BF000000 From d26047b2324b5f3b4525ab5c1c8b8b3ee4147579 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Mon, 1 Jul 2024 21:01:49 +0200 Subject: [PATCH 08/21] Improve size calculation in samples --- .../library/build.gradle.kts | 21 ++++++----- .../calendar/core/Extensions.wasmJs.kt | 9 ----- compose-multiplatform/sample/build.gradle.kts | 22 ++++++------ .../drawable/compose-multiplatform.xml | 36 ------------------- .../src/commonMain/kotlin/Example4Page.kt | 14 ++------ .../src/commonMain/kotlin/Example7Page.kt | 14 +++----- 6 files changed, 32 insertions(+), 84 deletions(-) delete mode 100644 compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml diff --git a/compose-multiplatform/library/build.gradle.kts b/compose-multiplatform/library/build.gradle.kts index c6636023..a9de7644 100644 --- a/compose-multiplatform/library/build.gradle.kts +++ b/compose-multiplatform/library/build.gradle.kts @@ -1,4 +1,3 @@ - import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi @@ -20,9 +19,11 @@ kotlin { binaries.library() } - androidTarget {} + androidTarget { + publishLibraryVariants("release") + } - jvm("jvm") // jvm("desktop") + jvm("desktop") listOf( iosX64(), @@ -38,12 +39,15 @@ kotlin { applyDefaultHierarchyTemplate() sourceSets { - val jvmMain by getting val commonMain by getting val wasmJsMain by getting val nativeMain by getting - - androidMain.get().dependsOn(jvmMain) + val desktopMain by getting + val androidMain by getting + val jvmMain by creating { + dependsOn(commonMain) + } + androidMain.dependsOn(jvmMain) androidMain.dependencies { implementation(compose.preview) } @@ -61,7 +65,8 @@ kotlin { wasmJsMain.dependsOn(this) dependencies {} } - jvmMain.dependencies { + desktopMain.dependsOn(jvmMain) + desktopMain.dependencies { implementation(compose.desktop.currentOs) } } @@ -72,7 +77,7 @@ kotlin { } android { - namespace = "com.kizitonwose.calendarx" + namespace = "com.kizitonwose.calendar.compose.multiplatform" compileSdk = Android.compileSdk sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt index 9fd45963..379479f8 100644 --- a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt @@ -17,12 +17,3 @@ actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo#firstday private fun jsFirstDayFromTag(languageTag: String): Int = js("new Intl.Locale(languageTag).weekInfo?.firstDay") - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale -//internal external interface IntlLocale { -// val language: String -// val script: String? -// val region: String? -// val baseName: String -//} -//internal fun parseLanguageTagToIntlLocale(languageTag: String): IntlLocale = js("new Intl.Locale(languageTag)") diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts index 379a213b..12467c72 100644 --- a/compose-multiplatform/sample/build.gradle.kts +++ b/compose-multiplatform/sample/build.gradle.kts @@ -32,7 +32,7 @@ kotlin { androidTarget {} - jvm("jvm") // jvm("desktop") + jvm("desktop") listOf( iosX64(), @@ -48,12 +48,15 @@ kotlin { applyDefaultHierarchyTemplate() sourceSets { - val jvmMain by getting val commonMain by getting val wasmJsMain by getting val nativeMain by getting - - androidMain.get().dependsOn(jvmMain) + val desktopMain by getting + val androidMain by getting + val jvmMain by creating { + dependsOn(commonMain) + } + androidMain.dependsOn(jvmMain) androidMain.dependencies { implementation(compose.preview) } @@ -73,9 +76,8 @@ kotlin { wasmJsMain.dependsOn(this) dependencies {} } - jvmMain.dependencies { -// implementation(compose.desktop.currentOs) - } + desktopMain.dependsOn(jvmMain) + desktopMain.dependencies {} } @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { @@ -84,7 +86,7 @@ kotlin { } android { - namespace = "com.kizitonwose.calendarx" + namespace = "com.kizitonwose.calendar.compose.multiplatform" compileSdk = Android.compileSdk sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") @@ -92,7 +94,7 @@ android { sourceSets["main"].resources.srcDirs("src/commonMain/resources") defaultConfig { - applicationId = "com.kizitonwose.calendarx" + applicationId = "com.kizitonwose.calendar.compose.multiplatform" minSdk = Android.minSdkSampleApp targetSdk = Android.targetSdk versionCode = 1 @@ -132,7 +134,7 @@ compose.desktop { nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) - packageName = "com.kizitonwose.calendar" + packageName = "com.kizitonwose.calendar.compose.multiplatform" packageVersion = "1.0.0" } } diff --git a/compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml b/compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml deleted file mode 100644 index b7d4281f..00000000 --- a/compose-multiplatform/sample/src/commonMain/composeResources/drawable/compose-multiplatform.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt index ed5d475e..717f3d83 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt @@ -2,6 +2,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -15,15 +16,12 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -44,11 +42,9 @@ fun Example4Page() { val currentMonth = remember { YearMonth.now() } val startMonth = remember { currentMonth } val endMonth = remember { currentMonth.plusMonths(500) } - val widthPx = remember { mutableStateOf(0) } - Column( + BoxWithConstraints( modifier = Modifier .fillMaxSize() - .onSizeChanged { widthPx.value = it.width } .background(Color.White), ) { val state = rememberCalendarState( @@ -66,11 +62,7 @@ fun Example4Page() { monthContainer = { _, container -> Box( modifier = Modifier - .width( - with(LocalDensity.current) { - (widthPx.value * 0.73f).toDp() - }, - ) + .width(maxWidth * 0.73f) .padding(8.dp) .clip(shape = RoundedCornerShape(8.dp)) .border( diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt index aa0db9e3..aa93c795 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example7Page.kt @@ -1,8 +1,10 @@ + import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -22,8 +24,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -40,11 +40,9 @@ fun Example7Page() { val startDate = remember { currentDate.minusDays(500) } val endDate = remember { currentDate.plusDays(500) } var selection by remember { mutableStateOf(null) } - val widthPx = remember { mutableStateOf(0) } - Column( + BoxWithConstraints( modifier = Modifier .fillMaxSize() - .onSizeChanged { widthPx.value = it.width } .background(Color.White), ) { val state = rememberWeekCalendarState( @@ -62,11 +60,7 @@ fun Example7Page() { Day( // If paged scrolling is disabled (calendarScrollPaged = false), // you must set the day width on the WeekCalendar! - modifier = Modifier.width( - with(LocalDensity.current) { - (widthPx.value / 9f).toDp() - }, - ), + modifier = Modifier.width(this@BoxWithConstraints.maxWidth / 9f), date = day.date, selected = selection == day.date, ) { From e3e22aff3a5738e77fc03ba434b2816b0d994a73 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Tue, 2 Jul 2024 20:15:58 +0200 Subject: [PATCH 09/21] Improve samples --- .../calendar/core/FirstDayFromMap.kt | 6 +- .../src/androidMain/AndroidManifest.xml | 2 +- .../src/androidMain/kotlin/Device.android.kt | 1 + .../compose/multiplatform}/MainActivity.kt | 5 +- .../src/androidMain/res/values/strings.xml | 4 +- .../sample/src/commonMain/kotlin/App.kt | 165 ++++++++++++------ .../sample/src/commonMain/kotlin/Device.kt | 1 + .../src/commonMain/kotlin/Example3Page.kt | 16 +- .../src/commonMain/kotlin/Example5Page.kt | 5 +- .../sample/src/commonMain/kotlin/Format.kt | 8 +- .../sample/src/commonMain/kotlin/ListPage.kt | 2 +- .../sample/src/commonMain/kotlin/Utils.kt | 11 +- .../src/desktopMain/kotlin/Device.desktop.kt | 1 + .../{jvmMain => desktopMain}/kotlin/main.kt | 0 .../sample/src/iosMain/kotlin/Device.ios.kt | 1 + .../src/wasmJsMain/kotlin/Device.wasmJs.kt | 1 + .../src/wasmJsMain/kotlin/Format.wasmJs.kt | 7 +- 17 files changed, 161 insertions(+), 75 deletions(-) create mode 100644 compose-multiplatform/sample/src/androidMain/kotlin/Device.android.kt rename compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/{calendarx => calendar/compose/multiplatform}/MainActivity.kt (89%) create mode 100644 compose-multiplatform/sample/src/commonMain/kotlin/Device.kt create mode 100644 compose-multiplatform/sample/src/desktopMain/kotlin/Device.desktop.kt rename compose-multiplatform/sample/src/{jvmMain => desktopMain}/kotlin/main.kt (100%) create mode 100644 compose-multiplatform/sample/src/iosMain/kotlin/Device.ios.kt create mode 100644 compose-multiplatform/sample/src/wasmJsMain/kotlin/Device.wasmJs.kt diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt index 3e6189a3..e4a0e91f 100644 --- a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/FirstDayFromMap.kt @@ -5,11 +5,11 @@ import androidx.compose.ui.text.toLowerCase import kotlinx.datetime.DayOfWeek internal fun firstDayFromMap(locale: Locale): DayOfWeek = - firstDays[locale.region]?.let { firstDayValues[it] } ?: DayOfWeek.SUNDAY + firstDays[locale.region]?.let { firstDayValues[it] } ?: DayOfWeek.MONDAY -private val EnLocale = Locale("en-US") +private val enLocale = Locale("en-US") private var firstDayValues = DayOfWeek.entries.associateBy { - it.name.toLowerCase(EnLocale).take(3) + it.name.toLowerCase(enLocale).take(3) } // https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-core/supplemental/weekData.json#L7 diff --git a/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml b/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml index c5db0b15..b87d9441 100644 --- a/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml +++ b/compose-multiplatform/sample/src/androidMain/AndroidManifest.xml @@ -20,4 +20,4 @@ - \ No newline at end of file + diff --git a/compose-multiplatform/sample/src/androidMain/kotlin/Device.android.kt b/compose-multiplatform/sample/src/androidMain/kotlin/Device.android.kt new file mode 100644 index 00000000..3b8f0b67 --- /dev/null +++ b/compose-multiplatform/sample/src/androidMain/kotlin/Device.android.kt @@ -0,0 +1 @@ +actual fun isMobile(): Boolean = true diff --git a/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt b/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendar/compose/multiplatform/MainActivity.kt similarity index 89% rename from compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt rename to compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendar/compose/multiplatform/MainActivity.kt index c750bbc5..be455aec 100644 --- a/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendarx/MainActivity.kt +++ b/compose-multiplatform/sample/src/androidMain/kotlin/com/kizitonwose/calendar/compose/multiplatform/MainActivity.kt @@ -1,4 +1,4 @@ -package com.kizitonwose.calendarx +package com.kizitonwose.calendar.compose.multiplatform import App import android.os.Bundle @@ -10,7 +10,6 @@ import androidx.compose.ui.tooling.preview.Preview class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { App() } @@ -21,4 +20,4 @@ class MainActivity : ComponentActivity() { @Composable fun AppAndroidPreview() { App() -} \ No newline at end of file +} diff --git a/compose-multiplatform/sample/src/androidMain/res/values/strings.xml b/compose-multiplatform/sample/src/androidMain/res/values/strings.xml index 3385b162..5b10d4cb 100644 --- a/compose-multiplatform/sample/src/androidMain/res/values/strings.xml +++ b/compose-multiplatform/sample/src/androidMain/res/values/strings.xml @@ -1,3 +1,3 @@ - KotlinProject - \ No newline at end of file + Calendar Multiplatform Sample + diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt index a8bb12c6..78a4fae8 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -1,15 +1,22 @@ import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -19,6 +26,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost @@ -26,51 +34,27 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import kotlinx.coroutines.launch import org.jetbrains.compose.ui.tooling.preview.Preview +import kotlin.math.roundToInt @Composable @Preview fun App() { - MaterialTheme { - Box( + MaterialTheme(MaterialTheme.colorScheme.copy(primary = Colors.primary)) { + BoxWithConstraints( Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter, ) { - val primaryColor = Colors.primary - var toolBarTitle by remember { mutableStateOf("") } - var toolBarVisible by remember { mutableStateOf(true) } - val navController = rememberNavController() - val coroutineScope = rememberCoroutineScope() - val snackbarHostState = remember { SnackbarHostState() } - LaunchedEffect(navController) { - navController.currentBackStackEntryFlow.collect { backStackEntry -> - val page = Page.valueOf(backStackEntry.destination.route ?: return@collect) - toolBarTitle = page.title - toolBarVisible = page.showToolBar + if (maxWidth >= 600.dp) { + val widthPx = maxWidth.value.roundToInt() + val count = if (widthPx in 650..800) 2 else widthPx / 400 + Row { + repeat(count) { + VerticalDivider() + Demo(modifier = Modifier.weight(1f)) + } } - } - MaterialTheme(colorScheme = MaterialTheme.colorScheme.copy(primary = primaryColor)) { - Scaffold( - modifier = Modifier.widthIn(max = 600.dp), - topBar = { - if (toolBarVisible) { - AppToolBar(title = toolBarTitle, navController) - } - }, - snackbarHost = { - SnackbarHost(hostState = snackbarHostState) - }, - content = { - AppNavHost( - modifier = Modifier.padding(it), - navController = navController, - showSnack = { message -> - coroutineScope.launch { - snackbarHostState.showSnackbar(message) - } - }, - ) - }, - ) + } else { + Demo(modifier = Modifier.fillMaxSize()) } } } @@ -78,24 +62,85 @@ fun App() { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun AppToolBar(title: String, navController: NavHostController) { - TopAppBar( - colors = TopAppBarDefaults.topAppBarColors(), - title = { Text(text = title) }, - navigationIcon = navIcon@{ - val destination = navController.currentBackStackEntry?.destination - val page = Page.valueOf(destination?.route ?: return@navIcon) - if (page == Page.List) { - Unit - } else { - NavigationIcon { - navController.popBackStack() +private fun Demo(modifier: Modifier = Modifier) { + var toolBarTitle by remember { mutableStateOf("") } + var toolBarVisible by remember { mutableStateOf(true) } + var toolBarBackButtonVisible by remember { mutableStateOf(true) } + val navController = rememberNavController() + val coroutineScope = rememberCoroutineScope() + val snackbarHostState = remember { SnackbarHostState() } + LaunchedEffect(navController) { + navController.currentBackStackEntryFlow.collect { backStackEntry -> + val page = Page.valueOf(backStackEntry.destination.route ?: return@collect) + toolBarTitle = page.title + toolBarVisible = page.showToolBar + toolBarBackButtonVisible = page != Page.List + } + } + Scaffold( + modifier = modifier, + topBar = { + if (toolBarVisible) { + Column { + ExampleToolbar( + title = toolBarTitle, + colors = if (isMobile()) blueToolbar else whiteToolbar, + navigationIcon = navIcon@{ + if (toolBarBackButtonVisible) { + NavigationIcon( + tint = if (isMobile()) Color.White else Color.Black, + ) { + navController.popBackStack() + } + } + }, + ) + // Add divider to separate the white toolbar. + if (!isMobile()) { + HorizontalDivider() + } } } }, + snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + }, + content = { + AppNavHost( + modifier = Modifier + .padding(it), + navController = navController, + showSnack = { message -> + coroutineScope.launch { + snackbarHostState.showSnackbar(message) + } + }, + ) + }, ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ExampleToolbar( + title: String, + colors: TopAppBarColors = blueToolbar, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit = {}, +) = TopAppBar( + modifier = modifier.height(64.dp), + colors = colors, + title = { + Box(Modifier.fillMaxHeight()) { + Text( + modifier = Modifier.align(Alignment.Center), + text = title, + ) + } + }, + navigationIcon = navigationIcon, +) + @Composable private fun AppNavHost( modifier: Modifier = Modifier, @@ -120,7 +165,7 @@ private fun AppNavHost( }, ) } - verticallyAnimatedComposable(Page.Example3.name) { Example3Page() } + verticallyAnimatedComposable(Page.Example3.name) { Example3Page { navController.popBackStack() } } horizontallyAnimatedComposable(Page.Example4.name) { Example4Page() } horizontallyAnimatedComposable(Page.Example5.name) { Example5Page { navController.popBackStack() } } horizontallyAnimatedComposable(Page.Example6.name) { Example6Page() } @@ -128,3 +173,21 @@ private fun AppNavHost( horizontallyAnimatedComposable(Page.Example9.name) { Example9Page() } } } + + +@OptIn(ExperimentalMaterial3Api::class) +private val blueToolbar + @Composable + get() = TopAppBarDefaults.topAppBarColors( + containerColor = Colors.primary, + titleContentColor = Color.White, + ) + +@OptIn(ExperimentalMaterial3Api::class) +private val whiteToolbar + @Composable + get() = TopAppBarDefaults.topAppBarColors( + containerColor = Color.White, + titleContentColor = Color.Black, + ) + diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Device.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Device.kt new file mode 100644 index 00000000..3fd8c663 --- /dev/null +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Device.kt @@ -0,0 +1 @@ +expect fun isMobile(): Boolean diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt index ea4ff919..c68d7974 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt @@ -1,3 +1,4 @@ + import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -7,6 +8,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -18,6 +20,8 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LocalContentColor import androidx.compose.material3.Text @@ -63,7 +67,7 @@ private val selectedItemColor: Color = Colors.example5TextGrey private val inActiveTextColor: Color = Colors.example5TextGreyLight @Composable -fun Example3Page() { +fun Example3Page(close: () -> Unit = {}) { val currentMonth = remember { YearMonth.now() } val startMonth = remember { currentMonth.minusMonths(500) } val endMonth = remember { currentMonth.plusMonths(500) } @@ -144,6 +148,16 @@ fun Example3Page() { FlightInformation(flight) } } + if (!isMobile()) { + Spacer(Modifier.height(28.dp)) + Button( + onClick = close, + colors = ButtonDefaults.buttonColors().copy(containerColor = toolbarColor), + modifier = Modifier.align(Alignment.CenterHorizontally), + ) { + Text("Close") + } + } } } } diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt index 30d11d76..6298e1a6 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example5Page.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -47,8 +46,8 @@ fun Example5Page(close: () -> Unit = {}) { firstVisibleWeekDate = currentDate, ) val visibleWeek = rememberFirstVisibleWeekAfterScroll(state) - TopAppBar( - title = { Text(text = getWeekPageTitle(visibleWeek)) }, + ExampleToolbar( + title = getWeekPageTitle(visibleWeek), navigationIcon = { NavigationIcon(onBackClick = close) }, ) WeekCalendar( diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt index acfc7e99..405ebb9d 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Format.kt @@ -11,12 +11,12 @@ fun YearMonth.displayText(short: Boolean = false): String { } fun Month.displayText(short: Boolean = true): String { - return getDisplayName(short, EnLocale) + return getDisplayName(short, enLocale) } fun DayOfWeek.displayText(uppercase: Boolean = false): String { - return getShortDisplayName(EnLocale).let { value -> - if (uppercase) value.toUpperCase(EnLocale) else value + return getShortDisplayName(enLocale).let { value -> + if (uppercase) value.toUpperCase(enLocale) else value } } @@ -24,7 +24,7 @@ expect fun Month.getDisplayName(short: Boolean, locale: Locale): String expect fun DayOfWeek.getShortDisplayName(locale: Locale): String -private val EnLocale = Locale("en-US") +private val enLocale = Locale("en-US") fun getWeekPageTitle(week: Week): String { val firstDate = week.days.first().date diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt b/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt index 7c778331..b88cffc9 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/ListPage.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.unit.dp enum class Page(val title: String, val subtitle: String, val showToolBar: Boolean) { List( - title = "Calendar Compose Sample", + title = "Calendar Library", subtitle = "", showToolBar = true, ), diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt index 24b61eeb..42b37b43 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import com.kizitonwose.calendar.compose.CalendarLayoutInfo @@ -51,7 +52,11 @@ fun Modifier.clickable( } @Composable -fun NavigationIcon(onBackClick: () -> Unit) { +fun NavigationIcon( + tint: Color = Color.White, + imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack, + onBackClick: () -> Unit, +) { Box( modifier = Modifier .fillMaxHeight() @@ -61,9 +66,9 @@ fun NavigationIcon(onBackClick: () -> Unit) { .clickable(role = Role.Button, onClick = onBackClick), ) { Icon( - tint = Color.White, + tint = tint, modifier = Modifier.align(Alignment.Center), - imageVector = Icons.AutoMirrored.Filled.ArrowBack, + imageVector = imageVector, contentDescription = "Back", ) } diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/Device.desktop.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/Device.desktop.kt new file mode 100644 index 00000000..974565dc --- /dev/null +++ b/compose-multiplatform/sample/src/desktopMain/kotlin/Device.desktop.kt @@ -0,0 +1 @@ +actual fun isMobile(): Boolean = false diff --git a/compose-multiplatform/sample/src/jvmMain/kotlin/main.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt similarity index 100% rename from compose-multiplatform/sample/src/jvmMain/kotlin/main.kt rename to compose-multiplatform/sample/src/desktopMain/kotlin/main.kt diff --git a/compose-multiplatform/sample/src/iosMain/kotlin/Device.ios.kt b/compose-multiplatform/sample/src/iosMain/kotlin/Device.ios.kt new file mode 100644 index 00000000..3b8f0b67 --- /dev/null +++ b/compose-multiplatform/sample/src/iosMain/kotlin/Device.ios.kt @@ -0,0 +1 @@ +actual fun isMobile(): Boolean = true diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Device.wasmJs.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Device.wasmJs.kt new file mode 100644 index 00000000..974565dc --- /dev/null +++ b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Device.wasmJs.kt @@ -0,0 +1 @@ +actual fun isMobile(): Boolean = false diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt index 46e06b1e..7be940b6 100644 --- a/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt +++ b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Format.wasmJs.kt @@ -1,15 +1,16 @@ import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toLowerCase import kotlinx.datetime.DayOfWeek import kotlinx.datetime.Month actual fun Month.getDisplayName(short: Boolean, locale: Locale): String { - val name = name.capitalize(EnLocale) + val name = name.toLowerCase(enLocale).capitalize(enLocale) return if (short) name.take(3) else name } actual fun DayOfWeek.getShortDisplayName(locale: Locale): String { - return name.capitalize(EnLocale).first().toString() + return name.toLowerCase(enLocale).capitalize(enLocale).take(3) } -private val EnLocale = Locale("en-US") +private val enLocale = Locale("en-US") From 2ba43092ee8e09ff50adbdc1cd313bc8443921d1 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Thu, 4 Jul 2024 19:03:17 +0200 Subject: [PATCH 10/21] Use serializable object for `rememberSaveable` --- .../library/gradle.properties | 3 +++ .../compose/heatmapcalendar/HeatMapWeek.kt | 3 +-- .../compose/weekcalendar/WeekCalendar.kt | 3 ++- .../compose/weekcalendar/WeekCalendarState.kt | 15 +++++++------ .../kizitonwose/calendar/core/CalendarDay.kt | 2 +- .../calendar/core/CalendarMonth.kt | 2 +- .../kizitonwose/calendar/core/Extensions.kt | 1 - .../calendar/core/JvmSerializableLocalDate.kt | 21 +++++++++++++++++++ .../com/kizitonwose/calendar/core/Week.kt | 2 +- .../com/kizitonwose/calendar/core/WeekDay.kt | 2 +- .../kizitonwose/calendar/core/YearMonth.kt | 4 ++++ .../kizitonwose/calendar/core/Converters.kt | 2 ++ gradle/libs.versions.toml | 3 ++- 13 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 compose-multiplatform/library/gradle.properties create mode 100644 compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializableLocalDate.kt diff --git a/compose-multiplatform/library/gradle.properties b/compose-multiplatform/library/gradle.properties new file mode 100644 index 00000000..c4254cbc --- /dev/null +++ b/compose-multiplatform/library/gradle.properties @@ -0,0 +1,3 @@ +POM_ARTIFACT_ID=compose-multiplatform +POM_DESCRIPTION=A highly customizable calendar library for Compose Multiplatform, backed by LazyRow/LazyColumn. +POM_PACKAGING=aar diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt index 611f53a9..99e88ff4 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt @@ -3,7 +3,6 @@ package com.kizitonwose.calendar.compose.heatmapcalendar import androidx.compose.runtime.Immutable import com.kizitonwose.calendar.compose.HeatMapCalendar import com.kizitonwose.calendar.core.CalendarDay -import com.kizitonwose.calendar.core.JvmSerializable /** * Represents a week on the heatmap calendar. @@ -16,4 +15,4 @@ import com.kizitonwose.calendar.core.JvmSerializable * @param days the days in this week. */ @Immutable -data class HeatMapWeek(val days: List) : JvmSerializable +data class HeatMapWeek(val days: List) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt index 2d2cfc9c..63f57649 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendar.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clipToBounds import com.kizitonwose.calendar.compose.CalendarDefaults.flingBehavior import com.kizitonwose.calendar.core.Week import com.kizitonwose.calendar.core.WeekDay +import com.kizitonwose.calendar.core.toJvmSerializableLocalDate @Composable internal fun WeekCalendarImpl( @@ -38,7 +39,7 @@ internal fun WeekCalendarImpl( ) { items( count = state.weekIndexCount, - key = { offset -> state.store[offset].days.first().date }, + key = { offset -> state.store[offset].days.first().date.toJvmSerializableLocalDate() }, ) { offset -> val week = state.store[offset] Column( diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt index a37f3a25..1982a956 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.listSaver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import com.kizitonwose.calendar.core.JvmSerializableLocalDate import com.kizitonwose.calendar.core.Week import com.kizitonwose.calendar.core.WeekDayPosition import com.kizitonwose.calendar.core.YearMonth @@ -22,6 +23,8 @@ import com.kizitonwose.calendar.core.atEndOfMonth import com.kizitonwose.calendar.core.atStartOfMonth import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale import com.kizitonwose.calendar.core.now +import com.kizitonwose.calendar.core.toJvmSerializableLocalDate +import com.kizitonwose.calendar.core.toLocalDate import com.kizitonwose.calendar.data.DataStore import com.kizitonwose.calendar.data.VisibleItemState import com.kizitonwose.calendar.data.getWeekCalendarAdjustedRange @@ -265,9 +268,9 @@ class WeekCalendarState internal constructor( val Saver: Saver = listSaver( save = { listOf( - it.startDate, - it.endDate, - it.firstVisibleWeek.days.first().date, + it.startDate.toJvmSerializableLocalDate(), + it.endDate.toJvmSerializableLocalDate(), + it.firstVisibleWeek.days.first().date.toJvmSerializableLocalDate(), it.firstDayOfWeek, it.listState.firstVisibleItemIndex, it.listState.firstVisibleItemScrollOffset, @@ -275,9 +278,9 @@ class WeekCalendarState internal constructor( }, restore = { WeekCalendarState( - startDate = it[0] as LocalDate, - endDate = it[1] as LocalDate, - firstVisibleWeekDate = it[2] as LocalDate, + startDate = (it[0] as JvmSerializableLocalDate).toLocalDate(), + endDate = (it[1] as JvmSerializableLocalDate).toLocalDate(), + firstVisibleWeekDate = (it[2] as JvmSerializableLocalDate).toLocalDate(), firstDayOfWeek = it[3] as DayOfWeek, visibleItemState = VisibleItemState( firstVisibleItemIndex = it[4] as Int, diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt index b8ee8d41..5e9f002d 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt @@ -10,4 +10,4 @@ import kotlinx.datetime.LocalDate * @param position the [DayPosition] for this day. */ @Immutable -data class CalendarDay(val date: LocalDate, val position: DayPosition) : JvmSerializable +data class CalendarDay(val date: LocalDate, val position: DayPosition) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt index 98bb35c4..511dc03c 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt @@ -12,7 +12,7 @@ import androidx.compose.runtime.Immutable data class CalendarMonth internal constructor( val yearMonth: YearMonth, val weekDays: List>, -) : JvmSerializable { +) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index fabeb03c..64bc8577 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -72,7 +72,6 @@ internal fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeU internal fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) internal fun YearMonth.lengthOfMonth(): Int { - DateTimeUnit.YEAR.months val thisMonthStart = atStartOfMonth() val nextMonthStart = thisMonthStart.plusMonths(1) return thisMonthStart.daysUntil(nextMonthStart) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializableLocalDate.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializableLocalDate.kt new file mode 100644 index 00000000..b0693f30 --- /dev/null +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/JvmSerializableLocalDate.kt @@ -0,0 +1,21 @@ +package com.kizitonwose.calendar.core + +import kotlinx.datetime.LocalDate + +internal data class JvmSerializableLocalDate( + val year: Int, + val monthNumber: Int, + val dayOfMonth: Int, +) : JvmSerializable + +internal fun JvmSerializableLocalDate.toLocalDate() = LocalDate( + year = year, + monthNumber = monthNumber, + dayOfMonth = dayOfMonth, +) + +internal fun LocalDate.toJvmSerializableLocalDate() = JvmSerializableLocalDate( + year = year, + monthNumber = monthNumber, + dayOfMonth = dayOfMonth, +) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt index 90c38706..67b0ad0b 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Week.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.Immutable * @param days the days in this week. */ @Immutable -data class Week internal constructor(val days: List) : JvmSerializable { +data class Week internal constructor(val days: List) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt index b9840869..7b72f8e6 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt @@ -10,4 +10,4 @@ import kotlinx.datetime.LocalDate * @param position the [WeekDayPosition] for this day. */ @Immutable -data class WeekDay(val date: LocalDate, val position: WeekDayPosition) : JvmSerializable +data class WeekDay(val date: LocalDate, val position: WeekDayPosition) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt index dd0099be..c8bb958d 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt @@ -7,6 +7,9 @@ import kotlinx.datetime.number @Immutable data class YearMonth(val year: Int, val month: Month) : Comparable, JvmSerializable { + constructor(year: Int, monthNumber: Int) : + this(year = year, month = Month(monthNumber)) + init { try { atStartOfMonth() @@ -14,6 +17,7 @@ data class YearMonth(val year: Int, val month: Month) : Comparable, J throw IllegalArgumentException("Year value $year is out of range", e) } } + /** * Same as java.time.YearMonth.compareTo() */ diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt index 9fa240cf..b1b278b1 100644 --- a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt @@ -3,3 +3,5 @@ package com.kizitonwose.calendar.core import java.time.YearMonth as jtYearMonth fun YearMonth.toJavaYearMonth(): jtYearMonth = jtYearMonth.of(year, month) + +fun jtYearMonth.toKotlinYearMonth(): YearMonth = YearMonth(year, month) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4474c8e5..2569460f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,11 +30,12 @@ compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = " compose-material = { module = "androidx.compose.material:material", version.ref = "compose" } compose-activity = { module = "androidx.activity:activity-compose", version = "1.9.0" } compose-navigation = { module = "androidx.navigation:navigation-compose", version = "2.7.7" } -jetbrains-compose-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version = "2.8.0-alpha02" } compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" } compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" } +jetbrains-compose-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version = "2.7.0-alpha07" } + [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } androidLibrary = { id = "com.android.library", version.ref = "agp" } From 0891c1077a614516367f54b2ae62c52c39ade8d3 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Thu, 4 Jul 2024 19:19:11 +0200 Subject: [PATCH 11/21] Remove unused files --- .../Configuration/Config.xcconfig | 3 - .../iosApp.xcodeproj/project.pbxproj | 403 ------------------ .../AccentColor.colorset/Contents.json | 11 - .../AppIcon.appiconset/Contents.json | 14 - .../AppIcon.appiconset/app-icon-1024.png | Bin 67285 -> 0 bytes .../iosApp/Assets.xcassets/Contents.json | 6 - .../sample-iosApp/iosApp/ContentView.swift | 21 - .../sample-iosApp/iosApp/Info.plist | 50 --- .../Preview Assets.xcassets/Contents.json | 6 - .../sample-iosApp/iosApp/iOSApp.swift | 10 - 10 files changed, 524 deletions(-) delete mode 100644 compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig delete mode 100644 compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/ContentView.swift delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/Info.plist delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json delete mode 100644 compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift diff --git a/compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig b/compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig deleted file mode 100644 index 8e809e32..00000000 --- a/compose-multiplatform/sample-iosApp/Configuration/Config.xcconfig +++ /dev/null @@ -1,3 +0,0 @@ -TEAM_ID= -BUNDLE_ID=com.kizitonwose.calendarx.KotlinProject -APP_NAME=KotlinProject \ No newline at end of file diff --git a/compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj b/compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj deleted file mode 100644 index 24eb3b48..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,403 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; - 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; - 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; - 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; - 7555FF7B242A565900829871 /* KotlinProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KotlinProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - B92378962B6B1156000C7307 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 058557D7273AAEEB004C7B11 /* Preview Content */ = { - isa = PBXGroup; - children = ( - 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - 42799AB246E5F90AF97AA0EF /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; - 7555FF72242A565900829871 = { - isa = PBXGroup; - children = ( - AB1DB47929225F7C00F7AF9C /* Configuration */, - 7555FF7D242A565900829871 /* iosApp */, - 7555FF7C242A565900829871 /* Products */, - 42799AB246E5F90AF97AA0EF /* Frameworks */, - ); - sourceTree = ""; - }; - 7555FF7C242A565900829871 /* Products */ = { - isa = PBXGroup; - children = ( - 7555FF7B242A565900829871 /* KotlinProject.app */, - ); - name = Products; - sourceTree = ""; - }; - 7555FF7D242A565900829871 /* iosApp */ = { - isa = PBXGroup; - children = ( - 058557BA273AAA24004C7B11 /* Assets.xcassets */, - 7555FF82242A565900829871 /* ContentView.swift */, - 7555FF8C242A565B00829871 /* Info.plist */, - 2152FB032600AC8F00CF470E /* iOSApp.swift */, - 058557D7273AAEEB004C7B11 /* Preview Content */, - ); - path = iosApp; - sourceTree = ""; - }; - AB1DB47929225F7C00F7AF9C /* Configuration */ = { - isa = PBXGroup; - children = ( - AB3632DC29227652001CCB65 /* Config.xcconfig */, - ); - path = Configuration; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 7555FF7A242A565900829871 /* iosApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; - buildPhases = ( - F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, - 7555FF77242A565900829871 /* Sources */, - B92378962B6B1156000C7307 /* Frameworks */, - 7555FF79242A565900829871 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = iosApp; - packageProductDependencies = ( - ); - productName = iosApp; - productReference = 7555FF7B242A565900829871 /* KotlinProject.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 7555FF73242A565900829871 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1540; - ORGANIZATIONNAME = orgName; - TargetAttributes = { - 7555FF7A242A565900829871 = { - CreatedOnToolsVersion = 11.3.1; - }; - }; - }; - buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 7555FF72242A565900829871; - packageReferences = ( - ); - productRefGroup = 7555FF7C242A565900829871 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 7555FF7A242A565900829871 /* iosApp */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 7555FF79242A565900829871 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, - 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Compile Kotlin Framework"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 7555FF77242A565900829871 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, - 7555FF83242A565900829871 /* ContentView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 7555FFA3242A565B00829871 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.3; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 7555FFA4242A565B00829871 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.3; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 7555FFA6242A565B00829871 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = "${TEAM_ID}"; - ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", - ); - INFOPLIST_FILE = iosApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.3; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-framework", - ComposeApp, - ); - PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; - PRODUCT_NAME = "${APP_NAME}"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 7555FFA7242A565B00829871 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = "${TEAM_ID}"; - ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", - ); - INFOPLIST_FILE = iosApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.3; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-framework", - ComposeApp, - ); - PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; - PRODUCT_NAME = "${APP_NAME}"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7555FFA3242A565B00829871 /* Debug */, - 7555FFA4242A565B00829871 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7555FFA6242A565B00829871 /* Debug */, - 7555FFA7242A565B00829871 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 7555FF73242A565900829871 /* Project object */; -} \ No newline at end of file diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index 0afb3cf0..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors": [ - { - "idiom": "universal" - } - ], - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index bdf2efb2..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "images": [ - { - "filename": "app-icon-1024.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" - } - ], - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png deleted file mode 100644 index 53fc536fb9ac5c1dbb27c7e1da13db3760070a11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67285 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 BXNCX( diff --git a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json deleted file mode 100644 index 74d6a722..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/compose-multiplatform/sample-iosApp/iosApp/ContentView.swift b/compose-multiplatform/sample-iosApp/iosApp/ContentView.swift deleted file mode 100644 index 3cd5c325..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/ContentView.swift +++ /dev/null @@ -1,21 +0,0 @@ -import UIKit -import SwiftUI -import ComposeApp - -struct ComposeView: UIViewControllerRepresentable { - func makeUIViewController(context: Context) -> UIViewController { - MainViewControllerKt.MainViewController() - } - - func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} -} - -struct ContentView: View { - var body: some View { - ComposeView() - .ignoresSafeArea(.keyboard) // Compose has own keyboard handler - } -} - - - diff --git a/compose-multiplatform/sample-iosApp/iosApp/Info.plist b/compose-multiplatform/sample-iosApp/iosApp/Info.plist deleted file mode 100644 index 412e3781..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/Info.plist +++ /dev/null @@ -1,50 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - - UILaunchScreen - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 74d6a722..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift b/compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift deleted file mode 100644 index 0648e860..00000000 --- a/compose-multiplatform/sample-iosApp/iosApp/iOSApp.swift +++ /dev/null @@ -1,10 +0,0 @@ -import SwiftUI - -@main -struct iOSApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} \ No newline at end of file From 79e49aba1785f9f4ad21987eb234d1ef2ccebe57 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Thu, 4 Jul 2024 19:43:32 +0200 Subject: [PATCH 12/21] Configure multiplatform publishing. --- .github/workflows/publish-release.yml | 2 +- .github/workflows/publish-snapshot.yml | 2 +- .../calendar/buildsrc/{Dependencies.kt => Build.kt} | 5 +++++ compose-multiplatform/library/build.gradle.kts | 5 +++++ compose/build.gradle.kts | 5 +++++ core/build.gradle.kts | 5 +++++ data/build.gradle.kts | 5 +++++ gradle.properties | 1 - view/build.gradle.kts | 5 +++++ 9 files changed, 32 insertions(+), 3 deletions(-) rename buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/{Dependencies.kt => Build.kt} (86%) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index f08d6421..3b1e15b4 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -35,7 +35,7 @@ jobs: - name: Deploy Release to Maven run: ./gradlew publish - if: "!endsWith(env.VERSION_NAME, '-SNAPSHOT')" + # if: "!endsWith(env.VERSION_NAME, '-SNAPSHOT')" env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index f4a4300a..c5386057 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -36,7 +36,7 @@ jobs: - name: Publish Snapshot to Maven run: ./gradlew publish - if: endsWith(env.VERSION_NAME, '-SNAPSHOT') + # if: endsWith(env.VERSION_NAME, '-SNAPSHOT') env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} diff --git a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Dependencies.kt b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt similarity index 86% rename from buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Dependencies.kt rename to buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt index af701d2f..817a5079 100644 --- a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Dependencies.kt +++ b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt @@ -11,6 +11,11 @@ object Config { val compatibleJavaLanguageVersion = JavaLanguageVersion.of(compatibleJavaVersion.majorVersion.toInt()) } +object Versions { + val core = "2.6.0-SNAPSHOT" + val multiplatfrom = "2.6.0-alpha01" +} + object Android { const val minSdkViewLibrary = 19 const val minSdkComposeLibrary = 21 diff --git a/compose-multiplatform/library/build.gradle.kts b/compose-multiplatform/library/build.gradle.kts index a9de7644..e405c8e1 100644 --- a/compose-multiplatform/library/build.gradle.kts +++ b/compose-multiplatform/library/build.gradle.kts @@ -1,5 +1,6 @@ import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config +import com.kizitonwose.calendar.buildsrc.Versions import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl @@ -109,3 +110,7 @@ android { debugImplementation(compose.uiTooling) } } + +mavenPublishing { + coordinates(version = Versions.multiplatfrom) +} diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index fe2a6465..778e1eec 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -1,6 +1,7 @@ import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config +import com.kizitonwose.calendar.buildsrc.Versions plugins { alias(libs.plugins.androidLibrary) @@ -42,3 +43,7 @@ dependencies { testImplementation(libs.test.junit) } + +mavenPublishing { + coordinates(version = Versions.core) +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5be90b00..8d78089f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,4 +1,5 @@ import com.kizitonwose.calendar.buildsrc.Config +import com.kizitonwose.calendar.buildsrc.Versions plugins { alias(libs.plugins.kotlinJvm) @@ -20,3 +21,7 @@ kotlin { dependencies { implementation(libs.compose.runtime) // Only needed for @Immutable annotation. } + +mavenPublishing { + coordinates(version = Versions.core) +} diff --git a/data/build.gradle.kts b/data/build.gradle.kts index e612955e..7e73d147 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -1,5 +1,6 @@ import com.kizitonwose.calendar.buildsrc.Config +import com.kizitonwose.calendar.buildsrc.Versions plugins { alias(libs.plugins.kotlinJvm) @@ -24,3 +25,7 @@ dependencies { testImplementation(libs.test.junit) } + +mavenPublishing { + coordinates(version = Versions.core) +} diff --git a/gradle.properties b/gradle.properties index 422f821b..76002a3d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,6 @@ SONATYPE_HOST=S01 RELEASE_SIGNING_ENABLED=true GROUP=com.kizitonwose.calendar -VERSION_NAME=2.6.0-SNAPSHOT POM_NAME=Calendar POM_INCEPTION_YEAR=2019 diff --git a/view/build.gradle.kts b/view/build.gradle.kts index 79012081..d0528fae 100644 --- a/view/build.gradle.kts +++ b/view/build.gradle.kts @@ -1,5 +1,6 @@ import com.kizitonwose.calendar.buildsrc.Android import com.kizitonwose.calendar.buildsrc.Config +import com.kizitonwose.calendar.buildsrc.Versions plugins { alias(libs.plugins.androidLibrary) @@ -34,3 +35,7 @@ dependencies { // Expose RecyclerView which is CalendarView"s superclass. api(libs.androidx.recyclerview) } + +mavenPublishing { + coordinates(version = Versions.core) +} From c65e51de1924a438294ac524364ac586d5396142 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Fri, 5 Jul 2024 16:52:32 +0200 Subject: [PATCH 13/21] Change browser page title --- .../sample/src/wasmJsMain/resources/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose-multiplatform/sample/src/wasmJsMain/resources/index.html b/compose-multiplatform/sample/src/wasmJsMain/resources/index.html index 0be283fe..ffd7e720 100644 --- a/compose-multiplatform/sample/src/wasmJsMain/resources/index.html +++ b/compose-multiplatform/sample/src/wasmJsMain/resources/index.html @@ -3,10 +3,10 @@ - KotlinProject + Calendar Library - \ No newline at end of file + From 2e6831d478cb2c0fac2ecc67bb586ae179136289 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Fri, 5 Jul 2024 16:56:34 +0200 Subject: [PATCH 14/21] Bump compose version. --- .../java/com/kizitonwose/calendar/buildsrc/Build.kt | 2 +- .../kizitonwose/calendar/compose/CalendarDefaults.kt | 10 +++------- compose-multiplatform/sample/build.gradle.kts | 9 ++++++--- gradle/libs.versions.toml | 4 ++-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt index 817a5079..e72984f0 100644 --- a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt +++ b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt @@ -12,7 +12,7 @@ object Config { } object Versions { - val core = "2.6.0-SNAPSHOT" + val core = "2.6.0-alpha01" val multiplatfrom = "2.6.0-alpha01" } diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt index 3856d891..5bb39f4e 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarDefaults.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.foundation.gestures.ScrollableDefaults import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider -import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout +import androidx.compose.foundation.gestures.snapping.SnapPosition import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable @@ -22,7 +22,7 @@ internal object CalendarDefaults { @Composable private fun pagedFlingBehavior(state: LazyListState): FlingBehavior { val snappingLayout = remember(state) { - val provider = SnapLayoutInfoProvider(state, CalendarSnapPositionInLayout()) + val provider = SnapLayoutInfoProvider(state, SnapPosition.Start) CalendarSnapLayoutInfoProvider(provider) } return rememberSnapFlingBehavior(snappingLayout) @@ -46,9 +46,5 @@ private fun CalendarSnapLayoutInfoProvider( * In compose 1.3, the default was single page snapping (zero), but this changed * in compose 1.4 to decayed page snapping which is not great for calendar usage. */ - override fun calculateApproachOffset(initialVelocity: Float): Float = 0f + override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float = 0f } - -@OptIn(ExperimentalFoundationApi::class) -@Suppress("FunctionName") -private fun CalendarSnapPositionInLayout() = SnapPositionInLayout { _, _, _, _, _ -> 0 } diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts index 12467c72..d1776791 100644 --- a/compose-multiplatform/sample/build.gradle.kts +++ b/compose-multiplatform/sample/build.gradle.kts @@ -67,7 +67,8 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) - implementation(project(":compose-multiplatform:library")) + implementation("com.kizitonwose.calendar:compose-multiplatform:2.6.0-alpha01") +// implementation(project(":compose-multiplatform:library")) implementation(libs.jetbrains.compose.navigation) } val nonJvmMain by creating { @@ -77,7 +78,9 @@ kotlin { dependencies {} } desktopMain.dependsOn(jvmMain) - desktopMain.dependencies {} + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + } } @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { @@ -130,7 +133,7 @@ android { compose.desktop { application { - mainClass = "Calendar Sample" + mainClass = "MainKt" nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2569460f..1ca08a8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ compose-navigation = { module = "androidx.navigation:navigation-compose", versio compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" } compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" } -jetbrains-compose-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version = "2.7.0-alpha07" } +jetbrains-compose-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version = "2.8.0-alpha02" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } @@ -48,4 +48,4 @@ versionCheck = { id = "com.github.ben-manes.versions", version = "0.51.0" } # KMM kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -jetbrainsCompose = { id = "org.jetbrains.compose", version = "1.6.10" } +jetbrainsCompose = { id = "org.jetbrains.compose", version = "1.7.0-alpha01" } From 8906b9a416cc768271f14c9690ea2524aa2ea8dc Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sat, 6 Jul 2024 17:59:34 +0200 Subject: [PATCH 15/21] Publish with macOS --- .github/workflows/publish-release.yml | 2 +- .github/workflows/publish-snapshot.yml | 2 +- .../src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 3b1e15b4..33ed4754 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -8,7 +8,7 @@ on: jobs: publish-release: name: Publish Release - runs-on: ubuntu-latest + runs-on: macos-latest if: github.repository == 'kizitonwose/Calendar' steps: - name: Checkout Repository diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index c5386057..ba9cd6d3 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -9,7 +9,7 @@ on: jobs: publish-snapshot: name: Publish Snapshot - runs-on: ubuntu-latest + runs-on: macos-latest if: github.repository == 'kizitonwose/Calendar' steps: - name: Checkout Repository diff --git a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt index e72984f0..438a929f 100644 --- a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt +++ b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt @@ -12,8 +12,8 @@ object Config { } object Versions { - val core = "2.6.0-alpha01" - val multiplatfrom = "2.6.0-alpha01" + val core = "2.6.0-alpha02" + val multiplatfrom = "2.6.0-alpha02" } object Android { From 7ff3b09a4ab00ea8af9f40283d03b9e8ce53d100 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Sun, 7 Jul 2024 12:16:40 +0200 Subject: [PATCH 16/21] Lint fixes --- .editorconfig | 3 +++ .../com/kizitonwose/calendar/core/YearMonth.kt | 1 - .../kizitonwose/calendar/core/Extensions.wasmJs.kt | 3 ++- compose-multiplatform/sample/build.gradle.kts | 4 ++-- .../sample/src/commonMain/kotlin/App.kt | 2 -- .../sample/src/commonMain/kotlin/AppIcons.kt | 2 ++ .../sample/src/commonMain/kotlin/Example3Page.kt | 12 ------------ .../src/desktopMain/kotlin/{main.kt => Main.kt} | 0 .../sample/src/iosMain/kotlin/MainViewController.kt | 3 ++- .../src/wasmJsMain/kotlin/{main.kt => Main.kt} | 2 +- 10 files changed, 12 insertions(+), 20 deletions(-) rename compose-multiplatform/sample/src/desktopMain/kotlin/{main.kt => Main.kt} (100%) rename compose-multiplatform/sample/src/wasmJsMain/kotlin/{main.kt => Main.kt} (99%) diff --git a/.editorconfig b/.editorconfig index 6b37b845..d9e3ecad 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,9 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true +[**/generated/**/*] +ktlint = disabled + [*.{kt,kts}] ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt index c8bb958d..a48f00e8 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt @@ -6,7 +6,6 @@ import kotlinx.datetime.number @Immutable data class YearMonth(val year: Int, val month: Month) : Comparable, JvmSerializable { - constructor(year: Int, monthNumber: Int) : this(year = year, month = Month(monthNumber)) diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt index 379479f8..b728b271 100644 --- a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt @@ -10,7 +10,8 @@ actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { return try { val firstDay = jsFirstDayFromTag(locale.toLanguageTag()) daysOfWeek(firstDayOfWeek = DayOfWeek.MONDAY)[firstDay - 1] - } catch (e: Exception) { // Unavailable on Firefox + // Unavailable on Firefox + } catch (e: Exception) { firstDayFromMap(locale) } } diff --git a/compose-multiplatform/sample/build.gradle.kts b/compose-multiplatform/sample/build.gradle.kts index d1776791..b247be8b 100644 --- a/compose-multiplatform/sample/build.gradle.kts +++ b/compose-multiplatform/sample/build.gradle.kts @@ -67,8 +67,8 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) - implementation("com.kizitonwose.calendar:compose-multiplatform:2.6.0-alpha01") -// implementation(project(":compose-multiplatform:library")) +// implementation("com.kizitonwose.calendar:compose-multiplatform:2.6.0-alpha02") + implementation(project(":compose-multiplatform:library")) implementation(libs.jetbrains.compose.navigation) } val nonJvmMain by creating { diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt index 78a4fae8..33d27d97 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/App.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/App.kt @@ -174,7 +174,6 @@ private fun AppNavHost( } } - @OptIn(ExperimentalMaterial3Api::class) private val blueToolbar @Composable @@ -190,4 +189,3 @@ private val whiteToolbar containerColor = Color.White, titleContentColor = Color.Black, ) - diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt b/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt index 6fc067b6..a28ccc71 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/AppIcons.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:backing-property-naming") + import androidx.compose.foundation.Image import androidx.compose.material.icons.Icons import androidx.compose.material.icons.materialIcon diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt index c68d7974..1e59242d 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt @@ -318,18 +318,6 @@ private fun AirportInformation(airport: Flight.Airport, isDeparture: Boolean) { } } -// The default dark them ripple is too bright so we tone it down. -//private val Example3RippleConfiguration = RippleConfiguration( -// color = Color.Gray, -// // Copied from RippleTheme#DarkThemeRippleAlpha -// rippleAlpha = RippleAlpha( -// pressedAlpha = 0.10f, -// focusedAlpha = 0.12f, -// draggedAlpha = 0.08f, -// hoveredAlpha = 0.04f, -// ), -//) - @Preview @Composable private fun Example3Preview() { diff --git a/compose-multiplatform/sample/src/desktopMain/kotlin/main.kt b/compose-multiplatform/sample/src/desktopMain/kotlin/Main.kt similarity index 100% rename from compose-multiplatform/sample/src/desktopMain/kotlin/main.kt rename to compose-multiplatform/sample/src/desktopMain/kotlin/Main.kt diff --git a/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt b/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt index fa143d45..5f9d8e00 100644 --- a/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt +++ b/compose-multiplatform/sample/src/iosMain/kotlin/MainViewController.kt @@ -1,3 +1,4 @@ import androidx.compose.ui.window.ComposeUIViewController -fun MainViewController() = ComposeUIViewController { App() } \ No newline at end of file +@Suppress("FunctionName") +fun MainViewController() = ComposeUIViewController { App() } diff --git a/compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Main.kt similarity index 99% rename from compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt rename to compose-multiplatform/sample/src/wasmJsMain/kotlin/Main.kt index d2fd60c1..48a60ac3 100644 --- a/compose-multiplatform/sample/src/wasmJsMain/kotlin/main.kt +++ b/compose-multiplatform/sample/src/wasmJsMain/kotlin/Main.kt @@ -7,4 +7,4 @@ fun main() { ComposeViewport(document.body!!) { App() } -} \ No newline at end of file +} From f65fdb6475462541f64e79c5409553230471f7d5 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 17 Jul 2024 19:34:09 +0800 Subject: [PATCH 17/21] Add BCV to check APIs https://github.com/Kotlin/binary-compatibility-validator --- .github/workflows/check.yml | 14 + build.gradle.kts | 7 + .../library/api/android/library.api | 337 ++++++++++++++++++ .../library/api/desktop/library.api | 337 ++++++++++++++++++ compose/api/compose.api | 182 ++++++++++ core/api/core.api | 89 +++++ data/api/data.api | 79 ++++ gradle/libs.versions.toml | 1 + view/api/view.api | 161 +++++++++ 9 files changed, 1207 insertions(+) create mode 100644 compose-multiplatform/library/api/android/library.api create mode 100644 compose-multiplatform/library/api/desktop/library.api create mode 100644 compose/api/compose.api create mode 100644 core/api/core.api create mode 100644 data/api/data.api create mode 100644 view/api/view.api diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b605d661..24660920 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -38,6 +38,20 @@ jobs: - name: Run Ktlint run: ./gradlew lintKotlin + api-check: + name: API Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 17 + - uses: gradle/actions/setup-gradle@v3 + with: + gradle-home-cache-cleanup: true + - run: ./gradlew apiCheck + unit-tests: name: Unit tests runs-on: ubuntu-latest diff --git a/build.gradle.kts b/build.gradle.kts index 86219a5d..bf01c7dc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,12 +9,19 @@ plugins { alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.jetbrainsCompose) apply false alias(libs.plugins.versionCheck) + alias(libs.plugins.bcv) } allprojects { apply(plugin = rootProject.libs.plugins.kotlinter.get().pluginId) } +apiValidation { + ignoredProjects += listOf( + "sample", + ) +} + // tasks.register("clean").configure { // delete(rootProject.layout.buildDirectory) // } diff --git a/compose-multiplatform/library/api/android/library.api b/compose-multiplatform/library/api/android/library.api new file mode 100644 index 00000000..5eca3398 --- /dev/null +++ b/compose-multiplatform/library/api/android/library.api @@ -0,0 +1,337 @@ +public final class com/kizitonwose/calendar/compose/CalendarItemInfo : androidx/compose/foundation/lazy/LazyListItemInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListItemInfo;Lcom/kizitonwose/calendar/core/CalendarMonth;)V + public fun getContentType ()Ljava/lang/Object; + public fun getIndex ()I + public fun getKey ()Ljava/lang/Object; + public final fun getMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun getOffset ()I + public fun getSize ()I +} + +public final class com/kizitonwose/calendar/compose/CalendarKt { + public static final fun HeatMapCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState;Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition;ZLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V + public static final fun HorizontalCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/CalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lcom/kizitonwose/calendar/compose/ContentHeightMode;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V + public static final fun VerticalCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/CalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lcom/kizitonwose/calendar/compose/ContentHeightMode;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V + public static final fun WeekCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/kizitonwose/calendar/compose/CalendarLayoutInfo : androidx/compose/foundation/lazy/LazyListLayoutInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListLayoutInfo;Lkotlin/jvm/functions/Function1;)V + public fun getAfterContentPadding ()I + public fun getBeforeContentPadding ()I + public fun getMainAxisItemSpacing ()I + public fun getOrientation ()Landroidx/compose/foundation/gestures/Orientation; + public fun getReverseLayout ()Z + public fun getTotalItemsCount ()I + public fun getViewportEndOffset ()I + public fun getViewportSize-YbymL2g ()J + public fun getViewportStartOffset ()I + public fun getVisibleItemsInfo ()Ljava/util/List; + public final fun getVisibleMonthsInfo ()Ljava/util/List; +} + +public final class com/kizitonwose/calendar/compose/CalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/CalendarState$Companion; + public final fun animateScrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/CalendarLayoutInfo; + public final fun getOutDateStyle ()Lcom/kizitonwose/calendar/core/OutDateStyle; + public final fun getStartMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setOutDateStyle (Lcom/kizitonwose/calendar/core/OutDateStyle;)V + public final fun setStartMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V +} + +public final class com/kizitonwose/calendar/compose/CalendarState$Companion { +} + +public final class com/kizitonwose/calendar/compose/CalendarStateKt { + public static final fun rememberCalendarState (Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Ljava/time/DayOfWeek;Lcom/kizitonwose/calendar/core/OutDateStyle;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/CalendarState; +} + +public final class com/kizitonwose/calendar/compose/ComposableSingletons$CalendarMonthsKt { + public static final field INSTANCE Lcom/kizitonwose/calendar/compose/ComposableSingletons$CalendarMonthsKt; + public static field lambda-1 Lkotlin/jvm/functions/Function5; + public static field lambda-2 Lkotlin/jvm/functions/Function5; + public fun ()V + public final fun getLambda-1$library_release ()Lkotlin/jvm/functions/Function5; + public final fun getLambda-2$library_release ()Lkotlin/jvm/functions/Function5; +} + +public final class com/kizitonwose/calendar/compose/ContentHeightMode : java/lang/Enum { + public static final field Fill Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static final field Wrap Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static fun values ()[Lcom/kizitonwose/calendar/compose/ContentHeightMode; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState$Companion; + public final fun animateScrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/CalendarLayoutInfo; + public final fun getStartMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setStartMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState$Companion { +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarStateKt { + public static final fun rememberHeatMapCalendarState (Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Ljava/time/DayOfWeek;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek { + public static final field $stable I + public fun (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek; + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition : java/lang/Enum { + public static final field End Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static final field Start Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static fun values ()[Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarItemInfo : androidx/compose/foundation/lazy/LazyListItemInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListItemInfo;Lcom/kizitonwose/calendar/core/Week;)V + public fun getContentType ()Ljava/lang/Object; + public fun getIndex ()I + public fun getKey ()Ljava/lang/Object; + public fun getOffset ()I + public fun getSize ()I + public final fun getWeek ()Lcom/kizitonwose/calendar/core/Week; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo : androidx/compose/foundation/lazy/LazyListLayoutInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListLayoutInfo;Lkotlin/jvm/functions/Function1;)V + public fun getAfterContentPadding ()I + public fun getBeforeContentPadding ()I + public fun getMainAxisItemSpacing ()I + public fun getOrientation ()Landroidx/compose/foundation/gestures/Orientation; + public fun getReverseLayout ()Z + public fun getTotalItemsCount ()I + public fun getViewportEndOffset ()I + public fun getViewportSize-YbymL2g ()J + public fun getViewportStartOffset ()I + public fun getVisibleItemsInfo ()Ljava/util/List; + public final fun getVisibleWeeksInfo ()Ljava/util/List; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion; + public final fun animateScrollToWeek (Lkotlinx/datetime/LocalDate;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndDate ()Lkotlinx/datetime/LocalDate; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo; + public final fun getStartDate ()Lkotlinx/datetime/LocalDate; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToWeek (Lkotlinx/datetime/LocalDate;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndDate (Lkotlinx/datetime/LocalDate;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setStartDate (Lkotlinx/datetime/LocalDate;)V +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion { + public final fun getSaver ()Landroidx/compose/runtime/saveable/Saver; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarStateKt { + public static final fun rememberWeekCalendarState (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Ljava/time/DayOfWeek;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState; +} + +public final class com/kizitonwose/calendar/core/CalendarDay { + public static final field $stable I + public fun (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)V + public final fun component1 ()Lkotlinx/datetime/LocalDate; + public final fun component2 ()Lcom/kizitonwose/calendar/core/DayPosition; + public final fun copy (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)Lcom/kizitonwose/calendar/core/CalendarDay; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/CalendarDay;Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/CalendarDay; + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Lkotlinx/datetime/LocalDate; + public final fun getPosition ()Lcom/kizitonwose/calendar/core/DayPosition; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/CalendarMonth { + public static final field $stable I + public final fun component1 ()Lcom/kizitonwose/calendar/core/YearMonth; + public final fun component2 ()Ljava/util/List; + public final fun copy (Lcom/kizitonwose/calendar/core/YearMonth;Ljava/util/List;)Lcom/kizitonwose/calendar/core/CalendarMonth; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/CalendarMonth;Lcom/kizitonwose/calendar/core/YearMonth;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun equals (Ljava/lang/Object;)Z + public final fun getWeekDays ()Ljava/util/List; + public final fun getYearMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/ConvertersKt { + public static final fun toJavaYearMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Ljava/time/YearMonth; + public static final fun toKotlinYearMonth (Ljava/time/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; +} + +public final class com/kizitonwose/calendar/core/DayPosition : java/lang/Enum { + public static final field InDate Lcom/kizitonwose/calendar/core/DayPosition; + public static final field MonthDate Lcom/kizitonwose/calendar/core/DayPosition; + public static final field OutDate Lcom/kizitonwose/calendar/core/DayPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/DayPosition; + public static fun values ()[Lcom/kizitonwose/calendar/core/DayPosition; +} + +public final class com/kizitonwose/calendar/core/ExtensionsKt { + public static final fun atDay (Lcom/kizitonwose/calendar/core/YearMonth;I)Lkotlinx/datetime/LocalDate; + public static final fun atEndOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun atStartOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun daysOfWeek (Ljava/time/DayOfWeek;)Ljava/util/List; + public static synthetic fun daysOfWeek$default (Ljava/time/DayOfWeek;ILjava/lang/Object;)Ljava/util/List; + public static final fun getNextMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun getPreviousMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun getYearMonth (Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun minusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun now (Lcom/kizitonwose/calendar/core/YearMonth$Companion;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun now (Lkotlinx/datetime/LocalDate$Companion;)Lkotlinx/datetime/LocalDate; + public static final fun plusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; +} + +public final class com/kizitonwose/calendar/core/Extensions_jvmKt { + public static final fun firstDayOfWeekFromLocale (Landroidx/compose/ui/text/intl/Locale;)Ljava/time/DayOfWeek; + public static synthetic fun firstDayOfWeekFromLocale$default (Landroidx/compose/ui/text/intl/Locale;ILjava/lang/Object;)Ljava/time/DayOfWeek; +} + +public final class com/kizitonwose/calendar/core/OutDateStyle : java/lang/Enum { + public static final field EndOfGrid Lcom/kizitonwose/calendar/core/OutDateStyle; + public static final field EndOfRow Lcom/kizitonwose/calendar/core/OutDateStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/OutDateStyle; + public static fun values ()[Lcom/kizitonwose/calendar/core/OutDateStyle; +} + +public final class com/kizitonwose/calendar/core/Week { + public static final field $stable I + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/kizitonwose/calendar/core/Week; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/Week;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/Week; + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/WeekDay { + public static final field $stable I + public fun (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;)V + public final fun component1 ()Lkotlinx/datetime/LocalDate; + public final fun component2 ()Lcom/kizitonwose/calendar/core/WeekDayPosition; + public final fun copy (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;)Lcom/kizitonwose/calendar/core/WeekDay; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/WeekDay;Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/WeekDay; + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Lkotlinx/datetime/LocalDate; + public final fun getPosition ()Lcom/kizitonwose/calendar/core/WeekDayPosition; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/WeekDayPosition : java/lang/Enum { + public static final field InDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static final field OutDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static final field RangeDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static fun values ()[Lcom/kizitonwose/calendar/core/WeekDayPosition; +} + +public final class com/kizitonwose/calendar/core/YearMonth : java/io/Serializable, java/lang/Comparable { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/core/YearMonth$Companion; + public fun (II)V + public fun (ILjava/time/Month;)V + public fun compareTo (Lcom/kizitonwose/calendar/core/YearMonth;)I + public synthetic fun compareTo (Ljava/lang/Object;)I + public final fun component1 ()I + public final fun component2 ()Ljava/time/Month; + public final fun copy (ILjava/time/Month;)Lcom/kizitonwose/calendar/core/YearMonth; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/YearMonth;ILjava/time/Month;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/YearMonth; + public fun equals (Ljava/lang/Object;)Z + public final fun getMonth ()Ljava/time/Month; + public final fun getYear ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/YearMonth$Companion { +} + +public final class com/kizitonwose/calendar/data/WeekData { + public static final field $stable I + public final fun copy (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/data/WeekData; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/WeekData;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;ILjava/lang/Object;)Lcom/kizitonwose/calendar/data/WeekData; + public fun equals (Ljava/lang/Object;)Z + public final fun getWeek ()Lcom/kizitonwose/calendar/core/Week; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/data/WeekDataKt { + public static final fun getWeekCalendarAdjustedRange (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Ljava/time/DayOfWeek;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public static final fun getWeekCalendarData (Lkotlinx/datetime/LocalDate;ILkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/data/WeekData; + public static final fun getWeekIndex (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I + public static final fun getWeekIndicesCount (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I +} + +public final class com/kizitonwose/calendar/data/WeekDateRange { + public static final field $stable I + public fun (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)V + public final fun component1 ()Lkotlinx/datetime/LocalDate; + public final fun component2 ()Lkotlinx/datetime/LocalDate; + public final fun copy (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/WeekDateRange;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;ILjava/lang/Object;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public fun equals (Ljava/lang/Object;)Z + public final fun getEndDateAdjusted ()Lkotlinx/datetime/LocalDate; + public final fun getStartDateAdjusted ()Lkotlinx/datetime/LocalDate; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + diff --git a/compose-multiplatform/library/api/desktop/library.api b/compose-multiplatform/library/api/desktop/library.api new file mode 100644 index 00000000..b4a70081 --- /dev/null +++ b/compose-multiplatform/library/api/desktop/library.api @@ -0,0 +1,337 @@ +public final class com/kizitonwose/calendar/compose/CalendarItemInfo : androidx/compose/foundation/lazy/LazyListItemInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListItemInfo;Lcom/kizitonwose/calendar/core/CalendarMonth;)V + public fun getContentType ()Ljava/lang/Object; + public fun getIndex ()I + public fun getKey ()Ljava/lang/Object; + public final fun getMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun getOffset ()I + public fun getSize ()I +} + +public final class com/kizitonwose/calendar/compose/CalendarKt { + public static final fun HeatMapCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState;Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition;ZLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V + public static final fun HorizontalCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/CalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lcom/kizitonwose/calendar/compose/ContentHeightMode;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V + public static final fun VerticalCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/CalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lcom/kizitonwose/calendar/compose/ContentHeightMode;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V + public static final fun WeekCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/kizitonwose/calendar/compose/CalendarLayoutInfo : androidx/compose/foundation/lazy/LazyListLayoutInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListLayoutInfo;Lkotlin/jvm/functions/Function1;)V + public fun getAfterContentPadding ()I + public fun getBeforeContentPadding ()I + public fun getMainAxisItemSpacing ()I + public fun getOrientation ()Landroidx/compose/foundation/gestures/Orientation; + public fun getReverseLayout ()Z + public fun getTotalItemsCount ()I + public fun getViewportEndOffset ()I + public fun getViewportSize-YbymL2g ()J + public fun getViewportStartOffset ()I + public fun getVisibleItemsInfo ()Ljava/util/List; + public final fun getVisibleMonthsInfo ()Ljava/util/List; +} + +public final class com/kizitonwose/calendar/compose/CalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/CalendarState$Companion; + public final fun animateScrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/CalendarLayoutInfo; + public final fun getOutDateStyle ()Lcom/kizitonwose/calendar/core/OutDateStyle; + public final fun getStartMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setOutDateStyle (Lcom/kizitonwose/calendar/core/OutDateStyle;)V + public final fun setStartMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V +} + +public final class com/kizitonwose/calendar/compose/CalendarState$Companion { +} + +public final class com/kizitonwose/calendar/compose/CalendarStateKt { + public static final fun rememberCalendarState (Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Ljava/time/DayOfWeek;Lcom/kizitonwose/calendar/core/OutDateStyle;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/CalendarState; +} + +public final class com/kizitonwose/calendar/compose/ComposableSingletons$CalendarMonthsKt { + public static final field INSTANCE Lcom/kizitonwose/calendar/compose/ComposableSingletons$CalendarMonthsKt; + public static field lambda-1 Lkotlin/jvm/functions/Function5; + public static field lambda-2 Lkotlin/jvm/functions/Function5; + public fun ()V + public final fun getLambda-1$library ()Lkotlin/jvm/functions/Function5; + public final fun getLambda-2$library ()Lkotlin/jvm/functions/Function5; +} + +public final class com/kizitonwose/calendar/compose/ContentHeightMode : java/lang/Enum { + public static final field Fill Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static final field Wrap Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static fun values ()[Lcom/kizitonwose/calendar/compose/ContentHeightMode; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState$Companion; + public final fun animateScrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/CalendarLayoutInfo; + public final fun getStartMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToMonth (Lcom/kizitonwose/calendar/core/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setStartMonth (Lcom/kizitonwose/calendar/core/YearMonth;)V +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState$Companion { +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarStateKt { + public static final fun rememberHeatMapCalendarState (Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;Ljava/time/DayOfWeek;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek { + public static final field $stable I + public fun (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek; + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition : java/lang/Enum { + public static final field End Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static final field Start Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static fun values ()[Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarItemInfo : androidx/compose/foundation/lazy/LazyListItemInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListItemInfo;Lcom/kizitonwose/calendar/core/Week;)V + public fun getContentType ()Ljava/lang/Object; + public fun getIndex ()I + public fun getKey ()Ljava/lang/Object; + public fun getOffset ()I + public fun getSize ()I + public final fun getWeek ()Lcom/kizitonwose/calendar/core/Week; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo : androidx/compose/foundation/lazy/LazyListLayoutInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListLayoutInfo;Lkotlin/jvm/functions/Function1;)V + public fun getAfterContentPadding ()I + public fun getBeforeContentPadding ()I + public fun getMainAxisItemSpacing ()I + public fun getOrientation ()Landroidx/compose/foundation/gestures/Orientation; + public fun getReverseLayout ()Z + public fun getTotalItemsCount ()I + public fun getViewportEndOffset ()I + public fun getViewportSize-YbymL2g ()J + public fun getViewportStartOffset ()I + public fun getVisibleItemsInfo ()Ljava/util/List; + public final fun getVisibleWeeksInfo ()Ljava/util/List; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion; + public final fun animateScrollToWeek (Lkotlinx/datetime/LocalDate;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndDate ()Lkotlinx/datetime/LocalDate; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo; + public final fun getStartDate ()Lkotlinx/datetime/LocalDate; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToWeek (Lkotlinx/datetime/LocalDate;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndDate (Lkotlinx/datetime/LocalDate;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setStartDate (Lkotlinx/datetime/LocalDate;)V +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion { + public final fun getSaver ()Landroidx/compose/runtime/saveable/Saver; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarStateKt { + public static final fun rememberWeekCalendarState (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Ljava/time/DayOfWeek;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState; +} + +public final class com/kizitonwose/calendar/core/CalendarDay { + public static final field $stable I + public fun (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)V + public final fun component1 ()Lkotlinx/datetime/LocalDate; + public final fun component2 ()Lcom/kizitonwose/calendar/core/DayPosition; + public final fun copy (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)Lcom/kizitonwose/calendar/core/CalendarDay; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/CalendarDay;Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/CalendarDay; + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Lkotlinx/datetime/LocalDate; + public final fun getPosition ()Lcom/kizitonwose/calendar/core/DayPosition; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/CalendarMonth { + public static final field $stable I + public final fun component1 ()Lcom/kizitonwose/calendar/core/YearMonth; + public final fun component2 ()Ljava/util/List; + public final fun copy (Lcom/kizitonwose/calendar/core/YearMonth;Ljava/util/List;)Lcom/kizitonwose/calendar/core/CalendarMonth; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/CalendarMonth;Lcom/kizitonwose/calendar/core/YearMonth;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun equals (Ljava/lang/Object;)Z + public final fun getWeekDays ()Ljava/util/List; + public final fun getYearMonth ()Lcom/kizitonwose/calendar/core/YearMonth; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/ConvertersKt { + public static final fun toJavaYearMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Ljava/time/YearMonth; + public static final fun toKotlinYearMonth (Ljava/time/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; +} + +public final class com/kizitonwose/calendar/core/DayPosition : java/lang/Enum { + public static final field InDate Lcom/kizitonwose/calendar/core/DayPosition; + public static final field MonthDate Lcom/kizitonwose/calendar/core/DayPosition; + public static final field OutDate Lcom/kizitonwose/calendar/core/DayPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/DayPosition; + public static fun values ()[Lcom/kizitonwose/calendar/core/DayPosition; +} + +public final class com/kizitonwose/calendar/core/ExtensionsKt { + public static final fun atDay (Lcom/kizitonwose/calendar/core/YearMonth;I)Lkotlinx/datetime/LocalDate; + public static final fun atEndOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun atStartOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun daysOfWeek (Ljava/time/DayOfWeek;)Ljava/util/List; + public static synthetic fun daysOfWeek$default (Ljava/time/DayOfWeek;ILjava/lang/Object;)Ljava/util/List; + public static final fun getNextMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun getPreviousMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun getYearMonth (Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun minusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun now (Lcom/kizitonwose/calendar/core/YearMonth$Companion;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun now (Lkotlinx/datetime/LocalDate$Companion;)Lkotlinx/datetime/LocalDate; + public static final fun plusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; +} + +public final class com/kizitonwose/calendar/core/Extensions_jvmKt { + public static final fun firstDayOfWeekFromLocale (Landroidx/compose/ui/text/intl/Locale;)Ljava/time/DayOfWeek; + public static synthetic fun firstDayOfWeekFromLocale$default (Landroidx/compose/ui/text/intl/Locale;ILjava/lang/Object;)Ljava/time/DayOfWeek; +} + +public final class com/kizitonwose/calendar/core/OutDateStyle : java/lang/Enum { + public static final field EndOfGrid Lcom/kizitonwose/calendar/core/OutDateStyle; + public static final field EndOfRow Lcom/kizitonwose/calendar/core/OutDateStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/OutDateStyle; + public static fun values ()[Lcom/kizitonwose/calendar/core/OutDateStyle; +} + +public final class com/kizitonwose/calendar/core/Week { + public static final field $stable I + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/kizitonwose/calendar/core/Week; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/Week;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/Week; + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/WeekDay { + public static final field $stable I + public fun (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;)V + public final fun component1 ()Lkotlinx/datetime/LocalDate; + public final fun component2 ()Lcom/kizitonwose/calendar/core/WeekDayPosition; + public final fun copy (Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;)Lcom/kizitonwose/calendar/core/WeekDay; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/WeekDay;Lkotlinx/datetime/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/WeekDay; + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Lkotlinx/datetime/LocalDate; + public final fun getPosition ()Lcom/kizitonwose/calendar/core/WeekDayPosition; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/WeekDayPosition : java/lang/Enum { + public static final field InDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static final field OutDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static final field RangeDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static fun values ()[Lcom/kizitonwose/calendar/core/WeekDayPosition; +} + +public final class com/kizitonwose/calendar/core/YearMonth : java/io/Serializable, java/lang/Comparable { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/core/YearMonth$Companion; + public fun (II)V + public fun (ILjava/time/Month;)V + public fun compareTo (Lcom/kizitonwose/calendar/core/YearMonth;)I + public synthetic fun compareTo (Ljava/lang/Object;)I + public final fun component1 ()I + public final fun component2 ()Ljava/time/Month; + public final fun copy (ILjava/time/Month;)Lcom/kizitonwose/calendar/core/YearMonth; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/YearMonth;ILjava/time/Month;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/YearMonth; + public fun equals (Ljava/lang/Object;)Z + public final fun getMonth ()Ljava/time/Month; + public final fun getYear ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/YearMonth$Companion { +} + +public final class com/kizitonwose/calendar/data/WeekData { + public static final field $stable I + public final fun copy (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/data/WeekData; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/WeekData;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;ILjava/lang/Object;)Lcom/kizitonwose/calendar/data/WeekData; + public fun equals (Ljava/lang/Object;)Z + public final fun getWeek ()Lcom/kizitonwose/calendar/core/Week; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/data/WeekDataKt { + public static final fun getWeekCalendarAdjustedRange (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;Ljava/time/DayOfWeek;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public static final fun getWeekCalendarData (Lkotlinx/datetime/LocalDate;ILkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/data/WeekData; + public static final fun getWeekIndex (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I + public static final fun getWeekIndicesCount (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I +} + +public final class com/kizitonwose/calendar/data/WeekDateRange { + public static final field $stable I + public fun (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)V + public final fun component1 ()Lkotlinx/datetime/LocalDate; + public final fun component2 ()Lkotlinx/datetime/LocalDate; + public final fun copy (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/WeekDateRange;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;ILjava/lang/Object;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public fun equals (Ljava/lang/Object;)Z + public final fun getEndDateAdjusted ()Lkotlinx/datetime/LocalDate; + public final fun getStartDateAdjusted ()Lkotlinx/datetime/LocalDate; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + diff --git a/compose/api/compose.api b/compose/api/compose.api new file mode 100644 index 00000000..336cfe9a --- /dev/null +++ b/compose/api/compose.api @@ -0,0 +1,182 @@ +public final class com/kizitonwose/calendar/compose/CalendarItemInfo : androidx/compose/foundation/lazy/LazyListItemInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListItemInfo;Lcom/kizitonwose/calendar/core/CalendarMonth;)V + public fun getContentType ()Ljava/lang/Object; + public fun getIndex ()I + public fun getKey ()Ljava/lang/Object; + public final fun getMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun getOffset ()I + public fun getSize ()I +} + +public final class com/kizitonwose/calendar/compose/CalendarKt { + public static final fun HeatMapCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState;Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition;ZLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V + public static final fun HorizontalCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/CalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lcom/kizitonwose/calendar/compose/ContentHeightMode;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V + public static final fun VerticalCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/CalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lcom/kizitonwose/calendar/compose/ContentHeightMode;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V + public static final fun WeekCalendar (Landroidx/compose/ui/Modifier;Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState;ZZZLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/kizitonwose/calendar/compose/CalendarLayoutInfo : androidx/compose/foundation/lazy/LazyListLayoutInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListLayoutInfo;Lkotlin/jvm/functions/Function1;)V + public fun getAfterContentPadding ()I + public fun getBeforeContentPadding ()I + public fun getMainAxisItemSpacing ()I + public fun getOrientation ()Landroidx/compose/foundation/gestures/Orientation; + public fun getReverseLayout ()Z + public fun getTotalItemsCount ()I + public fun getViewportEndOffset ()I + public fun getViewportSize-YbymL2g ()J + public fun getViewportStartOffset ()I + public fun getVisibleItemsInfo ()Ljava/util/List; + public final fun getVisibleMonthsInfo ()Ljava/util/List; +} + +public final class com/kizitonwose/calendar/compose/CalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/CalendarState$Companion; + public final fun animateScrollToMonth (Ljava/time/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndMonth ()Ljava/time/YearMonth; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/CalendarLayoutInfo; + public final fun getOutDateStyle ()Lcom/kizitonwose/calendar/core/OutDateStyle; + public final fun getStartMonth ()Ljava/time/YearMonth; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToMonth (Ljava/time/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndMonth (Ljava/time/YearMonth;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setOutDateStyle (Lcom/kizitonwose/calendar/core/OutDateStyle;)V + public final fun setStartMonth (Ljava/time/YearMonth;)V +} + +public final class com/kizitonwose/calendar/compose/CalendarState$Companion { +} + +public final class com/kizitonwose/calendar/compose/CalendarStateKt { + public static final fun rememberCalendarState (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/DayOfWeek;Lcom/kizitonwose/calendar/core/OutDateStyle;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/CalendarState; +} + +public final class com/kizitonwose/calendar/compose/ComposableSingletons$CalendarMonthsKt { + public static final field INSTANCE Lcom/kizitonwose/calendar/compose/ComposableSingletons$CalendarMonthsKt; + public static field lambda-1 Lkotlin/jvm/functions/Function5; + public static field lambda-2 Lkotlin/jvm/functions/Function5; + public fun ()V + public final fun getLambda-1$compose_release ()Lkotlin/jvm/functions/Function5; + public final fun getLambda-2$compose_release ()Lkotlin/jvm/functions/Function5; +} + +public final class com/kizitonwose/calendar/compose/ContentHeightMode : java/lang/Enum { + public static final field Fill Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static final field Wrap Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/compose/ContentHeightMode; + public static fun values ()[Lcom/kizitonwose/calendar/compose/ContentHeightMode; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState$Companion; + public final fun animateScrollToMonth (Ljava/time/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndMonth ()Ljava/time/YearMonth; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/CalendarLayoutInfo; + public final fun getStartMonth ()Ljava/time/YearMonth; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToMonth (Ljava/time/YearMonth;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndMonth (Ljava/time/YearMonth;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setStartMonth (Ljava/time/YearMonth;)V +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState$Companion { +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarStateKt { + public static final fun rememberHeatMapCalendarState (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/DayOfWeek;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek : java/io/Serializable { + public static final field $stable I + public fun (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek; + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition : java/lang/Enum { + public static final field End Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static final field Start Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; + public static fun values ()[Lcom/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarItemInfo : androidx/compose/foundation/lazy/LazyListItemInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListItemInfo;Lcom/kizitonwose/calendar/core/Week;)V + public fun getContentType ()Ljava/lang/Object; + public fun getIndex ()I + public fun getKey ()Ljava/lang/Object; + public fun getOffset ()I + public fun getSize ()I + public final fun getWeek ()Lcom/kizitonwose/calendar/core/Week; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo : androidx/compose/foundation/lazy/LazyListLayoutInfo { + public static final field $stable I + public fun (Landroidx/compose/foundation/lazy/LazyListLayoutInfo;Lkotlin/jvm/functions/Function1;)V + public fun getAfterContentPadding ()I + public fun getBeforeContentPadding ()I + public fun getMainAxisItemSpacing ()I + public fun getOrientation ()Landroidx/compose/foundation/gestures/Orientation; + public fun getReverseLayout ()Z + public fun getTotalItemsCount ()I + public fun getViewportEndOffset ()I + public fun getViewportSize-YbymL2g ()J + public fun getViewportStartOffset ()I + public fun getVisibleItemsInfo ()Ljava/util/List; + public final fun getVisibleWeeksInfo ()Ljava/util/List; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState : androidx/compose/foundation/gestures/ScrollableState { + public static final field $stable I + public static final field Companion Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion; + public final fun animateScrollToWeek (Ljava/time/LocalDate;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun dispatchRawDelta (F)F + public final fun getEndDate ()Ljava/time/LocalDate; + public final fun getFirstDayOfWeek ()Ljava/time/DayOfWeek; + public final fun getFirstVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getInteractionSource ()Landroidx/compose/foundation/interaction/InteractionSource; + public final fun getLastVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getLayoutInfo ()Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo; + public final fun getStartDate ()Ljava/time/LocalDate; + public fun isScrollInProgress ()Z + public fun scroll (Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun scrollToWeek (Ljava/time/LocalDate;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setEndDate (Ljava/time/LocalDate;)V + public final fun setFirstDayOfWeek (Ljava/time/DayOfWeek;)V + public final fun setStartDate (Ljava/time/LocalDate;)V +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion { + public final fun getSaver ()Landroidx/compose/runtime/saveable/Saver; +} + +public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarStateKt { + public static final fun rememberWeekCalendarState (Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/DayOfWeek;Landroidx/compose/runtime/Composer;II)Lcom/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState; +} + diff --git a/core/api/core.api b/core/api/core.api new file mode 100644 index 00000000..dca5da5f --- /dev/null +++ b/core/api/core.api @@ -0,0 +1,89 @@ +public final class com/kizitonwose/calendar/core/CalendarDay : java/io/Serializable { + public fun (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)V + public final fun component1 ()Ljava/time/LocalDate; + public final fun component2 ()Lcom/kizitonwose/calendar/core/DayPosition; + public final fun copy (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)Lcom/kizitonwose/calendar/core/CalendarDay; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/CalendarDay;Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/CalendarDay; + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Ljava/time/LocalDate; + public final fun getPosition ()Lcom/kizitonwose/calendar/core/DayPosition; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/CalendarMonth : java/io/Serializable { + public fun (Ljava/time/YearMonth;Ljava/util/List;)V + public final fun component1 ()Ljava/time/YearMonth; + public final fun component2 ()Ljava/util/List; + public final fun copy (Ljava/time/YearMonth;Ljava/util/List;)Lcom/kizitonwose/calendar/core/CalendarMonth; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/CalendarMonth;Ljava/time/YearMonth;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun equals (Ljava/lang/Object;)Z + public final fun getWeekDays ()Ljava/util/List; + public final fun getYearMonth ()Ljava/time/YearMonth; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/DayPosition : java/lang/Enum { + public static final field InDate Lcom/kizitonwose/calendar/core/DayPosition; + public static final field MonthDate Lcom/kizitonwose/calendar/core/DayPosition; + public static final field OutDate Lcom/kizitonwose/calendar/core/DayPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/DayPosition; + public static fun values ()[Lcom/kizitonwose/calendar/core/DayPosition; +} + +public final class com/kizitonwose/calendar/core/ExtensionsKt { + public static final fun atStartOfMonth (Ljava/time/YearMonth;)Ljava/time/LocalDate; + public static final fun daysOfWeek ()Ljava/util/List; + public static final fun daysOfWeek (Ljava/time/DayOfWeek;)Ljava/util/List; + public static synthetic fun daysOfWeek$default (Ljava/time/DayOfWeek;ILjava/lang/Object;)Ljava/util/List; + public static final fun firstDayOfWeekFromLocale ()Ljava/time/DayOfWeek; + public static final fun firstDayOfWeekFromLocale (Ljava/util/Locale;)Ljava/time/DayOfWeek; + public static synthetic fun firstDayOfWeekFromLocale$default (Ljava/util/Locale;ILjava/lang/Object;)Ljava/time/DayOfWeek; + public static final fun getNextMonth (Ljava/time/YearMonth;)Ljava/time/YearMonth; + public static final fun getPreviousMonth (Ljava/time/YearMonth;)Ljava/time/YearMonth; + public static final fun getYearMonth (Ljava/time/LocalDate;)Ljava/time/YearMonth; +} + +public final class com/kizitonwose/calendar/core/OutDateStyle : java/lang/Enum { + public static final field EndOfGrid Lcom/kizitonwose/calendar/core/OutDateStyle; + public static final field EndOfRow Lcom/kizitonwose/calendar/core/OutDateStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/OutDateStyle; + public static fun values ()[Lcom/kizitonwose/calendar/core/OutDateStyle; +} + +public final class com/kizitonwose/calendar/core/Week : java/io/Serializable { + public fun (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lcom/kizitonwose/calendar/core/Week; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/Week;Ljava/util/List;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/Week; + public fun equals (Ljava/lang/Object;)Z + public final fun getDays ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/WeekDay : java/io/Serializable { + public fun (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;)V + public final fun component1 ()Ljava/time/LocalDate; + public final fun component2 ()Lcom/kizitonwose/calendar/core/WeekDayPosition; + public final fun copy (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;)Lcom/kizitonwose/calendar/core/WeekDay; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/core/WeekDay;Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/WeekDayPosition;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/WeekDay; + public fun equals (Ljava/lang/Object;)Z + public final fun getDate ()Ljava/time/LocalDate; + public final fun getPosition ()Lcom/kizitonwose/calendar/core/WeekDayPosition; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/core/WeekDayPosition : java/lang/Enum { + public static final field InDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static final field OutDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static final field RangeDate Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/core/WeekDayPosition; + public static fun values ()[Lcom/kizitonwose/calendar/core/WeekDayPosition; +} + diff --git a/data/api/data.api b/data/api/data.api new file mode 100644 index 00000000..8c53e103 --- /dev/null +++ b/data/api/data.api @@ -0,0 +1,79 @@ +public final class com/kizitonwose/calendar/data/DataStore : java/util/Map, kotlin/jvm/internal/markers/KMutableMap { + public fun (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Ljava/util/Map;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun clear ()V + public fun containsKey (I)Z + public final fun containsKey (Ljava/lang/Object;)Z + public fun containsValue (Ljava/lang/Object;)Z + public final fun entrySet ()Ljava/util/Set; + public fun get (I)Ljava/lang/Object; + public final fun get (Ljava/lang/Object;)Ljava/lang/Object; + public fun getEntries ()Ljava/util/Set; + public fun getKeys ()Ljava/util/Set; + public fun getSize ()I + public fun getValues ()Ljava/util/Collection; + public fun isEmpty ()Z + public final fun keySet ()Ljava/util/Set; + public fun put (ILjava/lang/Object;)Ljava/lang/Object; + public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + public fun putAll (Ljava/util/Map;)V + public fun remove (I)Ljava/lang/Object; + public final fun remove (Ljava/lang/Object;)Ljava/lang/Object; + public final fun size ()I + public final fun values ()Ljava/util/Collection; +} + +public final class com/kizitonwose/calendar/data/ExtensionsKt { + public static final fun daysUntil (Ljava/time/DayOfWeek;Ljava/time/DayOfWeek;)I +} + +public final class com/kizitonwose/calendar/data/MonthData { + public final fun copy (Ljava/time/YearMonth;II)Lcom/kizitonwose/calendar/data/MonthData; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/MonthData;Ljava/time/YearMonth;IIILjava/lang/Object;)Lcom/kizitonwose/calendar/data/MonthData; + public fun equals (Ljava/lang/Object;)Z + public final fun getCalendarMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/data/MonthDataKt { + public static final fun getCalendarMonthData (Ljava/time/YearMonth;ILjava/time/DayOfWeek;Lcom/kizitonwose/calendar/core/OutDateStyle;)Lcom/kizitonwose/calendar/data/MonthData; + public static final fun getHeatMapCalendarMonthData (Ljava/time/YearMonth;ILjava/time/DayOfWeek;)Lcom/kizitonwose/calendar/data/MonthData; + public static final fun getMonthIndex (Ljava/time/YearMonth;Ljava/time/YearMonth;)I + public static final fun getMonthIndicesCount (Ljava/time/YearMonth;Ljava/time/YearMonth;)I +} + +public final class com/kizitonwose/calendar/data/UtilsKt { + public static final fun checkDateRange (Ljava/time/LocalDate;Ljava/time/LocalDate;)V + public static final fun checkDateRange (Ljava/time/YearMonth;Ljava/time/YearMonth;)V +} + +public final class com/kizitonwose/calendar/data/WeekData { + public final fun copy (Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/LocalDate;)Lcom/kizitonwose/calendar/data/WeekData; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/WeekData;Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/LocalDate;ILjava/lang/Object;)Lcom/kizitonwose/calendar/data/WeekData; + public fun equals (Ljava/lang/Object;)Z + public final fun getWeek ()Lcom/kizitonwose/calendar/core/Week; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/kizitonwose/calendar/data/WeekDataKt { + public static final fun getWeekCalendarAdjustedRange (Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/DayOfWeek;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public static final fun getWeekCalendarData (Ljava/time/LocalDate;ILjava/time/LocalDate;Ljava/time/LocalDate;)Lcom/kizitonwose/calendar/data/WeekData; + public static final fun getWeekIndex (Ljava/time/LocalDate;Ljava/time/LocalDate;)I + public static final fun getWeekIndicesCount (Ljava/time/LocalDate;Ljava/time/LocalDate;)I +} + +public final class com/kizitonwose/calendar/data/WeekDateRange { + public fun (Ljava/time/LocalDate;Ljava/time/LocalDate;)V + public final fun component1 ()Ljava/time/LocalDate; + public final fun component2 ()Ljava/time/LocalDate; + public final fun copy (Ljava/time/LocalDate;Ljava/time/LocalDate;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/data/WeekDateRange;Ljava/time/LocalDate;Ljava/time/LocalDate;ILjava/lang/Object;)Lcom/kizitonwose/calendar/data/WeekDateRange; + public fun equals (Ljava/lang/Object;)Z + public final fun getEndDateAdjusted ()Ljava/time/LocalDate; + public final fun getStartDateAdjusted ()Ljava/time/LocalDate; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ca08a8b..75d3da56 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,6 +45,7 @@ kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlinter = { id = "org.jmailen.kotlinter", version = "4.3.0" } mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.28.0" } versionCheck = { id = "com.github.ben-manes.versions", version = "0.51.0" } +bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.15.1" # KMM kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } diff --git a/view/api/view.api b/view/api/view.api new file mode 100644 index 00000000..fdc70dcb --- /dev/null +++ b/view/api/view.api @@ -0,0 +1,161 @@ +public abstract interface class com/kizitonwose/calendar/view/Binder { + public abstract fun bind (Lcom/kizitonwose/calendar/view/ViewContainer;Ljava/lang/Object;)V + public abstract fun create (Landroid/view/View;)Lcom/kizitonwose/calendar/view/ViewContainer; +} + +public class com/kizitonwose/calendar/view/CalendarView : androidx/recyclerview/widget/RecyclerView { + public fun (Landroid/content/Context;)V + public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V + public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V + public final fun findFirstVisibleDay ()Lcom/kizitonwose/calendar/core/CalendarDay; + public final fun findFirstVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun findLastVisibleDay ()Lcom/kizitonwose/calendar/core/CalendarDay; + public final fun findLastVisibleMonth ()Lcom/kizitonwose/calendar/core/CalendarMonth; + public final fun getDayBinder ()Lcom/kizitonwose/calendar/view/MonthDayBinder; + public final fun getDaySize ()Lcom/kizitonwose/calendar/view/DaySize; + public final fun getDayViewResource ()I + public final fun getMonthFooterBinder ()Lcom/kizitonwose/calendar/view/MonthHeaderFooterBinder; + public final fun getMonthFooterResource ()I + public final fun getMonthHeaderBinder ()Lcom/kizitonwose/calendar/view/MonthHeaderFooterBinder; + public final fun getMonthHeaderResource ()I + public final fun getMonthMargins ()Lcom/kizitonwose/calendar/view/MarginValues; + public final fun getMonthScrollListener ()Lkotlin/jvm/functions/Function1; + public final fun getMonthViewClass ()Ljava/lang/String; + public final fun getOrientation ()I + public final fun getOutDateStyle ()Lcom/kizitonwose/calendar/core/OutDateStyle; + public final fun getScrollPaged ()Z + public final fun notifyCalendarChanged ()V + public final fun notifyDateChanged (Ljava/time/LocalDate;)V + public final fun notifyDateChanged (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)V + public final fun notifyDateChanged (Ljava/time/LocalDate;[Lcom/kizitonwose/calendar/core/DayPosition;)V + public static synthetic fun notifyDateChanged$default (Lcom/kizitonwose/calendar/view/CalendarView;Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;ILjava/lang/Object;)V + public final fun notifyDayChanged (Lcom/kizitonwose/calendar/core/CalendarDay;)V + public final fun notifyMonthChanged (Ljava/time/YearMonth;)V + public final fun scrollToDate (Ljava/time/LocalDate;)V + public final fun scrollToDate (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)V + public static synthetic fun scrollToDate$default (Lcom/kizitonwose/calendar/view/CalendarView;Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;ILjava/lang/Object;)V + public final fun scrollToDay (Lcom/kizitonwose/calendar/core/CalendarDay;)V + public final fun scrollToMonth (Ljava/time/YearMonth;)V + public final fun setDayBinder (Lcom/kizitonwose/calendar/view/MonthDayBinder;)V + public final fun setDaySize (Lcom/kizitonwose/calendar/view/DaySize;)V + public final fun setDayViewResource (I)V + public final fun setMonthFooterBinder (Lcom/kizitonwose/calendar/view/MonthHeaderFooterBinder;)V + public final fun setMonthFooterResource (I)V + public final fun setMonthHeaderBinder (Lcom/kizitonwose/calendar/view/MonthHeaderFooterBinder;)V + public final fun setMonthHeaderResource (I)V + public final fun setMonthMargins (Lcom/kizitonwose/calendar/view/MarginValues;)V + public final fun setMonthScrollListener (Lkotlin/jvm/functions/Function1;)V + public final fun setMonthViewClass (Ljava/lang/String;)V + public final fun setOrientation (I)V + public final fun setOutDateStyle (Lcom/kizitonwose/calendar/core/OutDateStyle;)V + public final fun setScrollPaged (Z)V + public final fun setup (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/DayOfWeek;)V + public final fun smoothScrollToDate (Ljava/time/LocalDate;)V + public final fun smoothScrollToDate (Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;)V + public static synthetic fun smoothScrollToDate$default (Lcom/kizitonwose/calendar/view/CalendarView;Ljava/time/LocalDate;Lcom/kizitonwose/calendar/core/DayPosition;ILjava/lang/Object;)V + public final fun smoothScrollToDay (Lcom/kizitonwose/calendar/core/CalendarDay;)V + public final fun smoothScrollToMonth (Ljava/time/YearMonth;)V + public final fun updateMonthData ()V + public final fun updateMonthData (Ljava/time/YearMonth;)V + public final fun updateMonthData (Ljava/time/YearMonth;Ljava/time/YearMonth;)V + public final fun updateMonthData (Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/DayOfWeek;)V + public static synthetic fun updateMonthData$default (Lcom/kizitonwose/calendar/view/CalendarView;Ljava/time/YearMonth;Ljava/time/YearMonth;Ljava/time/DayOfWeek;ILjava/lang/Object;)V +} + +public final class com/kizitonwose/calendar/view/DaySize : java/lang/Enum { + public static final field FreeForm Lcom/kizitonwose/calendar/view/DaySize; + public static final field Rectangle Lcom/kizitonwose/calendar/view/DaySize; + public static final field SeventhWidth Lcom/kizitonwose/calendar/view/DaySize; + public static final field Square Lcom/kizitonwose/calendar/view/DaySize; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/kizitonwose/calendar/view/DaySize; + public static fun values ()[Lcom/kizitonwose/calendar/view/DaySize; +} + +public final class com/kizitonwose/calendar/view/MarginValues { + public fun ()V + public fun (IIII)V + public synthetic fun (IIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()I + public final fun component2 ()I + public final fun component3 ()I + public final fun component4 ()I + public final fun copy (IIII)Lcom/kizitonwose/calendar/view/MarginValues; + public static synthetic fun copy$default (Lcom/kizitonwose/calendar/view/MarginValues;IIIIILjava/lang/Object;)Lcom/kizitonwose/calendar/view/MarginValues; + public fun equals (Ljava/lang/Object;)Z + public final fun getBottom ()I + public final fun getEnd ()I + public final fun getStart ()I + public final fun getTop ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class com/kizitonwose/calendar/view/MonthDayBinder : com/kizitonwose/calendar/view/Binder { +} + +public abstract interface class com/kizitonwose/calendar/view/MonthHeaderFooterBinder : com/kizitonwose/calendar/view/Binder { +} + +public class com/kizitonwose/calendar/view/ViewContainer { + public fun (Landroid/view/View;)V + public final fun getView ()Landroid/view/View; +} + +public class com/kizitonwose/calendar/view/WeekCalendarView : androidx/recyclerview/widget/RecyclerView { + public fun (Landroid/content/Context;)V + public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V + public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V + public final fun findFirstVisibleDay ()Lcom/kizitonwose/calendar/core/WeekDay; + public final fun findFirstVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun findLastVisibleDay ()Lcom/kizitonwose/calendar/core/WeekDay; + public final fun findLastVisibleWeek ()Lcom/kizitonwose/calendar/core/Week; + public final fun getDayBinder ()Lcom/kizitonwose/calendar/view/WeekDayBinder; + public final fun getDaySize ()Lcom/kizitonwose/calendar/view/DaySize; + public final fun getDayViewResource ()I + public final fun getScrollPaged ()Z + public final fun getWeekFooterBinder ()Lcom/kizitonwose/calendar/view/WeekHeaderFooterBinder; + public final fun getWeekFooterResource ()I + public final fun getWeekHeaderBinder ()Lcom/kizitonwose/calendar/view/WeekHeaderFooterBinder; + public final fun getWeekHeaderResource ()I + public final fun getWeekMargins ()Lcom/kizitonwose/calendar/view/MarginValues; + public final fun getWeekScrollListener ()Lkotlin/jvm/functions/Function1; + public final fun getWeekViewClass ()Ljava/lang/String; + public final fun notifyCalendarChanged ()V + public final fun notifyDateChanged (Ljava/time/LocalDate;)V + public final fun notifyDayChanged (Lcom/kizitonwose/calendar/core/WeekDay;)V + public final fun notifyWeekChanged (Lcom/kizitonwose/calendar/core/WeekDay;)V + public final fun notifyWeekChanged (Ljava/time/LocalDate;)V + public final fun scrollToDate (Ljava/time/LocalDate;)V + public final fun scrollToDay (Lcom/kizitonwose/calendar/core/WeekDay;)V + public final fun scrollToWeek (Lcom/kizitonwose/calendar/core/WeekDay;)V + public final fun scrollToWeek (Ljava/time/LocalDate;)V + public final fun setDayBinder (Lcom/kizitonwose/calendar/view/WeekDayBinder;)V + public final fun setDaySize (Lcom/kizitonwose/calendar/view/DaySize;)V + public final fun setDayViewResource (I)V + public final fun setScrollPaged (Z)V + public final fun setWeekFooterBinder (Lcom/kizitonwose/calendar/view/WeekHeaderFooterBinder;)V + public final fun setWeekFooterResource (I)V + public final fun setWeekHeaderBinder (Lcom/kizitonwose/calendar/view/WeekHeaderFooterBinder;)V + public final fun setWeekHeaderResource (I)V + public final fun setWeekMargins (Lcom/kizitonwose/calendar/view/MarginValues;)V + public final fun setWeekScrollListener (Lkotlin/jvm/functions/Function1;)V + public final fun setWeekViewClass (Ljava/lang/String;)V + public final fun setup (Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/DayOfWeek;)V + public final fun smoothScrollToDate (Ljava/time/LocalDate;)V + public final fun smoothScrollToDay (Lcom/kizitonwose/calendar/core/WeekDay;)V + public final fun smoothScrollToWeek (Lcom/kizitonwose/calendar/core/WeekDay;)V + public final fun smoothScrollToWeek (Ljava/time/LocalDate;)V + public final fun updateWeekData ()V + public final fun updateWeekData (Ljava/time/LocalDate;)V + public final fun updateWeekData (Ljava/time/LocalDate;Ljava/time/LocalDate;)V + public final fun updateWeekData (Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/DayOfWeek;)V + public static synthetic fun updateWeekData$default (Lcom/kizitonwose/calendar/view/WeekCalendarView;Ljava/time/LocalDate;Ljava/time/LocalDate;Ljava/time/DayOfWeek;ILjava/lang/Object;)V +} + +public abstract interface class com/kizitonwose/calendar/view/WeekDayBinder : com/kizitonwose/calendar/view/Binder { +} + +public abstract interface class com/kizitonwose/calendar/view/WeekHeaderFooterBinder : com/kizitonwose/calendar/view/Binder { +} + From b0e4255a6264207c3481a7159230c2ffda4d2f65 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 17 Jul 2024 20:05:42 +0800 Subject: [PATCH 18/21] Enable Explicit API mode https://github.com/Kotlin/KEEP/blob/master/proposals/explicit-api-mode.md --- build.gradle.kts | 11 +++ .../kizitonwose/calendar/compose/Calendar.kt | 16 ++--- .../calendar/compose/CalendarLayoutInfo.kt | 6 +- .../calendar/compose/CalendarState.kt | 28 ++++---- .../calendar/compose/ContentHeightMode.kt | 2 +- .../heatmapcalendar/HeatMapCalendarState.kt | 26 +++---- .../compose/heatmapcalendar/HeatMapWeek.kt | 2 +- .../HeatMapWeekHeaderPosition.kt | 2 +- .../weekcalendar/WeekCalendarLayoutInfo.kt | 6 +- .../compose/weekcalendar/WeekCalendarState.kt | 28 ++++---- .../kizitonwose/calendar/core/CalendarDay.kt | 2 +- .../calendar/core/CalendarMonth.kt | 2 +- .../kizitonwose/calendar/core/DayPosition.kt | 2 +- .../kizitonwose/calendar/core/Extensions.kt | 24 +++---- .../calendar/core/JvmSerializable.kt | 2 +- .../kizitonwose/calendar/core/OutDateStyle.kt | 2 +- .../com/kizitonwose/calendar/core/Week.kt | 2 +- .../com/kizitonwose/calendar/core/WeekDay.kt | 2 +- .../calendar/core/WeekDayPosition.kt | 2 +- .../kizitonwose/calendar/core/YearMonth.kt | 6 +- .../com/kizitonwose/calendar/data/WeekData.kt | 14 ++-- .../calendar/core/Extensions.ios.kt | 2 +- .../kizitonwose/calendar/core/Converters.kt | 4 +- .../calendar/core/Extensions.jvm.kt | 2 +- .../calendar/core/JvmSerializable.jvm.kt | 2 +- .../calendar/core/JvmSerializable.nonJvm.kt | 2 +- .../calendar/core/Extensions.wasmJs.kt | 2 +- .../kizitonwose/calendar/compose/Calendar.kt | 16 ++--- .../calendar/compose/CalendarLayoutInfo.kt | 6 +- .../calendar/compose/CalendarState.kt | 28 ++++---- .../calendar/compose/ContentHeightMode.kt | 2 +- .../heatmapcalendar/HeatMapCalendarState.kt | 26 +++---- .../compose/heatmapcalendar/HeatMapWeek.kt | 2 +- .../HeatMapWeekHeaderPosition.kt | 2 +- .../weekcalendar/WeekCalendarLayoutInfo.kt | 6 +- .../compose/weekcalendar/WeekCalendarState.kt | 28 ++++---- .../kizitonwose/calendar/core/CalendarDay.kt | 2 +- .../calendar/core/CalendarMonth.kt | 2 +- .../kizitonwose/calendar/core/DayPosition.kt | 2 +- .../kizitonwose/calendar/core/Extensions.kt | 12 ++-- .../kizitonwose/calendar/core/OutDateStyle.kt | 2 +- .../com/kizitonwose/calendar/core/Week.kt | 2 +- .../com/kizitonwose/calendar/core/WeekDay.kt | 2 +- .../calendar/core/WeekDayPosition.kt | 2 +- .../kizitonwose/calendar/data/DataStore.kt | 2 +- .../kizitonwose/calendar/data/Extensions.kt | 2 +- .../kizitonwose/calendar/data/MonthData.kt | 12 ++-- .../com/kizitonwose/calendar/data/Utils.kt | 4 +- .../com/kizitonwose/calendar/data/WeekData.kt | 14 ++-- .../com/kizitonwose/calendar/view/Binder.kt | 20 +++--- .../kizitonwose/calendar/view/CalendarView.kt | 68 +++++++++---------- .../com/kizitonwose/calendar/view/DaySize.kt | 2 +- .../kizitonwose/calendar/view/MarginValues.kt | 2 +- .../calendar/view/WeekCalendarView.kt | 68 +++++++++---------- 54 files changed, 274 insertions(+), 263 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bf01c7dc..d716f9e5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin + plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidLibrary) apply false @@ -14,6 +17,14 @@ plugins { allprojects { apply(plugin = rootProject.libs.plugins.kotlinter.get().pluginId) + + plugins.withType().configureEach { + extensions.configure { + if ("sample" !in project.name) { + explicitApi() + } + } + } } apiValidation { diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt index 0c5ad92f..981d03e9 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/Calendar.kt @@ -58,7 +58,7 @@ import kotlinx.datetime.DayOfWeek * customisations are rendered. */ @Composable -fun HorizontalCalendar( +public fun HorizontalCalendar( modifier: Modifier = Modifier, state: CalendarState = rememberCalendarState(), calendarScrollPaged: Boolean = true, @@ -71,7 +71,7 @@ fun HorizontalCalendar( monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)? = null, monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)? = null, -) = Calendar( +): Unit = Calendar( modifier = modifier, state = state, calendarScrollPaged = calendarScrollPaged, @@ -121,7 +121,7 @@ fun HorizontalCalendar( * customisations are rendered. */ @Composable -fun VerticalCalendar( +public fun VerticalCalendar( modifier: Modifier = Modifier, state: CalendarState = rememberCalendarState(), calendarScrollPaged: Boolean = false, @@ -134,7 +134,7 @@ fun VerticalCalendar( monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)? = null, monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)? = null, -) = Calendar( +): Unit = Calendar( modifier = modifier, state = state, calendarScrollPaged = calendarScrollPaged, @@ -232,7 +232,7 @@ private fun Calendar( * placed below each week on the calendar. */ @Composable -fun WeekCalendar( +public fun WeekCalendar( modifier: Modifier = Modifier, state: WeekCalendarState = rememberWeekCalendarState(), calendarScrollPaged: Boolean = true, @@ -242,7 +242,7 @@ fun WeekCalendar( dayContent: @Composable BoxScope.(WeekDay) -> Unit, weekHeader: (@Composable ColumnScope.(Week) -> Unit)? = null, weekFooter: (@Composable ColumnScope.(Week) -> Unit)? = null, -) = WeekCalendarImpl( +): Unit = WeekCalendarImpl( modifier = modifier, state = state, calendarScrollPaged = calendarScrollPaged, @@ -274,7 +274,7 @@ fun WeekCalendar( * placed above each month on the calendar. */ @Composable -fun HeatMapCalendar( +public fun HeatMapCalendar( modifier: Modifier = Modifier, state: HeatMapCalendarState = rememberHeatMapCalendarState(), weekHeaderPosition: HeatMapWeekHeaderPosition = HeatMapWeekHeaderPosition.Start, @@ -283,7 +283,7 @@ fun HeatMapCalendar( dayContent: @Composable ColumnScope.(day: CalendarDay, week: HeatMapWeek) -> Unit, weekHeader: (@Composable ColumnScope.(DayOfWeek) -> Unit)? = null, monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, -) = HeatMapCalendarImpl( +): Unit = HeatMapCalendarImpl( modifier = modifier, state = state, weekHeaderPosition = weekHeaderPosition, diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt index d78948da..6fce652c 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt @@ -11,12 +11,12 @@ import com.kizitonwose.calendar.core.CalendarMonth * Use [CalendarState.layoutInfo] to retrieve this. * @see LazyListLayoutInfo */ -class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> CalendarMonth) : +public class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> CalendarMonth) : LazyListLayoutInfo by info { /** * The list of [CalendarItemInfo] representing all the currently visible months. */ - val visibleMonthsInfo: List + public val visibleMonthsInfo: List get() = visibleItemsInfo.map { CalendarItemInfo(it, month(it.index)) } @@ -30,4 +30,4 @@ class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> C * @see CalendarLayoutInfo * @see LazyListItemInfo */ -class CalendarItemInfo(info: LazyListItemInfo, val month: CalendarMonth) : LazyListItemInfo by info +public class CalendarItemInfo(info: LazyListItemInfo, public val month: CalendarMonth) : LazyListItemInfo by info diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt index 323930a4..3ae6bd8a 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/CalendarState.kt @@ -37,7 +37,7 @@ import kotlinx.datetime.DayOfWeek * @param outDateStyle the initial value for [CalendarState.outDateStyle] */ @Composable -fun rememberCalendarState( +public fun rememberCalendarState( startMonth: YearMonth = YearMonth.now(), endMonth: YearMonth = startMonth, firstVisibleMonth: YearMonth = startMonth, @@ -77,7 +77,7 @@ fun rememberCalendarState( * @param outDateStyle the preferred style for out date generation. */ @Stable -class CalendarState internal constructor( +public class CalendarState internal constructor( startMonth: YearMonth, endMonth: YearMonth, firstDayOfWeek: DayOfWeek, @@ -89,7 +89,7 @@ class CalendarState internal constructor( private var _startMonth by mutableStateOf(startMonth) /** The first month on the calendar. */ - var startMonth: YearMonth + public var startMonth: YearMonth get() = _startMonth set(value) { if (value != startMonth) { @@ -102,7 +102,7 @@ class CalendarState internal constructor( private var _endMonth by mutableStateOf(endMonth) /** The last month on the calendar. */ - var endMonth: YearMonth + public var endMonth: YearMonth get() = _endMonth set(value) { if (value != endMonth) { @@ -115,7 +115,7 @@ class CalendarState internal constructor( private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) /** The first day of week on the calendar. */ - var firstDayOfWeek: DayOfWeek + public var firstDayOfWeek: DayOfWeek get() = _firstDayOfWeek set(value) { if (value != firstDayOfWeek) { @@ -128,7 +128,7 @@ class CalendarState internal constructor( private var _outDateStyle by mutableStateOf(outDateStyle) /** The preferred style for out date generation. */ - var outDateStyle: OutDateStyle + public var outDateStyle: OutDateStyle get() = _outDateStyle set(value) { if (value != outDateStyle) { @@ -142,7 +142,7 @@ class CalendarState internal constructor( * * @see [lastVisibleMonth] */ - val firstVisibleMonth: com.kizitonwose.calendar.core.CalendarMonth by derivedStateOf { + public val firstVisibleMonth: com.kizitonwose.calendar.core.CalendarMonth by derivedStateOf { store[listState.firstVisibleItemIndex] } @@ -151,7 +151,7 @@ class CalendarState internal constructor( * * @see [firstVisibleMonth] */ - val lastVisibleMonth: com.kizitonwose.calendar.core.CalendarMonth by derivedStateOf { + public val lastVisibleMonth: com.kizitonwose.calendar.core.CalendarMonth by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -173,7 +173,7 @@ class CalendarState internal constructor( * * see [LazyListLayoutInfo] */ - val layoutInfo: CalendarLayoutInfo + public val layoutInfo: CalendarLayoutInfo get() = CalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } /** @@ -181,7 +181,7 @@ class CalendarState internal constructor( * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in * progress, use [isScrollInProgress]. */ - val interactionSource: InteractionSource + public val interactionSource: InteractionSource get() = listState.interactionSource internal val listState = LazyListState( @@ -228,7 +228,7 @@ class CalendarState internal constructor( * * @see [animateScrollToMonth] */ - suspend fun scrollToMonth(month: YearMonth) { + public suspend fun scrollToMonth(month: YearMonth) { listState.scrollToItem(getScrollIndex(month) ?: return) } @@ -238,7 +238,7 @@ class CalendarState internal constructor( * @param month the month to which to scroll. Must be within the * range of [startMonth] and [endMonth]. */ - suspend fun animateScrollToMonth(month: YearMonth) { + public suspend fun animateScrollToMonth(month: YearMonth) { listState.animateScrollToItem(getScrollIndex(month) ?: return) } @@ -261,9 +261,9 @@ class CalendarState internal constructor( override suspend fun scroll( scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit, - ) = listState.scroll(scrollPriority, block) + ): Unit = listState.scroll(scrollPriority, block) - companion object { + public companion object { internal val Saver: Saver = listSaver( save = { listOf( diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt index 370751fe..d7611f71 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/ContentHeightMode.kt @@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier /** * Determines how the height of the day content is calculated. */ -enum class ContentHeightMode { +public enum class ContentHeightMode { /** * The day container will wrap its height. This allows you to * use [Modifier.aspectRatio] if you want square day content diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt index 774a13c3..bbf0fc97 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt @@ -37,7 +37,7 @@ import kotlinx.datetime.DayOfWeek * @param firstVisibleMonth the initial value for [HeatMapCalendarState.firstVisibleMonth] */ @Composable -fun rememberHeatMapCalendarState( +public fun rememberHeatMapCalendarState( startMonth: YearMonth = YearMonth.now(), endMonth: YearMonth = startMonth, firstVisibleMonth: YearMonth = startMonth, @@ -73,7 +73,7 @@ fun rememberHeatMapCalendarState( * @param firstVisibleMonth the initial value for [HeatMapCalendarState.firstVisibleMonth] */ @Stable -class HeatMapCalendarState internal constructor( +public class HeatMapCalendarState internal constructor( startMonth: YearMonth, endMonth: YearMonth, firstVisibleMonth: YearMonth, @@ -84,7 +84,7 @@ class HeatMapCalendarState internal constructor( private var _startMonth by mutableStateOf(startMonth) /** The first month on the calendar. */ - var startMonth: YearMonth + public var startMonth: YearMonth get() = _startMonth set(value) { if (value != startMonth) { @@ -97,7 +97,7 @@ class HeatMapCalendarState internal constructor( private var _endMonth by mutableStateOf(endMonth) /** The last month on the calendar. */ - var endMonth: YearMonth + public var endMonth: YearMonth get() = _endMonth set(value) { if (value != endMonth) { @@ -110,7 +110,7 @@ class HeatMapCalendarState internal constructor( private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) /** The first day of week on the calendar. */ - var firstDayOfWeek: DayOfWeek + public var firstDayOfWeek: DayOfWeek get() = _firstDayOfWeek set(value) { if (value != firstDayOfWeek) { @@ -124,7 +124,7 @@ class HeatMapCalendarState internal constructor( * * @see [lastVisibleMonth] */ - val firstVisibleMonth: CalendarMonth by derivedStateOf { + public val firstVisibleMonth: CalendarMonth by derivedStateOf { store[listState.firstVisibleItemIndex] } @@ -133,7 +133,7 @@ class HeatMapCalendarState internal constructor( * * @see [firstVisibleMonth] */ - val lastVisibleMonth: CalendarMonth by derivedStateOf { + public val lastVisibleMonth: CalendarMonth by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -153,7 +153,7 @@ class HeatMapCalendarState internal constructor( * If you want to run some side effects like sending an analytics event or updating a state * based on this value consider using "snapshotFlow". */ - val layoutInfo: CalendarLayoutInfo + public val layoutInfo: CalendarLayoutInfo get() = CalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } /** @@ -161,7 +161,7 @@ class HeatMapCalendarState internal constructor( * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in * progress, use [isScrollInProgress]. */ - val interactionSource: InteractionSource + public val interactionSource: InteractionSource get() = listState.interactionSource internal val listState = LazyListState( @@ -201,7 +201,7 @@ class HeatMapCalendarState internal constructor( * * @see [animateScrollToMonth] */ - suspend fun scrollToMonth(month: YearMonth) { + public suspend fun scrollToMonth(month: YearMonth) { listState.scrollToItem(getScrollIndex(month) ?: return) } @@ -211,7 +211,7 @@ class HeatMapCalendarState internal constructor( * @param month the month to which to scroll. Must be within the * range of [startMonth] and [endMonth]. */ - suspend fun animateScrollToMonth(month: YearMonth) { + public suspend fun animateScrollToMonth(month: YearMonth) { listState.animateScrollToItem(getScrollIndex(month) ?: return) } @@ -234,9 +234,9 @@ class HeatMapCalendarState internal constructor( override suspend fun scroll( scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit, - ) = listState.scroll(scrollPriority, block) + ): Unit = listState.scroll(scrollPriority, block) - companion object { + public companion object { internal val Saver: Saver = listSaver( save = { listOf( diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt index 99e88ff4..b0ebf682 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt @@ -15,4 +15,4 @@ import com.kizitonwose.calendar.core.CalendarDay * @param days the days in this week. */ @Immutable -data class HeatMapWeek(val days: List) +public data class HeatMapWeek(val days: List) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt index ef077405..41cebfe0 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt @@ -6,7 +6,7 @@ import com.kizitonwose.calendar.compose.HeatMapCalendar * Determines the position of the week header * composable (Mon, Tue, Wed...) in the [HeatMapCalendar] */ -enum class HeatMapWeekHeaderPosition { +public enum class HeatMapWeekHeaderPosition { /** * The header is positioned at the start of the calendar. */ diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt index b8c7fbac..302e051f 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt @@ -12,14 +12,14 @@ import com.kizitonwose.calendar.core.Week * * @see LazyListLayoutInfo */ -class WeekCalendarLayoutInfo( +public class WeekCalendarLayoutInfo( info: LazyListLayoutInfo, private val getIndexData: (Int) -> Week, ) : LazyListLayoutInfo by info { /** * The list of [WeekCalendarItemInfo] representing all the currently visible weeks. */ - val visibleWeeksInfo: List + public val visibleWeeksInfo: List get() = visibleItemsInfo.map { info -> WeekCalendarItemInfo(info, getIndexData(info.index)) } @@ -33,5 +33,5 @@ class WeekCalendarLayoutInfo( * @see WeekCalendarLayoutInfo * @see LazyListItemInfo */ -class WeekCalendarItemInfo(info: LazyListItemInfo, val week: Week) : +public class WeekCalendarItemInfo(info: LazyListItemInfo, public val week: Week) : LazyListItemInfo by info diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt index 1982a956..4c2b7c09 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -43,7 +43,7 @@ import kotlinx.datetime.LocalDate * @param firstVisibleWeekDate the date which will have its week visible initially. */ @Composable -fun rememberWeekCalendarState( +public fun rememberWeekCalendarState( startDate: LocalDate = YearMonth.now().atStartOfMonth(), endDate: LocalDate = YearMonth.now().atEndOfMonth(), firstVisibleWeekDate: LocalDate = LocalDate.now(), @@ -83,7 +83,7 @@ fun rememberWeekCalendarState( * @param firstVisibleWeekDate the date which will have its week visible initially. */ @Stable -class WeekCalendarState internal constructor( +public class WeekCalendarState internal constructor( startDate: LocalDate, endDate: LocalDate, firstVisibleWeekDate: LocalDate, @@ -110,7 +110,7 @@ class WeekCalendarState internal constructor( * in the week to which this date belongs, depending on the provided [firstDayOfWeek]. * Such days will have their [WeekDayPosition] set to [WeekDayPosition.InDate] */ - var startDate: LocalDate + public var startDate: LocalDate get() = _startDate set(value) { if (value != _startDate) { @@ -127,7 +127,7 @@ class WeekCalendarState internal constructor( * in the week to which this date belongs. Such days will have their [WeekDayPosition] * set to [WeekDayPosition.OutDate] */ - var endDate: LocalDate + public var endDate: LocalDate get() = _endDate set(value) { if (value != _endDate) { @@ -140,7 +140,7 @@ class WeekCalendarState internal constructor( private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) /** The first day of week on the calendar. */ - var firstDayOfWeek: DayOfWeek + public var firstDayOfWeek: DayOfWeek get() = _firstDayOfWeek set(value) { if (value != _firstDayOfWeek) { @@ -152,14 +152,14 @@ class WeekCalendarState internal constructor( /** * The first week that is visible. */ - val firstVisibleWeek: Week by derivedStateOf { + public val firstVisibleWeek: Week by derivedStateOf { store[listState.firstVisibleItemIndex] } /** * The last week that is visible. */ - val lastVisibleWeek: Week by derivedStateOf { + public val lastVisibleWeek: Week by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -179,7 +179,7 @@ class WeekCalendarState internal constructor( * If you want to run some side effects like sending an analytics event or updating a state * based on this value consider using "snapshotFlow". */ - val layoutInfo: WeekCalendarLayoutInfo + public val layoutInfo: WeekCalendarLayoutInfo get() = WeekCalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } internal val store = DataStore { offset -> @@ -222,7 +222,7 @@ class WeekCalendarState internal constructor( * * @see [animateScrollToWeek] */ - suspend fun scrollToWeek(date: LocalDate) { + public suspend fun scrollToWeek(date: LocalDate) { listState.scrollToItem(getScrollIndex(date) ?: return) } @@ -231,7 +231,7 @@ class WeekCalendarState internal constructor( * * @param date the week to which to scroll. */ - suspend fun animateScrollToWeek(date: LocalDate) { + public suspend fun animateScrollToWeek(date: LocalDate) { listState.animateScrollToItem(getScrollIndex(date) ?: return) } @@ -240,7 +240,7 @@ class WeekCalendarState internal constructor( * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in * progress, use [isScrollInProgress]. */ - val interactionSource: InteractionSource + public val interactionSource: InteractionSource get() = listState.interactionSource /** @@ -254,7 +254,7 @@ class WeekCalendarState internal constructor( override suspend fun scroll( scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit, - ) = listState.scroll(scrollPriority, block) + ): Unit = listState.scroll(scrollPriority, block) private fun getScrollIndex(date: LocalDate): Int? { if (date !in startDateAdjusted..endDateAdjusted) { @@ -264,8 +264,8 @@ class WeekCalendarState internal constructor( return getWeekIndex(startDateAdjusted, date) } - companion object { - val Saver: Saver = listSaver( + public companion object { + public val Saver: Saver = listSaver( save = { listOf( it.startDate.toJvmSerializableLocalDate(), diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt index 5e9f002d..07be0a3f 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarDay.kt @@ -10,4 +10,4 @@ import kotlinx.datetime.LocalDate * @param position the [DayPosition] for this day. */ @Immutable -data class CalendarDay(val date: LocalDate, val position: DayPosition) +public data class CalendarDay(val date: LocalDate, val position: DayPosition) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt index 511dc03c..5429245c 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/CalendarMonth.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.Immutable * @param weekDays the weeks in this month. */ @Immutable -data class CalendarMonth internal constructor( +public data class CalendarMonth internal constructor( val yearMonth: YearMonth, val weekDays: List>, ) { diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt index 116e5458..53dddafd 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/DayPosition.kt @@ -3,7 +3,7 @@ package com.kizitonwose.calendar.core /** * Describes the position of a [CalendarDay] in the month. */ -enum class DayPosition { +public enum class DayPosition { /** * The day is positioned at the start of the month to * ensure proper alignment of the first day of the week. diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index 64bc8577..7c40c5d5 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -20,7 +20,7 @@ import kotlinx.datetime.until * * @see [firstDayOfWeekFromLocale] */ -fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List { +public fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List { val pivot = 7 - firstDayOfWeek.ordinal val daysOfWeek = DayOfWeek.entries // Order `daysOfWeek` array so that firstDayOfWeek is at the start position. @@ -30,33 +30,33 @@ fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List) { +public data class Week internal constructor(val days: List) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt index 7b72f8e6..8271341d 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDay.kt @@ -10,4 +10,4 @@ import kotlinx.datetime.LocalDate * @param position the [WeekDayPosition] for this day. */ @Immutable -data class WeekDay(val date: LocalDate, val position: WeekDayPosition) +public data class WeekDay(val date: LocalDate, val position: WeekDayPosition) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt index 8dfc6809..6c3c5e47 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/WeekDayPosition.kt @@ -3,7 +3,7 @@ package com.kizitonwose.calendar.core /** * Describes the position of a [WeekDay] on the calendar. */ -enum class WeekDayPosition { +public enum class WeekDayPosition { /** * The day is positioned at the start of the calendar to * ensure proper alignment of the first day of the week. diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt index a48f00e8..03e1fcea 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt @@ -5,8 +5,8 @@ import kotlinx.datetime.Month import kotlinx.datetime.number @Immutable -data class YearMonth(val year: Int, val month: Month) : Comparable, JvmSerializable { - constructor(year: Int, monthNumber: Int) : +public data class YearMonth(val year: Int, val month: Month) : Comparable, JvmSerializable { + public constructor(year: Int, monthNumber: Int) : this(year = year, month = Month(monthNumber)) init { @@ -28,5 +28,5 @@ data class YearMonth(val year: Int, val month: Month) : Comparable, J return cmp } - companion object + public companion object } diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt index 1015a741..6865c1d8 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/data/WeekData.kt @@ -11,12 +11,12 @@ import com.kizitonwose.calendar.core.weeksUntil import kotlinx.datetime.DayOfWeek import kotlinx.datetime.LocalDate -data class WeekDateRange( +public data class WeekDateRange( val startDateAdjusted: LocalDate, val endDateAdjusted: LocalDate, ) -fun getWeekCalendarAdjustedRange( +public fun getWeekCalendarAdjustedRange( startDate: LocalDate, endDate: LocalDate, firstDayOfWeek: DayOfWeek, @@ -28,7 +28,7 @@ fun getWeekCalendarAdjustedRange( return WeekDateRange(startDateAdjusted = startDateAdjusted, endDateAdjusted = endDateAdjusted) } -fun getWeekCalendarData( +public fun getWeekCalendarData( startDateAdjusted: LocalDate, offset: Int, desiredStartDate: LocalDate, @@ -38,12 +38,12 @@ fun getWeekCalendarData( return WeekData(firstDayInWeek, desiredStartDate, desiredEndDate) } -data class WeekData internal constructor( +public data class WeekData internal constructor( private val firstDayInWeek: LocalDate, private val desiredStartDate: LocalDate, private val desiredEndDate: LocalDate, ) { - val week = Week((0 until 7).map { dayOffset -> getDay(dayOffset) }) + val week: Week = Week((0 until 7).map { dayOffset -> getDay(dayOffset) }) private fun getDay(dayOffset: Int): WeekDay { val date = firstDayInWeek.plusDays(dayOffset) @@ -56,11 +56,11 @@ data class WeekData internal constructor( } } -fun getWeekIndex(startDateAdjusted: LocalDate, date: LocalDate): Int { +public fun getWeekIndex(startDateAdjusted: LocalDate, date: LocalDate): Int { return startDateAdjusted.weeksUntil(date) } -fun getWeekIndicesCount(startDateAdjusted: LocalDate, endDateAdjusted: LocalDate): Int { +public fun getWeekIndicesCount(startDateAdjusted: LocalDate, endDateAdjusted: LocalDate): Int { // Add one to include the start week itself! return getWeekIndex(startDateAdjusted, endDateAdjusted) + 1 } diff --git a/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.ios.kt b/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.ios.kt index f0a164db..2a03b858 100644 --- a/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.ios.kt +++ b/compose-multiplatform/library/src/iosMain/kotlin/com/kizitonwose/calendar/core/Extensions.ios.kt @@ -8,7 +8,7 @@ import platform.Foundation.NSLocale /** * Returns the first day of the week from the provided locale. */ -actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { +public actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { val firstWeekday = NSCalendar.currentCalendar.let { it.setLocale(NSLocale(locale.toLanguageTag())) // https://developer.apple.com/documentation/foundation/calendar/2293656-firstweekday diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt index b1b278b1..c78263e5 100644 --- a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Converters.kt @@ -2,6 +2,6 @@ package com.kizitonwose.calendar.core import java.time.YearMonth as jtYearMonth -fun YearMonth.toJavaYearMonth(): jtYearMonth = jtYearMonth.of(year, month) +public fun YearMonth.toJavaYearMonth(): jtYearMonth = jtYearMonth.of(year, month) -fun jtYearMonth.toKotlinYearMonth(): YearMonth = YearMonth(year, month) +public fun jtYearMonth.toKotlinYearMonth(): YearMonth = YearMonth(year, month) diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt index 1282928e..c99f00dd 100644 --- a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/Extensions.jvm.kt @@ -5,5 +5,5 @@ import java.time.DayOfWeek import java.time.temporal.WeekFields import java.util.Locale as JavaLocale -actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek = +public actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek = WeekFields.of(JavaLocale.forLanguageTag(locale.toLanguageTag())).firstDayOfWeek diff --git a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt index 2b9ed644..199695a3 100644 --- a/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt +++ b/compose-multiplatform/library/src/jvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.jvm.kt @@ -1,3 +1,3 @@ package com.kizitonwose.calendar.core -actual typealias JvmSerializable = java.io.Serializable +public actual typealias JvmSerializable = java.io.Serializable diff --git a/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt b/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt index bbce3a20..7147a4bd 100644 --- a/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt +++ b/compose-multiplatform/library/src/nonJvmMain/kotlin/com/kizitonwose/calendar/core/JvmSerializable.nonJvm.kt @@ -1,3 +1,3 @@ package com.kizitonwose.calendar.core -actual interface JvmSerializable +public actual interface JvmSerializable diff --git a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt index b728b271..de8d954c 100644 --- a/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt +++ b/compose-multiplatform/library/src/wasmJsMain/kotlin/com/kizitonwose/calendar/core/Extensions.wasmJs.kt @@ -6,7 +6,7 @@ import kotlinx.datetime.DayOfWeek /** * Returns the first day of the week from the provided locale. */ -actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { +public actual fun firstDayOfWeekFromLocale(locale: Locale): DayOfWeek { return try { val firstDay = jsFirstDayFromTag(locale.toLanguageTag()) daysOfWeek(firstDayOfWeek = DayOfWeek.MONDAY)[firstDay - 1] diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/Calendar.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/Calendar.kt index a9ba05b1..9d5bee28 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/Calendar.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/Calendar.kt @@ -58,7 +58,7 @@ import java.time.DayOfWeek * customisations are rendered. */ @Composable -fun HorizontalCalendar( +public fun HorizontalCalendar( modifier: Modifier = Modifier, state: CalendarState = rememberCalendarState(), calendarScrollPaged: Boolean = true, @@ -71,7 +71,7 @@ fun HorizontalCalendar( monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)? = null, monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)? = null, -) = Calendar( +): Unit = Calendar( modifier = modifier, state = state, calendarScrollPaged = calendarScrollPaged, @@ -121,7 +121,7 @@ fun HorizontalCalendar( * customisations are rendered. */ @Composable -fun VerticalCalendar( +public fun VerticalCalendar( modifier: Modifier = Modifier, state: CalendarState = rememberCalendarState(), calendarScrollPaged: Boolean = false, @@ -134,7 +134,7 @@ fun VerticalCalendar( monthBody: (@Composable ColumnScope.(CalendarMonth, content: @Composable () -> Unit) -> Unit)? = null, monthFooter: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, monthContainer: (@Composable LazyItemScope.(CalendarMonth, container: @Composable () -> Unit) -> Unit)? = null, -) = Calendar( +): Unit = Calendar( modifier = modifier, state = state, calendarScrollPaged = calendarScrollPaged, @@ -232,7 +232,7 @@ private fun Calendar( * placed below each week on the calendar. */ @Composable -fun WeekCalendar( +public fun WeekCalendar( modifier: Modifier = Modifier, state: WeekCalendarState = rememberWeekCalendarState(), calendarScrollPaged: Boolean = true, @@ -242,7 +242,7 @@ fun WeekCalendar( dayContent: @Composable BoxScope.(WeekDay) -> Unit, weekHeader: (@Composable ColumnScope.(Week) -> Unit)? = null, weekFooter: (@Composable ColumnScope.(Week) -> Unit)? = null, -) = WeekCalendarImpl( +): Unit = WeekCalendarImpl( modifier = modifier, state = state, calendarScrollPaged = calendarScrollPaged, @@ -274,7 +274,7 @@ fun WeekCalendar( * placed above each month on the calendar. */ @Composable -fun HeatMapCalendar( +public fun HeatMapCalendar( modifier: Modifier = Modifier, state: HeatMapCalendarState = rememberHeatMapCalendarState(), weekHeaderPosition: HeatMapWeekHeaderPosition = HeatMapWeekHeaderPosition.Start, @@ -283,7 +283,7 @@ fun HeatMapCalendar( dayContent: @Composable ColumnScope.(day: CalendarDay, week: HeatMapWeek) -> Unit, weekHeader: (@Composable ColumnScope.(DayOfWeek) -> Unit)? = null, monthHeader: (@Composable ColumnScope.(CalendarMonth) -> Unit)? = null, -) = HeatMapCalendarImpl( +): Unit = HeatMapCalendarImpl( modifier = modifier, state = state, weekHeaderPosition = weekHeaderPosition, diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt index d78948da..6fce652c 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarLayoutInfo.kt @@ -11,12 +11,12 @@ import com.kizitonwose.calendar.core.CalendarMonth * Use [CalendarState.layoutInfo] to retrieve this. * @see LazyListLayoutInfo */ -class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> CalendarMonth) : +public class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> CalendarMonth) : LazyListLayoutInfo by info { /** * The list of [CalendarItemInfo] representing all the currently visible months. */ - val visibleMonthsInfo: List + public val visibleMonthsInfo: List get() = visibleItemsInfo.map { CalendarItemInfo(it, month(it.index)) } @@ -30,4 +30,4 @@ class CalendarLayoutInfo(info: LazyListLayoutInfo, private val month: (Int) -> C * @see CalendarLayoutInfo * @see LazyListItemInfo */ -class CalendarItemInfo(info: LazyListItemInfo, val month: CalendarMonth) : LazyListItemInfo by info +public class CalendarItemInfo(info: LazyListItemInfo, public val month: CalendarMonth) : LazyListItemInfo by info diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt index a66be734..0f5e901b 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/CalendarState.kt @@ -37,7 +37,7 @@ import java.time.YearMonth * @param outDateStyle the initial value for [CalendarState.outDateStyle] */ @Composable -fun rememberCalendarState( +public fun rememberCalendarState( startMonth: YearMonth = YearMonth.now(), endMonth: YearMonth = startMonth, firstVisibleMonth: YearMonth = startMonth, @@ -77,7 +77,7 @@ fun rememberCalendarState( * @param outDateStyle the preferred style for out date generation. */ @Stable -class CalendarState internal constructor( +public class CalendarState internal constructor( startMonth: YearMonth, endMonth: YearMonth, firstDayOfWeek: DayOfWeek, @@ -89,7 +89,7 @@ class CalendarState internal constructor( private var _startMonth by mutableStateOf(startMonth) /** The first month on the calendar. */ - var startMonth: YearMonth + public var startMonth: YearMonth get() = _startMonth set(value) { if (value != startMonth) { @@ -102,7 +102,7 @@ class CalendarState internal constructor( private var _endMonth by mutableStateOf(endMonth) /** The last month on the calendar. */ - var endMonth: YearMonth + public var endMonth: YearMonth get() = _endMonth set(value) { if (value != endMonth) { @@ -115,7 +115,7 @@ class CalendarState internal constructor( private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) /** The first day of week on the calendar. */ - var firstDayOfWeek: DayOfWeek + public var firstDayOfWeek: DayOfWeek get() = _firstDayOfWeek set(value) { if (value != firstDayOfWeek) { @@ -128,7 +128,7 @@ class CalendarState internal constructor( private var _outDateStyle by mutableStateOf(outDateStyle) /** The preferred style for out date generation. */ - var outDateStyle: OutDateStyle + public var outDateStyle: OutDateStyle get() = _outDateStyle set(value) { if (value != outDateStyle) { @@ -142,7 +142,7 @@ class CalendarState internal constructor( * * @see [lastVisibleMonth] */ - val firstVisibleMonth: CalendarMonth by derivedStateOf { + public val firstVisibleMonth: CalendarMonth by derivedStateOf { store[listState.firstVisibleItemIndex] } @@ -151,7 +151,7 @@ class CalendarState internal constructor( * * @see [firstVisibleMonth] */ - val lastVisibleMonth: CalendarMonth by derivedStateOf { + public val lastVisibleMonth: CalendarMonth by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -173,7 +173,7 @@ class CalendarState internal constructor( * * see [LazyListLayoutInfo] */ - val layoutInfo: CalendarLayoutInfo + public val layoutInfo: CalendarLayoutInfo get() = CalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } /** @@ -181,7 +181,7 @@ class CalendarState internal constructor( * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in * progress, use [isScrollInProgress]. */ - val interactionSource: InteractionSource + public val interactionSource: InteractionSource get() = listState.interactionSource internal val listState = LazyListState( @@ -228,7 +228,7 @@ class CalendarState internal constructor( * * @see [animateScrollToMonth] */ - suspend fun scrollToMonth(month: YearMonth) { + public suspend fun scrollToMonth(month: YearMonth) { listState.scrollToItem(getScrollIndex(month) ?: return) } @@ -238,7 +238,7 @@ class CalendarState internal constructor( * @param month the month to which to scroll. Must be within the * range of [startMonth] and [endMonth]. */ - suspend fun animateScrollToMonth(month: YearMonth) { + public suspend fun animateScrollToMonth(month: YearMonth) { listState.animateScrollToItem(getScrollIndex(month) ?: return) } @@ -261,9 +261,9 @@ class CalendarState internal constructor( override suspend fun scroll( scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit, - ) = listState.scroll(scrollPriority, block) + ): Unit = listState.scroll(scrollPriority, block) - companion object { + public companion object { internal val Saver: Saver = listSaver( save = { listOf( diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/ContentHeightMode.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/ContentHeightMode.kt index 370751fe..d7611f71 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/ContentHeightMode.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/ContentHeightMode.kt @@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier /** * Determines how the height of the day content is calculated. */ -enum class ContentHeightMode { +public enum class ContentHeightMode { /** * The day container will wrap its height. This allows you to * use [Modifier.aspectRatio] if you want square day content diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt index 9fbcd240..bb13151e 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapCalendarState.kt @@ -37,7 +37,7 @@ import java.time.YearMonth * @param firstVisibleMonth the initial value for [HeatMapCalendarState.firstVisibleMonth] */ @Composable -fun rememberHeatMapCalendarState( +public fun rememberHeatMapCalendarState( startMonth: YearMonth = YearMonth.now(), endMonth: YearMonth = startMonth, firstVisibleMonth: YearMonth = startMonth, @@ -73,7 +73,7 @@ fun rememberHeatMapCalendarState( * @param firstVisibleMonth the initial value for [HeatMapCalendarState.firstVisibleMonth] */ @Stable -class HeatMapCalendarState internal constructor( +public class HeatMapCalendarState internal constructor( startMonth: YearMonth, endMonth: YearMonth, firstVisibleMonth: YearMonth, @@ -84,7 +84,7 @@ class HeatMapCalendarState internal constructor( private var _startMonth by mutableStateOf(startMonth) /** The first month on the calendar. */ - var startMonth: YearMonth + public var startMonth: YearMonth get() = _startMonth set(value) { if (value != startMonth) { @@ -97,7 +97,7 @@ class HeatMapCalendarState internal constructor( private var _endMonth by mutableStateOf(endMonth) /** The last month on the calendar. */ - var endMonth: YearMonth + public var endMonth: YearMonth get() = _endMonth set(value) { if (value != endMonth) { @@ -110,7 +110,7 @@ class HeatMapCalendarState internal constructor( private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) /** The first day of week on the calendar. */ - var firstDayOfWeek: DayOfWeek + public var firstDayOfWeek: DayOfWeek get() = _firstDayOfWeek set(value) { if (value != firstDayOfWeek) { @@ -124,7 +124,7 @@ class HeatMapCalendarState internal constructor( * * @see [lastVisibleMonth] */ - val firstVisibleMonth: CalendarMonth by derivedStateOf { + public val firstVisibleMonth: CalendarMonth by derivedStateOf { store[listState.firstVisibleItemIndex] } @@ -133,7 +133,7 @@ class HeatMapCalendarState internal constructor( * * @see [firstVisibleMonth] */ - val lastVisibleMonth: CalendarMonth by derivedStateOf { + public val lastVisibleMonth: CalendarMonth by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -153,7 +153,7 @@ class HeatMapCalendarState internal constructor( * If you want to run some side effects like sending an analytics event or updating a state * based on this value consider using "snapshotFlow". */ - val layoutInfo: CalendarLayoutInfo + public val layoutInfo: CalendarLayoutInfo get() = CalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } /** @@ -161,7 +161,7 @@ class HeatMapCalendarState internal constructor( * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in * progress, use [isScrollInProgress]. */ - val interactionSource: InteractionSource + public val interactionSource: InteractionSource get() = listState.interactionSource internal val listState = LazyListState( @@ -201,7 +201,7 @@ class HeatMapCalendarState internal constructor( * * @see [animateScrollToMonth] */ - suspend fun scrollToMonth(month: YearMonth) { + public suspend fun scrollToMonth(month: YearMonth) { listState.scrollToItem(getScrollIndex(month) ?: return) } @@ -211,7 +211,7 @@ class HeatMapCalendarState internal constructor( * @param month the month to which to scroll. Must be within the * range of [startMonth] and [endMonth]. */ - suspend fun animateScrollToMonth(month: YearMonth) { + public suspend fun animateScrollToMonth(month: YearMonth) { listState.animateScrollToItem(getScrollIndex(month) ?: return) } @@ -234,9 +234,9 @@ class HeatMapCalendarState internal constructor( override suspend fun scroll( scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit, - ) = listState.scroll(scrollPriority, block) + ): Unit = listState.scroll(scrollPriority, block) - companion object { + public companion object { internal val Saver: Saver = listSaver( save = { listOf( diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt index 355a0e14..ba6b471c 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeek.kt @@ -16,4 +16,4 @@ import java.io.Serializable * @param days the days in this week. */ @Immutable -data class HeatMapWeek(val days: List) : Serializable +public data class HeatMapWeek(val days: List) : Serializable diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt index ef077405..41cebfe0 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/heatmapcalendar/HeatMapWeekHeaderPosition.kt @@ -6,7 +6,7 @@ import com.kizitonwose.calendar.compose.HeatMapCalendar * Determines the position of the week header * composable (Mon, Tue, Wed...) in the [HeatMapCalendar] */ -enum class HeatMapWeekHeaderPosition { +public enum class HeatMapWeekHeaderPosition { /** * The header is positioned at the start of the calendar. */ diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt index b8c7fbac..302e051f 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarLayoutInfo.kt @@ -12,14 +12,14 @@ import com.kizitonwose.calendar.core.Week * * @see LazyListLayoutInfo */ -class WeekCalendarLayoutInfo( +public class WeekCalendarLayoutInfo( info: LazyListLayoutInfo, private val getIndexData: (Int) -> Week, ) : LazyListLayoutInfo by info { /** * The list of [WeekCalendarItemInfo] representing all the currently visible weeks. */ - val visibleWeeksInfo: List + public val visibleWeeksInfo: List get() = visibleItemsInfo.map { info -> WeekCalendarItemInfo(info, getIndexData(info.index)) } @@ -33,5 +33,5 @@ class WeekCalendarLayoutInfo( * @see WeekCalendarLayoutInfo * @see LazyListItemInfo */ -class WeekCalendarItemInfo(info: LazyListItemInfo, val week: Week) : +public class WeekCalendarItemInfo(info: LazyListItemInfo, public val week: Week) : LazyListItemInfo by info diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt index 9a271885..32bf2721 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -39,7 +39,7 @@ import java.time.YearMonth * @param firstVisibleWeekDate the date which will have its week visible initially. */ @Composable -fun rememberWeekCalendarState( +public fun rememberWeekCalendarState( startDate: LocalDate = YearMonth.now().atStartOfMonth(), endDate: LocalDate = YearMonth.now().atEndOfMonth(), firstVisibleWeekDate: LocalDate = LocalDate.now(), @@ -79,7 +79,7 @@ fun rememberWeekCalendarState( * @param firstVisibleWeekDate the date which will have its week visible initially. */ @Stable -class WeekCalendarState internal constructor( +public class WeekCalendarState internal constructor( startDate: LocalDate, endDate: LocalDate, firstVisibleWeekDate: LocalDate, @@ -106,7 +106,7 @@ class WeekCalendarState internal constructor( * in the week to which this date belongs, depending on the provided [firstDayOfWeek]. * Such days will have their [WeekDayPosition] set to [WeekDayPosition.InDate] */ - var startDate: LocalDate + public var startDate: LocalDate get() = _startDate set(value) { if (value != _startDate) { @@ -123,7 +123,7 @@ class WeekCalendarState internal constructor( * in the week to which this date belongs. Such days will have their [WeekDayPosition] * set to [WeekDayPosition.OutDate] */ - var endDate: LocalDate + public var endDate: LocalDate get() = _endDate set(value) { if (value != _endDate) { @@ -136,7 +136,7 @@ class WeekCalendarState internal constructor( private var _firstDayOfWeek by mutableStateOf(firstDayOfWeek) /** The first day of week on the calendar. */ - var firstDayOfWeek: DayOfWeek + public var firstDayOfWeek: DayOfWeek get() = _firstDayOfWeek set(value) { if (value != _firstDayOfWeek) { @@ -148,14 +148,14 @@ class WeekCalendarState internal constructor( /** * The first week that is visible. */ - val firstVisibleWeek: Week by derivedStateOf { + public val firstVisibleWeek: Week by derivedStateOf { store[listState.firstVisibleItemIndex] } /** * The last week that is visible. */ - val lastVisibleWeek: Week by derivedStateOf { + public val lastVisibleWeek: Week by derivedStateOf { store[listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0] } @@ -175,7 +175,7 @@ class WeekCalendarState internal constructor( * If you want to run some side effects like sending an analytics event or updating a state * based on this value consider using "snapshotFlow". */ - val layoutInfo: WeekCalendarLayoutInfo + public val layoutInfo: WeekCalendarLayoutInfo get() = WeekCalendarLayoutInfo(listState.layoutInfo) { index -> store[index] } internal val store = DataStore { offset -> @@ -218,7 +218,7 @@ class WeekCalendarState internal constructor( * * @see [animateScrollToWeek] */ - suspend fun scrollToWeek(date: LocalDate) { + public suspend fun scrollToWeek(date: LocalDate) { listState.scrollToItem(getScrollIndex(date) ?: return) } @@ -227,7 +227,7 @@ class WeekCalendarState internal constructor( * * @param date the week to which to scroll. */ - suspend fun animateScrollToWeek(date: LocalDate) { + public suspend fun animateScrollToWeek(date: LocalDate) { listState.animateScrollToItem(getScrollIndex(date) ?: return) } @@ -236,7 +236,7 @@ class WeekCalendarState internal constructor( * calendar is being dragged. If you want to know whether the fling (or animated scroll) is in * progress, use [isScrollInProgress]. */ - val interactionSource: InteractionSource + public val interactionSource: InteractionSource get() = listState.interactionSource /** @@ -250,7 +250,7 @@ class WeekCalendarState internal constructor( override suspend fun scroll( scrollPriority: MutatePriority, block: suspend ScrollScope.() -> Unit, - ) = listState.scroll(scrollPriority, block) + ): Unit = listState.scroll(scrollPriority, block) private fun getScrollIndex(date: LocalDate): Int? { if (date !in startDateAdjusted..endDateAdjusted) { @@ -260,8 +260,8 @@ class WeekCalendarState internal constructor( return getWeekIndex(startDateAdjusted, date) } - companion object { - val Saver: Saver = listSaver( + public companion object { + public val Saver: Saver = listSaver( save = { listOf( it.startDate, diff --git a/core/src/main/java/com/kizitonwose/calendar/core/CalendarDay.kt b/core/src/main/java/com/kizitonwose/calendar/core/CalendarDay.kt index d5e2a44b..619b610c 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/CalendarDay.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/CalendarDay.kt @@ -11,4 +11,4 @@ import java.time.LocalDate * @param position the [DayPosition] for this day. */ @Immutable -data class CalendarDay(val date: LocalDate, val position: DayPosition) : Serializable +public data class CalendarDay(val date: LocalDate, val position: DayPosition) : Serializable diff --git a/core/src/main/java/com/kizitonwose/calendar/core/CalendarMonth.kt b/core/src/main/java/com/kizitonwose/calendar/core/CalendarMonth.kt index cf5ed175..62511279 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/CalendarMonth.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/CalendarMonth.kt @@ -11,7 +11,7 @@ import java.time.YearMonth * @param weekDays the weeks in this month. */ @Immutable -data class CalendarMonth( +public data class CalendarMonth( val yearMonth: YearMonth, val weekDays: List>, ) : Serializable { diff --git a/core/src/main/java/com/kizitonwose/calendar/core/DayPosition.kt b/core/src/main/java/com/kizitonwose/calendar/core/DayPosition.kt index 116e5458..53dddafd 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/DayPosition.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/DayPosition.kt @@ -3,7 +3,7 @@ package com.kizitonwose.calendar.core /** * Describes the position of a [CalendarDay] in the month. */ -enum class DayPosition { +public enum class DayPosition { /** * The day is positioned at the start of the month to * ensure proper alignment of the first day of the week. diff --git a/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt b/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt index 7df1adfb..eb90a58f 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/Extensions.kt @@ -13,7 +13,7 @@ import java.util.Locale * @see [firstDayOfWeekFromLocale] */ @JvmOverloads -fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List { +public fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List { val pivot = 7 - firstDayOfWeek.ordinal val daysOfWeek = DayOfWeek.entries // Order `daysOfWeek` array so that firstDayOfWeek is at the start position. @@ -24,20 +24,20 @@ fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): List) : Serializable { +public data class Week(val days: List) : Serializable { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/core/src/main/java/com/kizitonwose/calendar/core/WeekDay.kt b/core/src/main/java/com/kizitonwose/calendar/core/WeekDay.kt index c7e367e1..4ff93452 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/WeekDay.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/WeekDay.kt @@ -11,4 +11,4 @@ import java.time.LocalDate * @param position the [WeekDayPosition] for this day. */ @Immutable -data class WeekDay(val date: LocalDate, val position: WeekDayPosition) : Serializable +public data class WeekDay(val date: LocalDate, val position: WeekDayPosition) : Serializable diff --git a/core/src/main/java/com/kizitonwose/calendar/core/WeekDayPosition.kt b/core/src/main/java/com/kizitonwose/calendar/core/WeekDayPosition.kt index 8dfc6809..6c3c5e47 100644 --- a/core/src/main/java/com/kizitonwose/calendar/core/WeekDayPosition.kt +++ b/core/src/main/java/com/kizitonwose/calendar/core/WeekDayPosition.kt @@ -3,7 +3,7 @@ package com.kizitonwose.calendar.core /** * Describes the position of a [WeekDay] on the calendar. */ -enum class WeekDayPosition { +public enum class WeekDayPosition { /** * The day is positioned at the start of the calendar to * ensure proper alignment of the first day of the week. diff --git a/data/src/main/java/com/kizitonwose/calendar/data/DataStore.kt b/data/src/main/java/com/kizitonwose/calendar/data/DataStore.kt index 7445bd89..13b08dcf 100644 --- a/data/src/main/java/com/kizitonwose/calendar/data/DataStore.kt +++ b/data/src/main/java/com/kizitonwose/calendar/data/DataStore.kt @@ -4,7 +4,7 @@ package com.kizitonwose.calendar.data * Basically [MutableMap.getOrPut] but allows us read the map * in multiple places without calling `getOrPut` everywhere. */ -class DataStore( +public class DataStore( private val store: MutableMap = HashMap(), private val create: (offset: Int) -> V, ) : MutableMap by store { diff --git a/data/src/main/java/com/kizitonwose/calendar/data/Extensions.kt b/data/src/main/java/com/kizitonwose/calendar/data/Extensions.kt index 69303a3b..eb53661f 100644 --- a/data/src/main/java/com/kizitonwose/calendar/data/Extensions.kt +++ b/data/src/main/java/com/kizitonwose/calendar/data/Extensions.kt @@ -3,4 +3,4 @@ package com.kizitonwose.calendar.data import java.time.DayOfWeek // E.g DayOfWeek.SATURDAY.daysUntil(DayOfWeek.TUESDAY) = 3 -fun DayOfWeek.daysUntil(other: DayOfWeek) = (7 + (other.value - value)) % 7 +public fun DayOfWeek.daysUntil(other: DayOfWeek): Int = (7 + (other.value - value)) % 7 diff --git a/data/src/main/java/com/kizitonwose/calendar/data/MonthData.kt b/data/src/main/java/com/kizitonwose/calendar/data/MonthData.kt index 5b38d842..1eab49ca 100644 --- a/data/src/main/java/com/kizitonwose/calendar/data/MonthData.kt +++ b/data/src/main/java/com/kizitonwose/calendar/data/MonthData.kt @@ -12,7 +12,7 @@ import java.time.DayOfWeek import java.time.YearMonth import java.time.temporal.ChronoUnit -data class MonthData internal constructor( +public data class MonthData internal constructor( private val month: YearMonth, private val inDays: Int, private val outDays: Int, @@ -27,7 +27,7 @@ data class MonthData internal constructor( private val nextMonth = month.nextMonth - val calendarMonth = + val calendarMonth: CalendarMonth = CalendarMonth(month, rows.map { week -> week.map { dayOffset -> getDay(dayOffset) } }) private fun getDay(dayOffset: Int): CalendarDay { @@ -42,7 +42,7 @@ data class MonthData internal constructor( } } -fun getCalendarMonthData( +public fun getCalendarMonthData( startMonth: YearMonth, offset: Int, firstDayOfWeek: DayOfWeek, @@ -64,7 +64,7 @@ fun getCalendarMonthData( return MonthData(month, inDays, outDays) } -fun getHeatMapCalendarMonthData( +public fun getHeatMapCalendarMonthData( startMonth: YearMonth, offset: Int, firstDayOfWeek: DayOfWeek, @@ -82,11 +82,11 @@ fun getHeatMapCalendarMonthData( return MonthData(month, inDays, outDays) } -fun getMonthIndex(startMonth: YearMonth, targetMonth: YearMonth): Int { +public fun getMonthIndex(startMonth: YearMonth, targetMonth: YearMonth): Int { return ChronoUnit.MONTHS.between(startMonth, targetMonth).toInt() } -fun getMonthIndicesCount(startMonth: YearMonth, endMonth: YearMonth): Int { +public fun getMonthIndicesCount(startMonth: YearMonth, endMonth: YearMonth): Int { // Add one to include the start month itself! return getMonthIndex(startMonth, endMonth) + 1 } diff --git a/data/src/main/java/com/kizitonwose/calendar/data/Utils.kt b/data/src/main/java/com/kizitonwose/calendar/data/Utils.kt index 33ff3376..ab9cfc8f 100644 --- a/data/src/main/java/com/kizitonwose/calendar/data/Utils.kt +++ b/data/src/main/java/com/kizitonwose/calendar/data/Utils.kt @@ -3,13 +3,13 @@ package com.kizitonwose.calendar.data import java.time.LocalDate import java.time.YearMonth -fun checkDateRange(startMonth: YearMonth, endMonth: YearMonth) { +public fun checkDateRange(startMonth: YearMonth, endMonth: YearMonth) { check(endMonth >= startMonth) { "startMonth: $startMonth is greater than endMonth: $endMonth" } } -fun checkDateRange(startDate: LocalDate, endDate: LocalDate) { +public fun checkDateRange(startDate: LocalDate, endDate: LocalDate) { check(endDate >= startDate) { "startDate: $startDate is greater than endDate: $endDate" } diff --git a/data/src/main/java/com/kizitonwose/calendar/data/WeekData.kt b/data/src/main/java/com/kizitonwose/calendar/data/WeekData.kt index 8652bb7e..006383cd 100644 --- a/data/src/main/java/com/kizitonwose/calendar/data/WeekData.kt +++ b/data/src/main/java/com/kizitonwose/calendar/data/WeekData.kt @@ -7,12 +7,12 @@ import java.time.DayOfWeek import java.time.LocalDate import java.time.temporal.ChronoUnit -data class WeekDateRange( +public data class WeekDateRange( val startDateAdjusted: LocalDate, val endDateAdjusted: LocalDate, ) -fun getWeekCalendarAdjustedRange( +public fun getWeekCalendarAdjustedRange( startDate: LocalDate, endDate: LocalDate, firstDayOfWeek: DayOfWeek, @@ -25,7 +25,7 @@ fun getWeekCalendarAdjustedRange( return WeekDateRange(startDateAdjusted = startDateAdjusted, endDateAdjusted = endDateAdjusted) } -fun getWeekCalendarData( +public fun getWeekCalendarData( startDateAdjusted: LocalDate, offset: Int, desiredStartDate: LocalDate, @@ -35,12 +35,12 @@ fun getWeekCalendarData( return WeekData(firstDayInWeek, desiredStartDate, desiredEndDate) } -data class WeekData internal constructor( +public data class WeekData internal constructor( private val firstDayInWeek: LocalDate, private val desiredStartDate: LocalDate, private val desiredEndDate: LocalDate, ) { - val week = Week((0 until 7).map { dayOffset -> getDay(dayOffset) }) + val week: Week = Week((0 until 7).map { dayOffset -> getDay(dayOffset) }) private fun getDay(dayOffset: Int): WeekDay { val date = firstDayInWeek.plusDays(dayOffset.toLong()) @@ -53,11 +53,11 @@ data class WeekData internal constructor( } } -fun getWeekIndex(startDateAdjusted: LocalDate, date: LocalDate): Int { +public fun getWeekIndex(startDateAdjusted: LocalDate, date: LocalDate): Int { return ChronoUnit.WEEKS.between(startDateAdjusted, date).toInt() } -fun getWeekIndicesCount(startDateAdjusted: LocalDate, endDateAdjusted: LocalDate): Int { +public fun getWeekIndicesCount(startDateAdjusted: LocalDate, endDateAdjusted: LocalDate): Int { // Add one to include the start week itself! return getWeekIndex(startDateAdjusted, endDateAdjusted) + 1 } diff --git a/view/src/main/java/com/kizitonwose/calendar/view/Binder.kt b/view/src/main/java/com/kizitonwose/calendar/view/Binder.kt index 68912bbf..f47504be 100644 --- a/view/src/main/java/com/kizitonwose/calendar/view/Binder.kt +++ b/view/src/main/java/com/kizitonwose/calendar/view/Binder.kt @@ -6,7 +6,7 @@ import com.kizitonwose.calendar.core.CalendarMonth import com.kizitonwose.calendar.core.Week import com.kizitonwose.calendar.core.WeekDay -open class ViewContainer(val view: View) +public open class ViewContainer(public val view: View) /** * Responsible for managing a view for a given [Data]. @@ -14,19 +14,19 @@ open class ViewContainer(val view: View) * be called whenever the view needs to be used to * bind an instance of the associated [Data]. */ -interface Binder { - fun create(view: View): Container - fun bind(container: Container, data: Data) +public interface Binder { + public fun create(view: View): Container + public fun bind(container: Container, data: Data) } -interface WeekDayBinder : Binder +public interface WeekDayBinder : Binder -interface WeekHeaderFooterBinder : Binder +public interface WeekHeaderFooterBinder : Binder -interface MonthDayBinder : Binder +public interface MonthDayBinder : Binder -interface MonthHeaderFooterBinder : Binder +public interface MonthHeaderFooterBinder : Binder -typealias MonthScrollListener = (CalendarMonth) -> Unit +public typealias MonthScrollListener = (CalendarMonth) -> Unit -typealias WeekScrollListener = (Week) -> Unit +public typealias WeekScrollListener = (Week) -> Unit diff --git a/view/src/main/java/com/kizitonwose/calendar/view/CalendarView.kt b/view/src/main/java/com/kizitonwose/calendar/view/CalendarView.kt index 7e877edf..ec6f77de 100644 --- a/view/src/main/java/com/kizitonwose/calendar/view/CalendarView.kt +++ b/view/src/main/java/com/kizitonwose/calendar/view/CalendarView.kt @@ -19,12 +19,12 @@ import java.time.DayOfWeek import java.time.LocalDate import java.time.YearMonth -open class CalendarView : RecyclerView { +public open class CalendarView : RecyclerView { /** * The [MonthDayBinder] instance used for managing day * cell view creation and reuse on the calendar. */ - var dayBinder: MonthDayBinder<*>? = null + public var dayBinder: MonthDayBinder<*>? = null set(value) { field = value invalidateViewHolders() @@ -34,7 +34,7 @@ open class CalendarView : RecyclerView { * The [MonthHeaderFooterBinder] instance used for managing header views. * The header view is shown above each month on the Calendar. */ - var monthHeaderBinder: MonthHeaderFooterBinder<*>? = null + public var monthHeaderBinder: MonthHeaderFooterBinder<*>? = null set(value) { field = value invalidateViewHolders() @@ -44,7 +44,7 @@ open class CalendarView : RecyclerView { * The [MonthHeaderFooterBinder] instance used for managing footer views. * The footer view is shown below each month on the Calendar. */ - var monthFooterBinder: MonthHeaderFooterBinder<*>? = null + public var monthFooterBinder: MonthHeaderFooterBinder<*>? = null set(value) { field = value invalidateViewHolders() @@ -54,13 +54,13 @@ open class CalendarView : RecyclerView { * Called when the calendar scrolls to a new month. * Mostly beneficial if [scrollPaged] is `true`. */ - var monthScrollListener: MonthScrollListener? = null + public var monthScrollListener: MonthScrollListener? = null /** * The xml resource that is inflated and used as the day cell view. * This must be provided. */ - var dayViewResource = 0 + public var dayViewResource: Int = 0 set(value) { if (field != value) { check(value != 0) { "Invalid 'dayViewResource' value." } @@ -73,7 +73,7 @@ open class CalendarView : RecyclerView { * The xml resource that is inflated and used as a header for every month. * Set zero to disable. */ - var monthHeaderResource = 0 + public var monthHeaderResource: Int = 0 set(value) { if (field != value) { field = value @@ -85,7 +85,7 @@ open class CalendarView : RecyclerView { * The xml resource that is inflated and used as a footer for every month. * Set zero to disable. */ - var monthFooterResource = 0 + public var monthFooterResource: Int = 0 set(value) { if (field != value) { field = value @@ -100,7 +100,7 @@ open class CalendarView : RecyclerView { * **You should exclude the name and constructor of this class from code * obfuscation if enabled**. */ - var monthViewClass: String? = null + public var monthViewClass: String? = null set(value) { if (field != value) { field = value @@ -113,7 +113,7 @@ open class CalendarView : RecyclerView { * This determines the scroll direction of the calendar. */ @Orientation - var orientation: Int = HORIZONTAL + public var orientation: Int = HORIZONTAL set(value) { if (field != value) { field = value @@ -127,7 +127,7 @@ open class CalendarView : RecyclerView { * snap to the nearest month after a scroll or swipe action. * If `false`, the calendar scrolls normally. */ - var scrollPaged = false + public var scrollPaged: Boolean = false set(value) { if (field != value) { field = value @@ -141,7 +141,7 @@ open class CalendarView : RecyclerView { * * @see [DayPosition] */ - var outDateStyle = OutDateStyle.EndOfRow + public var outDateStyle: OutDateStyle = OutDateStyle.EndOfRow set(value) { if (field != value) { field = value @@ -153,7 +153,7 @@ open class CalendarView : RecyclerView { * Determines how the size of each day on the calendar is calculated. * Can be [DaySize.Square], [DaySize.SeventhWidth] or [DaySize.FreeForm]. */ - var daySize: DaySize = DaySize.Square + public var daySize: DaySize = DaySize.Square set(value) { if (field != value) { field = value @@ -165,7 +165,7 @@ open class CalendarView : RecyclerView { * The margins, in pixels to be applied on each month view. * this can be used to add a space between two months. */ - var monthMargins = MarginValues() + public var monthMargins: MarginValues = MarginValues() set(value) { if (field != value) { field = value @@ -190,13 +190,13 @@ open class CalendarView : RecyclerView { private var endMonth: YearMonth? = null private var firstDayOfWeek: DayOfWeek? = null - constructor(context: Context) : super(context) + public constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + public constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { init(attrs, 0, 0) } - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : + public constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init(attrs, defStyleAttr, defStyleAttr) } @@ -282,7 +282,7 @@ open class CalendarView : RecyclerView { * shows the view for the month without any animations. * For a smooth scrolling effect, use [smoothScrollToMonth] */ - fun scrollToMonth(month: YearMonth) { + public fun scrollToMonth(month: YearMonth) { calendarLayoutManager.scrollToIndex(month) } @@ -290,7 +290,7 @@ open class CalendarView : RecyclerView { * Scroll to a specific month on the calendar using a smooth scrolling animation. * Just like [scrollToMonth], but with a smooth scrolling animation. */ - fun smoothScrollToMonth(month: YearMonth) { + public fun smoothScrollToMonth(month: YearMonth) { calendarLayoutManager.smoothScrollToIndex(month) } @@ -301,7 +301,7 @@ open class CalendarView : RecyclerView { * in horizontal mode. No animation is performed. * For a smooth scrolling effect, use [smoothScrollToDay]. */ - fun scrollToDay(day: CalendarDay) { + public fun scrollToDay(day: CalendarDay) { calendarLayoutManager.scrollToDay(day) } @@ -309,7 +309,7 @@ open class CalendarView : RecyclerView { * Shortcut for [scrollToDay] with a [LocalDate] instance. */ @JvmOverloads - fun scrollToDate(date: LocalDate, position: DayPosition = DayPosition.MonthDate) { + public fun scrollToDate(date: LocalDate, position: DayPosition = DayPosition.MonthDate) { scrollToDay(CalendarDay(date, position)) } @@ -317,7 +317,7 @@ open class CalendarView : RecyclerView { * Scroll to a specific [CalendarDay] using a smooth scrolling animation. * Just like [scrollToDay], but with a smooth scrolling animation. */ - fun smoothScrollToDay(day: CalendarDay) { + public fun smoothScrollToDay(day: CalendarDay) { calendarLayoutManager.smoothScrollToDay(day) } @@ -325,7 +325,7 @@ open class CalendarView : RecyclerView { * Shortcut for [smoothScrollToDay] with a [LocalDate] instance. */ @JvmOverloads - fun smoothScrollToDate(date: LocalDate, position: DayPosition = DayPosition.MonthDate) { + public fun smoothScrollToDate(date: LocalDate, position: DayPosition = DayPosition.MonthDate) { smoothScrollToDay(CalendarDay(date, position)) } @@ -334,7 +334,7 @@ open class CalendarView : RecyclerView { * This causes [MonthDayBinder.bind] to be called with the [ViewContainer] * at this position. Use this to reload a date cell on the Calendar. */ - fun notifyDayChanged(day: CalendarDay) { + public fun notifyDayChanged(day: CalendarDay) { calendarAdapter.reloadDay(day) } @@ -342,7 +342,7 @@ open class CalendarView : RecyclerView { * Shortcut for [notifyDayChanged] with a [LocalDate] instance. */ @JvmOverloads - fun notifyDateChanged(date: LocalDate, position: DayPosition = DayPosition.MonthDate) { + public fun notifyDateChanged(date: LocalDate, position: DayPosition = DayPosition.MonthDate) { notifyDayChanged(CalendarDay(date, position)) } @@ -357,7 +357,7 @@ open class CalendarView : RecyclerView { * specified day positions. This causes [MonthDayBinder.bind] to be called * with the [ViewContainer] at the relevant [DayPosition] values. */ - fun notifyDateChanged( + public fun notifyDateChanged( date: LocalDate, vararg position: DayPosition, ) { @@ -375,7 +375,7 @@ open class CalendarView : RecyclerView { * [MonthHeaderFooterBinder.bind] will be called for this month's header view if available. * [MonthHeaderFooterBinder.bind] will be called for this month's footer view if available. */ - fun notifyMonthChanged(month: YearMonth) { + public fun notifyMonthChanged(month: YearMonth) { calendarAdapter.reloadMonth(month) } @@ -383,7 +383,7 @@ open class CalendarView : RecyclerView { * Notify the CalendarView to reload all months. * @see [notifyMonthChanged]. */ - fun notifyCalendarChanged() { + public fun notifyCalendarChanged() { calendarAdapter.reloadCalendar() } @@ -392,7 +392,7 @@ open class CalendarView : RecyclerView { * * @return The first visible month or null if not found. */ - fun findFirstVisibleMonth(): CalendarMonth? { + public fun findFirstVisibleMonth(): CalendarMonth? { return calendarAdapter.findFirstVisibleMonth() } @@ -401,7 +401,7 @@ open class CalendarView : RecyclerView { * * @return The last visible month or null if not found. */ - fun findLastVisibleMonth(): CalendarMonth? { + public fun findLastVisibleMonth(): CalendarMonth? { return calendarAdapter.findLastVisibleMonth() } @@ -411,7 +411,7 @@ open class CalendarView : RecyclerView { * * @return The first visible day or null if not found. */ - fun findFirstVisibleDay(): CalendarDay? { + public fun findFirstVisibleDay(): CalendarDay? { return calendarAdapter.findFirstVisibleDay() } @@ -421,7 +421,7 @@ open class CalendarView : RecyclerView { * * @return The last visible day or null if not found. */ - fun findLastVisibleDay(): CalendarDay? { + public fun findLastVisibleDay(): CalendarDay? { return calendarAdapter.findLastVisibleDay() } @@ -433,7 +433,7 @@ open class CalendarView : RecyclerView { * @param endMonth The last month on the calendar. * @param firstDayOfWeek A [DayOfWeek] to be the first day of week. */ - fun setup(startMonth: YearMonth, endMonth: YearMonth, firstDayOfWeek: DayOfWeek) { + public fun setup(startMonth: YearMonth, endMonth: YearMonth, firstDayOfWeek: DayOfWeek) { checkDateRange(startMonth = startMonth, endMonth = endMonth) this.startMonth = startMonth this.endMonth = endMonth @@ -459,7 +459,7 @@ open class CalendarView : RecyclerView { * the calendar with a large date range instead of updating the range frequently. */ @JvmOverloads - fun updateMonthData( + public fun updateMonthData( startMonth: YearMonth = requireStartMonth(), endMonth: YearMonth = requireEndMonth(), firstDayOfWeek: DayOfWeek = requireFirstDayOfWeek(), diff --git a/view/src/main/java/com/kizitonwose/calendar/view/DaySize.kt b/view/src/main/java/com/kizitonwose/calendar/view/DaySize.kt index a9e9acab..11746da8 100644 --- a/view/src/main/java/com/kizitonwose/calendar/view/DaySize.kt +++ b/view/src/main/java/com/kizitonwose/calendar/view/DaySize.kt @@ -5,7 +5,7 @@ import android.view.ViewGroup /** * Determines how the size of each day on the calendar is calculated. */ -enum class DaySize { +public enum class DaySize { /** * Each day will have both width and height matching * the width of the calendar divided by 7. diff --git a/view/src/main/java/com/kizitonwose/calendar/view/MarginValues.kt b/view/src/main/java/com/kizitonwose/calendar/view/MarginValues.kt index 5c5f8dfc..fc1d7d5d 100644 --- a/view/src/main/java/com/kizitonwose/calendar/view/MarginValues.kt +++ b/view/src/main/java/com/kizitonwose/calendar/view/MarginValues.kt @@ -2,7 +2,7 @@ package com.kizitonwose.calendar.view import androidx.annotation.Px -data class MarginValues( +public data class MarginValues( @Px val start: Int = 0, @Px val top: Int = 0, @Px val end: Int = 0, diff --git a/view/src/main/java/com/kizitonwose/calendar/view/WeekCalendarView.kt b/view/src/main/java/com/kizitonwose/calendar/view/WeekCalendarView.kt index dfc6a935..e4fc63f4 100644 --- a/view/src/main/java/com/kizitonwose/calendar/view/WeekCalendarView.kt +++ b/view/src/main/java/com/kizitonwose/calendar/view/WeekCalendarView.kt @@ -14,12 +14,12 @@ import com.kizitonwose.calendar.view.internal.weekcalendar.WeekCalendarLayoutMan import java.time.DayOfWeek import java.time.LocalDate -open class WeekCalendarView : RecyclerView { +public open class WeekCalendarView : RecyclerView { /** * The [WeekDayBinder] instance used for managing day * cell view creation and reuse. */ - var dayBinder: WeekDayBinder<*>? = null + public var dayBinder: WeekDayBinder<*>? = null set(value) { field = value invalidateViewHolders() @@ -29,7 +29,7 @@ open class WeekCalendarView : RecyclerView { * The [WeekHeaderFooterBinder] instance used for managing header views. * The header view is shown above each week on the Calendar. */ - var weekHeaderBinder: WeekHeaderFooterBinder<*>? = null + public var weekHeaderBinder: WeekHeaderFooterBinder<*>? = null set(value) { field = value invalidateViewHolders() @@ -39,7 +39,7 @@ open class WeekCalendarView : RecyclerView { * The [WeekHeaderFooterBinder] instance used for managing footer views. * The footer view is shown below each week on the Calendar. */ - var weekFooterBinder: WeekHeaderFooterBinder<*>? = null + public var weekFooterBinder: WeekHeaderFooterBinder<*>? = null set(value) { field = value invalidateViewHolders() @@ -49,13 +49,13 @@ open class WeekCalendarView : RecyclerView { * Called when the calendar scrolls to a new week. * Mostly beneficial if [scrollPaged] is `true`. */ - var weekScrollListener: WeekScrollListener? = null + public var weekScrollListener: WeekScrollListener? = null /** * The xml resource that is inflated and used as the day cell view. * This must be provided. */ - var dayViewResource = 0 + public var dayViewResource: Int = 0 set(value) { if (field != value) { check(value != 0) { "Invalid 'dayViewResource' value." } @@ -68,7 +68,7 @@ open class WeekCalendarView : RecyclerView { * The xml resource that is inflated and used as a header for every week. * Set zero to disable. */ - var weekHeaderResource = 0 + public var weekHeaderResource: Int = 0 set(value) { if (field != value) { field = value @@ -80,7 +80,7 @@ open class WeekCalendarView : RecyclerView { * The xml resource that is inflated and used as a footer for every week. * Set zero to disable. */ - var weekFooterResource = 0 + public var weekFooterResource: Int = 0 set(value) { if (field != value) { field = value @@ -95,7 +95,7 @@ open class WeekCalendarView : RecyclerView { * **You should exclude the name and constructor of this class from code * obfuscation if enabled**. */ - var weekViewClass: String? = null + public var weekViewClass: String? = null set(value) { if (field != value) { field = value @@ -108,7 +108,7 @@ open class WeekCalendarView : RecyclerView { * snap to the nearest week after a scroll or swipe action. * If `false`, the calendar scrolls normally. */ - var scrollPaged = true + public var scrollPaged: Boolean = true set(value) { if (field != value) { field = value @@ -120,7 +120,7 @@ open class WeekCalendarView : RecyclerView { * Determines how the size of each day on the calendar is calculated. * Can be [DaySize.Square], [DaySize.SeventhWidth] or [DaySize.FreeForm]. */ - var daySize: DaySize = DaySize.Square + public var daySize: DaySize = DaySize.Square set(value) { if (field != value) { field = value @@ -132,7 +132,7 @@ open class WeekCalendarView : RecyclerView { * The margins, in pixels to be applied each week view. * this can be used to add a space between two weeks. */ - var weekMargins = MarginValues() + public var weekMargins: MarginValues = MarginValues() set(value) { if (field != value) { field = value @@ -155,13 +155,13 @@ open class WeekCalendarView : RecyclerView { private var endDate: LocalDate? = null private var firstDayOfWeek: DayOfWeek? = null - constructor(context: Context) : super(context) + public constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + public constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { init(attrs, 0, 0) } - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : + public constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init(attrs, defStyleAttr, defStyleAttr) } @@ -224,7 +224,7 @@ open class WeekCalendarView : RecyclerView { * This instantly shows the view for the week without any animations. * For a smooth scrolling effect, use [smoothScrollToWeek] */ - fun scrollToWeek(date: LocalDate) { + public fun scrollToWeek(date: LocalDate) { calendarLayoutManager.scrollToIndex(date) } @@ -233,7 +233,7 @@ open class WeekCalendarView : RecyclerView { * using a smooth scrolling animation. * Just like [scrollToWeek], but with a smooth scrolling animation. */ - fun smoothScrollToWeek(date: LocalDate) { + public fun smoothScrollToWeek(date: LocalDate) { calendarLayoutManager.smoothScrollToIndex(date) } @@ -243,7 +243,7 @@ open class WeekCalendarView : RecyclerView { * No animation is performed. * For a smooth scrolling effect, use [smoothScrollToDate]. */ - fun scrollToDate(date: LocalDate) { + public fun scrollToDate(date: LocalDate) { calendarLayoutManager.scrollToDay(date) } @@ -251,7 +251,7 @@ open class WeekCalendarView : RecyclerView { * Scroll to a specific date using a smooth scrolling animation. * Just like [scrollToDate], but with a smooth scrolling animation. */ - fun smoothScrollToDate(date: LocalDate) { + public fun smoothScrollToDate(date: LocalDate) { calendarLayoutManager.smoothScrollToDay(date) } @@ -260,7 +260,7 @@ open class WeekCalendarView : RecyclerView { * This instantly shows the view for the week without any animations. * For a smooth scrolling effect, use [smoothScrollToWeek] */ - fun scrollToWeek(day: WeekDay) { + public fun scrollToWeek(day: WeekDay) { scrollToWeek(day.date) } @@ -269,7 +269,7 @@ open class WeekCalendarView : RecyclerView { * using a smooth scrolling animation. * Just like [scrollToWeek], but with a smooth scrolling animation. */ - fun smoothScrollToWeek(day: WeekDay) { + public fun smoothScrollToWeek(day: WeekDay) { smoothScrollToWeek(day.date) } @@ -279,7 +279,7 @@ open class WeekCalendarView : RecyclerView { * No animation is performed. * For a smooth scrolling effect, use [smoothScrollToDay]. */ - fun scrollToDay(day: WeekDay) { + public fun scrollToDay(day: WeekDay) { scrollToDate(day.date) } @@ -287,7 +287,7 @@ open class WeekCalendarView : RecyclerView { * Scroll to a specific [WeekDay] using a smooth scrolling animation. * Just like [scrollToDay], but with a smooth scrolling animation. */ - fun smoothScrollToDay(day: WeekDay) { + public fun smoothScrollToDay(day: WeekDay) { smoothScrollToDate(day.date) } @@ -296,7 +296,7 @@ open class WeekCalendarView : RecyclerView { * This causes [WeekDayBinder.bind] to be called with the [ViewContainer] * at this position. Use this to reload a date cell on the Calendar. */ - fun notifyDateChanged(date: LocalDate) { + public fun notifyDateChanged(date: LocalDate) { calendarAdapter.reloadDay(date) } @@ -305,7 +305,7 @@ open class WeekCalendarView : RecyclerView { * This causes [WeekDayBinder.bind] to be called with the [ViewContainer] * at this position. Use this to reload a date cell on the Calendar. */ - fun notifyDayChanged(day: WeekDay) { + public fun notifyDayChanged(day: WeekDay) { notifyDateChanged(day.date) } @@ -316,7 +316,7 @@ open class WeekCalendarView : RecyclerView { * [WeekHeaderFooterBinder.bind] will be called for this week's header view if available. * [WeekHeaderFooterBinder.bind] will be called for this week's footer view if available. */ - fun notifyWeekChanged(date: LocalDate) { + public fun notifyWeekChanged(date: LocalDate) { calendarAdapter.reloadWeek(date) } @@ -327,7 +327,7 @@ open class WeekCalendarView : RecyclerView { * [WeekHeaderFooterBinder.bind] will be called for this week's header view if available. * [WeekHeaderFooterBinder.bind] will be called for this week's footer view if available. */ - fun notifyWeekChanged(day: WeekDay) { + public fun notifyWeekChanged(day: WeekDay) { notifyWeekChanged(day.date) } @@ -336,7 +336,7 @@ open class WeekCalendarView : RecyclerView { * * @see [notifyWeekChanged] */ - fun notifyCalendarChanged() { + public fun notifyCalendarChanged() { calendarAdapter.reloadCalendar() } @@ -345,7 +345,7 @@ open class WeekCalendarView : RecyclerView { * * @return The first visible week or null if not found. */ - fun findFirstVisibleWeek(): Week? { + public fun findFirstVisibleWeek(): Week? { return calendarAdapter.findFirstVisibleWeek() } @@ -354,7 +354,7 @@ open class WeekCalendarView : RecyclerView { * * @return The last visible week or null if not found. */ - fun findLastVisibleWeek(): Week? { + public fun findLastVisibleWeek(): Week? { return calendarAdapter.findLastVisibleWeek() } @@ -364,7 +364,7 @@ open class WeekCalendarView : RecyclerView { * * @return The first visible day or null if not found. */ - fun findFirstVisibleDay(): WeekDay? { + public fun findFirstVisibleDay(): WeekDay? { return calendarAdapter.findFirstVisibleDay() } @@ -374,7 +374,7 @@ open class WeekCalendarView : RecyclerView { * * @return The last visible day or null if not found. */ - fun findLastVisibleDay(): WeekDay? { + public fun findLastVisibleDay(): WeekDay? { return calendarAdapter.findLastVisibleDay() } @@ -386,7 +386,7 @@ open class WeekCalendarView : RecyclerView { * @param endDate A date in the last week on the calendar. * @param firstDayOfWeek A [DayOfWeek] to be the first day of week. */ - fun setup(startDate: LocalDate, endDate: LocalDate, firstDayOfWeek: DayOfWeek) { + public fun setup(startDate: LocalDate, endDate: LocalDate, firstDayOfWeek: DayOfWeek) { checkDateRange(startDate = startDate, endDate = endDate) this.startDate = startDate this.endDate = endDate @@ -411,7 +411,7 @@ open class WeekCalendarView : RecyclerView { * the calendar with a large date range instead of updating the range frequently. */ @JvmOverloads - fun updateWeekData( + public fun updateWeekData( startDate: LocalDate = requireStartDate(), endDate: LocalDate = requireEndDate(), firstDayOfWeek: DayOfWeek = requireFirstDayOfWeek(), From d618fc50bb33428da47eac748851150a3f0c4528 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Wed, 17 Jul 2024 21:12:45 +0200 Subject: [PATCH 19/21] Add documentation for public helper functions --- .../library/api/android/library.api | 24 +++-- .../library/api/desktop/library.api | 24 +++-- .../compose/weekcalendar/WeekCalendarState.kt | 2 +- .../kizitonwose/calendar/core/Extensions.kt | 52 ++++------- .../kizitonwose/calendar/core/YearMonth.kt | 92 ++++++++++++++++++- .../kizitonwose/calendar/data/MonthData.kt | 7 +- .../kotlin/ContinuousSelectionHelper.kt | 6 +- .../src/commonMain/kotlin/Example1Page.kt | 9 +- .../src/commonMain/kotlin/Example2Page.kt | 1 - .../src/commonMain/kotlin/Example3Page.kt | 10 +- .../src/commonMain/kotlin/Example4Page.kt | 1 - .../src/commonMain/kotlin/Example9Page.kt | 8 +- .../kotlin/Example9PageAnimatedVisibility.kt | 2 - .../sample/src/commonMain/kotlin/Flight.kt | 2 - .../sample/src/commonMain/kotlin/Utils.kt | 10 +- compose/api/compose.api | 1 - .../compose/weekcalendar/WeekCalendarState.kt | 2 +- 17 files changed, 157 insertions(+), 96 deletions(-) diff --git a/compose-multiplatform/library/api/android/library.api b/compose-multiplatform/library/api/android/library.api index 5eca3398..a64c47e0 100644 --- a/compose-multiplatform/library/api/android/library.api +++ b/compose-multiplatform/library/api/android/library.api @@ -173,7 +173,6 @@ public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarSta } public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion { - public final fun getSaver ()Landroidx/compose/runtime/saveable/Saver; } public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarStateKt { @@ -222,18 +221,11 @@ public final class com/kizitonwose/calendar/core/DayPosition : java/lang/Enum { } public final class com/kizitonwose/calendar/core/ExtensionsKt { - public static final fun atDay (Lcom/kizitonwose/calendar/core/YearMonth;I)Lkotlinx/datetime/LocalDate; - public static final fun atEndOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; - public static final fun atStartOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; public static final fun daysOfWeek (Ljava/time/DayOfWeek;)Ljava/util/List; public static synthetic fun daysOfWeek$default (Ljava/time/DayOfWeek;ILjava/lang/Object;)Ljava/util/List; - public static final fun getNextMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun getPreviousMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; public static final fun getYearMonth (Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun minusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun now (Lcom/kizitonwose/calendar/core/YearMonth$Companion;)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun now (Lkotlinx/datetime/LocalDate$Companion;)Lkotlinx/datetime/LocalDate; - public static final fun plusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun now (Lkotlinx/datetime/LocalDate$Companion;Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; + public static synthetic fun now$default (Lkotlinx/datetime/LocalDate$Companion;Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;ILjava/lang/Object;)Lkotlinx/datetime/LocalDate; } public final class com/kizitonwose/calendar/core/Extensions_jvmKt { @@ -302,6 +294,18 @@ public final class com/kizitonwose/calendar/core/YearMonth : java/io/Serializabl } public final class com/kizitonwose/calendar/core/YearMonth$Companion { + public final fun now (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lcom/kizitonwose/calendar/core/YearMonth; + public static synthetic fun now$default (Lcom/kizitonwose/calendar/core/YearMonth$Companion;Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/YearMonth; +} + +public final class com/kizitonwose/calendar/core/YearMonthKt { + public static final fun atDay (Lcom/kizitonwose/calendar/core/YearMonth;I)Lkotlinx/datetime/LocalDate; + public static final fun atEndOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun atStartOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun lengthOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)I + public static final fun minus (Lcom/kizitonwose/calendar/core/YearMonth;ILkotlinx/datetime/DateTimeUnit$MonthBased;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun monthsUntil (Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;)I + public static final fun plus (Lcom/kizitonwose/calendar/core/YearMonth;ILkotlinx/datetime/DateTimeUnit$MonthBased;)Lcom/kizitonwose/calendar/core/YearMonth; } public final class com/kizitonwose/calendar/data/WeekData { diff --git a/compose-multiplatform/library/api/desktop/library.api b/compose-multiplatform/library/api/desktop/library.api index b4a70081..87fdf312 100644 --- a/compose-multiplatform/library/api/desktop/library.api +++ b/compose-multiplatform/library/api/desktop/library.api @@ -173,7 +173,6 @@ public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarSta } public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion { - public final fun getSaver ()Landroidx/compose/runtime/saveable/Saver; } public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarStateKt { @@ -222,18 +221,11 @@ public final class com/kizitonwose/calendar/core/DayPosition : java/lang/Enum { } public final class com/kizitonwose/calendar/core/ExtensionsKt { - public static final fun atDay (Lcom/kizitonwose/calendar/core/YearMonth;I)Lkotlinx/datetime/LocalDate; - public static final fun atEndOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; - public static final fun atStartOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; public static final fun daysOfWeek (Ljava/time/DayOfWeek;)Ljava/util/List; public static synthetic fun daysOfWeek$default (Ljava/time/DayOfWeek;ILjava/lang/Object;)Ljava/util/List; - public static final fun getNextMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun getPreviousMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lcom/kizitonwose/calendar/core/YearMonth; public static final fun getYearMonth (Lkotlinx/datetime/LocalDate;)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun minusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun now (Lcom/kizitonwose/calendar/core/YearMonth$Companion;)Lcom/kizitonwose/calendar/core/YearMonth; - public static final fun now (Lkotlinx/datetime/LocalDate$Companion;)Lkotlinx/datetime/LocalDate; - public static final fun plusMonths (Lcom/kizitonwose/calendar/core/YearMonth;I)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun now (Lkotlinx/datetime/LocalDate$Companion;Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lkotlinx/datetime/LocalDate; + public static synthetic fun now$default (Lkotlinx/datetime/LocalDate$Companion;Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;ILjava/lang/Object;)Lkotlinx/datetime/LocalDate; } public final class com/kizitonwose/calendar/core/Extensions_jvmKt { @@ -302,6 +294,18 @@ public final class com/kizitonwose/calendar/core/YearMonth : java/io/Serializabl } public final class com/kizitonwose/calendar/core/YearMonth$Companion { + public final fun now (Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;)Lcom/kizitonwose/calendar/core/YearMonth; + public static synthetic fun now$default (Lcom/kizitonwose/calendar/core/YearMonth$Companion;Lkotlinx/datetime/Clock;Lkotlinx/datetime/TimeZone;ILjava/lang/Object;)Lcom/kizitonwose/calendar/core/YearMonth; +} + +public final class com/kizitonwose/calendar/core/YearMonthKt { + public static final fun atDay (Lcom/kizitonwose/calendar/core/YearMonth;I)Lkotlinx/datetime/LocalDate; + public static final fun atEndOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun atStartOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)Lkotlinx/datetime/LocalDate; + public static final fun lengthOfMonth (Lcom/kizitonwose/calendar/core/YearMonth;)I + public static final fun minus (Lcom/kizitonwose/calendar/core/YearMonth;ILkotlinx/datetime/DateTimeUnit$MonthBased;)Lcom/kizitonwose/calendar/core/YearMonth; + public static final fun monthsUntil (Lcom/kizitonwose/calendar/core/YearMonth;Lcom/kizitonwose/calendar/core/YearMonth;)I + public static final fun plus (Lcom/kizitonwose/calendar/core/YearMonth;ILkotlinx/datetime/DateTimeUnit$MonthBased;)Lcom/kizitonwose/calendar/core/YearMonth; } public final class com/kizitonwose/calendar/data/WeekData { diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt index 4c2b7c09..22016034 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -265,7 +265,7 @@ public class WeekCalendarState internal constructor( } public companion object { - public val Saver: Saver = listSaver( + internal val Saver: Saver = listSaver( save = { listOf( it.startDate.toJvmSerializableLocalDate(), diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt index 7c40c5d5..71fcd83f 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/Extensions.kt @@ -6,12 +6,10 @@ import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.DayOfWeek import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone -import kotlinx.datetime.daysUntil import kotlinx.datetime.isoDayNumber import kotlinx.datetime.minus -import kotlinx.datetime.monthsUntil import kotlinx.datetime.plus -import kotlinx.datetime.toLocalDateTime +import kotlinx.datetime.todayIn import kotlinx.datetime.until /** @@ -32,33 +30,26 @@ public fun daysOfWeek(firstDayOfWeek: DayOfWeek = firstDayOfWeekFromLocale()): L */ public expect fun firstDayOfWeekFromLocale(locale: Locale = Locale.current): DayOfWeek -public fun YearMonth.atStartOfMonth(): LocalDate = atDay(1) - -public fun YearMonth.atEndOfMonth(): LocalDate = atDay(lengthOfMonth()) - -public fun YearMonth.atDay(day: Int): LocalDate = LocalDate(year, month, day) - -public val YearMonth.nextMonth: YearMonth - get() = this.plusMonths(1) - -public val YearMonth.previousMonth: YearMonth - get() = this.minusMonths(1) - -public fun LocalDate.Companion.now(): LocalDate = Clock.System.now() - .toLocalDateTime(TimeZone.currentSystemDefault()) - .date - -public fun YearMonth.Companion.now(): YearMonth = LocalDate.now().yearMonth - -public fun YearMonth.plusMonths(value: Int): YearMonth = - atStartOfMonth().plusMonths(value).yearMonth - -public fun YearMonth.minusMonths(value: Int): YearMonth = - atStartOfMonth().minusMonths(value).yearMonth +/** + * Obtains the current [LocalDate] from the specified [clock] and [timeZone]. + * + * Using this method allows the use of an alternate clock or timezone for testing. + */ +public fun LocalDate.Companion.now( + clock: Clock = Clock.System, + timeZone: TimeZone = TimeZone.currentSystemDefault(), +): LocalDate = clock.todayIn(timeZone) +/** + * Returns the [YearMonth] value for this date. + */ public val LocalDate.yearMonth: YearMonth get() = YearMonth(year, month) +internal fun YearMonth.plusMonths(value: Int): YearMonth = plus(value, DateTimeUnit.MONTH) + +internal fun YearMonth.minusMonths(value: Int): YearMonth = minus(value, DateTimeUnit.MONTH) + internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) @@ -71,15 +62,6 @@ internal fun LocalDate.plusMonths(value: Int): LocalDate = plus(value, DateTimeU internal fun LocalDate.minusMonths(value: Int): LocalDate = minus(value, DateTimeUnit.MONTH) -internal fun YearMonth.lengthOfMonth(): Int { - val thisMonthStart = atStartOfMonth() - val nextMonthStart = thisMonthStart.plusMonths(1) - return thisMonthStart.daysUntil(nextMonthStart) -} - -internal fun YearMonth.monthsUntil(other: YearMonth): Int = - atStartOfMonth().monthsUntil(other.atStartOfMonth()) - internal fun LocalDate.weeksUntil(other: LocalDate): Int = until(other, DateTimeUnit.WEEK) diff --git a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt index 03e1fcea..61e2af50 100644 --- a/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt +++ b/compose-multiplatform/library/src/commonMain/kotlin/com/kizitonwose/calendar/core/YearMonth.kt @@ -1,8 +1,17 @@ package com.kizitonwose.calendar.core import androidx.compose.runtime.Immutable +import kotlinx.datetime.Clock +import kotlinx.datetime.DateTimeArithmeticException +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.LocalDate import kotlinx.datetime.Month +import kotlinx.datetime.TimeZone +import kotlinx.datetime.daysUntil +import kotlinx.datetime.minus +import kotlinx.datetime.monthsUntil import kotlinx.datetime.number +import kotlinx.datetime.plus @Immutable public data class YearMonth(val year: Int, val month: Month) : Comparable, JvmSerializable { @@ -13,7 +22,7 @@ public data class YearMonth(val year: Int, val month: Month) : Comparable week.map { dayOffset -> getDay(dayOffset) } }) diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt b/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt index d75d2cc2..e8fdaabb 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/ContinuousSelectionHelper.kt @@ -1,7 +1,5 @@ import com.kizitonwose.calendar.core.atEndOfMonth import com.kizitonwose.calendar.core.atStartOfMonth -import com.kizitonwose.calendar.core.nextMonth -import com.kizitonwose.calendar.core.previousMonth import com.kizitonwose.calendar.core.yearMonth import kotlinx.datetime.LocalDate import kotlinx.datetime.daysUntil @@ -48,7 +46,7 @@ object ContinuousSelectionHelper { ): Boolean { if (startDate.yearMonth == endDate.yearMonth) return false if (inDate.yearMonth == startDate.yearMonth) return true - val firstDateInThisMonth = inDate.yearMonth.nextMonth.atStartOfMonth() + val firstDateInThisMonth = inDate.yearMonth.next.atStartOfMonth() return firstDateInThisMonth in startDate..endDate && startDate != firstDateInThisMonth } @@ -59,7 +57,7 @@ object ContinuousSelectionHelper { ): Boolean { if (startDate.yearMonth == endDate.yearMonth) return false if (outDate.yearMonth == endDate.yearMonth) return true - val lastDateInThisMonth = outDate.yearMonth.previousMonth.atEndOfMonth() + val lastDateInThisMonth = outDate.yearMonth.previous.atEndOfMonth() return lastDateInThisMonth in startDate..endDate && endDate != lastDateInThisMonth } } diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt index f6e147ca..23a58cb0 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example1Page.kt @@ -27,11 +27,6 @@ import com.kizitonwose.calendar.core.CalendarDay import com.kizitonwose.calendar.core.DayPosition import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.daysOfWeek -import com.kizitonwose.calendar.core.minusMonths -import com.kizitonwose.calendar.core.nextMonth -import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths -import com.kizitonwose.calendar.core.previousMonth import kotlinx.coroutines.launch import kotlinx.datetime.DayOfWeek import org.jetbrains.compose.ui.tooling.preview.Preview @@ -61,12 +56,12 @@ fun Example1Page(adjacentMonths: Int = 500) { currentMonth = visibleMonth.yearMonth, goToPrevious = { coroutineScope.launch { - state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previous) } }, goToNext = { coroutineScope.launch { - state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.next) } }, ) diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt index a050dd56..c8356c50 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example2Page.kt @@ -46,7 +46,6 @@ import com.kizitonwose.calendar.core.DayPosition import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.daysOfWeek import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths import kotlinx.datetime.DayOfWeek import kotlinx.datetime.LocalDate import org.jetbrains.compose.ui.tooling.preview.Preview diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt index 1e59242d..492f5685 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example3Page.kt @@ -1,4 +1,3 @@ - import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -49,11 +48,6 @@ import com.kizitonwose.calendar.core.DayPosition import com.kizitonwose.calendar.core.OutDateStyle import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.daysOfWeek -import com.kizitonwose.calendar.core.minusMonths -import com.kizitonwose.calendar.core.nextMonth -import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths -import com.kizitonwose.calendar.core.previousMonth import kotlinx.coroutines.launch import kotlinx.datetime.DayOfWeek import org.jetbrains.compose.ui.tooling.preview.Preview @@ -107,12 +101,12 @@ fun Example3Page(close: () -> Unit = {}) { currentMonth = visibleMonth.yearMonth, goToPrevious = { coroutineScope.launch { - state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previous) } }, goToNext = { coroutineScope.launch { - state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.next) } }, ) diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt index 717f3d83..c57b5c97 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example4Page.kt @@ -34,7 +34,6 @@ import com.kizitonwose.calendar.core.DayPosition import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths import org.jetbrains.compose.ui.tooling.preview.Preview @Composable diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt index 054321dd..8a36a605 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example9Page.kt @@ -55,11 +55,7 @@ import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.atEndOfMonth import com.kizitonwose.calendar.core.atStartOfMonth import com.kizitonwose.calendar.core.daysOfWeek -import com.kizitonwose.calendar.core.minusMonths -import com.kizitonwose.calendar.core.nextMonth import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths -import com.kizitonwose.calendar.core.previousMonth import com.kizitonwose.calendar.core.yearMonth import kotlinx.coroutines.launch import kotlinx.datetime.DayOfWeek @@ -224,7 +220,7 @@ object Example9PageSharedComponents { val targetDate = weekState.firstVisibleWeek.days.first().date.minusDays(1) weekState.animateScrollToWeek(targetDate) } else { - val targetMonth = monthState.firstVisibleMonth.yearMonth.previousMonth + val targetMonth = monthState.firstVisibleMonth.yearMonth.previous monthState.animateScrollToMonth(targetMonth) } } @@ -235,7 +231,7 @@ object Example9PageSharedComponents { val targetDate = weekState.firstVisibleWeek.days.last().date.plusDays(1) weekState.animateScrollToWeek(targetDate) } else { - val targetMonth = monthState.firstVisibleMonth.yearMonth.nextMonth + val targetMonth = monthState.firstVisibleMonth.yearMonth.next monthState.animateScrollToMonth(targetMonth) } } diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt index 16a36949..078b5b74 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Example9PageAnimatedVisibility.kt @@ -28,9 +28,7 @@ import com.kizitonwose.calendar.core.WeekDayPosition import com.kizitonwose.calendar.core.atEndOfMonth import com.kizitonwose.calendar.core.atStartOfMonth import com.kizitonwose.calendar.core.daysOfWeek -import com.kizitonwose.calendar.core.minusMonths import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths import com.kizitonwose.calendar.core.yearMonth import kotlinx.coroutines.launch import kotlinx.datetime.LocalDate diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt index ec603ea5..8e68fafe 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Flight.kt @@ -1,9 +1,7 @@ import androidx.compose.ui.graphics.Color import com.kizitonwose.calendar.core.YearMonth import com.kizitonwose.calendar.core.atDay -import com.kizitonwose.calendar.core.minusMonths import com.kizitonwose.calendar.core.now -import com.kizitonwose.calendar.core.plusMonths import kotlinx.datetime.LocalDateTime import kotlinx.datetime.atTime import kotlinx.datetime.format.DayOfWeekNames diff --git a/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt b/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt index 42b37b43..45cc3c0b 100644 --- a/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt +++ b/compose-multiplatform/sample/src/commonMain/kotlin/Utils.kt @@ -27,6 +27,9 @@ import com.kizitonwose.calendar.compose.CalendarState import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarState import com.kizitonwose.calendar.core.CalendarMonth import com.kizitonwose.calendar.core.Week +import com.kizitonwose.calendar.core.YearMonth +import com.kizitonwose.calendar.core.minus +import com.kizitonwose.calendar.core.plus import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.datetime.DateTimeUnit @@ -179,5 +182,10 @@ private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50 } internal fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) - internal fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) +internal fun YearMonth.plusMonths(value: Int): YearMonth = plus(value, DateTimeUnit.MONTH) +internal fun YearMonth.minusMonths(value: Int): YearMonth = minus(value, DateTimeUnit.MONTH) +internal val YearMonth.next: YearMonth + get() = this.minusMonths(1) +internal val YearMonth.previous: YearMonth + get() = this.minusMonths(1) diff --git a/compose/api/compose.api b/compose/api/compose.api index 336cfe9a..81615668 100644 --- a/compose/api/compose.api +++ b/compose/api/compose.api @@ -173,7 +173,6 @@ public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarSta } public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState$Companion { - public final fun getSaver ()Landroidx/compose/runtime/saveable/Saver; } public final class com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarStateKt { diff --git a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt index 32bf2721..7cc76c4f 100644 --- a/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt +++ b/compose/src/main/java/com/kizitonwose/calendar/compose/weekcalendar/WeekCalendarState.kt @@ -261,7 +261,7 @@ public class WeekCalendarState internal constructor( } public companion object { - public val Saver: Saver = listSaver( + internal val Saver: Saver = listSaver( save = { listOf( it.startDate, From 951071b1e8c0ebb4f6b12ffda56a61a15cc04398 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Thu, 18 Jul 2024 11:35:06 +0200 Subject: [PATCH 20/21] Update docs --- README.md | 69 ++++++++++++++++++++++++++++++++----------------- docs/Compose.md | 34 ++++++++++++++++-------- 2 files changed, 69 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 33fb1cc1..4fb89cce 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # Calendar -A highly customizable calendar library for Android, backed by RecyclerView for the view system, and LazyRow/LazyColumn for compose. +A highly customizable calendar library for Android and Compose Multiplatform, backed by RecyclerView for the view system, and LazyRow/LazyColumn for compose. [![Tests](https://github.com/kizitonwose/Calendar/workflows/Check/badge.svg?branch=main)](https://github.com/kizitonwose/Calendar/actions) -[![Maven Central](https://img.shields.io/badge/dynamic/xml.svg?label=Maven%20Central&color=blue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[not(contains(text(),%27-%27))][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar) -[![Maven Central Beta](https://img.shields.io/badge/dynamic/xml.svg?label=Maven%20Central%20Beta&color=slateblue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[contains(text(),%27beta%27)][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar) +[![Android Library](https://img.shields.io/badge/dynamic/xml.svg?label=Android%20Library&color=blue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[not(contains(text(),%27-%27))][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar) +[![Android Library Beta](https://img.shields.io/badge/dynamic/xml.svg?label=Android%20Library%20Beta&color=slateblue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[contains(text(),%27beta%27)][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar) +[![Multiplatform Library Alpha](https://img.shields.io/badge/dynamic/xml.svg?label=Multiplatform%20Library%20Alpha&color=slateblue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/compose-multiplatform/maven-metadata.xml&query=(//metadata/versioning/versions/version)[contains(text(),%27alpha%27)][last()])](https://central.sonatype.com/search?q=g:com.kizitonwose.calendar) [![License](https://img.shields.io/badge/License-MIT-0097A7.svg)](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md) [![Twitter](https://img.shields.io/badge/Twitter-@kizitonwose-9C27B0.svg)](https://twitter.com/kizitonwose) @@ -39,17 +40,25 @@ A highly customizable calendar library for Android, backed by RecyclerView for t It's important to check out the sample app. There are lots of examples provided for both view and compose implementations. Most techniques that you would want to implement are already done in the examples. -Download the sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk) +Download the Android sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk) -View the sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample) +View the Android sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample) + +View the multiplatform sample project online at https://calendar.kizitonwose.dev + +View the multiplatform sample project's source code [here](https://github.com/kizitonwose/Calendar/tree/main/compose-multiplatform/sample) ## Setup +The library provides two compose artifacts: `com.kizitonwose.calendar:compose` which uses the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) APIs for pure Android projects and `com.kizitonwose.calendar:compose-multiplatform` which uses the [kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) library for Compose Multiplatform projects. + +The Android view library artifact `com.kizitonwose.calendar:view` also uses the `java.time` APIs and can exist alongside the Android compose artifact in an Android project if needed. + #### Step 1 -**This step is required ONLY if your app's `minSdkVersion` is below 26. Jump to [step 2](#step-2) if this does not apply to you.** +**This step is required ONLY if your Android app's `minSdkVersion` is below 26. Jump to [step 2](#step-2) if this does not apply to you.** -Apps with `minSdkVersion` below 26 have to enable [Java 8+ API desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) for backward compatibility since `java.time` classes were added in Java 8 which is supported natively starting from Android SDK 26. To set up your project for desugaring, you need to first ensure that you are using [Android Gradle plugin](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin) 4.0.0 or higher. +Android apps with `minSdkVersion` below 26 have to enable [Java 8+ API desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) for backward compatibility since `java.time` classes were added in Java 8 which is supported natively starting from Android SDK 26. To set up your project for desugaring, you need to first ensure that you are using [Android Gradle plugin](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin) 4.0.0 or higher. Then include the following in your app's `build.gradle` file: @@ -81,17 +90,27 @@ dependencies { You can find the latest version of `desugar_jdk_libs` [here](https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs). -#### Step 2 +#### Step 2A - For pure Android projects without multiplatform setup -Add the desired calendar library (view or compose) to your app `build.gradle`: +Add the desired calendar library (view or compose) to your app's `build.gradle.kts`: -```groovy +```kotlin dependencies { - // The view calendar library - implementation 'com.kizitonwose.calendar:view:' + // The view calendar library for Android + implementation("com.kizitonwose.calendar:view:") + + // The compose calendar library for Android + implementation("com.kizitonwose.calendar:compose:") +} +``` + +#### Step 2B - For Compose Multiplatform projects - // The compose calendar library - implementation 'com.kizitonwose.calendar:compose:' +Add the multiplatform calendar library to your project's `build.gradle.kts`: + +```kotlin +commonMain.dependencies { + implementation("com.kizitonwose.calendar:compose-multiplatform:") } ``` @@ -99,18 +118,18 @@ You can find the latest version of the library on the maven central badge above. Snapshots of the development version are available in [Sonatype’s snapshots repository](https://s01.oss.sonatype.org/content/repositories/snapshots/com/kizitonwose/calendar/). -If you're upgrading from version 1.x.x to 2.x.x, see the [migration guide](https://github.com/kizitonwose/calendar/blob/main/docs/MigrationGuide.md). +#### Compose UI version compatibility For the compose calendar library, ensure that you are using the library version that matches the Compose UI version in your project. If you use a version of the library that has a higher version of Compose UI than the one in your project, gradle will upgrade the Compose UI version in your project via transitive dependency. -| Compose UI | Calendar Library | -|:----------:|:----------------:| -| 1.2.x | 2.0.x | -| 1.3.x | 2.1.x - 2.2.x | -| 1.4.x | 2.3.x | -| 1.5.x | 2.4.x | -| 1.6.x | 2.5.x | -| 1.7.x | 2.6.x | +| Compose UI | Android Calendar Library | Multiplatform Calendar Library | +|:----------:|:------------------------:|:------------------------------:| +| 1.2.x | 2.0.x | - | +| 1.3.x | 2.1.x - 2.2.x | - | +| 1.4.x | 2.3.x | - | +| 1.5.x | 2.4.x | - | +| 1.6.x | 2.5.x | - | +| 1.7.x | 2.6.x | 2.6.x | ## Usage @@ -119,6 +138,10 @@ You can find the relevant documentation for the library in the links below. |[View-based documentation](https://github.com/kizitonwose/Calendar/blob/main/docs/View.md)|[Compose documentation](https://github.com/kizitonwose/Calendar/blob/main/docs/Compose.md)| |:-:|:-:| +## Migration + +If you're upgrading from calendar library version 1.x.x to 2.x.x, see the [migration guide](https://github.com/kizitonwose/calendar/blob/main/docs/MigrationGuide.md). + ## Share your creations Made a cool calendar with this library? Share an image [here](https://github.com/kizitonwose/Calendar/issues/1). diff --git a/docs/Compose.md b/docs/Compose.md index 8310306a..05b1fb68 100644 --- a/docs/Compose.md +++ b/docs/Compose.md @@ -3,6 +3,7 @@ ## Table of Contents - [Quick links](#quick-links) +- [Compose Multiplatform Information](#compose-multipletform-information) - [Compose versions](#compose-versions) - [Calendar Composables](#calendar-composables) - [Usage](#usage) @@ -23,25 +24,36 @@ Check out the sample app if you have not yet done so. Most techniques that you would want to implement are already done in the examples. -Download the sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk) +Download the Android sample app [here](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk) -Read the sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample) +Read the Android sample app's source code [here](https://github.com/kizitonwose/Calendar/tree/main/sample) + +View the multiplatform sample project online at https://calendar.kizitonwose.dev + +Read the multiplatform sample project's source code [here](https://github.com/kizitonwose/Calendar/tree/main/compose-multiplatform/sample) Add the library to your project [here](https://github.com/kizitonwose/Calendar#setup) **If you are looking for the view-based documentation, you can find it [here](https://github.com/kizitonwose/Calendar/blob/main/docs/View.md)** -## Compose versions +## Compose Multiplatform Information + +The APIs for the compose libraries for Android and Multiplatform projects have been designed such that you can copy examples across both projects and they would work without code changes as the classes have the same names and package declarations. The only difference in some cases would be that the code for the Android calendar library needs to import classes such as `LocalDate` and `YearMonth` from the `java.time` package while the multiplaform calendar library needs to import such classes from the [kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) library. + +Note that the `YearMonth` class does not yet exist in the `kotlinx-datetime` library, therefore the multiplatfrom calendar library includes a minimal `YearMonth` class implementation to bridge this gap until the class [is hopefully added](https://github.com/Kotlin/kotlinx-datetime/issues/168) to the `kotlinx-datetime` library. + +The functions `plusMonths`, `minusMonths`, `plusDays` and `minusDays` used in the examples are provided out of the box by the `java.time` library, but can be easily added for multiplatform projects using the `kotlinx-datetime` library if needed: + +```kotlin +fun YearMonth.plusMonths(value: Int): YearMonth = plus(value, DateTimeUnit.MONTH) +fun YearMonth.minusMonths(value: Int): YearMonth = minus(value, DateTimeUnit.MONTH) +fun LocalDate.plusDays(value: Int): LocalDate = plus(value, DateTimeUnit.DAY) +fun LocalDate.minusDays(value: Int): LocalDate = minus(value, DateTimeUnit.DAY) +``` -Ensure that you are using the library version that matches with the Compose UI version in your project. If you use a version of the library that has a higher version of Compose UI than the one in your project, gradle will upgrade the Compose UI version in your project via transitive dependency. +## Compose UI versions -| Compose UI | Calendar Library | -|:----------:|:----------------:| -| 1.2.x | 2.0.x | -| 1.3.x | 2.1.x - 2.2.x | -| 1.4.x | 2.3.x | -| 1.5.x | 2.4.x | -| 1.6.x | 2.5.x | +Ensure that you are using the library version that matches the Compose UI version in your project. If you use a version of the library that has a higher version of Compose UI than the one in your project, gradle will upgrade the Compose UI version in your project via transitive dependency. See the compatibility table [here](https://github.com/kizitonwose/Calendar#compose-ui-version-compatibility). ## Calendar Composables From 234f23fcbe1b74187cacd022b5722c91a8a24206 Mon Sep 17 00:00:00 2001 From: Kizito Nwose Date: Thu, 18 Jul 2024 17:43:45 +0200 Subject: [PATCH 21/21] Prepare next development version. --- .github/workflows/publish-release.yml | 1 + .github/workflows/publish-snapshot.yml | 1 + .../src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 33ed4754..f7ee8973 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -6,6 +6,7 @@ on: - '*.*.*' jobs: + # Use macOS so iOS artifacts are published publish-release: name: Publish Release runs-on: macos-latest diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index ba9cd6d3..9fbcc4ad 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -7,6 +7,7 @@ on: - main jobs: + # Use macOS so iOS artifacts are published publish-snapshot: name: Publish Snapshot runs-on: macos-latest diff --git a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt index 438a929f..69ba66a0 100644 --- a/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt +++ b/buildSrc/src/main/java/com/kizitonwose/calendar/buildsrc/Build.kt @@ -12,8 +12,8 @@ object Config { } object Versions { - val core = "2.6.0-alpha02" - val multiplatfrom = "2.6.0-alpha02" + val core = "2.6.0-SNAPSHOT" + val multiplatfrom = "2.6.0-SNAPSHOT" } object Android {