From 1b70c01f3895830aaf540fbb785ec537d947d7a3 Mon Sep 17 00:00:00 2001 From: Ji Sungbin Date: Fri, 14 Jul 2023 10:42:35 +0900 Subject: [PATCH] Restructuring a sugar module (#804) * Restructuring a sugar project * Migrating from a single-module structure to a multi-module structure * Improvements to sugared-kdoc * Change sugar component creation conditions (introduced @Sugarable) * Regenerated sugar components * Fix casa-processor build error * Make spotless happy * Cleanup * Fix tests * Rebuild * Fix tests * Fix tests --- casa-processor/build.gradle.kts | 4 +- .../casa/processor/CasaProcessorProvider.kt | 16 +- .../duckie/quackquack/casa/processor/poet.kt | 269 ++-- .../duckie/quackquack/casa/processor/utils.kt | 8 +- .../processor/CasaProcessorGenerationTest.kt | 32 +- .../duckie/quackquack/casa/processor/stubs.kt | 11 +- casa-processor/version.txt | 1 - .../duckie/quackquack/catalog/CasaModels.kt | 154 --- .../duckie/quackquack/catalog/MainActivity.kt | 12 +- .../team/duckie/quackquack/catalog/toast.kt | 7 +- gradle/libs.versions.toml | 4 + settings.gradle.kts | 26 +- .../build.gradle.kts | 16 +- .../sugar/compiler/SugarCompilerExtension.kt | 36 +- .../sugar/compiler/SugarCompilerRegistrar.kt | 64 + .../sugar/compiler}/ir/SugarIrTransformer.kt | 69 +- sugar-compiler/version.txt | 1 + sugar-core/build.gradle.kts | 29 + sugar-core/codegen/build.gradle.kts | 22 + .../quackquack/sugar/codegen/codegen.kt | 152 +++ .../duckie/quackquack/sugar/codegen/utils.kt | 131 ++ .../error/build.gradle.kts | 10 +- .../quackquack/sugar/error/SugarErrors.kt | 67 + sugar-core/error/version.txt | 1 + sugar-core/names/build.gradle.kts | 20 + .../quackquack/sugar/names/SugarNames.kt | 62 + sugar-core/names/version.txt | 1 + sugar-core/node/build.gradle.kts | 22 + .../sugar/node/SugarComponentNode.kt | 78 ++ .../quackquack/sugar/node/SugarParameter.kt | 58 + sugar-core/node/version.txt | 1 + .../core/SugarCoreCommandLineProcessor.kt | 47 +- .../sugar/core/SugarCoreExtension.kt | 39 + .../sugar/core/SugarCoreRegistrar.kt | 25 +- sugar-core/visitor/build.gradle.kts | 22 + .../sugar/visitor/SugarCoreVisitor.kt | 171 +-- sugar-core/visitor/version.txt | 1 + .../quackquack/sugar/material/annotations.kt | 52 +- .../duckie/quackquack/sugar/material/optin.kt | 1 - .../duckie/quackquack/sugar/material/typer.kt | 11 +- .../sugar/processor/ir/SugarIrData.kt | 221 ---- .../quackquack/sugar/processor/ir/errors.kt | 79 -- .../quackquack/sugar/processor/ir/names.kt | 58 - .../sugar/processor/poet/PoetUtils.kt | 26 - .../quackquack/sugar/processor/poet/poet.kt | 150 --- sugar-processor/version.txt | 1 - sugar-test/build.gradle.kts | 23 + .../sugar/test/SugarCompilerErrorTest.kt | 150 +-- .../sugar/test/SugarCompilerTransformTest.kt | 77 +- .../quackquack/sugar/test/SugarCoreTest.kt | 239 ++-- .../quackquack/sugar/test/TestCompilation.kt | 40 + .../duckie/quackquack/sugar/test}/stubs.kt | 4 +- ui-sugar/.nospotless | 0 ui-sugar/build.gradle.kts | 27 + ui-sugar/src/main/AndroidManifest.xml | 8 + .../team/duckie/quackquack/ui/sugar/button.kt | 300 +++++ .../team/duckie/quackquack/ui/sugar/tag.kt | 164 +++ .../team/duckie/quackquack/ui/sugar/text.kt | 178 +-- .../duckie/quackquack/ui/sugar/textfield.kt | 1127 +++++++++++++++++ ui-sugar/version.txt | 1 + ui/build.gradle.kts | 21 +- .../compose-metrics/ui_debug-module.json | 38 +- .../ui_debugUnitTest-module.json | 6 +- .../compose-metrics/ui_release-module.json | 12 +- .../ui_releaseUnitTest-module.json | 6 +- .../compose-reports/ui_debug-classes.txt | 87 ++ .../compose-reports/ui_debug-composables.csv | 17 + .../compose-reports/ui_debug-composables.txt | 254 ++++ .../ui_release-composables.csv | 22 - .../ui_release-composables.txt | 154 --- .../team/duckie/quackquack/ui/button.kt | 4 +- .../kotlin/team/duckie/quackquack/ui/icon.kt | 2 - .../kotlin/team/duckie/quackquack/ui/image.kt | 4 - .../team/duckie/quackquack/ui/sugar/button.kt | 760 ----------- .../team/duckie/quackquack/ui/sugar/tag.kt | 368 ------ .../team/duckie/quackquack/ui/switch.kt | 2 - .../kotlin/team/duckie/quackquack/ui/tab.kt | 2 - .../kotlin/team/duckie/quackquack/ui/tag.kt | 4 +- .../kotlin/team/duckie/quackquack/ui/text.kt | 2 + .../team/duckie/quackquack/ui/textfield.kt | 10 +- .../quackquack/ui/snapshot/TagSnapshot.kt | 21 +- .../duckie/quackquack/ui/uitest/TagTest.kt | 9 +- util-backend-kotlinc/build.gradle.kts | 1 - .../util/backend/kotlinc/IrUtils.kt | 7 + .../build.gradle.kts | 1 - .../util/backend/kotlinpoet/utils.kt | 33 +- util-backend-ksp/build.gradle.kts | 4 - util-backend-ksp/version.txt | 1 - .../quackquack/util/backend/test/stub/aide.kt | 21 - .../quackquack/util/backend/test/stub/casa.kt | 3 +- .../util/backend/test/stub/compose.kt | 14 +- .../util/backend/test/stub/sugar.kt | 20 +- .../quackquack/util/backend/test/utils.kt | 3 +- .../duckie/quackquack/util/backend/utils.kt | 30 - util-backend/version.txt | 1 - 95 files changed, 3619 insertions(+), 2951 deletions(-) delete mode 100644 casa-processor/version.txt delete mode 100644 catalog/src/main/kotlin/team/duckie/quackquack/catalog/CasaModels.kt rename {sugar-processor => sugar-compiler}/build.gradle.kts (67%) rename sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrExtension.kt => sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerExtension.kt (50%) create mode 100644 sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerRegistrar.kt rename {sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor => sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler}/ir/SugarIrTransformer.kt (64%) create mode 100644 sugar-compiler/version.txt create mode 100644 sugar-core/build.gradle.kts create mode 100644 sugar-core/codegen/build.gradle.kts create mode 100644 sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/codegen.kt create mode 100644 sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/utils.kt rename build-logic/src/main/kotlin/SugarPoetConfig.kt => sugar-core/error/build.gradle.kts (55%) create mode 100644 sugar-core/error/src/main/kotlin/team/duckie/quackquack/sugar/error/SugarErrors.kt create mode 100644 sugar-core/error/version.txt create mode 100644 sugar-core/names/build.gradle.kts create mode 100644 sugar-core/names/src/main/kotlin/team/duckie/quackquack/sugar/names/SugarNames.kt create mode 100644 sugar-core/names/version.txt create mode 100644 sugar-core/node/build.gradle.kts create mode 100644 sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarComponentNode.kt create mode 100644 sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarParameter.kt create mode 100644 sugar-core/node/version.txt rename sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarCommandLineProcessor.kt => sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreCommandLineProcessor.kt (52%) create mode 100644 sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreExtension.kt rename sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarComponentRegistrar.kt => sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreRegistrar.kt (79%) create mode 100644 sugar-core/visitor/build.gradle.kts rename sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrVisitor.kt => sugar-core/visitor/src/main/kotlin/team/duckie/quackquack/sugar/visitor/SugarCoreVisitor.kt (56%) create mode 100644 sugar-core/visitor/version.txt delete mode 100644 sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrData.kt delete mode 100644 sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/errors.kt delete mode 100644 sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/names.kt delete mode 100644 sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/PoetUtils.kt delete mode 100644 sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/poet.kt delete mode 100644 sugar-processor/version.txt create mode 100644 sugar-test/build.gradle.kts rename sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrErrorTest.kt => sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerErrorTest.kt (59%) rename sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrTransformTest.kt => sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerTransformTest.kt (54%) rename sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarPoetTest.kt => sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCoreTest.kt (65%) create mode 100644 sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/TestCompilation.kt rename {sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor => sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test}/stubs.kt (94%) create mode 100644 ui-sugar/.nospotless create mode 100644 ui-sugar/build.gradle.kts create mode 100644 ui-sugar/src/main/AndroidManifest.xml create mode 100644 ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt create mode 100644 ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt rename {ui => ui-sugar}/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt (79%) create mode 100644 ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/textfield.kt create mode 100644 ui-sugar/version.txt delete mode 100644 ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt delete mode 100644 ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt rename {util-backend => util-backend-kotlinpoet}/build.gradle.kts (91%) rename util-backend/src/main/kotlin/team/duckie/quackquack/util/backend/PoetUtils.kt => util-backend-kotlinpoet/src/main/kotlin/team/duckie/quackquack/util/backend/kotlinpoet/utils.kt (53%) delete mode 100644 util-backend-ksp/version.txt delete mode 100644 util-backend-test/src/main/kotlin/team/duckie/quackquack/util/backend/test/stub/aide.kt delete mode 100644 util-backend/src/main/kotlin/team/duckie/quackquack/util/backend/utils.kt delete mode 100644 util-backend/version.txt diff --git a/casa-processor/build.gradle.kts b/casa-processor/build.gradle.kts index a70073f7c..542bdb249 100644 --- a/casa-processor/build.gradle.kts +++ b/casa-processor/build.gradle.kts @@ -5,12 +5,9 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -@file:Suppress("INLINE_FROM_HIGHER_PLATFORM") - plugins { quackquack("jvm-kotlin") quackquack("test-kotest") - quackquack("quack-publishing") alias(libs.plugins.kotlin.ksp) } @@ -27,6 +24,7 @@ dependencies { libs.kotlin.collections.immutable, libs.google.autoservice.annotation, projects.utilBackendKsp, + projects.utilBackendKotlinpoet, ) testImplementations( libs.test.kotlin.compilation.ksp, diff --git a/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorProvider.kt b/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorProvider.kt index 11bbf7ba9..46a28ce2d 100644 --- a/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorProvider.kt +++ b/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorProvider.kt @@ -14,7 +14,6 @@ import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.processing.SymbolProcessorProvider -import com.google.devtools.ksp.symbol.KSAnnotated private const val CasaPathArg = "CasaPath" @@ -35,13 +34,12 @@ private class CasaSymbolProcessor( logger: KSPLogger, options: Map, ) : SymbolProcessor { - private val processor = CasaProcessor( - codeGenerator = codeGenerator, - logger = logger, - casaPath = options[CasaPathArg]?.toString(), - ) + private val processor = + CasaProcessor( + codeGenerator = codeGenerator, + logger = logger, + casaPath = options[CasaPathArg]?.toString(), + ) - override fun process(resolver: Resolver): List { - return processor.resolve(resolver) - } + override fun process(resolver: Resolver) = processor.resolve(resolver) } diff --git a/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/poet.kt b/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/poet.kt index 2c5b63705..469961546 100644 --- a/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/poet.kt +++ b/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/poet.kt @@ -18,18 +18,17 @@ import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.buildCodeBlock import com.squareup.kotlinpoet.withIndent import kotlinx.collections.immutable.ImmutableList -import team.duckie.quackquack.util.backend.FormatterOffComment -import team.duckie.quackquack.util.backend.SuppressAnnotation -import team.duckie.quackquack.util.backend.getGeneratedFileComment +import team.duckie.quackquack.util.backend.kotlinpoet.getGeneratedFileComment import team.duckie.quackquack.util.backend.ksp.generateBuildOrLocalFile import team.duckie.quackquack.util.backend.ksp.requireContainingFile private val GeneratedComment = getGeneratedFileComment("casa-processor") -private val RequiredImports = listOf( - "kotlinx.collections.immutable.persistentListOf", - "kotlinx.collections.immutable.toImmutableList", - "androidx.compose.runtime.Composable", -) +private val RequiredImports = + listOf( + "kotlinx.collections.immutable.persistentListOf", + "kotlinx.collections.immutable.toImmutableList", + "androidx.compose.runtime.Composable", + ) internal fun generateCasaModels( codeGenerator: CodeGenerator, @@ -37,72 +36,76 @@ internal fun generateCasaModels( casas: Sequence, casaPath: String?, ) { - val imports = mutableListOf().also { imports -> - imports += RequiredImports - } - val casasWithDomainGroup = casas.groupBy { declaration -> - declaration.requireContainingFile.fileName.removeSuffix(".kt") - } - val groupedCasasWithNameGroup = buildMap(capacity = casasWithDomainGroup.size) { - casasWithDomainGroup.forEach { (domain, casas) -> - val casasWithNameGroup = casas.groupBy { casa -> - casa.parseSugarRefer().substringAfterLast(".") + val imports = + mutableListOf().also { imports -> + imports += RequiredImports + } + val casasWithDomainGroup = + casas.groupBy { declaration -> + declaration.requireContainingFile.fileName.removeSuffix(".kt") + } + val groupedCasasWithNameGroup = + buildMap(capacity = casasWithDomainGroup.size) { + casasWithDomainGroup.forEach { (domain, casas) -> + val casasWithNameGroup = casas.groupBy { casa -> + casa.parseSugarRefer().substringAfterLast(".") + } + set(domain, casasWithNameGroup) } - set(domain, casasWithNameGroup) } - } - val casaModelPropertySpecs = buildList { - groupedCasasWithNameGroup.forEach { (domain, casasWithNameGroup) -> - casasWithNameGroup.forEach { (name, casas) -> - val (_imports, casaModelPropertySpec) = createCasaModelPropertySpecWithImports( - domain = domain, - name = name, - casas = casas, - ) - imports += _imports - add(casaModelPropertySpec) + val casaModelPropertySpecs = + buildList { + groupedCasasWithNameGroup.forEach { (domain, casasWithNameGroup) -> + casasWithNameGroup.forEach { (name, casas) -> + val (_imports, casaModelPropertySpec) = createCasaModelPropertySpecWithImports( + domain = domain, + name = name, + casas = casas, + ) + imports += _imports + add(casaModelPropertySpec) + } } } - } - val casaModelPropertySpecsAccessor = PropertySpec - .builder( - name = "casaModels", - type = ImmutableList::class.asClassName().parameterizedBy(CasaModelCn), - ) - .initializer( - codeBlock = buildCodeBlock { - addStatement("persistentListOf(") - withIndent { - casaModelPropertySpecs.forEach { casaModelPropertySpec -> - addStatement("${casaModelPropertySpec.name},") + val casaModelPropertySpecsAccessor = + PropertySpec + .builder( + name = "casaModels", + type = ImmutableList::class.asClassName().parameterizedBy(CasaModelCn), + ) + .initializer( + codeBlock = buildCodeBlock { + addStatement("persistentListOf(") + withIndent { + casaModelPropertySpecs.forEach { casaModelPropertySpec -> + addStatement("${casaModelPropertySpec.name},") + } } - } - addStatement(")") - }, - ) - .addModifiers(KModifier.PUBLIC) - .build() + addStatement(")") + }, + ) + .addModifiers(KModifier.PUBLIC) + .build() // TODO(3): OptIn 어노테이션 자동 추가 - val casaModelFileSpec = FileSpec - .builder( - packageName = "", - fileName = "CasaModels", - ) - .addFileComment(GeneratedComment) - .addFileComment(FormatterOffComment) - .addAnnotation(SuppressAnnotation) - .apply { - // memberImports += if (packageName.isNotEmpty()) { - // Import("$packageName.$import") - // } else { - // Import(import) - // } - addImport(packageName = "", imports) - casaModelPropertySpecs.forEach(::addProperty) - } - .addProperty(casaModelPropertySpecsAccessor) - .build() + val casaModelFileSpec = + FileSpec + .builder( + packageName = "", + fileName = "CasaModels", + ) + .addFileComment(GeneratedComment) + .apply { + // memberImports += if (packageName.isNotEmpty()) { + // Import("$packageName.$import") + // } else { + // Import(import) + // } + addImport(packageName = "", imports) + casaModelPropertySpecs.forEach(::addProperty) + } + .addProperty(casaModelPropertySpecsAccessor) + .build() generateBuildOrLocalFile( codeGenerator = codeGenerator, @@ -115,29 +118,32 @@ internal fun generateCasaModels( private fun KSFunctionDeclaration.toCasaComponentLiteralWithImport(): Pair { val import = "team.duckie.quackquack.ui.sugar.${simpleName.asString()}" - val parameterValueMap = parameters.mapNotNull { parameter -> - if (parameter.hasDefault) { - return@mapNotNull null - } - val name = parameter.name!!.asString() - val value = parameter.annotations.singleOrNullStrict { annotation -> - annotation.shortName.asString() == CasaValueSn - } - if (value != null) { - name to value.arguments.single().value as String - } else if (parameter.type.resolve().isMarkedNullable) { - name to "null" - } else { - error("Argument $name is non-null and no `CasaValue` was provided.") + val parameterValueMap = + parameters.mapNotNull { parameter -> + if (parameter.hasDefault) { + return@mapNotNull null + } + val name = parameter.name!!.asString() + val value = + parameter.annotations.singleOrNullStrict { annotation -> + annotation.shortName.asString() == CasaValueSn + } + if (value != null) { + name to value.arguments.single().value as String + } else if (parameter.type.resolve().isMarkedNullable) { + name to "null" + } else { + error("Argument $name is non-null and no `CasaValue` was provided.") + } } - } - val componentLiteral = buildString { - appendLine("${simpleName.asString()}(") - parameterValueMap.forEach { (name, value) -> - appendLineWithIndent("$name = $value,") + val componentLiteral = + buildString { + appendLine("${simpleName.asString()}(") + parameterValueMap.forEach { (name, value) -> + appendLineWithIndent("$name = $value,") + } + append(")") } - append(")") - } return import to componentLiteral } @@ -154,50 +160,63 @@ private fun createCasaModelPropertySpecWithImports( name: String, casas: List, ): Pair, PropertySpec> { - val imports = casas.first().parameters.map { parameter -> - parameter.type.resolve().declaration.qualifiedName!!.asString() - }.toMutableList() - val kdocString = casas.first().docString.orEmpty() - .split("This document was automatically generated") - .first() - .trimIndent() - val components = buildString { - appendLine("persistentListOf Unit>>(") - casas.forEach { casa -> - appendCasaComponentPairWithImport(casa = casa).let { (import, _) -> - imports += import + val imports = + casas + .first() + .parameters + .map { parameter -> + parameter.type.resolve().declaration.qualifiedName!!.asString() } - appendLine(",") + .toMutableList() + val kdocString = + casas + .first() + .docString + .orEmpty() + .split("This document was automatically generated") + .first() + .trimIndent() + val components = + buildString { + appendLine("persistentListOf Unit>>(") + casas.forEach { casa -> + appendCasaComponentPairWithImport(casa = casa).let { (import, _) -> + imports += import + } + appendLine(",") + } + append(").toImmutableList()") } - append(").toImmutableList()") - } - val casaModelPropertySpec = PropertySpec - .builder( - name = "${domain}${name}CasaModel", - type = CasaModelCn, - ) - .addModifiers(KModifier.PRIVATE) - .initializer( - codeBlock = buildCodeBlock { - addStatement("CasaModel(") - withIndent { - addStatement("name = %S,", name) - addStatement("domain = %S,", domain) - addStatement("kdocDefaultSection = %S,", kdocString) - addStatement("components = %L,", components) - } - addStatement(")") - }, - ) - .build() + val casaModelPropertySpec = + PropertySpec + .builder( + name = "${domain}${name}CasaModel", + type = CasaModelCn, + ) + .addModifiers(KModifier.PRIVATE) + .initializer( + codeBlock = buildCodeBlock { + addStatement("CasaModel(") + withIndent { + addStatement("name = %S,", name) + addStatement("domain = %S,", domain) + addStatement("kdocDefaultSection = %S,", kdocString) + addStatement("components = %L,", components) + } + addStatement(")") + }, + ) + .build() return imports to casaModelPropertySpec } private fun KSFunctionDeclaration.parseSugarRefer(): String { - val sugarRefer = annotations.singleOrNullStrict { annotation -> - annotation.shortName.asString() == SugarReferSn - } ?: error("casa-processor only supports sugar components.") + val sugarRefer = + annotations.singleOrNullStrict { annotation -> + annotation.shortName.asString() == SugarReferSn + } ?: error("casa-processor only supports sugar components.") + return sugarRefer.arguments.first().value as String } diff --git a/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/utils.kt b/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/utils.kt index 52a34d84f..5f2e45673 100644 --- a/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/utils.kt +++ b/casa-processor/src/main/kotlin/team/duckie/quackquack/casa/processor/utils.kt @@ -21,9 +21,5 @@ internal fun Sequence.singleOrNullStrict(predicate: (T) -> Boolean): T? { return single } -internal fun StringBuilder.appendLineWithIndent( - value: String?, - indentSize: Int = 2, -): StringBuilder { - return appendLine("${" ".repeat(indentSize)}$value") -} +internal fun StringBuilder.appendLineWithIndent(value: String?, indentSize: Int = 2) = + appendLine("${" ".repeat(indentSize)}$value") diff --git a/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorGenerationTest.kt b/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorGenerationTest.kt index 073352988..782999571 100644 --- a/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorGenerationTest.kt +++ b/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/CasaProcessorGenerationTest.kt @@ -5,15 +5,7 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -@file:Suppress( - "RedundantUnitReturnType", - "RedundantVisibilityModifier", - "RedundantUnitExpression", - "RedundantSuppression", - "LongMethod", - "HasPlatformType", - "UnusedReceiverParameter", -) +@file:Suppress("RedundantVisibilityModifier") package team.duckie.quackquack.casa.processor @@ -101,8 +93,8 @@ import team.duckie.quackquack.sugar.material.SugarRefer @Casa @SugarRefer("team.duckie.quackquack.ui.QuackButton") fun Button1( - text: String = "default value", - boolean: Boolean = true, + text: String = "default value", + boolean: Boolean = true, ) = Unit @Composable @@ -121,17 +113,9 @@ fun Button2( val expect = """ // This file was automatically generated by casa-processor. // Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") - import androidx.compose.runtime.Composable import kotlin.Boolean import kotlin.String -import kotlin.Suppress import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -236,17 +220,15 @@ fun Text(text: String) = Unit } } - private fun compile(useKspIncremental: Boolean, vararg sourceFiles: SourceFile): KotlinCompilation.Result { - return prepareCompilation(useKspIncremental, *sourceFiles).compile() - } + private fun compile(useKspIncremental: Boolean, vararg sourceFiles: SourceFile) = + prepareCompilation(useKspIncremental, *sourceFiles).compile() - private fun prepareCompilation(useKspIncremental: Boolean, vararg sourceFiles: SourceFile): KotlinCompilation { - return KotlinCompilation().apply { + private fun prepareCompilation(useKspIncremental: Boolean, vararg sourceFiles: SourceFile) = + KotlinCompilation().apply { workingDir = tempDir sources = sourceFiles.asList() + stubs jvmTarget = JvmTarget.JVM_17.toString() kspIncremental = useKspIncremental symbolProcessorProviders = listOf(CasaProcessorProvider()) } - } } diff --git a/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/stubs.kt b/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/stubs.kt index d4b873b2d..cd6741bd2 100644 --- a/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/stubs.kt +++ b/casa-processor/src/test/kotlin/team/duckie/quackquack/casa/processor/stubs.kt @@ -12,8 +12,9 @@ import team.duckie.quackquack.util.backend.test.stub.CasaStub import team.duckie.quackquack.util.backend.test.stub.ComposeStub import team.duckie.quackquack.util.backend.test.stub.SugarStub -val stubs = listOf( - kotlin("Composable.kt", ComposeStub.Composable), - kotlin("CasaAnnotations.kt", CasaStub.Annotations), - kotlin("SugarAnnotations.kt", SugarStub.Annotations), -) +val stubs = + listOf( + kotlin("Composable.kt", ComposeStub.Composable), + kotlin("CasaAnnotations.kt", CasaStub.Annotations), + kotlin("SugarAnnotations.kt", SugarStub.Annotations), + ) diff --git a/casa-processor/version.txt b/casa-processor/version.txt deleted file mode 100644 index 3a3bf97af..000000000 --- a/casa-processor/version.txt +++ /dev/null @@ -1 +0,0 @@ -2.0.0-alpha01 \ No newline at end of file diff --git a/catalog/src/main/kotlin/team/duckie/quackquack/catalog/CasaModels.kt b/catalog/src/main/kotlin/team/duckie/quackquack/catalog/CasaModels.kt deleted file mode 100644 index 6b624918b..000000000 --- a/catalog/src/main/kotlin/team/duckie/quackquack/catalog/CasaModels.kt +++ /dev/null @@ -1,154 +0,0 @@ -// This file was automatically generated by casa-processor. -// Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") -@file:OptIn(ExperimentalQuackQuackApi::class) - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import kotlin.Boolean -import kotlin.Function0 -import kotlin.String -import kotlin.Suppress -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList -import team.duckie.quackquack.casa.material.CasaModel -import team.duckie.quackquack.ui.sugar.QuackBody1 -import team.duckie.quackquack.ui.sugar.QuackBody2 -import team.duckie.quackquack.ui.sugar.QuackBody3 -import team.duckie.quackquack.ui.sugar.QuackFilledTag -import team.duckie.quackquack.ui.sugar.QuackGrayscaleFlatTag -import team.duckie.quackquack.ui.sugar.QuackGrayscaleOutlinedTag -import team.duckie.quackquack.ui.sugar.QuackHeadLine1 -import team.duckie.quackquack.ui.sugar.QuackHeadLine2 -import team.duckie.quackquack.ui.sugar.QuackLarge1 -import team.duckie.quackquack.ui.sugar.QuackMediumButton -import team.duckie.quackquack.ui.sugar.QuackOutlinedTag -import team.duckie.quackquack.ui.sugar.QuackPrimaryFilledSmallButton -import team.duckie.quackquack.ui.sugar.QuackPrimaryLargeButton -import team.duckie.quackquack.ui.sugar.QuackPrimaryOutlinedRoundSmallButton -import team.duckie.quackquack.ui.sugar.QuackPrimaryOutlinedSmallButton -import team.duckie.quackquack.ui.sugar.QuackSecondaryLargeButton -import team.duckie.quackquack.ui.sugar.QuackSecondaryRoundSmallButton -import team.duckie.quackquack.ui.sugar.QuackSecondarySmallButton -import team.duckie.quackquack.ui.sugar.QuackSubtitle -import team.duckie.quackquack.ui.sugar.QuackSubtitle2 -import team.duckie.quackquack.ui.sugar.QuackTitle1 -import team.duckie.quackquack.ui.sugar.QuackTitle2 -import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi - -private val buttonQuackButtonCasaModel: CasaModel = CasaModel( - name = "QuackButton", - domain = "button", - kdocDefaultSection = "버튼을 그립니다.", - components = persistentListOf Unit>>( - "QuackPrimaryLargeButton" to { QuackPrimaryLargeButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackSecondaryLargeButton" to { QuackSecondaryLargeButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackMediumButton" to { QuackMediumButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackPrimaryFilledSmallButton" to { QuackPrimaryFilledSmallButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackPrimaryOutlinedSmallButton" to { QuackPrimaryOutlinedSmallButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackPrimaryOutlinedRoundSmallButton" to { QuackPrimaryOutlinedRoundSmallButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackSecondarySmallButton" to { QuackSecondarySmallButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - "QuackSecondaryRoundSmallButton" to { QuackSecondaryRoundSmallButton( - text = "QuackButton is experimental", - onClick = {}, - ) }, - ).toImmutableList(), -) - - -private val tagQuackTagCasaModel: CasaModel = CasaModel( - name = "QuackTag", - domain = "tag", - kdocDefaultSection = "태그를 그립니다.", - components = persistentListOf Unit>>( - "QuackOutlinedTag" to { QuackOutlinedTag( - text = "QuackTagPreview", - onClick = {}, - ) }, - "QuackFilledTag" to { QuackFilledTag( - text = "QuackTagPreview", - onClick = {}, - ) }, - "QuackGrayscaleFlatTag" to { QuackGrayscaleFlatTag( - text = "QuackTagPreview", - onClick = {}, - ) }, - "QuackGrayscaleOutlinedTag" to { QuackGrayscaleOutlinedTag( - text = "QuackTagPreview", - onClick = {}, - ) }, - ).toImmutableList(), -) - -private val textQuackTextCasaModel: CasaModel = CasaModel( - name = "QuackText", - domain = "text", - kdocDefaultSection = "텍스트를 그립니다.", - components = persistentListOf Unit>>( - "QuackBody1" to { QuackBody1( - text = "QuackText", - ) }, - "QuackBody2" to { QuackBody2( - text = "QuackText", - ) }, - "QuackBody3" to { QuackBody3( - text = "QuackText", - ) }, - "QuackHeadLine1" to { QuackHeadLine1( - text = "QuackText", - ) }, - "QuackHeadLine2" to { QuackHeadLine2( - text = "QuackText", - ) }, - "QuackLarge1" to { QuackLarge1( - text = "QuackText", - ) }, - "QuackSubtitle" to { QuackSubtitle( - text = "QuackText", - ) }, - "QuackSubtitle2" to { QuackSubtitle2( - text = "QuackText", - ) }, - "QuackTitle1" to { QuackTitle1( - text = "QuackText", - ) }, - "QuackTitle2" to { QuackTitle2( - text = "QuackText", - ) }, - ).toImmutableList(), -) - -public val casaModels: ImmutableList = persistentListOf( - buttonQuackButtonCasaModel, - tagQuackTagCasaModel, - textQuackTextCasaModel, -) - diff --git a/catalog/src/main/kotlin/team/duckie/quackquack/catalog/MainActivity.kt b/catalog/src/main/kotlin/team/duckie/quackquack/catalog/MainActivity.kt index 19b2daeb6..61f5fd0fc 100644 --- a/catalog/src/main/kotlin/team/duckie/quackquack/catalog/MainActivity.kt +++ b/catalog/src/main/kotlin/team/duckie/quackquack/catalog/MainActivity.kt @@ -6,7 +6,7 @@ */ @file:OptIn(ExperimentalQuackQuackApi::class, ExperimentalDesignToken::class) -@file:Suppress("UnnecessaryOptInAnnotation") +@file:Suppress("UnnecessaryOptInAnnotation", "UnusedPrivateMember", "unused") package team.duckie.quackquack.catalog @@ -17,13 +17,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import casaModels -import team.duckie.quackquack.casa.ui.CasaScreen -import team.duckie.quackquack.casa.ui.theme.CasaTheme import team.duckie.quackquack.ui.optin.ExperimentalDesignToken import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi @@ -31,9 +29,10 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - CasaTheme { + Text("TODO") + /*CasaTheme { CasaScreen(models = casaModels) - } + }*/ /*QuackTheme( plugins = rememberQuackPlugins { +QuackImageGifPlugin @@ -51,7 +50,6 @@ class MainActivity : ComponentActivity() { } } -@Suppress("UnusedPrivateMember", "unused") @Composable private fun Preview(content: @Composable ColumnScope.() -> Unit) { Column( diff --git a/catalog/src/main/kotlin/team/duckie/quackquack/catalog/toast.kt b/catalog/src/main/kotlin/team/duckie/quackquack/catalog/toast.kt index 9a67342e0..686ac5931 100644 --- a/catalog/src/main/kotlin/team/duckie/quackquack/catalog/toast.kt +++ b/catalog/src/main/kotlin/team/duckie/quackquack/catalog/toast.kt @@ -5,11 +5,12 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ +@file:Suppress("unused") + package team.duckie.quackquack.catalog import android.app.Activity import android.widget.Toast -internal fun Activity.toast(message: String): Toast { - return Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).also(Toast::show) -} +internal fun Activity.toast(message: String) = + Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).also(Toast::show) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fdab6a667..900abb27a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,8 @@ gradle-dependency-graph = "1.1.0" google-autoservice-standard = "1.1.1" google-autoservice-ksp = "1.1.0" +jetbrains-annotation = "24.0.0" + kotlin-core = "1.8.22" kotlin-coroutines = "1.7.2" kotlin-dokka = "1.8.20" @@ -73,6 +75,8 @@ gradle-publish-maven = { module = "com.vanniktech:gradle-maven-publish-plugin", google-autoservice-annotation = { module = "com.google.auto.service:auto-service-annotations", version.ref = "google-autoservice-standard" } google-autoservice-ksp-processor = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "google-autoservice-ksp" } +jetbrains-annotation = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotation" } + kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-core" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin-core" } kotlin-embeddable-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin-core" } diff --git a/settings.gradle.kts b/settings.gradle.kts index b391ddb42..72c9cab1f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,14 +30,6 @@ buildCache { include( ":catalog", - ":util", - ":util-modifier", - ":util-backend", - ":util-backend-ksp", - ":util-backend-kotlinc", - ":util-backend-test", - ":util-compose-runtime-test", - ":util-compose-snapshot-test", ":runtime", ":material", ":material-icon", @@ -50,11 +42,27 @@ include( ":ui-plugin:interceptor", ":ui-plugin:interceptor:textfield", ":ui-sample", + ":ui-sugar", ":sugar-material", - ":sugar-processor", + ":sugar-compiler", + ":sugar-core", + ":sugar-core:node", + ":sugar-core:visitor", + ":sugar-core:codegen", + ":sugar-core:names", + ":sugar-core:error", + ":sugar-test", ":casa-ui", ":casa-annotation", ":casa-material", ":casa-processor", + ":util", + ":util-modifier", + ":util-backend-ksp", + ":util-backend-kotlinc", + ":util-backend-kotlinpoet", + ":util-backend-test", + ":util-compose-runtime-test", + ":util-compose-snapshot-test", ":bom", ) diff --git a/sugar-processor/build.gradle.kts b/sugar-compiler/build.gradle.kts similarity index 67% rename from sugar-processor/build.gradle.kts rename to sugar-compiler/build.gradle.kts index 5cbcab61f..34d10284f 100644 --- a/sugar-processor/build.gradle.kts +++ b/sugar-compiler/build.gradle.kts @@ -5,11 +5,8 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -@file:Suppress("INLINE_FROM_HIGHER_PLATFORM") - plugins { quackquack("jvm-kotlin") - quackquack("test-kotest") quackquack("quack-publishing") alias(libs.plugins.kotlin.ksp) } @@ -21,16 +18,13 @@ ksp { dependencies { compileOnly(libs.kotlin.embeddable.compiler) + ksp(libs.google.autoservice.ksp.processor) implementations( libs.google.autoservice.annotation, - libs.kotlin.kotlinpoet.core, - projects.casaAnnotation.orArtifact(), - projects.sugarMaterial.orArtifact(), + projects.sugarCore.names.orArtifact(), + projects.sugarCore.error.orArtifact(), + projects.sugarCore.node.orArtifact(), + projects.sugarCore.visitor.orArtifact(), projects.utilBackendKotlinc.orArtifact(), ) - ksp(libs.google.autoservice.ksp.processor) - testImplementations( - libs.test.kotlin.compilation.core, - projects.utilBackendTest, - ) } diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrExtension.kt b/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerExtension.kt similarity index 50% rename from sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrExtension.kt rename to sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerExtension.kt index f54a07850..9c2eafbf5 100644 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrExtension.kt +++ b/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerExtension.kt @@ -5,48 +5,40 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -package team.duckie.quackquack.sugar.processor.ir +package team.duckie.quackquack.sugar.compiler import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import team.duckie.quackquack.sugar.processor.poet.generateSugarComponentFiles +import team.duckie.quackquack.sugar.compiler.ir.SugarIrTransformer +import team.duckie.quackquack.sugar.node.SugarComponentNode +import team.duckie.quackquack.sugar.visitor.SugarCoreVisitor import team.duckie.quackquack.util.backend.kotlinc.Logger -internal class SugarIrExtension( - private val logger: Logger, - private val sugarPath: String, - private val poet: Boolean, -) : IrGenerationExtension { +internal class SugarCompilerExtension(private val logger: Logger) : IrGenerationExtension { override fun generate( moduleFragment: IrModuleFragment, pluginContext: IrPluginContext, ) { - val sugarIrDatas = mutableListOf() - val visitor = SugarIrVisitor( + val nodes = mutableListOf() + val visitor = SugarCoreVisitor( context = pluginContext, logger = logger, - addSugarIrData = sugarIrDatas::add, + addSugarComponentNode = nodes::add, ) val transformer = SugarIrTransformer( context = pluginContext, logger = logger, ) + moduleFragment.accept(visitor, null) - if (poet) { - generateSugarComponentFiles( - irDatas = sugarIrDatas, - sugarPath = sugarPath, - ) - } - moduleFragment.transform(transformer, sugarIrDatas.asMap()) + moduleFragment.transform(transformer, nodes.asMap()) } } -private fun List.asMap(): Map { - return buildMap(capacity = size) { - this@asMap.forEach { sugarIrData -> - set(sugarIrData.referFqn.asString(), sugarIrData) +private fun List.asMap() = + buildMap(capacity = size) { + this@asMap.forEach { SugarComponentNode -> + set(SugarComponentNode.referFqn.asString(), SugarComponentNode) } } -} diff --git a/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerRegistrar.kt b/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerRegistrar.kt new file mode 100644 index 000000000..8f86947c3 --- /dev/null +++ b/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/SugarCompilerRegistrar.kt @@ -0,0 +1,64 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +@file:Suppress("DEPRECATION", "unused") +@file:OptIn(ExperimentalCompilerApi::class) + +package team.duckie.quackquack.sugar.compiler + +import com.google.auto.service.AutoService +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.com.intellij.mock.MockProject +import org.jetbrains.kotlin.com.intellij.openapi.extensions.LoadingOrder +import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar +import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration +import team.duckie.quackquack.sugar.visitor.SugarCoreVisitor +import team.duckie.quackquack.util.backend.kotlinc.getLogger + +/** + * ### Deprecated된 메서드를 사용하는 이유 + * + * Compose Compiler의 [`Default Arguments Transform`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt;l=341-365)에 + * 의해 모든 컴포저블 함수에서 default argument의 값이 null로 변경됩니다. 하지만 sugar component를 + * 생성하기 위해선 default value의 값을 보존해야 합니다. 이를 위해 [SugarCoreVisitor]가 Compose Compiler + * 보다 먼저 적용될 수 있도록 Compiler Plugin의 적용 순서를 조정할 수 있는 deprecated된 [registerProjectComponents] + * 메서드를 사용합니다. deprecated 되지 않은 [CompilerPluginRegistrar]를 사용하면 Compiler Plugin의 적용 + * 순서를 조정할 수 없습니다. + */ +@AutoService(ComponentRegistrar::class) +class SugarCompilerRegistrar : ComponentRegistrar { + override val supportsK2 = false + + override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { + project.extensionArea + .getExtensionPoint(IrGenerationExtension.extensionPointName) + .registerExtension(configuration.getSugarIrExtension(), LoadingOrder.FIRST, project) + } + + companion object { + /** + * [ComponentRegistrar]의 complie test는 DeprecatedError 상태로 항상 테스트에 실패합니다. + * 이를 해결하기 위해 [SugarCompilerRegistrar]의 [CompilerPluginRegistrar] 버전을 제공합니다. + * 이 함수는 오직 테스트 코드에서만 사용돼야 합니다. (테스트 환경에서는 Compose Compiler가 + * 적용되지 않으니 유효합니다.) + */ + @TestOnly + fun asPluginRegistrar() = object : CompilerPluginRegistrar() { + override val supportsK2 = false + + override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { + IrGenerationExtension.registerExtension(configuration.getSugarIrExtension()) + } + } + + private fun CompilerConfiguration.getSugarIrExtension() = + SugarCompilerExtension(logger = getLogger("sugar-compiler")) + } +} diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrTransformer.kt b/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/ir/SugarIrTransformer.kt similarity index 64% rename from sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrTransformer.kt rename to sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/ir/SugarIrTransformer.kt index c49c691b3..d92bf2439 100644 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrTransformer.kt +++ b/sugar-compiler/src/main/kotlin/team/duckie/quackquack/sugar/compiler/ir/SugarIrTransformer.kt @@ -7,7 +7,7 @@ @file:OptIn(UnsafeCastFunction::class) -package team.duckie.quackquack.sugar.processor.ir +package team.duckie.quackquack.sugar.compiler.ir import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.ir.IrStatement @@ -24,6 +24,11 @@ import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction import org.jetbrains.kotlin.utils.addToStdlib.cast +import team.duckie.quackquack.sugar.error.SugarTransformError +import team.duckie.quackquack.sugar.error.SugarVisitError +import team.duckie.quackquack.sugar.names.SugarGeneratedFileFqn +import team.duckie.quackquack.sugar.names.SugarReferFqn +import team.duckie.quackquack.sugar.node.SugarComponentNode import team.duckie.quackquack.util.backend.kotlinc.Logger import team.duckie.quackquack.util.backend.kotlinc.isQuackComponent import team.duckie.quackquack.util.backend.kotlinc.locationOf @@ -31,62 +36,51 @@ import team.duckie.quackquack.util.backend.kotlinc.locationOf internal class SugarIrTransformer( @Suppress("unused") private val context: IrPluginContext, private val logger: Logger, -) : IrElementTransformer> { +) : IrElementTransformer> { override fun visitModuleFragment( declaration: IrModuleFragment, - data: Map, + data: Map, ): IrModuleFragment { - declaration.files.forEach { file -> - file.accept(this, data) - } + declaration.files.forEach { file -> file.accept(this, data) } return declaration } override fun visitFile( declaration: IrFile, - data: Map, + data: Map, ): IrFile { if (declaration.hasAnnotation(SugarGeneratedFileFqn)) { - declaration.declarations.forEach { item -> - item.accept(this, data) - } + declaration.declarations.forEach { item -> item.accept(this, data) } } return declaration } override fun visitSimpleFunction( declaration: IrSimpleFunction, - data: Map, + data: Map, ): IrStatement { if (declaration.isQuackComponent) { - if (declaration.hasAnnotation(NoSugarFqn)) { - return super.visitSimpleFunction(declaration, data) - } - - // run으로 throwError 하는 게 더 가독성이 좋음 - val referAnnotation = declaration.getAnnotation(SugarReferFqn) ?: run { - logger.throwError( - message = PoetError.sugarComponentButNoSugarRefer(declaration.name.asString()), - location = declaration.file.locationOf(declaration), - ) - } + val referAnnotation = + declaration.getAnnotation(SugarReferFqn) + ?: return super.visitSimpleFunction(declaration, data) val referFqn = referAnnotation.getReferFqName() data[referFqn]?.let { referIrData -> declaration.valueParameters.forEach { parameter -> - parameter.defaultValue = referIrData.findMatchedDefaultValue( - sugarComponentName = declaration.name.asString(), - parameter = parameter, - error = { message -> - logger.throwError( - message = message, - location = declaration.file.locationOf(parameter), - ) - }, - ) + parameter.defaultValue = + referIrData.findMatchedDefaultValue( + sugarComponentName = declaration.name.asString(), + parameter = parameter, + error = { message -> + logger.throwError( + message = message, + location = declaration.file.locationOf(parameter), + ) + }, + ) } } ?: logger.throwError( - message = SugarVisitError.noMatchedSugarIrData(declaration.name.asString()), + message = SugarVisitError.noMatchedSugarComponentNode(declaration.name.asString()), location = declaration.file.locationOf(declaration), ) } @@ -101,14 +95,15 @@ private fun IrConstructorCall.getReferFqName(): String { return referFqnExpression.cast>().value } -private fun SugarIrData.findMatchedDefaultValue( +private fun SugarComponentNode.findMatchedDefaultValue( sugarComponentName: String, parameter: IrValueParameter, error: (message: String) -> Unit, ): IrExpressionBody? { - val matched = parameters.find { referParameter -> - referParameter.name.asString() == parameter.name.asString() - } + val matched = + parameters.find { referParameter -> + referParameter.name.asString() == parameter.name.asString() + } if (matched == null) { error( SugarTransformError.sugarComponentAndSugarReferHasDifferentParameters( diff --git a/sugar-compiler/version.txt b/sugar-compiler/version.txt new file mode 100644 index 000000000..311be597a --- /dev/null +++ b/sugar-compiler/version.txt @@ -0,0 +1 @@ +2.0.0-alpha01 diff --git a/sugar-core/build.gradle.kts b/sugar-core/build.gradle.kts new file mode 100644 index 000000000..0a67b386d --- /dev/null +++ b/sugar-core/build.gradle.kts @@ -0,0 +1,29 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("jvm-kotlin") + alias(libs.plugins.kotlin.ksp) +} + +ksp { + arg("autoserviceKsp.verify", "true") + arg("autoserviceKsp.verbose", "true") +} + +dependencies { + compileOnly(libs.kotlin.embeddable.compiler) + implementations( + libs.google.autoservice.annotation, + libs.jetbrains.annotation, + projects.sugarCore.node, + projects.sugarCore.visitor, + projects.sugarCore.codegen, + projects.utilBackendKotlinc, + ) + ksp(libs.google.autoservice.ksp.processor) +} diff --git a/sugar-core/codegen/build.gradle.kts b/sugar-core/codegen/build.gradle.kts new file mode 100644 index 000000000..c22407141 --- /dev/null +++ b/sugar-core/codegen/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("jvm-kotlin") +} + +dependencies { + implementations( + libs.kotlin.embeddable.compiler, + libs.kotlin.kotlinpoet.core, + projects.sugarCore.node, + projects.sugarCore.names, + projects.sugarCore.error, + projects.utilBackendKotlinc, + projects.utilBackendKotlinpoet, + ) +} diff --git a/sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/codegen.kt b/sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/codegen.kt new file mode 100644 index 000000000..5bb55e043 --- /dev/null +++ b/sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/codegen.kt @@ -0,0 +1,152 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +package team.duckie.quackquack.sugar.codegen + +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.buildCodeBlock +import com.squareup.kotlinpoet.withIndent +import java.io.File +import org.jetbrains.kotlin.ir.declarations.name +import org.jetbrains.kotlin.ir.types.classFqName +import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.name.FqName +import team.duckie.quackquack.sugar.names.CasaCn +import team.duckie.quackquack.sugar.names.ComposableCn +import team.duckie.quackquack.sugar.names.NonRestartableComposableCn +import team.duckie.quackquack.sugar.names.SugarCompilerApiCn +import team.duckie.quackquack.sugar.names.SugarFqn +import team.duckie.quackquack.sugar.names.SugarGeneratedFileCn +import team.duckie.quackquack.sugar.names.SugarGeneratorUsageCn +import team.duckie.quackquack.sugar.names.SugarReferCn +import team.duckie.quackquack.sugar.node.SugarComponentNode +import team.duckie.quackquack.sugar.node.SugarParameter +import team.duckie.quackquack.util.backend.kotlinc.addImports +import team.duckie.quackquack.util.backend.kotlinc.toFqnStringOrEmpty +import team.duckie.quackquack.util.backend.kotlinpoet.addAnnotations +import team.duckie.quackquack.util.backend.kotlinpoet.addFunctions +import team.duckie.quackquack.util.backend.kotlinpoet.getGeneratedFileComment + +private val GeneratedComment = getGeneratedFileComment("sugar-core") + +@Suppress("OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION") +private val SugarCompilerOptInAnnotation = + AnnotationSpec + .builder(OptIn::class) + .addMember( + "%T::class, %T::class", + SugarCompilerApiCn, + SugarGeneratorUsageCn, + ) + .useSiteTarget(AnnotationSpec.UseSiteTarget.FILE) + .build() + +private val SugarGeneratedFileMarkerAnnotation = + AnnotationSpec + .builder(SugarGeneratedFileCn) + .useSiteTarget(AnnotationSpec.UseSiteTarget.FILE) + .build() + +fun generateSugarComponentFiles(sugarComponentNodes: List, sugarPath: String) { + val fileGroupedNodeDatas = sugarComponentNodes.groupBy { node -> node.owner.file.name } + + fileGroupedNodeDatas.forEach { (fileName, componentNode) -> + val (imports, funSpecs) = componentNode.toFunSpecsWithImports() + val ktSpec = + FileSpec + .builder( + packageName = sugarPath.bestGuessToKotlinPackageName(), + fileName = fileName.substringBeforeLast("."), + ) + .addFileComment(GeneratedComment) + .addAnnotations( + SugarCompilerOptInAnnotation, + SugarGeneratedFileMarkerAnnotation, + ) + .addImports(imports.toMutableList().apply { add(SugarFqn) }) + .addFunctions(funSpecs) + .build() + + File(sugarPath, fileName) + .also { file -> + if (!file.exists()) { + file.parentFile?.mkdirs() + file.createNewFile() + } + } + .writeText(ktSpec.toString()) + } +} + +private fun List.toFunSpecsWithImports(): Pair, List> { + val imports = mutableListOf() + val funSpecs = mutableListOf() + forEach { sugarIrData -> + imports += sugarIrData.referFqn + sugarIrData.tokenFqExpressions.forEach { tokenFqExpression -> + val (_imports, funSpec) = sugarIrData.toFunSpecWithImports(tokenFqExpression) + imports += _imports + funSpecs += funSpec + } + } + return imports to funSpecs +} + +private fun SugarComponentNode.toFunSpecWithImports(tokenFqExpression: String): Pair, FunSpec> { + val imports = mutableListOf() + + val sugarReferAnnotation = + AnnotationSpec + .builder(SugarReferCn) + .addMember("%S", referFqn.asString()) + .build() + + val sugarName = toSugarComponentName(tokenFqExpression) + val sugarBody = + buildCodeBlock { + addStatement("%L(", referFqn.shortName().asString()) + withIndent { + parameters.forEach { parameter -> + imports += parameter.type.classFqName!! + imports += parameter.imports + + val parameterName = parameter.name.asString() + val parameterValue = if (parameter.isToken) tokenFqExpression else parameterName + + addStatement("%L = %L,", parameterName, parameterValue) + } + } + addStatement(")") + } + + val optinCns = + optins.map { irOptin -> + ClassName.bestGuess(irOptin.toFqnStringOrEmpty()) + } + + val funSpec = + FunSpec + .builder(sugarName) + .addAnnotations( + CasaCn, + ComposableCn, + NonRestartableComposableCn, + *optinCns.toTypedArray(), + ) + .addAnnotation(sugarReferAnnotation) + .addModifiers(KModifier.PUBLIC) + .addParameters(parametersWithoutToken.map(SugarParameter::toParameterSpec)) + .addCode(sugarBody) + .addKdoc(kdocGetter(tokenFqExpression)) + .build() + + return imports to funSpec +} diff --git a/sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/utils.kt b/sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/utils.kt new file mode 100644 index 000000000..786c72a60 --- /dev/null +++ b/sugar-core/codegen/src/main/kotlin/team/duckie/quackquack/sugar/codegen/utils.kt @@ -0,0 +1,131 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +package team.duckie.quackquack.sugar.codegen + +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.LambdaTypeName +import com.squareup.kotlinpoet.ParameterSpec +import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType +import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType +import org.jetbrains.kotlin.ir.types.isMarkedNullable +import org.jetbrains.kotlin.ir.util.isFunction +import org.jetbrains.kotlin.ir.util.isSuspendFunction +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaAnnotation.Companion.addAnnotation +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.getClassFqNameUnsafe +import org.jetbrains.kotlin.utils.addToStdlib.applyIf +import team.duckie.quackquack.sugar.error.NotSupportedError +import team.duckie.quackquack.sugar.names.CasaValueCn +import team.duckie.quackquack.sugar.names.ComposableCn +import team.duckie.quackquack.sugar.names.QuackComponentPrefix +import team.duckie.quackquack.sugar.names.SugarTokenName +import team.duckie.quackquack.sugar.node.SugarComponentNode +import team.duckie.quackquack.sugar.node.SugarParameter +import team.duckie.quackquack.util.backend.kotlinc.unsafeClassName + +internal fun String.bestGuessToKotlinPackageName(): String { + // make sure testable + // require(contains("src/main/kotlin")) { "The given package is not a Kotlin package." } + return substringAfterLast("src/main/kotlin/").replace("/", ".") +} + +// TODO: Testing +internal fun SugarComponentNode.toSugarComponentName(tokenFqExpression: String): String { + val tokenExpression = tokenFqExpression + .substringAfterLast(".") + .replaceFirstChar(Char::titlecase) + return sugarName?.replace(SugarTokenName, tokenExpression) + ?: referFqn + .shortName() + .asString() + .toMutableList() + .apply { addAll(QuackComponentPrefix.length, tokenExpression.toList()) } + .joinToString("") +} + +/** 제공된 정보를 [ParameterSpec]으로 변환합니다. */ +internal fun SugarParameter.toParameterSpec(): ParameterSpec { + val parameterTypedBuilder = + ParameterSpec + .builder( + name = name.asString(), + type = when { + type.isFunction() || type.isSuspendFunction() -> { + val funArguments = type.toIrBasedKotlinType().arguments + + /* + * maintainer notes: 모든 **함수형 타입**은 `Function`으로 처리되며 이는 `IrFunction`과는 + * 다른 유형임. 코틀린의 **함수 정의**는 `IrFunction`으로 해석되고, **함수형 타입**은 코틀린 + * 네이티브 타입인 `KotlinType`으로 해석됨. + * + * `Function`은 value parameter의 타입과 return의 타입을 generic으로 받는 인터페이스임. + * 즉, `Function`은 메타데이터가 없어서 컴파일 시점에서는 인자의 타입만 조회 가능함. + * 따라서 람다의 인자명 정책으로 `P{$index}`를 사용함. + * + * receiver extension은 **첫 번째** value parameter로 치환됨. + * > `String.() -> Unit` == `(String) -> Unit` + * + * return type은 **마지막** value parameter로 치환됨. + */ + val referLambdaParameters = + if (funArguments.size >= 2) { + funArguments.dropLast(1).mapIndexed { index, argument -> + require(!argument.type.isFunctionOrSuspendFunctionType) { + NotSupportedError.nestedFunctionalType("${owner.name.asString()}#${name.asString()}") + } + + val argumentTypeFqn = argument.type.constructor.getClassFqNameUnsafe() + val argumentTypeCn = ClassName.bestGuess(argumentTypeFqn.toString()) + + ParameterSpec + .builder( + name = "P$index", + type = argumentTypeCn, + ) + .build() + } + } else { + emptyList() + } + val referLambdaReturnTypeFqn = funArguments.last().type.constructor.getClassFqNameUnsafe() + val referLambdaReturnTypeCn = ClassName.bestGuess(referLambdaReturnTypeFqn.toString()) + + LambdaTypeName + .get( + parameters = referLambdaParameters, + returnType = referLambdaReturnTypeCn, + ) + .copy(suspending = type.isSuspendFunction()) + } + else -> { + type.unsafeClassName + } + }.copy(nullable = type.isMarkedNullable()), + ) + + return parameterTypedBuilder + .applyIf(casaValueLiteral != null) { + addAnnotation( + AnnotationSpec + .builder(CasaValueCn) + .addMember("%S", casaValueLiteral!!) + .build(), + ) + } + .applyIf(isComposable) { + addAnnotation( + AnnotationSpec + .builder(ComposableCn) + .build(), + ) + } + .applyIf(defaultValue != null) { + defaultValue("%L()", "sugar") + } + .build() +} diff --git a/build-logic/src/main/kotlin/SugarPoetConfig.kt b/sugar-core/error/build.gradle.kts similarity index 55% rename from build-logic/src/main/kotlin/SugarPoetConfig.kt rename to sugar-core/error/build.gradle.kts index f313e5787..0d6ac202f 100644 --- a/build-logic/src/main/kotlin/SugarPoetConfig.kt +++ b/sugar-core/error/build.gradle.kts @@ -5,5 +5,11 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -// TODO: 작동 방식 변경 (gradle task) -const val sugarPoet = false +plugins { + quackquack("jvm-kotlin") + quackquack("quack-publishing") +} + +dependencies { + implementation(projects.sugarCore.names.orArtifact()) +} diff --git a/sugar-core/error/src/main/kotlin/team/duckie/quackquack/sugar/error/SugarErrors.kt b/sugar-core/error/src/main/kotlin/team/duckie/quackquack/sugar/error/SugarErrors.kt new file mode 100644 index 000000000..d0187bf16 --- /dev/null +++ b/sugar-core/error/src/main/kotlin/team/duckie/quackquack/sugar/error/SugarErrors.kt @@ -0,0 +1,67 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +@file:Suppress("unused") + +package team.duckie.quackquack.sugar.error + +import team.duckie.quackquack.sugar.names.QuackComponentPrefix +import team.duckie.quackquack.sugar.names.SugarTokenName + +object NotSupportedError { + fun nestedFunctionalType(name: String?) = + "Nested functional types are not currently supported due to implementation complexity." + + " ($name)".getIfGivenIsNotNull(name) +} + +object SourceError { + fun quackComponentFqnUnavailable(name: String?) = + "A Quack component was detected, but unable to look up a fully qualified name. " + + "Is it an anonymous object?" + " ($name)".getIfGivenIsNotNull(name) + + fun importClazzFqnUnavailable(name: String?) = + "Can't look up the fully qualified name of the class given as `clazz` in `@Imports`. " + + "Is it an anonymous class?" + " ($name)".getIfGivenIsNotNull(name) + + fun quackComponentWithoutSugarToken(name: String?) = + "A Quack component was detected, but no SugarToken was applied." + + " ($name)".getIfGivenIsNotNull(name) + + fun multipleSugarTokenIsNotAllowed(name: String?) = + "A Sugar component can only contain one SugarToken." + + " ($name)".getIfGivenIsNotNull(name) + + fun sugarNamePrefixIsNotQuack(name: String?) = + "Quack component names must start with `SugarName.PREFIX_NAME" + + " (= $QuackComponentPrefix)`." + " ($name)".getIfGivenIsNotNull(name) + + fun sugarNameWithoutTokenName(name: String?) = + "When specifying the sugar component name directly, " + + "`SugarName.TOKEN_NAME (= $SugarTokenName)` must be used." + + " ($name)".getIfGivenIsNotNull(name) + + fun sugarTokenButNoCompanionObject(name: String?) = + "The SugarToken class must include a companion object. " + + "See the sugar component creation policy for more information." + + " ($name)".getIfGivenIsNotNull(name) +} + +object SugarVisitError { + fun noMatchedSugarComponentNode(name: String?) = + "No SugarComponentNode was found for the given SugarRefer. " + + "Please report it in a GitHub Issue. (https://link.duckie.team/quackquack-bug)" + + " ($name)".getIfGivenIsNotNull(name) +} + +object SugarTransformError { + fun sugarComponentAndSugarReferHasDifferentParameters(name: String?) = + "The Sugar component has a parameter that doesn't exist in the SugarRefer." + + " ($name)".getIfGivenIsNotNull(name) +} + +private fun String.getIfGivenIsNotNull(given: Any?) = + if (given == null) "" else this diff --git a/sugar-core/error/version.txt b/sugar-core/error/version.txt new file mode 100644 index 000000000..311be597a --- /dev/null +++ b/sugar-core/error/version.txt @@ -0,0 +1 @@ +2.0.0-alpha01 diff --git a/sugar-core/names/build.gradle.kts b/sugar-core/names/build.gradle.kts new file mode 100644 index 000000000..daec655c1 --- /dev/null +++ b/sugar-core/names/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("jvm-kotlin") + quackquack("quack-publishing") +} + +dependencies { + implementations( + libs.kotlin.embeddable.compiler, + libs.kotlin.kotlinpoet.core, + projects.casaAnnotation.orArtifact(), + projects.sugarMaterial.orArtifact(), + ) +} diff --git a/sugar-core/names/src/main/kotlin/team/duckie/quackquack/sugar/names/SugarNames.kt b/sugar-core/names/src/main/kotlin/team/duckie/quackquack/sugar/names/SugarNames.kt new file mode 100644 index 000000000..1dcf09430 --- /dev/null +++ b/sugar-core/names/src/main/kotlin/team/duckie/quackquack/sugar/names/SugarNames.kt @@ -0,0 +1,62 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) +@file:Suppress( + "OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION", + "OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN", + "unused", +) + +package team.duckie.quackquack.sugar.names + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.asClassName +import org.jetbrains.kotlin.name.FqName +import team.duckie.quackquack.casa.annotation.Casa +import team.duckie.quackquack.casa.annotation.CasaValue +import team.duckie.quackquack.casa.annotation.SugarGeneratorUsage +import team.duckie.quackquack.sugar.material.Imports +import team.duckie.quackquack.sugar.material.SugarCompilerApi +import team.duckie.quackquack.sugar.material.SugarGeneratedFile +import team.duckie.quackquack.sugar.material.SugarName +import team.duckie.quackquack.sugar.material.SugarRefer +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable + +val RequiresOptInFqn = RequiresOptIn::class.qualifiedName!!.toFqnClass() + +val ComposableFqn = "androidx.compose.runtime.Composable".toFqnClass() +val ComposableCn = ComposableFqn.asString().toCnClass() +val NonRestartableComposableCn = "androidx.compose.runtime.NonRestartableComposable".toCnClass() + +const val QuackComponentPrefix = SugarName.PREFIX_NAME + +const val SugarDefaultName = SugarName.DEFAULT_NAME +const val SugarTokenName = SugarName.TOKEN_NAME + +val CasaCn = Casa::class.qualifiedName!!.toCnClass() +val CasaValueCn = CasaValue::class.asClassName() +val CasaValueFqn = CasaValue::class.qualifiedName!!.toFqnClass() + +val SugarCompilerApiCn = SugarCompilerApi::class.asClassName() +val SugarGeneratorUsageCn = SugarGeneratorUsage::class.asClassName() +val SugarGeneratedFileCn = SugarGeneratedFile::class.asClassName() +val SugarGeneratedFileFqn = SugarGeneratedFile::class.qualifiedName!!.toFqnClass() + +// sugar 함수에 리플렉션으로 접근 불가 +val SugarFqn = "team.duckie.quackquack.sugar.material.sugar".toFqnClass() +val SugarNameFqn = SugarName::class.qualifiedName!!.toFqnClass() +val SugarTokenFqn = SugarToken::class.qualifiedName!!.toFqnClass() +val SugarReferCn = SugarRefer::class.asClassName() +val SugarReferFqn = SugarRefer::class.qualifiedName!!.toFqnClass() + +val ImportsFqn = Imports::class.qualifiedName!!.toFqnClass() +val SugarableFqn = Sugarable::class.qualifiedName!!.toFqnClass() + +private fun String.toFqnClass() = FqName(this) +private fun String.toCnClass() = ClassName.bestGuess(this) diff --git a/sugar-core/names/version.txt b/sugar-core/names/version.txt new file mode 100644 index 000000000..311be597a --- /dev/null +++ b/sugar-core/names/version.txt @@ -0,0 +1 @@ +2.0.0-alpha01 diff --git a/sugar-core/node/build.gradle.kts b/sugar-core/node/build.gradle.kts new file mode 100644 index 000000000..c73be038e --- /dev/null +++ b/sugar-core/node/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("jvm-kotlin") + quackquack("quack-publishing") +} + +dependencies { + implementations( + libs.kotlin.embeddable.compiler, + projects.sugarCore.names.orArtifact(), + projects.sugarCore.error.orArtifact(), + projects.sugarMaterial.orArtifact(), + projects.casaAnnotation.orArtifact(), + projects.utilBackendKotlinc.orArtifact(), + ) +} diff --git a/sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarComponentNode.kt b/sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarComponentNode.kt new file mode 100644 index 000000000..c772adfff --- /dev/null +++ b/sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarComponentNode.kt @@ -0,0 +1,78 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +@file:Suppress("unused") + +package team.duckie.quackquack.sugar.node + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.name.FqName +import team.duckie.quackquack.sugar.material.SugarName +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.util.backend.kotlinc.toFqnStringOrEmpty + +/** + * 소스 파일의 IR을 방문하면서 수집할 정보들을 관리합니다. + * + * @param owner IR이 제공된 함수 + * @param referFqn IR이 제공된 함수의 [fully-qualified name][FqName]. + * [owner]에서 직접 가져오는 방식보다 안전한 방식으로 fqn이 제공됩니다. + * @param kdocGetter IR이 제공된 함수의 Sugared-KDoc을 계산하는 람다. + * 사용된 Sugar Token의 리터럴을 람다 인자로 제공해야 합니다. + * @param sugarName 생성할 sugar component의 네이밍 규칙. + * [`@SugarToken`][SugarName] 값을 가져옵니다. + * @param sugarToken 생성할 sugar component의 Sugar Token에 해당하는 [인자][IrValueParameter]. + * [`@SugarToken`][SugarToken]이 달린 인자를 가져옵니다. + * @param tokenFqExpressions Sugar Token의 expression 모음. 예를 들면 다음과 같습니다. + * + * ``` + * package team.duckie.theme + * + * @JvmInline + * value class Theme(val index: Int) { + * companion object { + * val Default = Theme(1) + * val Dark = Theme(2) + * val Light = Theme(3) + * val System = Theme(4) + * } + * } + * + * // ["team.duckie.theme.Theme.Default", "team.duckie.theme.Theme.Dark", "team.duckie.theme.Theme.Light", "team.duckie.theme.Theme.System"] + * ``` + * + * @param parameters IR이 제공된 함수의 인자 모음. sugar component 생성에 필요한 정보만 수집합니다. + * 자세한 수집 정보는 [SugarParameter]를 확인하세요. + */ +data class SugarComponentNode( + val owner: IrFunction, + val referFqn: FqName, + val kdocGetter: (usedTokenLiteral: String) -> String, + val sugarName: String?, + val sugarToken: IrValueParameter, + val tokenFqExpressions: List, + val parameters: List, + val optins: List, +) { + /** [parameters]에서 Sugar Token을 제외한 [요소][SugarParameter]만 불러옵니다. */ + val parametersWithoutToken: List = + parameters.toMutableList().apply { removeIf(SugarParameter::isToken) } + + override fun toString() = + """ + owner: ${owner.name.asString()} + referFqn: ${referFqn.asString()} + kdoc: ${kdocGetter("SugarToken")} + sugarName: $sugarName + sugarToken: ${sugarToken.name.asString()} + tokenExpressions: $tokenFqExpressions + parameters: ${parameters.joinToString("\n\n", prefix = "\n")} + optins: ${optins.joinToString(transform = IrConstructorCall::toFqnStringOrEmpty)} + """.trimIndent() +} diff --git a/sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarParameter.kt b/sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarParameter.kt new file mode 100644 index 000000000..553919134 --- /dev/null +++ b/sugar-core/node/src/main/kotlin/team/duckie/quackquack/sugar/node/SugarParameter.kt @@ -0,0 +1,58 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +package team.duckie.quackquack.sugar.node + +import org.jetbrains.kotlin.ir.backend.js.utils.asString +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.expressions.IrExpressionBody +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.dumpKotlinLike +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import team.duckie.quackquack.casa.annotation.CasaValue +import team.duckie.quackquack.sugar.material.Imports +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.names.ComposableFqn + +/** + * [IrValueParameter]에서 sugar component 생성에 필요한 정보를 관리합니다. + * + * @param name 인자의 이름 + * @param type 인자의 타입 + * @param isToken 인자가 [Sugar Token][SugarToken]인지 여부 + * @param isComposable 인자에 [`@Composable`][ComposableFqn] 어노테이션이 달려있는지 + * 여부 + * @param imports [type] 외에 추가로 import가 필요한 클래스의 [fully-qualified name][FqName]으로 + * 구성된 목록. 자세한 정보는 [`@Imports`][Imports] 어노테이션을 확인하세요. + * @param casaValueLiteral 만약 인자에 [`@CasaValue`][CasaValue] 어노테이션이 달려있다면 + * [CasaValue.literal]로 제공된 값 + * @param defaultValue 인자의 기본 값 + */ +data class SugarParameter( + val owner: IrFunction, + val name: Name, + val type: IrType, + val isToken: Boolean, + val isComposable: Boolean, + val imports: List, + val casaValueLiteral: String?, + val defaultValue: IrExpressionBody?, +) { + override fun toString() = + """ + owner: ${owner.name.asString()} + name: ${name.asString()} + type: ${type.asString()} + isToken: $isToken + isComposable: $isComposable + imports: ${imports.joinToString(transform = FqName::asString)} + casaValueLiteral: $casaValueLiteral + defaultValue: ${defaultValue?.dumpKotlinLike()} + """.trimIndent() +} diff --git a/sugar-core/node/version.txt b/sugar-core/node/version.txt new file mode 100644 index 000000000..311be597a --- /dev/null +++ b/sugar-core/node/version.txt @@ -0,0 +1 @@ +2.0.0-alpha01 diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarCommandLineProcessor.kt b/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreCommandLineProcessor.kt similarity index 52% rename from sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarCommandLineProcessor.kt rename to sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreCommandLineProcessor.kt index cfa4d41c1..9e598aed7 100644 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarCommandLineProcessor.kt +++ b/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreCommandLineProcessor.kt @@ -6,10 +6,12 @@ */ @file:OptIn(ExperimentalCompilerApi::class) +@file:Suppress("unused") -package team.duckie.quackquack.sugar.processor +package team.duckie.quackquack.sugar.core import com.google.auto.service.AutoService +import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption import org.jetbrains.kotlin.compiler.plugin.CliOption import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor @@ -17,35 +19,27 @@ import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfigurationKey -internal const val PluginId = "team.duckie.quackquack.sugar.processor" - -internal val KEY_SUGAR_PATH = CompilerConfigurationKey( - "Where the sugar components will be created - required", -) -internal val OPTION_SUGAR_PATH = CliOption( - optionName = "sugarPath", - valueDescription = "String", - description = KEY_SUGAR_PATH.toString(), - required = true, - allowMultipleOccurrences = false, -) - -internal val KEY_POET = CompilerConfigurationKey( - "Whether to enable sugar components generation - default is true", -) -internal val OPTION_POET = CliOption( - optionName = "poet", - valueDescription = "", - description = KEY_POET.toString(), - required = false, - allowMultipleOccurrences = false, -) +@VisibleForTesting +const val PluginId = "team.duckie.quackquack.sugar.core" + +internal val KEY_SUGAR_PATH = + CompilerConfigurationKey("Where the sugar components will be created - required") + +@VisibleForTesting +val OPTION_SUGAR_PATH = + CliOption( + optionName = "sugarPath", + valueDescription = "String", + description = KEY_SUGAR_PATH.toString(), + required = true, + allowMultipleOccurrences = false, + ) @AutoService(CommandLineProcessor::class) -class SugarCommandLineProcessor : CommandLineProcessor { +class SugarCoreCommandLineProcessor : CommandLineProcessor { override val pluginId = PluginId - override val pluginOptions = listOf(OPTION_SUGAR_PATH, OPTION_POET) + override val pluginOptions = listOf(OPTION_SUGAR_PATH) override fun processOption( option: AbstractCliOption, @@ -54,7 +48,6 @@ class SugarCommandLineProcessor : CommandLineProcessor { ) { when (val optionName = option.optionName) { OPTION_SUGAR_PATH.optionName -> configuration.put(KEY_SUGAR_PATH, value) - OPTION_POET.optionName -> configuration.put(KEY_POET, value) else -> error("Unknown plugin option: $optionName") } } diff --git a/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreExtension.kt b/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreExtension.kt new file mode 100644 index 000000000..e9cfafb6b --- /dev/null +++ b/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreExtension.kt @@ -0,0 +1,39 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +package team.duckie.quackquack.sugar.core + +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import team.duckie.quackquack.sugar.codegen.generateSugarComponentFiles +import team.duckie.quackquack.sugar.node.SugarComponentNode +import team.duckie.quackquack.sugar.visitor.SugarCoreVisitor +import team.duckie.quackquack.util.backend.kotlinc.Logger + +internal class SugarCoreExtension( + private val logger: Logger, + private val sugarPath: String, +) : IrGenerationExtension { + override fun generate( + moduleFragment: IrModuleFragment, + pluginContext: IrPluginContext, + ) { + val nodes = mutableListOf() + val visitor = SugarCoreVisitor( + context = pluginContext, + logger = logger, + addSugarComponentNode = nodes::add, + ) + + moduleFragment.accept(visitor, null) + generateSugarComponentFiles( + sugarComponentNodes = nodes, + sugarPath = sugarPath, + ) + } +} diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarComponentRegistrar.kt b/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreRegistrar.kt similarity index 79% rename from sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarComponentRegistrar.kt rename to sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreRegistrar.kt index d4eff1121..0d548bf7f 100644 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/SugarComponentRegistrar.kt +++ b/sugar-core/src/main/kotlin/team/duckie/quackquack/sugar/core/SugarCoreRegistrar.kt @@ -5,10 +5,10 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -@file:Suppress("DEPRECATION") +@file:Suppress("DEPRECATION", "unused", "UnstableApiUsage") @file:OptIn(ExperimentalCompilerApi::class) -package team.duckie.quackquack.sugar.processor +package team.duckie.quackquack.sugar.core import com.google.auto.service.AutoService import org.jetbrains.annotations.TestOnly @@ -19,8 +19,7 @@ import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi import org.jetbrains.kotlin.config.CompilerConfiguration -import team.duckie.quackquack.sugar.processor.ir.SugarIrExtension -import team.duckie.quackquack.sugar.processor.ir.SugarIrVisitor +import team.duckie.quackquack.sugar.visitor.SugarCoreVisitor import team.duckie.quackquack.util.backend.kotlinc.getLogger /** @@ -28,13 +27,13 @@ import team.duckie.quackquack.util.backend.kotlinc.getLogger * * Compose Compiler의 [`Default Arguments Transform`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt;l=341-365)에 * 의해 모든 컴포저블 함수에서 default argument의 값이 null로 변경됩니다. 하지만 sugar component를 - * 생성하기 위해선 default value의 값을 보존해야 합니다. 이를 위해 [SugarIrVisitor]가 Compose Compiler + * 생성하기 위해선 default value의 값을 보존해야 합니다. 이를 위해 [SugarCoreVisitor]가 Compose Compiler * 보다 먼저 적용될 수 있도록 Compiler Plugin의 적용 순서를 조정할 수 있는 deprecated된 [registerProjectComponents] * 메서드를 사용합니다. deprecated 되지 않은 [CompilerPluginRegistrar]를 사용하면 Compiler Plugin의 적용 * 순서를 조정할 수 없습니다. */ @AutoService(ComponentRegistrar::class) -class SugarComponentRegistrar : ComponentRegistrar { +class SugarCoreRegistrar : ComponentRegistrar { override val supportsK2 = false override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { @@ -43,15 +42,15 @@ class SugarComponentRegistrar : ComponentRegistrar { .registerExtension(configuration.getSugarIrExtension(), LoadingOrder.FIRST, project) } - internal companion object { + companion object { /** * [ComponentRegistrar]의 complie test는 DeprecatedError 상태로 항상 테스트에 실패합니다. - * 이를 해결하기 위해 [SugarComponentRegistrar]의 [CompilerPluginRegistrar] 버전을 제공합니다. + * 이를 해결하기 위해 [SugarCoreRegistrar]의 [CompilerPluginRegistrar] 버전을 제공합니다. * 이 함수는 오직 테스트 코드에서만 사용돼야 합니다. (테스트 환경에서는 Compose Compiler가 * 적용되지 않으니 유효합니다.) */ @TestOnly - internal fun asPluginRegistrar() = object : CompilerPluginRegistrar() { + fun asPluginRegistrar() = object : CompilerPluginRegistrar() { override val supportsK2 = false override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { @@ -59,14 +58,12 @@ class SugarComponentRegistrar : ComponentRegistrar { } } - private fun CompilerConfiguration.getSugarIrExtension(): SugarIrExtension { + private fun CompilerConfiguration.getSugarIrExtension(): SugarCoreExtension { val sugarPath = requireNotNull(this[KEY_SUGAR_PATH]) { "sugarPath was missing." } - val poet = this[KEY_POET]?.toBooleanStrict() ?: true - return SugarIrExtension( - logger = getLogger("sugar-processor"), + return SugarCoreExtension( + logger = getLogger("sugar-core"), sugarPath = sugarPath, - poet = poet, ) } } diff --git a/sugar-core/visitor/build.gradle.kts b/sugar-core/visitor/build.gradle.kts new file mode 100644 index 000000000..9139ef276 --- /dev/null +++ b/sugar-core/visitor/build.gradle.kts @@ -0,0 +1,22 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("jvm-kotlin") + quackquack("quack-publishing") +} + +dependencies { + implementations( + libs.kotlin.embeddable.compiler, + projects.sugarMaterial.orArtifact(), + projects.sugarCore.error.orArtifact(), + projects.sugarCore.names.orArtifact(), + projects.sugarCore.node.orArtifact(), + projects.utilBackendKotlinc.orArtifact(), + ) +} diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrVisitor.kt b/sugar-core/visitor/src/main/kotlin/team/duckie/quackquack/sugar/visitor/SugarCoreVisitor.kt similarity index 56% rename from sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrVisitor.kt rename to sugar-core/visitor/src/main/kotlin/team/duckie/quackquack/sugar/visitor/SugarCoreVisitor.kt index f861155bf..33cdc12b2 100644 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrVisitor.kt +++ b/sugar-core/visitor/src/main/kotlin/team/duckie/quackquack/sugar/visitor/SugarCoreVisitor.kt @@ -7,7 +7,7 @@ @file:OptIn(UnsafeCastFunction::class) -package team.duckie.quackquack.sugar.processor.ir +package team.duckie.quackquack.sugar.visitor import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.jvm.ir.psiElement @@ -37,14 +37,28 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction import org.jetbrains.kotlin.utils.addToStdlib.cast +import team.duckie.quackquack.sugar.error.SourceError +import team.duckie.quackquack.sugar.names.CasaValueFqn +import team.duckie.quackquack.sugar.names.ComposableFqn +import team.duckie.quackquack.sugar.names.ImportsFqn +import team.duckie.quackquack.sugar.names.QuackComponentPrefix +import team.duckie.quackquack.sugar.names.RequiresOptInFqn +import team.duckie.quackquack.sugar.names.SugarDefaultName +import team.duckie.quackquack.sugar.names.SugarGeneratedFileFqn +import team.duckie.quackquack.sugar.names.SugarNameFqn +import team.duckie.quackquack.sugar.names.SugarTokenFqn +import team.duckie.quackquack.sugar.names.SugarTokenName +import team.duckie.quackquack.sugar.names.SugarableFqn +import team.duckie.quackquack.sugar.node.SugarComponentNode +import team.duckie.quackquack.sugar.node.SugarParameter import team.duckie.quackquack.util.backend.kotlinc.Logger import team.duckie.quackquack.util.backend.kotlinc.isQuackComponent import team.duckie.quackquack.util.backend.kotlinc.locationOf -internal class SugarIrVisitor( +class SugarCoreVisitor( @Suppress("unused") private val context: IrPluginContext, private val logger: Logger, - private val addSugarIrData: (data: SugarIrData) -> Unit, + private val addSugarComponentNode: (data: SugarComponentNode) -> Unit, ) : IrElementVisitorVoid { override fun visitModuleFragment(declaration: IrModuleFragment) { declaration.files.forEach { file -> @@ -60,57 +74,61 @@ internal class SugarIrVisitor( } override fun visitSimpleFunction(declaration: IrSimpleFunction) { - if (declaration.isQuackComponent) { + if (declaration.isQuackComponent && declaration.hasAnnotation(SugarableFqn)) { val componentLocation = declaration.file.locationOf(declaration) - val componentFqn = declaration.fqNameWhenAvailable ?: logger.throwError( - message = SourceError.quackComponentFqnUnavailable(declaration.name.asString()), - location = componentLocation, - ) - - if (declaration.hasAnnotation(NoSugarFqn)) return + val componentFqn = + declaration.fqNameWhenAvailable + ?: logger.throwError( + message = SourceError.quackComponentFqnUnavailable(declaration.name.asString()), + location = componentLocation, + ) val sugarNameAnnotation = declaration.getAnnotation(SugarNameFqn) val sugarName = sugarNameAnnotation?.getSugarNameIfNotDefault(owner = declaration) var sugarToken: IrValueParameter? = null - val sugarParameters = declaration.valueParameters.map { parameter -> - val isSugarToken = parameter.hasAnnotation(SugarTokenFqn) - if (isSugarToken) { - check(sugarToken == null) { - SourceError.multipleSugarTokenIsNotAllowed(declaration.name.asString()) + val sugarParameters = + declaration.valueParameters.map { parameter -> + val isSugarToken = parameter.hasAnnotation(SugarTokenFqn) + if (isSugarToken) { + check(sugarToken == null) { + SourceError.multipleSugarTokenIsNotAllowed(declaration.name.asString()) + } + sugarToken = parameter } - sugarToken = parameter + parameter.toSugarParameter(owner = declaration, isToken = isSugarToken) } - parameter.toSugarParameter(owner = declaration, isToken = isSugarToken) - } - sugarToken ?: logger.throwError( - message = SourceError.quackComponentWithoutSugarToken(componentFqn.asString()), - location = componentLocation, - ) - val optins = declaration.annotations.filter { annotation -> - annotation.symbol.owner.parentAsClass.hasAnnotation(RequiresOptInFqn) - } + sugarToken + ?: logger.throwError( + message = SourceError.quackComponentWithoutSugarToken(componentFqn.asString()), + location = componentLocation, + ) - val sugarIrData = SugarIrData( - owner = declaration, - referFqn = componentFqn, - kdocGetter = { usedTokenLiteral -> - declaration.getSugarKDoc( - referFqn = componentFqn, - tokenName = sugarToken!!.name.asString(), - usedTokenLiteral = usedTokenLiteral, - ) - }, - sugarName = sugarName, - sugarToken = sugarToken!!, - tokenFqExpressions = sugarToken!!.getAllTokenFqExpressions(), - parameters = sugarParameters, - optins = optins, - ) - - // logger(with(logger) { sugarIrData.prependLogPrefix(withNewline = true) }) - addSugarIrData(sugarIrData) + val optins = + declaration.annotations.filter { annotation -> + annotation.symbol.owner.parentAsClass.hasAnnotation(RequiresOptInFqn) + } + + val sugarComponentNode = + SugarComponentNode( + owner = declaration, + referFqn = componentFqn, + kdocGetter = { usedTokenLiteral -> + declaration.getSugareKDoc( + referFqn = componentFqn, + tokenName = sugarToken!!.name.asString(), + usedTokenLiteral = usedTokenLiteral, + ) + }, + sugarName = sugarName, + sugarToken = sugarToken!!, + tokenFqExpressions = sugarToken!!.getAllTokenFqExpressions(), + parameters = sugarParameters, + optins = optins, + ) + + addSugarComponentNode(sugarComponentNode) } } } @@ -145,11 +163,13 @@ private fun IrValueParameter.toSugarParameter(owner: IrFunction, isToken: Boolea val sugarImports = getAnnotation(ImportsFqn)?.let { sugarImportsAnnotation -> // Assuming the first argument is always "clazz" val sugarImportsExpression = sugarImportsAnnotation.getValueArgument(0) - sugarImportsExpression.cast().elements.map { element -> - element.cast().classType.classFqName ?: error( - SourceError.importClazzFqnUnavailable(element.cast().type.asString()), - ) - } + sugarImportsExpression + .cast() + .elements + .map { element -> + element.cast().classType.classFqName + ?: error(SourceError.importClazzFqnUnavailable(element.cast().type.asString())) + } } val isComposable = hasAnnotation(ComposableFqn) @@ -165,34 +185,41 @@ private fun IrValueParameter.toSugarParameter(owner: IrFunction, isToken: Boolea ) } -// TODO(1): util-backend-kotlinc로 KDoc 추출 공통 로직 분리 -// TODO: default section은 없고 KDoc Tag만 있는 경우는 로직이 어떻게? -// TODO: subject의 fqn을 가져올 수는 없을까? -private fun IrSimpleFunction.getSugarKDoc( +private fun IrSimpleFunction.getSugareKDoc( referFqn: FqName, tokenName: String, usedTokenLiteral: String, ): String { val usedTokenComment = "This component uses [$usedTokenLiteral] as the token value for `$tokenName`." - val generatedDocComment = "This document was automatically generated by [${referFqn.shortName().asString()}].\n" + - "If any contents are broken, please check the original document." + val generatedDocComment = + "This document was automatically generated by [${referFqn.shortName().asString()}].\n" + + "If any contents are broken or wanna see the entire contents, please check the original document." val kdocArea = psiElement?.children?.firstOrNull { it is KDoc } as? KDoc - val kdocDefaultSection = kdocArea?.getDefaultSection() ?: return "" - - val kdocTags = kdocArea.children - .firstOrNull { it is KDocSection } - ?.children - ?.filterIsInstance() - .orEmpty() + val kdocDefaultSection = kdocArea?.getDefaultSection() ?: "" + + val kdocTags = + kdocArea + ?.children + ?.firstOrNull { it is KDocSection } + ?.children + ?.filterIsInstance() + .orEmpty() + + val kdocFirstSentence = + (kdocDefaultSection as? KDocSection)?.getContent()?.trim()?.let { defaultContent -> + if (defaultContent.contains(".")) defaultContent.split(".").first() + "." + else defaultContent + } return buildString { - appendLine(kdocDefaultSection.getContent().trim()) - appendLine("\n$usedTokenComment\n\n$generatedDocComment\n") + append(kdocFirstSentence?.plus("\n\n").orEmpty()) + appendLine("$usedTokenComment\n\n$generatedDocComment\n") for (tag in kdocTags) { - val tagName = tag.name?.let { "@$it " }.orEmpty() var subjectName = tag.getSubjectName() if (subjectName == tokenName) continue + + val tagName = tag.name?.let { "@$it " }.orEmpty() subjectName = subjectName?.plus(" ").orEmpty() appendLine("${tagName}${subjectName}${tag.getContent()}") @@ -204,12 +231,14 @@ private fun IrValueParameter.getAllTokenFqExpressions(): List { val tokenClass = type.getClass()!! val tokenClassName = tokenClass.name.asString() return tokenClass.companionObject()?.let { companion -> - val tokenableProperties = companion.properties.filter { property -> - property.visibility.isPublicAPI - } - val propertyFqExpressions = tokenableProperties.map { property -> - "$tokenClassName.${property.name.asString()}" - } + val tokenableProperties = + companion.properties.filter { property -> + property.visibility.isPublicAPI + } + val propertyFqExpressions = + tokenableProperties.map { property -> + "$tokenClassName.${property.name.asString()}" + } propertyFqExpressions.toList() } ?: error(SourceError.sugarTokenButNoCompanionObject(tokenClassName)) } diff --git a/sugar-core/visitor/version.txt b/sugar-core/visitor/version.txt new file mode 100644 index 000000000..311be597a --- /dev/null +++ b/sugar-core/visitor/version.txt @@ -0,0 +1 @@ +2.0.0-alpha01 diff --git a/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/annotations.kt b/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/annotations.kt index f27b0318c..83836546f 100644 --- a/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/annotations.kt +++ b/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/annotations.kt @@ -11,8 +11,7 @@ import kotlin.reflect.KClass /** * sugar component의 이름을 직접 명시합니다. 만약 직접 명시하지 않으면 기본 정책에 - * 맞게 네이밍이 진행됩니다. sugar component의 기본 네이밍 정책은 `sugar-processor` - * 모듈을 참고하세요. + * 맞게 네이밍이 진행됩니다. sugar component의 기본 네이밍 정책은 꽥꽥 웹 문서를 참고하세요. * * sugar component의 이름을 직접 명시할 때는 다음과 같은 규칙이 보장돼야 합니다. * @@ -69,8 +68,8 @@ public annotation class SugarName(val name: String = DEFAULT_NAME) { } /** - * sugar component에 사용할 sugar token을 나타냅니다. sugar token은 다음과 같은 - * 규칙을 보장해야 합니다. + * sugar component에 사용할 sugar token을 나타냅니다. + * sugar token은 다음과 같은 규칙을 보장해야 합니다. * * 1. companion object가 있어야 합니다. * 2. sugar token의 값들은 companion object 안에 public variable로 정의돼야 합니다. @@ -190,11 +189,11 @@ public annotation class SugarToken * } * ``` * - * 이 정보는 `sugar-processor`에서 원래 함수의 IR 정보를 조회하기 위해 추가됩니다. - * 자세한 정보는 `sugar-processor` 모듈을 참고하세요. + * 이 정보는 sugar component 처리 과정에서 원래 함수의 IR 정보를 조회하기 위해 추가됩니다. + * 자세한 정보는 꽥꽥 웹 문서를 참고하세요. * - * *이 어노테이션은 꽥꽥 컴파일러에서만 사용될 목적으로 설계됐습니다. 임의로 사용할 - * 경우 예상치 못한 버그가 발생할 수 있습니다.* + * 이 어노테이션은 꽥꽥 컴파일러에서만 사용될 목적으로 설계됐습니다. + * 임의로 사용할 경우 예상치 못한 버그가 발생할 수 있습니다. */ @SugarCompilerApi @MustBeDocumented @@ -203,37 +202,21 @@ public annotation class SugarToken public annotation class SugarRefer(val fqn: String) /** - * 이 컴포넌트의 sugar 생성을 무시합니다. 예를 들어 다음과 같은 코드가 있습니다. - * - * ``` - * @JvmInline - * value class Theme(val index: Int) { - * companion object { - * val Default = Theme(1) - * } - * } - * - * @NoSugar - * @Composable - * fun QuackAwesome(@SugarToken theme: Theme) { - * QuackTheme(theme = theme) - * } - * ``` - * - * 이 컴포넌트는 [`@NoSugar`][NoSugar]의 영향으로 sugar component가 생성되지 않습니다. + * 이 컴포넌트의 sugar 생성을 활성화합니다. + * 이 어노테이션이 붙은 컴포넌트만 sugar component가 생성됩니다. */ @MustBeDocumented @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.BINARY) -public annotation class NoSugar +public annotation class Sugarable /** - * `sugar-processor`에 의해 생성된 sugar components 파일임을 나타냅니다. 이 어노테이션이 - * 부착된 파일만 `SugarIrTransform`이 진행됩니다. 자세한 정보는 `sugar-processor` 모듈을 - * 참고하세요. + * 자동 생성된 sugar component 파일임을 나타냅니다. + * 이 어노테이션이 붙은 파일만 sugar component 처리가 진행됩니다. + * 자세한 정보는 꽥꽥 웹 문서를 참고하세요. * - * *이 어노테이션은 꽥꽥 컴파일러에서만 사용될 목적으로 설계됐습니다. 임의로 사용할 - * 경우 예상치 못한 버그가 발생할 수 있습니다.* + * 이 어노테이션은 꽥꽥 컴파일러에서만 사용될 목적으로 설계됐습니다. + * 임의로 사용할 경우 예상치 못한 버그가 발생할 수 있습니다. */ @SugarCompilerApi @MustBeDocumented @@ -242,9 +225,8 @@ public annotation class NoSugar public annotation class SugarGeneratedFile /** - * `sugar-processor`가 생성하는 sugar component 코드에 추가로 import 돼야 하는 클래스를 - * 나타냅니다. 함수의 인자에 적용될 수 있으며, 인자의 타입과 인자의 기본 값 타입이 다를 - * 경우 사용할 수 있습니다. + * 자동 생성되는 sugar component 코드에 추가로 import 돼야 하는 클래스를 나타냅니다. + * 함수의 인자에 적용될 수 있으며, 인자의 타입과 인자의 기본 값 타입이 다를 경우 사용할 수 있습니다. * * ``` * // file: flaver.kt diff --git a/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/optin.kt b/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/optin.kt index fd3e6657c..dd7c2e4db 100644 --- a/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/optin.kt +++ b/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/optin.kt @@ -7,7 +7,6 @@ package team.duckie.quackquack.sugar.material -/** `sugar-processor` 모듈에서만 사용돼야 함을 나타내는 optin 어노테이션입니다. */ @MustBeDocumented @RequiresOptIn( message = "This indicates that the feature should only be used in the Sugar Compiler. " + diff --git a/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/typer.kt b/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/typer.kt index e6ca545d3..e39dc64ad 100644 --- a/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/typer.kt +++ b/sugar-material/src/main/kotlin/team/duckie/quackquack/sugar/material/typer.kt @@ -8,7 +8,7 @@ package team.duckie.quackquack.sugar.material /** - * `sugar-processor`로 sugar component를 생성할 때 함수의 default argument가 있는 인자에 + * sugar component를 생성할 때 함수의 default argument가 있는 인자에 * 기본값으로 사용됩니다. * * ``` @@ -52,15 +52,12 @@ package team.duckie.quackquack.sugar.material * } * ``` * - * 자세한 내용은 `sugar-processor` 모듈을 참고하세요. - * - * *이 어노테이션은 꽥꽥 컴파일러에서만 사용될 목적으로 설계됐습니다. 임의로 사용할 - * 경우 예상치 못한 버그가 발생할 수 있습니다.* + * 이 어노테이션은 꽥꽥 컴파일러에서만 사용될 목적으로 설계됐습니다. + * 임의로 사용할 경우 예상치 못한 버그가 발생할 수 있습니다. */ @SugarCompilerApi -public fun sugar(): T { +public fun sugar(): T = throw NotImplementedError( "SugarIrTransform did not proceed. " + "Is the sugar-processor compiler plugin applied?", ) -} diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrData.kt b/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrData.kt deleted file mode 100644 index b6af86264..000000000 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/SugarIrData.kt +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Designed and developed by Duckie Team 2023. - * - * Licensed under the MIT. - * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE - */ - -package team.duckie.quackquack.sugar.processor.ir - -import com.squareup.kotlinpoet.AnnotationSpec -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.LambdaTypeName -import com.squareup.kotlinpoet.ParameterSpec -import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType -import org.jetbrains.kotlin.ir.backend.js.utils.asString -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrValueParameter -import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType -import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.expressions.IrExpressionBody -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.isMarkedNullable -import org.jetbrains.kotlin.ir.util.dumpKotlinLike -import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable -import org.jetbrains.kotlin.ir.util.isFunction -import org.jetbrains.kotlin.ir.util.isSuspendFunction -import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.getClassFqNameUnsafe -import org.jetbrains.kotlin.utils.addToStdlib.applyIf -import team.duckie.quackquack.casa.annotation.CasaValue -import team.duckie.quackquack.sugar.material.Imports -import team.duckie.quackquack.sugar.material.SugarName -import team.duckie.quackquack.sugar.material.SugarToken -import team.duckie.quackquack.util.backend.kotlinc.unsafeClassName - -/** - * [SugarIrVisitor]에서 IR을 방문하면서 수집할 정보들을 관리합니다. - * - * @param owner IR이 제공된 함수 - * @param referFqn IR이 제공된 함수의 [fully-qualified name][FqName]. [owner]에서 직접 - * 가져오는 방식보다 안전한 방식으로 fqn이 제공됩니다. - * @param kdocGetter IR이 제공된 함수의 Sugared-KDoc을 계산하는 람다. 사용된 Sugar Token의 - * 리터럴을 람다 인자로 제공해야 합니다. - * @param sugarName 생성할 sugar component의 네이밍 규칙. [`@SugarToken`][SugarName] - * 값을 가져옵니다. - * @param sugarToken 생성할 sugar component의 Sugar Token에 해당하는 [인자][IrValueParameter]. - * [`@SugarToken`][SugarToken]이 달린 인자를 가져옵니다. - * @param tokenFqExpressions Sugar Token의 expression 모음. 예를 들면 다음과 같습니다. - * - * ``` - * package team.duckie.theme - * - * @JvmInline - * value class Theme(val index: Int) { - * companion object { - * val Default = Theme(1) - * val Dark = Theme(2) - * val Light = Theme(3) - * val System = Theme(4) - * } - * } - * - * // ["team.duckie.theme.Theme.Default", "team.duckie.theme.Theme.Dark", "team.duckie.theme.Theme.Light", "team.duckie.theme.Theme.System"] - * ``` - * - * @param parameters IR이 제공된 함수의 인자 모음. sugar component 생성에 필요한 정보만 수집합니다. - * 자세한 수집 정보는 [SugarParameter]를 확인하세요. - */ -internal data class SugarIrData( - val owner: IrFunction, - val referFqn: FqName, - val kdocGetter: (usedTokenLiteral: String) -> String, - val sugarName: String?, - val sugarToken: IrValueParameter, - val tokenFqExpressions: List, - val parameters: List, - val optins: List, -) { - /** [parameters]에서 Sugar Token을 제외한 [요소][SugarParameter]만 불러옵니다. */ - val parametersWithoutToken: List = parameters.toMutableList().apply { - removeIf(SugarParameter::isToken) - } - - override fun toString(): String { - return """ - |owner: ${owner.name.asString()} - |referFqn: ${referFqn.asString()} - |kdoc: ${kdocGetter("SugarToken")} - |sugarName: $sugarName - |sugarToken: ${sugarToken.name.asString()} - |tokenExpressions: $tokenFqExpressions - |parameters: ${parameters.joinToString("\n\n", prefix = "\n")} - |optins: ${optins.joinToString(transform = IrConstructorCall::toFqnStringOrEmpty)} - """.trimMargin() - } -} - -/** 주어진 어노테이션의 fqn을 조회하여 반환하고, 만약 조회에 실패했다면 공백을 반환합니다. */ -internal fun IrConstructorCall.toFqnStringOrEmpty() = - symbol.owner.parentAsClass.fqNameWhenAvailable?.asString().orEmpty() - -/** - * [IrValueParameter]에서 sugar component 생성에 필요한 정보를 관리합니다. - * - * @param name 인자의 이름 - * @param type 인자의 타입 - * @param isToken 인자가 [Sugar Token][SugarToken]인지 여부 - * @param isComposable 인자에 [`@Composable`][ComposableFqn] 어노테이션이 달려있는지 - * 여부 - * @param imports [type] 외에 추가로 import가 필요한 클래스의 [fully-qualified name][FqName]으로 - * 구성된 목록. 자세한 정보는 [`@Imports`][Imports] 어노테이션을 확인하세요. - * @param casaValueLiteral 만약 인자에 [`@CasaValue`][CasaValue] 어노테이션이 달려있다면 - * [CasaValue.literal]로 제공된 값 - * @param defaultValue 인자의 기본 값 - */ -internal data class SugarParameter( - val owner: IrFunction, - val name: Name, - val type: IrType, - val isToken: Boolean, - val isComposable: Boolean, - val imports: List, - val casaValueLiteral: String?, - val defaultValue: IrExpressionBody?, -) { - /** 제공된 정보를 [ParameterSpec]으로 변환합니다. */ - fun toParameterSpec(): ParameterSpec { - val parameterTypedBuilder = ParameterSpec - .builder( - name = name.asString(), - type = when { - type.isFunction() || type.isSuspendFunction() -> { - val funArguments = type.toIrBasedKotlinType().arguments - - /* - * maintainer notes: 모든 **함수형 타입**은 `Function`으로 처리되며 이는 `IrFunction`과는 - * 다른 유형임. 코틀린의 **함수 정의**는 `IrFunction`으로 해석되고, **함수형 타입**은 코틀린 - * 네이티브 타입인 `KotlinType`으로 해석됨. - * - * `Function`은 value parameter의 타입과 return의 타입을 generic으로 받는 인터페이스임. - * 즉, `Function`은 메타데이터가 없어서 컴파일 시점에서는 인자의 타입만 조회 가능함. - * 따라서 람다의 인자명 정책으로 `P{$index}`를 사용함. - * - * receiver extension은 **첫 번째** value parameter로 치환됨. - * > `String.() -> Unit` == `(String) -> Unit` - * - * return type은 **마지막** value parameter로 치환됨. - */ - val referLambdaParameters = - if (funArguments.size >= 2) { - funArguments.dropLast(1).mapIndexed { index, argument -> - require(!argument.type.isFunctionOrSuspendFunctionType) { - NotSupportedError.nestedFunctionalType("${owner.name.asString()}#${name.asString()}") - } - - val argumentTypeFqn = argument.type.constructor.getClassFqNameUnsafe() - val argumentTypeCn = ClassName.bestGuess(argumentTypeFqn.toString()) - - ParameterSpec - .builder( - name = "P$index", - type = argumentTypeCn, - ) - .build() - } - } else { - emptyList() - } - val referLambdaReturnTypeFqn = funArguments.last().type.constructor.getClassFqNameUnsafe() - val referLambdaReturnTypeCn = ClassName.bestGuess(referLambdaReturnTypeFqn.toString()) - - LambdaTypeName - .get( - parameters = referLambdaParameters, - returnType = referLambdaReturnTypeCn, - ) - .copy(suspending = type.isSuspendFunction()) - } - else -> { - type.unsafeClassName - } - }.copy(nullable = type.isMarkedNullable()), - ) - - return parameterTypedBuilder - .applyIf(casaValueLiteral != null) { - addAnnotation( - AnnotationSpec - .builder(CasaValueCn) - .addMember("%S", casaValueLiteral!!) - .build(), - ) - } - .applyIf(isComposable) { - addAnnotation( - AnnotationSpec - .builder(ComposableCn) - .build(), - ) - } - .applyIf(defaultValue != null) { - defaultValue("%L()", "sugar") - } - .build() - } - - override fun toString(): String { - return """ - |owner: ${owner.name.asString()} - |name: ${name.asString()} - |type: ${type.asString()} - |isToken: $isToken - |isComposable: $isComposable - |imports: ${imports.joinToString(transform = FqName::asString)} - |casaValueLiteral: $casaValueLiteral - |defaultValue: ${defaultValue?.dumpKotlinLike()} - """.trimMargin() - } -} diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/errors.kt b/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/errors.kt deleted file mode 100644 index cb07817d0..000000000 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/errors.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Designed and developed by Duckie Team 2023. - * - * Licensed under the MIT. - * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE - */ - -package team.duckie.quackquack.sugar.processor.ir - -internal object NotSupportedError { - internal fun nestedFunctionalType(name: String?): String { - return "Nested functional types are not currently supported due to implementation complexity." + - " ($name)".getIfGivenIsNotNull(name) - } -} - -internal object SourceError { - internal fun quackComponentFqnUnavailable(name: String?): String { - return "A Quack component was detected, but unable to look up a fully qualified name. " + - "Is it an anonymous object?" + " ($name)".getIfGivenIsNotNull(name) - } - - internal fun importClazzFqnUnavailable(name: String?): String { - return "Can't look up the fully qualified name of the class given as `clazz` in `@Imports`. " + - "Is it an anonymous class?" + " ($name)".getIfGivenIsNotNull(name) - } - - internal fun quackComponentWithoutSugarToken(name: String?): String { - return "A Quack component was detected, but no SugarToken was applied." + - " ($name)".getIfGivenIsNotNull(name) - } - - internal fun multipleSugarTokenIsNotAllowed(name: String?): String { - return "A Sugar component can only contain one SugarToken." + - " ($name)".getIfGivenIsNotNull(name) - } - - internal fun sugarNamePrefixIsNotQuack(name: String?): String { - return "Quack component names must start with `SugarName.PREFIX_NAME" + - " (= $QuackComponentPrefix)`." + " ($name)".getIfGivenIsNotNull(name) - } - - internal fun sugarNameWithoutTokenName(name: String?): String { - return "When specifying the sugar component name directly, " + - "`SugarName.TOKEN_NAME (= $SugarTokenName)` must be used." + - " ($name)".getIfGivenIsNotNull(name) - } - - internal fun sugarTokenButNoCompanionObject(name: String?): String { - return "The SugarToken class must include a companion object. " + - "See the sugar component creation policy for more information." + - " ($name)".getIfGivenIsNotNull(name) - } -} - -internal object PoetError { - internal fun sugarComponentButNoSugarRefer(name: String?): String { - return "The SugarRefer for the Sugar component is missing." + - " ($name)".getIfGivenIsNotNull(name) - } -} - -internal object SugarVisitError { - internal fun noMatchedSugarIrData(name: String?): String { - return "No SugarIrData was found for the given SugarRefer. " + - "Please report it in a GitHub Issue. (https://link.duckie.team/quackquack-bug)" + - " ($name)".getIfGivenIsNotNull(name) - } -} - -internal object SugarTransformError { - internal fun sugarComponentAndSugarReferHasDifferentParameters(name: String?): String { - return "The Sugar component has a parameter that doesn't exist in the SugarRefer." + - " ($name)".getIfGivenIsNotNull(name) - } -} - -private fun String.getIfGivenIsNotNull(given: Any?) = - if (given == null) "" else this diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/names.kt b/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/names.kt deleted file mode 100644 index acc595bcb..000000000 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/ir/names.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Designed and developed by Duckie Team 2023. - * - * Licensed under the MIT. - * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE - */ - -@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) -@file:Suppress("OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN") - -package team.duckie.quackquack.sugar.processor.ir - -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.asClassName -import org.jetbrains.kotlin.name.FqName -import team.duckie.quackquack.casa.annotation.Casa -import team.duckie.quackquack.casa.annotation.CasaValue -import team.duckie.quackquack.casa.annotation.SugarGeneratorUsage -import team.duckie.quackquack.sugar.material.Imports -import team.duckie.quackquack.sugar.material.NoSugar -import team.duckie.quackquack.sugar.material.SugarCompilerApi -import team.duckie.quackquack.sugar.material.SugarGeneratedFile -import team.duckie.quackquack.sugar.material.SugarName -import team.duckie.quackquack.sugar.material.SugarRefer -import team.duckie.quackquack.sugar.material.SugarToken - -@Suppress("OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION") -internal val RequiresOptInFqn = RequiresOptIn::class.qualifiedName!!.toFqnClass() - -internal val ComposableFqn = "androidx.compose.runtime.Composable".toFqnClass() -internal val ComposableCn = ClassName.bestGuess(ComposableFqn.asString()) -internal val NonRestartableComposableCn = ClassName.bestGuess("androidx.compose.runtime.NonRestartableComposable") - -internal const val QuackComponentPrefix = SugarName.PREFIX_NAME - -internal const val SugarDefaultName = SugarName.DEFAULT_NAME -internal const val SugarTokenName = SugarName.TOKEN_NAME - -internal val CasaCn = ClassName.bestGuess(Casa::class.qualifiedName!!) -internal val CasaValueCn = CasaValue::class.asClassName() -internal val CasaValueFqn = CasaValue::class.qualifiedName!!.toFqnClass() - -internal val SugarCompilerApiCn = SugarCompilerApi::class.asClassName() -internal val SugarGeneratorUsageCn = SugarGeneratorUsage::class.asClassName() -internal val SugarGeneratedFileCn = SugarGeneratedFile::class.asClassName() -internal val SugarGeneratedFileFqn = SugarGeneratedFile::class.qualifiedName!!.toFqnClass() - -// sugar 함수에 리플렉션으로 접근 불가 -internal val SugarFqn = "team.duckie.quackquack.sugar.material.sugar".toFqnClass() -internal val SugarNameFqn = SugarName::class.qualifiedName!!.toFqnClass() -internal val SugarTokenFqn = SugarToken::class.qualifiedName!!.toFqnClass() -internal val SugarReferCn = SugarRefer::class.asClassName() -internal val SugarReferFqn = SugarRefer::class.qualifiedName!!.toFqnClass() - -internal val ImportsFqn = Imports::class.qualifiedName!!.toFqnClass() -internal val NoSugarFqn = NoSugar::class.qualifiedName!!.toFqnClass() - -private fun String.toFqnClass() = FqName(this) diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/PoetUtils.kt b/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/PoetUtils.kt deleted file mode 100644 index 1aaf611f9..000000000 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/PoetUtils.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Designed and developed by Duckie Team 2023. - * - * Licensed under the MIT. - * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE - */ - -package team.duckie.quackquack.sugar.processor.poet - -import team.duckie.quackquack.sugar.processor.ir.QuackComponentPrefix -import team.duckie.quackquack.sugar.processor.ir.SugarIrData -import team.duckie.quackquack.sugar.processor.ir.SugarTokenName - -// TODO: Testing -internal fun SugarIrData.toSugarComponentName(tokenFqExpression: String): String { - val tokenExpression = tokenFqExpression - .substringAfterLast(".") - .replaceFirstChar(Char::titlecase) - return sugarName?.replace(SugarTokenName, tokenExpression) - ?: referFqn - .shortName() - .asString() - .toMutableList() - .apply { addAll(QuackComponentPrefix.length, tokenExpression.toList()) } - .joinToString("") -} diff --git a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/poet.kt b/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/poet.kt deleted file mode 100644 index e826fa259..000000000 --- a/sugar-processor/src/main/kotlin/team/duckie/quackquack/sugar/processor/poet/poet.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Designed and developed by Duckie Team 2023. - * - * Licensed under the MIT. - * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE - */ - -package team.duckie.quackquack.sugar.processor.poet - -import com.squareup.kotlinpoet.AnnotationSpec -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.FileSpec -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.buildCodeBlock -import com.squareup.kotlinpoet.withIndent -import java.io.File -import org.jetbrains.kotlin.ir.declarations.name -import org.jetbrains.kotlin.ir.types.classFqName -import org.jetbrains.kotlin.ir.util.file -import org.jetbrains.kotlin.name.FqName -import team.duckie.quackquack.sugar.processor.ir.CasaCn -import team.duckie.quackquack.sugar.processor.ir.ComposableCn -import team.duckie.quackquack.sugar.processor.ir.NonRestartableComposableCn -import team.duckie.quackquack.sugar.processor.ir.SugarCompilerApiCn -import team.duckie.quackquack.sugar.processor.ir.SugarFqn -import team.duckie.quackquack.sugar.processor.ir.SugarGeneratedFileCn -import team.duckie.quackquack.sugar.processor.ir.SugarGeneratorUsageCn -import team.duckie.quackquack.sugar.processor.ir.SugarIrData -import team.duckie.quackquack.sugar.processor.ir.SugarParameter -import team.duckie.quackquack.sugar.processor.ir.SugarReferCn -import team.duckie.quackquack.sugar.processor.ir.toFqnStringOrEmpty -import team.duckie.quackquack.util.backend.FormatterOffComment -import team.duckie.quackquack.util.backend.SuppressAnnotation -import team.duckie.quackquack.util.backend.addAnnotations -import team.duckie.quackquack.util.backend.addFunctions -import team.duckie.quackquack.util.backend.bestGuessToKotlinPackageName -import team.duckie.quackquack.util.backend.getGeneratedFileComment -import team.duckie.quackquack.util.backend.kotlinc.addImports - -private val GeneratedComment = getGeneratedFileComment("sugar-processor") - -@Suppress("OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION") -private val SugarCompilerOptInAnnotation = AnnotationSpec - .builder(OptIn::class) - .addMember( - "%T::class, %T::class", - SugarCompilerApiCn, - SugarGeneratorUsageCn, - ) - .useSiteTarget(AnnotationSpec.UseSiteTarget.FILE) - .build() - -private val SugarGeneratedFileMarkerAnnotation = AnnotationSpec - .builder(SugarGeneratedFileCn) - .useSiteTarget(AnnotationSpec.UseSiteTarget.FILE) - .build() - -internal fun generateSugarComponentFiles(irDatas: List, sugarPath: String) { - val fileGroupedIrDatas = irDatas.groupBy { irData -> - irData.owner.file.name - } - - fileGroupedIrDatas.forEach { (fileName, irDatas) -> - val (imports, funSpecs) = irDatas.toFunSpecsWithImports() - val ktSpec = FileSpec - .builder( - packageName = sugarPath.bestGuessToKotlinPackageName(), - fileName = fileName.substringBeforeLast("."), - ) - .addFileComment(GeneratedComment) - .addFileComment(FormatterOffComment) - .addAnnotations( - SuppressAnnotation, - SugarCompilerOptInAnnotation, - SugarGeneratedFileMarkerAnnotation, - ) - .addImports(imports.toMutableList().apply { add(SugarFqn) }) - .addFunctions(funSpecs) - .build() - - File(sugarPath, fileName).also { file -> - if (!file.exists()) { - file.parentFile?.mkdirs() - file.createNewFile() - } - }.writeText(ktSpec.toString()) - } -} - -private fun List.toFunSpecsWithImports(): Pair, List> { - val imports = mutableListOf() - val funSpecs = mutableListOf() - forEach { sugarIrData -> - imports += sugarIrData.referFqn - sugarIrData.tokenFqExpressions.forEach { tokenFqExpression -> - val (_imports, funSpec) = sugarIrData.toFunSpecWithImports(tokenFqExpression) - imports += _imports - funSpecs += funSpec - } - } - return imports to funSpecs -} - -private fun SugarIrData.toFunSpecWithImports(tokenFqExpression: String): Pair, FunSpec> { - val imports = mutableListOf() - - val sugarReferAnnotation = AnnotationSpec - .builder(SugarReferCn) - .addMember("%S", referFqn.asString()) - .build() - - val sugarName = toSugarComponentName(tokenFqExpression) - val sugarBody = buildCodeBlock { - addStatement("%L(", referFqn.shortName().asString()) - withIndent { - parameters.forEach { parameter -> - imports += parameter.type.classFqName!! - imports += parameter.imports - - val parameterName = parameter.name.asString() - val parameterValue = if (parameter.isToken) tokenFqExpression else parameterName - - addStatement("%L = %L,", parameterName, parameterValue) - } - } - addStatement(")") - } - - val optinCns = optins.map { irOptin -> - ClassName.bestGuess(irOptin.toFqnStringOrEmpty()) - } - - val funSpec = FunSpec - .builder(sugarName) - .addAnnotations( - CasaCn, - ComposableCn, - NonRestartableComposableCn, - *optinCns.toTypedArray(), - ) - .addAnnotation(sugarReferAnnotation) - .addModifiers(KModifier.PUBLIC) - .addParameters(parametersWithoutToken.map(SugarParameter::toParameterSpec)) - .addCode(sugarBody) - .addKdoc(kdocGetter(tokenFqExpression)) - .build() - - return imports to funSpec -} diff --git a/sugar-processor/version.txt b/sugar-processor/version.txt deleted file mode 100644 index 3bf51efdd..000000000 --- a/sugar-processor/version.txt +++ /dev/null @@ -1 +0,0 @@ -2.0.0-alpha02 diff --git a/sugar-test/build.gradle.kts b/sugar-test/build.gradle.kts new file mode 100644 index 000000000..fe9c598a1 --- /dev/null +++ b/sugar-test/build.gradle.kts @@ -0,0 +1,23 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("jvm-kotlin") + quackquack("test-kotest") +} + +dependencies { + testImplementations( + libs.test.kotlin.compilation.core, + projects.sugarCompiler, + projects.sugarCore, + projects.sugarCore.error, + projects.sugarCore.visitor, + projects.sugarCore.codegen, + projects.utilBackendTest, + ) +} diff --git a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrErrorTest.kt b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerErrorTest.kt similarity index 59% rename from sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrErrorTest.kt rename to sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerErrorTest.kt index f9b4723d5..a4223ffa0 100644 --- a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrErrorTest.kt +++ b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerErrorTest.kt @@ -6,21 +6,10 @@ */ @file:OptIn(ExperimentalCompilerApi::class) -@file:Suppress( - "RedundantUnitReturnType", - "RedundantVisibilityModifier", - "RedundantUnitExpression", - "RedundantSuppression", - "LongMethod", - "HasPlatformType", - "KDocUnresolvedReference", -) -package team.duckie.quackquack.sugar.processor +package team.duckie.quackquack.sugar.test import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.PluginOption -import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import io.kotest.core.spec.style.ExpectSpec import io.kotest.core.test.Enabled @@ -28,18 +17,22 @@ import io.kotest.engine.spec.tempdir import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.jetbrains.kotlin.config.JvmTarget -import team.duckie.quackquack.sugar.processor.ir.NotSupportedError.nestedFunctionalType -import team.duckie.quackquack.sugar.processor.ir.PoetError.sugarComponentButNoSugarRefer -import team.duckie.quackquack.sugar.processor.ir.SourceError.multipleSugarTokenIsNotAllowed -import team.duckie.quackquack.sugar.processor.ir.SourceError.quackComponentWithoutSugarToken -import team.duckie.quackquack.sugar.processor.ir.SourceError.sugarNamePrefixIsNotQuack -import team.duckie.quackquack.sugar.processor.ir.SourceError.sugarNameWithoutTokenName -import team.duckie.quackquack.sugar.processor.ir.SourceError.sugarTokenButNoCompanionObject -import team.duckie.quackquack.sugar.processor.ir.SugarTransformError.sugarComponentAndSugarReferHasDifferentParameters - -class SugarIrErrorTest : ExpectSpec() { - private val tempDir = tempdir() +import team.duckie.quackquack.sugar.compiler.SugarCompilerRegistrar +import team.duckie.quackquack.sugar.error.NotSupportedError.nestedFunctionalType +import team.duckie.quackquack.sugar.error.SourceError.multipleSugarTokenIsNotAllowed +import team.duckie.quackquack.sugar.error.SourceError.quackComponentWithoutSugarToken +import team.duckie.quackquack.sugar.error.SourceError.sugarNamePrefixIsNotQuack +import team.duckie.quackquack.sugar.error.SourceError.sugarNameWithoutTokenName +import team.duckie.quackquack.sugar.error.SourceError.sugarTokenButNoCompanionObject +import team.duckie.quackquack.sugar.error.SugarTransformError.sugarComponentAndSugarReferHasDifferentParameters + +class SugarCompilerErrorTest : ExpectSpec() { + private val testCompilation = + TestCompilation(tempdir()).apply { + prepareSetting { + compilerPluginRegistrars = listOf(SugarCompilerRegistrar.asPluginRegistrar()) + } + } init { context("NotSupportedError") { @@ -51,13 +44,15 @@ class SugarIrErrorTest : ExpectSpec() { ) }, ) { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable import androidx.compose.runtime.Composable +@Sugarable @Composable fun QuackText( @SugarToken style: AwesomeType, @@ -74,12 +69,14 @@ fun QuackText( context("SourceError") { expect("quackComponentWithoutSugarToken") { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ import androidx.compose.runtime.Composable +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @Composable fun QuackText() {} """, @@ -90,36 +87,20 @@ fun QuackText() {} result.messages shouldContain quackComponentWithoutSugarToken("QuackText") } - expect("quackComponentWithoutSugarToken - @NoSugar applied") { - val result = compile( - kotlin( - "main.kt", - """ -import team.duckie.quackquack.sugar.material.NoSugar -import androidx.compose.runtime.Composable - -@NoSugar -@Composable -fun QuackText() {} - """, - ), - ) - - result.exitCode shouldBe KotlinCompilation.ExitCode.OK - } - expect("multipleSugarTokenIsNotAllowed") { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ -import team.duckie.quackquack.sugar.material.SugarToken import androidx.compose.runtime.Composable +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @Composable fun QuackText( - @SugarToken style: AwesomeType, - @SugarToken style2: AwesomeType2, + @SugarToken style: AwesomeType, + @SugarToken style2: AwesomeType2, ) {} """, ), @@ -130,14 +111,16 @@ fun QuackText( } expect("sugarNamePrefixIsNotQuack") { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ import androidx.compose.runtime.Composable import team.duckie.quackquack.sugar.material.SugarName import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @SugarName("Text") @Composable fun QuackText(@SugarToken type: AwesomeType) {} @@ -150,14 +133,16 @@ fun QuackText(@SugarToken type: AwesomeType) {} } expect("sugarNameWithoutTokenName") { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ import androidx.compose.runtime.Composable import team.duckie.quackquack.sugar.material.SugarName import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @SugarName("QuackText") @Composable fun QuackText(@SugarToken type: AwesomeType) {} @@ -170,13 +155,15 @@ fun QuackText(@SugarToken type: AwesomeType) {} } expect("sugarTokenButNoCompanionObject") { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ import androidx.compose.runtime.Composable import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @Composable fun QuackText(@SugarToken type: AwesomeType3) {} """, @@ -188,39 +175,17 @@ fun QuackText(@SugarToken type: AwesomeType3) {} } } - context("PoetError") { - expect("sugarComponentButNoSugarRefer") { - val result = compile( - kotlin( - "main.kt", - """ -@file:OptIn(SugarCompilerApi::class) -@file:SugarGeneratedFile - -import androidx.compose.runtime.Composable -import team.duckie.quackquack.sugar.material.SugarCompilerApi -import team.duckie.quackquack.sugar.material.SugarGeneratedFile - -@Composable -fun QuackOneText() {} - """, - ), - ) - - result.exitCode shouldBe KotlinCompilation.ExitCode.INTERNAL_ERROR - result.messages shouldContain sugarComponentButNoSugarRefer("QuackOneText") - } - } - context("SugarTransformError") { expect("sugarComponentAndSugarReferHasDifferentParameters") { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ -import team.duckie.quackquack.sugar.material.SugarToken import androidx.compose.runtime.Composable +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @Composable fun QuackText(@SugarToken style: AwesomeType) {} """, @@ -237,8 +202,8 @@ import team.duckie.quackquack.sugar.material.SugarGeneratedFile import team.duckie.quackquack.sugar.material.SugarRefer import team.duckie.quackquack.sugar.material.sugar -@Composable @SugarRefer("QuackText") +@Composable fun QuackOneText(newNumber: Int = sugar()) {} """, ), @@ -251,33 +216,4 @@ fun QuackOneText(newNumber: Int = sugar()) {} } } } - - private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result { - return prepareCompilation(*sourceFiles).compile() - } - - private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation { - return KotlinCompilation().apply { - workingDir = tempDir - sources = sourceFiles.asList() + stubs - jvmTarget = JvmTarget.JVM_17.toString() - inheritClassPath = true - supportsK2 = false - useK2 = false - pluginOptions = listOf( - PluginOption( - pluginId = PluginId, - optionName = OPTION_SUGAR_PATH.optionName, - optionValue = tempDir.path, - ), - PluginOption( - pluginId = PluginId, - optionName = OPTION_POET.optionName, - optionValue = "false", - ), - ) - compilerPluginRegistrars = listOf(SugarComponentRegistrar.asPluginRegistrar()) - commandLineProcessors = listOf(SugarCommandLineProcessor()) - } - } } diff --git a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrTransformTest.kt b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerTransformTest.kt similarity index 54% rename from sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrTransformTest.kt rename to sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerTransformTest.kt index 0650ed0c5..1c7dd531b 100644 --- a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarIrTransformTest.kt +++ b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCompilerTransformTest.kt @@ -6,56 +6,52 @@ */ @file:OptIn(ExperimentalCompilerApi::class) -@file:Suppress( - "RedundantUnitReturnType", - "RedundantVisibilityModifier", - "RedundantUnitExpression", - "RedundantSuppression", - "LongMethod", - "HasPlatformType", - "KDocUnresolvedReference", -) -package team.duckie.quackquack.sugar.processor +package team.duckie.quackquack.sugar.test import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.PluginOption -import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import io.kotest.core.spec.style.StringSpec import io.kotest.engine.spec.tempdir import io.kotest.matchers.shouldBe import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.utils.addToStdlib.cast +import team.duckie.quackquack.sugar.compiler.SugarCompilerRegistrar -class SugarIrTransformTest : StringSpec() { - private val tempDir = tempdir() +class SugarCompilerTransformTest : StringSpec() { + private val testCompilation = + TestCompilation(tempdir()).apply { + prepareSetting { + compilerPluginRegistrars = listOf(SugarCompilerRegistrar.asPluginRegistrar()) + } + } init { "Default Argument에 SugarIrTransform이 정상 작동함" { - val result = compile( + val result = testCompilation.compile( kotlin( "main.kt", """ -import team.duckie.quackquack.sugar.material.SugarToken import androidx.compose.runtime.Composable +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable var number = 0 +@Sugarable @Composable fun QuackText( - @SugarToken style: AwesomeType, - newNumber: Int = Int.MAX_VALUE, -) { - number = newNumber + @SugarToken style: AwesomeType, + newNumber: Int = Int.MAX_VALUE, +) { + number = newNumber } """, ), kotlin( "text-sugar.kt", """ -@file:OptIn(SugarCompilerApi::class) +@file:OptIn(SugarCompilerApi::class) @file:SugarGeneratedFile import androidx.compose.runtime.Composable @@ -67,10 +63,10 @@ import team.duckie.quackquack.sugar.material.sugar @Composable @SugarRefer("QuackText") fun QuackOneText(newNumber: Int = sugar()) { - QuackText( - style = AwesomeType.One, - newNumber = newNumber, - ) + QuackText( + style = AwesomeType.One, + newNumber = newNumber, + ) } """, ), @@ -93,33 +89,4 @@ fun QuackOneText(newNumber: Int = sugar()) { getNumberMethod.invoke(mainClass).cast() shouldBe Int.MAX_VALUE } } - - private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result { - return prepareCompilation(*sourceFiles).compile() - } - - private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation { - return KotlinCompilation().apply { - workingDir = tempDir - sources = sourceFiles.asList() + stubs - jvmTarget = JvmTarget.JVM_17.toString() - inheritClassPath = true - supportsK2 = false - useK2 = false - pluginOptions = listOf( - PluginOption( - pluginId = PluginId, - optionName = OPTION_SUGAR_PATH.optionName, - optionValue = tempDir.path, - ), - PluginOption( - pluginId = PluginId, - optionName = OPTION_POET.optionName, - optionValue = "false", - ), - ) - compilerPluginRegistrars = listOf(SugarComponentRegistrar.asPluginRegistrar()) - commandLineProcessors = listOf(SugarCommandLineProcessor()) - } - } } diff --git a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarPoetTest.kt b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCoreTest.kt similarity index 65% rename from sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarPoetTest.kt rename to sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCoreTest.kt index 9d194586a..2c2dbe1ea 100644 --- a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/SugarPoetTest.kt +++ b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/SugarCoreTest.kt @@ -5,57 +5,65 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ +@file:Suppress("RedundantVisibilityModifier", "KDocUnresolvedReference") @file:OptIn(ExperimentalCompilerApi::class) -@file:Suppress( - "RedundantUnitReturnType", - "RedundantVisibilityModifier", - "RedundantUnitExpression", - "RedundantSuppression", - "LongMethod", - "HasPlatformType", - "KDocUnresolvedReference", -) - -package team.duckie.quackquack.sugar.processor +package team.duckie.quackquack.sugar.test + +import team.duckie.quackquack.sugar.core.PluginId as SugarCorePluginId import com.tschuchort.compiletesting.KotlinCompilation import com.tschuchort.compiletesting.PluginOption -import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import io.kotest.core.spec.style.StringSpec import io.kotest.engine.spec.tempdir import io.kotest.matchers.shouldBe import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.jetbrains.kotlin.config.JvmTarget -import team.duckie.quackquack.util.backend.test.findGeneratedFileOrNull +import team.duckie.quackquack.sugar.core.OPTION_SUGAR_PATH +import team.duckie.quackquack.sugar.core.SugarCoreCommandLineProcessor +import team.duckie.quackquack.sugar.core.SugarCoreRegistrar import team.duckie.quackquack.util.backend.test.removePackageLine // TODO: @Imports 테스트 작성 // TODO: nullable한 인자 테스트 작성 -class SugarPoetTest : StringSpec() { - private val tempDir = tempdir() +class SugarCoreTest : StringSpec() { + private val testCompilation = + TestCompilation(tempdir()).apply { + prepareSetting { tempDir -> + compilerPluginRegistrars = listOf(SugarCoreRegistrar.asPluginRegistrar()) + pluginOptions = listOf( + PluginOption( + pluginId = SugarCorePluginId, + optionName = OPTION_SUGAR_PATH.optionName, + optionValue = tempDir.path, + ), + ) + commandLineProcessors = listOf(SugarCoreCommandLineProcessor()) + } + } init { """ - - @SugarName이 없을 때는 기본 정책대로 sugar component가 생성됨 - - KDoc이 없는 대상은 KDoc을 생성하지 않음 + - @SugarName이 없을 때는 기본 정책대로 sugar component를 생성함 + - KDoc이 없는 대상은 sugared kdoc만 생성함 """ { - val result = compile( + val result = testCompilation.compile( kotlin( "text.kt", """ -import team.duckie.quackquack.sugar.material.SugarToken import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @Composable fun QuackText( - modifier: Modifier = Modifier, - text: String, - @SugarToken style: AwesomeType2, - singleLine: Boolean = false, - softWrap: Boolean = true, + modifier: Modifier = Modifier, + text: String, + @SugarToken style: AwesomeType2, + singleLine: Boolean = false, + softWrap: Boolean = true, ) {} """, ), @@ -63,14 +71,8 @@ fun QuackText( @Language("kotlin") val expect = """ -// This file was automatically generated by sugar-processor. +// This file was automatically generated by sugar-core. // Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") @file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) @file:SugarGeneratedFile @@ -83,7 +85,6 @@ import androidx.compose.ui.Modifier import kotlin.Boolean import kotlin.OptIn import kotlin.String -import kotlin.Suppress import team.duckie.quackquack.casa.`annotation`.Casa import team.duckie.quackquack.casa.`annotation`.SugarGeneratorUsage import team.duckie.quackquack.sugar.material.SugarCompilerApi @@ -91,6 +92,12 @@ import team.duckie.quackquack.sugar.material.SugarGeneratedFile import team.duckie.quackquack.sugar.material.SugarRefer import team.duckie.quackquack.sugar.material.sugar +/** + * This component uses [AwesomeType2.One] as the token value for `style`. + * + * This document was automatically generated by [QuackText]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ @Casa @Composable @NonRestartableComposable @@ -110,6 +117,12 @@ public fun QuackOneText( ) } +/** + * This component uses [AwesomeType2.Two] as the token value for `style`. + * + * This document was automatically generated by [QuackText]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ @Casa @Composable @NonRestartableComposable @@ -129,6 +142,12 @@ public fun QuackTwoText( ) } +/** + * This component uses [AwesomeType2.Three] as the token value for `style`. + * + * This document was automatically generated by [QuackText]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ @Casa @Composable @NonRestartableComposable @@ -151,19 +170,20 @@ public fun QuackThreeText( """.trimIndent() result.exitCode shouldBe KotlinCompilation.ExitCode.OK - tempDir.findGeneratedFileOrNull("text.kt")?.readText()?.removePackageLine() shouldBe expect + testCompilation.findGeneratedFileOrNull("text.kt")?.readText()?.removePackageLine() shouldBe expect } """ - PREFIX_NAME + Awesome + TOKEN_NAME 조합으로 sugar component를 생성함 - - KDoc이 있는 대상은 Generated KDoc을 생성함 + - KDoc이 있는 대상은 SugarToken이 제외된 sugared fully-kdoc을 생성함 """ { - val result = compile( + val result = testCompilation.compile( kotlin( "text.kt", """ import team.duckie.quackquack.sugar.material.SugarToken import team.duckie.quackquack.sugar.material.SugarName +import team.duckie.quackquack.sugar.material.Sugarable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -174,13 +194,14 @@ import androidx.compose.ui.Modifier * @param text 표시할 문자 */ @SugarName(SugarName.PREFIX_NAME + "Awesome" + SugarName.TOKEN_NAME) +@Sugarable @Composable fun QuackText( - modifier: Modifier = Modifier, - text: String, - @SugarToken style: AwesomeType2, - singleLine: Boolean = false, - softWrap: Boolean = true, + modifier: Modifier = Modifier, + text: String, + @SugarToken style: AwesomeType2, + singleLine: Boolean = false, + softWrap: Boolean = true, ) {} """, ), @@ -188,14 +209,8 @@ fun QuackText( @Language("kotlin") val expect = """ -// This file was automatically generated by sugar-processor. +// This file was automatically generated by sugar-core. // Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") @file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) @file:SugarGeneratedFile @@ -208,7 +223,6 @@ import androidx.compose.ui.Modifier import kotlin.Boolean import kotlin.OptIn import kotlin.String -import kotlin.Suppress import team.duckie.quackquack.casa.`annotation`.Casa import team.duckie.quackquack.casa.`annotation`.SugarGeneratorUsage import team.duckie.quackquack.sugar.material.SugarCompilerApi @@ -222,7 +236,7 @@ import team.duckie.quackquack.sugar.material.sugar * This component uses [AwesomeType2.One] as the token value for `style`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param modifier 적용할 Modifier * @param text 표시할 문자 @@ -252,7 +266,7 @@ public fun QuackAwesomeOne( * This component uses [AwesomeType2.Two] as the token value for `style`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param modifier 적용할 Modifier * @param text 표시할 문자 @@ -282,7 +296,7 @@ public fun QuackAwesomeTwo( * This component uses [AwesomeType2.Three] as the token value for `style`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param modifier 적용할 Modifier * @param text 표시할 문자 @@ -309,27 +323,39 @@ public fun QuackAwesomeThree( """.trimIndent() result.exitCode shouldBe KotlinCompilation.ExitCode.OK - tempDir.findGeneratedFileOrNull("text.kt")?.readText()?.removePackageLine() shouldBe expect + testCompilation.findGeneratedFileOrNull("text.kt")?.readText()?.removePackageLine() shouldBe expect } - "DEFAULT_NAME을 사용하면 기본 정책대로 sugar component가 생성됨" { - val result = compile( + """ + - DEFAULT_NAME을 사용하면 기본 정책대로 sugar component를 생성함 + - 여러줄의 KDoc default content가 적용됐다면 첫 번째 줄만 default section으로 사용함 + """ { + val result = testCompilation.compile( kotlin( "text.kt", """ -import team.duckie.quackquack.sugar.material.SugarToken -import team.duckie.quackquack.sugar.material.SugarName import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.SugarName +import team.duckie.quackquack.sugar.material.Sugarable +/** + * AWESOME!. AWESOME!!. AWESOME!!!. + * AWESOME!!!!. AWESOME!!!!!. AWESOME!!!!!!. + * + * @param modifier 적용할 Modifier + * @param text 표시할 문자 + */ @SugarName(SugarName.DEFAULT_NAME) +@Sugarable @Composable fun QuackText( - modifier: Modifier = Modifier, - text: String, - @SugarToken style: AwesomeType, - singleLine: Boolean = false, - softWrap: Boolean = true, + modifier: Modifier = Modifier, + text: String, + @SugarToken style: AwesomeType, + singleLine: Boolean = false, + softWrap: Boolean = true, ) {} """, ), @@ -337,14 +363,8 @@ fun QuackText( @Language("kotlin") val expect = """ -// This file was automatically generated by sugar-processor. +// This file was automatically generated by sugar-core. // Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") @file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) @file:SugarGeneratedFile @@ -357,7 +377,6 @@ import androidx.compose.ui.Modifier import kotlin.Boolean import kotlin.OptIn import kotlin.String -import kotlin.Suppress import team.duckie.quackquack.casa.`annotation`.Casa import team.duckie.quackquack.casa.`annotation`.SugarGeneratorUsage import team.duckie.quackquack.sugar.material.SugarCompilerApi @@ -365,6 +384,17 @@ import team.duckie.quackquack.sugar.material.SugarGeneratedFile import team.duckie.quackquack.sugar.material.SugarRefer import team.duckie.quackquack.sugar.material.sugar +/** + * AWESOME!. + * + * This component uses [AwesomeType.One] as the token value for `style`. + * + * This document was automatically generated by [QuackText]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param modifier 적용할 Modifier + * @param text 표시할 문자 + */ @Casa @Composable @NonRestartableComposable @@ -387,27 +417,30 @@ public fun QuackOneText( """.trimIndent() result.exitCode shouldBe KotlinCompilation.ExitCode.OK - tempDir.findGeneratedFileOrNull("text.kt")?.readText()?.removePackageLine() shouldBe expect + testCompilation.findGeneratedFileOrNull("text.kt")?.readText()?.removePackageLine() shouldBe expect } "람다 인자가 지원됨" { - val result = compile( + val result = testCompilation.compile( kotlin( "checkbox.kt", """ -import team.duckie.quackquack.sugar.material.SugarToken import androidx.compose.runtime.Composable +import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable +@Sugarable @Composable fun QuackCheckbox( - @SugarToken style: AwesomeType, - onCheckChanged: (checked: Boolean) -> Unit, + @SugarToken style: AwesomeType, + onCheckChanged: (checked: Boolean) -> Unit, ) {} +@Sugarable @Composable fun QuackCheckbox2( - @SugarToken style: AwesomeType, - onCheckChanged: suspend Boolean.(checked: Boolean) -> Boolean, + @SugarToken style: AwesomeType, + onCheckChanged: suspend Boolean.(checked: Boolean) -> Boolean, ) {} """, ), @@ -415,14 +448,8 @@ fun QuackCheckbox2( @Language("kotlin") val expect = """ -// This file was automatically generated by sugar-processor. +// This file was automatically generated by sugar-core. // Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") @file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) @file:SugarGeneratedFile @@ -435,7 +462,6 @@ import androidx.compose.runtime.NonRestartableComposable import kotlin.Boolean import kotlin.Function1 import kotlin.OptIn -import kotlin.Suppress import kotlin.Unit import kotlin.coroutines.SuspendFunction2 import team.duckie.quackquack.casa.`annotation`.Casa @@ -445,6 +471,12 @@ import team.duckie.quackquack.sugar.material.SugarGeneratedFile import team.duckie.quackquack.sugar.material.SugarRefer import team.duckie.quackquack.sugar.material.sugar +/** + * This component uses [AwesomeType.One] as the token value for `style`. + * + * This document was automatically generated by [QuackCheckbox]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ @Casa @Composable @NonRestartableComposable @@ -456,6 +488,12 @@ public fun QuackOneCheckbox(onCheckChanged: (P0: Boolean) -> Unit) { ) } +/** + * This component uses [AwesomeType.One] as the token value for `style`. + * + * This document was automatically generated by [QuackCheckbox2]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ @Casa @Composable @NonRestartableComposable @@ -470,36 +508,7 @@ public fun QuackOneCheckbox2(onCheckChanged: suspend (P0: Boolean, P1: Boolean) """.trimIndent() result.exitCode shouldBe KotlinCompilation.ExitCode.OK - tempDir.findGeneratedFileOrNull("checkbox.kt")?.readText()?.removePackageLine() shouldBe expect - } - } - - private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result { - return prepareCompilation(*sourceFiles).compile() - } - - private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation { - return KotlinCompilation().apply { - workingDir = tempDir - sources = sourceFiles.asList() + stubs - jvmTarget = JvmTarget.JVM_17.toString() - inheritClassPath = true - supportsK2 = false - useK2 = false - pluginOptions = listOf( - PluginOption( - pluginId = PluginId, - optionName = OPTION_SUGAR_PATH.optionName, - optionValue = tempDir.path, - ), - PluginOption( - pluginId = PluginId, - optionName = OPTION_POET.optionName, - optionValue = "true", - ), - ) - compilerPluginRegistrars = listOf(SugarComponentRegistrar.asPluginRegistrar()) - commandLineProcessors = listOf(SugarCommandLineProcessor()) + testCompilation.findGeneratedFileOrNull("checkbox.kt")?.readText()?.removePackageLine() shouldBe expect } } } diff --git a/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/TestCompilation.kt b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/TestCompilation.kt new file mode 100644 index 000000000..72e94a1a2 --- /dev/null +++ b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/TestCompilation.kt @@ -0,0 +1,40 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +@file:Suppress("MemberVisibilityCanBePrivate") + +package team.duckie.quackquack.sugar.test + +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.SourceFile +import java.io.File +import org.jetbrains.kotlin.config.JvmTarget +import team.duckie.quackquack.util.backend.test.findGeneratedFileOrNull + +class TestCompilation(private val tempDir: File) { + private var prepareSetting: (KotlinCompilation.(tempDir: File) -> Unit)? = null + + fun compile(vararg sourceFiles: SourceFile) = + prepare(*sourceFiles).compile() + + fun prepare(vararg sourceFiles: SourceFile) = + KotlinCompilation().apply { + workingDir = tempDir + sources = sourceFiles.asList() + stubs + jvmTarget = JvmTarget.JVM_17.toString() + inheritClassPath = true + supportsK2 = false + useK2 = false + prepareSetting?.invoke(this, tempDir) + } + + fun prepareSetting(block: KotlinCompilation.(tempDir: File) -> Unit) { + prepareSetting = block + } + + fun findGeneratedFileOrNull(fileName: String) = tempDir.findGeneratedFileOrNull(fileName) +} diff --git a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/stubs.kt b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/stubs.kt similarity index 94% rename from sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/stubs.kt rename to sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/stubs.kt index c63da56fe..fd5502317 100644 --- a/sugar-processor/src/test/kotlin/team/duckie/quackquack/sugar/processor/stubs.kt +++ b/sugar-test/src/test/kotlin/team/duckie/quackquack/sugar/test/stubs.kt @@ -5,7 +5,7 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -package team.duckie.quackquack.sugar.processor +package team.duckie.quackquack.sugar.test import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import team.duckie.quackquack.util.backend.test.stub.ComposeStub @@ -37,6 +37,6 @@ value class AwesomeType2(val index: Int) { @JvmInline value class AwesomeType3(val index: Int) - """, + """, ), ) diff --git a/ui-sugar/.nospotless b/ui-sugar/.nospotless new file mode 100644 index 000000000..e69de29bb diff --git a/ui-sugar/build.gradle.kts b/ui-sugar/build.gradle.kts new file mode 100644 index 000000000..919af4683 --- /dev/null +++ b/ui-sugar/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Designed and developed by Duckie Team 2023. + * + * Licensed under the MIT. + * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE + */ + +plugins { + quackquack("android-library") + quackquack("android-compose") + quackquack("quack-publishing") +} + +android { + namespace = "team.duckie.quackquack.ui.sugar" +} + +dependencies { + api(projects.ui.orArtifact()) + implementations( + libs.compose.ui.core, + libs.compose.ui.text, + libs.compose.foundation, + projects.casaAnnotation.orArtifact(), + projects.sugarMaterial.orArtifact(), + ) +} diff --git a/ui-sugar/src/main/AndroidManifest.xml b/ui-sugar/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b1e11303c --- /dev/null +++ b/ui-sugar/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + diff --git a/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt new file mode 100644 index 000000000..3f1e8965a --- /dev/null +++ b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt @@ -0,0 +1,300 @@ +// This file was automatically generated by sugar-core. +// Do not modify it manually. +@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) +@file:SugarGeneratedFile + +package team.duckie.quackquack.ui.sugar + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import androidx.compose.ui.Modifier +import team.duckie.quackquack.casa.annotation.Casa +import team.duckie.quackquack.casa.annotation.CasaValue +import team.duckie.quackquack.casa.annotation.SugarGeneratorUsage +import team.duckie.quackquack.sugar.material.SugarCompilerApi +import team.duckie.quackquack.sugar.material.SugarGeneratedFile +import team.duckie.quackquack.sugar.material.SugarRefer +import team.duckie.quackquack.sugar.material.sugar +import team.duckie.quackquack.ui.QuackButton +import team.duckie.quackquack.ui.QuackButtonStyle +import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.PrimaryLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackPrimaryLargeButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.PrimaryLarge, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.SecondaryLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackSecondaryLargeButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.SecondaryLarge, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.Medium] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackMediumButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.Medium, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.PrimaryFilledSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackPrimaryFilledSmallButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.PrimaryFilledSmall, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.PrimaryOutlinedSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackPrimaryOutlinedSmallButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.PrimaryOutlinedSmall, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.PrimaryOutlinedRoundSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackPrimaryOutlinedRoundSmallButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.PrimaryOutlinedRoundSmall, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.SecondarySmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackSecondarySmallButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.SecondarySmall, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * This component uses [QuackButtonStyle.SecondaryRoundSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +fun QuackSecondaryRoundSmallButton( + @CasaValue("\"QuackButton is experimental\"") text: String, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackButton( + text = text, + style = QuackButtonStyle.SecondaryRoundSmall, + modifier = modifier, + enabled = enabled, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} diff --git a/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt new file mode 100644 index 000000000..c99116eab --- /dev/null +++ b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt @@ -0,0 +1,164 @@ +// This file was automatically generated by sugar-core. +// Do not modify it manually. +@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) +@file:SugarGeneratedFile + +package team.duckie.quackquack.ui.sugar + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import androidx.compose.ui.Modifier +import team.duckie.quackquack.casa.annotation.Casa +import team.duckie.quackquack.casa.annotation.CasaValue +import team.duckie.quackquack.casa.annotation.SugarGeneratorUsage +import team.duckie.quackquack.sugar.material.SugarCompilerApi +import team.duckie.quackquack.sugar.material.SugarGeneratedFile +import team.duckie.quackquack.sugar.material.SugarRefer +import team.duckie.quackquack.sugar.material.sugar +import team.duckie.quackquack.ui.QuackTag +import team.duckie.quackquack.ui.QuackTagStyle +import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi + +/** + * 태그를 그립니다. + * + * This component uses [QuackTagStyle.Outlined] as the token value for `style`. + * + * This document was automatically generated by [QuackTag]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param text 중앙에 표시할 텍스트 + * @param selected 선택 상태 여부 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 + * 항상 클릭 가능합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackTag") +fun QuackOutlinedTag( + @CasaValue("\"QuackTagPreview\"") text: String, + modifier: Modifier = sugar(), + selected: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackTag( + text = text, + style = QuackTagStyle.Outlined, + modifier = modifier, + selected = selected, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 태그를 그립니다. + * + * This component uses [QuackTagStyle.Filled] as the token value for `style`. + * + * This document was automatically generated by [QuackTag]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param text 중앙에 표시할 텍스트 + * @param selected 선택 상태 여부 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 + * 항상 클릭 가능합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackTag") +fun QuackFilledTag( + @CasaValue("\"QuackTagPreview\"") text: String, + modifier: Modifier = sugar(), + selected: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackTag( + text = text, + style = QuackTagStyle.Filled, + modifier = modifier, + selected = selected, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 태그를 그립니다. + * + * This component uses [QuackTagStyle.GrayscaleFlat] as the token value for `style`. + * + * This document was automatically generated by [QuackTag]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param text 중앙에 표시할 텍스트 + * @param selected 선택 상태 여부 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 + * 항상 클릭 가능합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackTag") +fun QuackGrayscaleFlatTag( + @CasaValue("\"QuackTagPreview\"") text: String, + modifier: Modifier = sugar(), + selected: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackTag( + text = text, + style = QuackTagStyle.GrayscaleFlat, + modifier = modifier, + selected = selected, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 태그를 그립니다. + * + * This component uses [QuackTagStyle.GrayscaleOutlined] as the token value for `style`. + * + * This document was automatically generated by [QuackTag]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param text 중앙에 표시할 텍스트 + * @param selected 선택 상태 여부 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 + * 항상 클릭 가능합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackTag") +fun QuackGrayscaleOutlinedTag( + @CasaValue("\"QuackTagPreview\"") text: String, + modifier: Modifier = sugar(), + selected: Boolean = sugar(), + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +) { + QuackTag( + text = text, + style = QuackTagStyle.GrayscaleOutlined, + modifier = modifier, + selected = selected, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt similarity index 79% rename from ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt rename to ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt index 772d7a3a5..face5b8e0 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt +++ b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/text.kt @@ -1,11 +1,5 @@ -// This file was automatically generated by sugar-processor. +// This file was automatically generated by sugar-core. // Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") @file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) @file:SugarGeneratedFile @@ -15,14 +9,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.NonRestartableComposable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow -import kotlin.Boolean -import kotlin.OptIn -import kotlin.String -import kotlin.Suppress -import kotlin.Unit -import team.duckie.quackquack.casa.`annotation`.Casa -import team.duckie.quackquack.casa.`annotation`.CasaValue -import team.duckie.quackquack.casa.`annotation`.SugarGeneratorUsage +import team.duckie.quackquack.casa.annotation.Casa +import team.duckie.quackquack.casa.annotation.CasaValue +import team.duckie.quackquack.casa.annotation.SugarGeneratorUsage import team.duckie.quackquack.material.QuackTypography import team.duckie.quackquack.sugar.material.SugarCompilerApi import team.duckie.quackquack.sugar.material.SugarGeneratedFile @@ -36,7 +25,7 @@ import team.duckie.quackquack.ui.QuackText * This component uses [QuackTypography.Body1] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -46,23 +35,23 @@ import team.duckie.quackquack.ui.QuackText * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackBody1( - modifier: Modifier = sugar(), +fun QuackBody1( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Body1, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -75,7 +64,7 @@ public fun QuackBody1( * This component uses [QuackTypography.Body2] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -85,23 +74,23 @@ public fun QuackBody1( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackBody2( - modifier: Modifier = sugar(), +fun QuackBody2( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Body2, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -114,7 +103,7 @@ public fun QuackBody2( * This component uses [QuackTypography.Body3] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -124,23 +113,23 @@ public fun QuackBody2( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackBody3( - modifier: Modifier = sugar(), +fun QuackBody3( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Body3, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -153,7 +142,7 @@ public fun QuackBody3( * This component uses [QuackTypography.HeadLine1] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -163,23 +152,23 @@ public fun QuackBody3( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackHeadLine1( - modifier: Modifier = sugar(), +fun QuackHeadLine1( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.HeadLine1, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -192,7 +181,7 @@ public fun QuackHeadLine1( * This component uses [QuackTypography.HeadLine2] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -202,23 +191,23 @@ public fun QuackHeadLine1( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackHeadLine2( - modifier: Modifier = sugar(), +fun QuackHeadLine2( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.HeadLine2, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -231,7 +220,7 @@ public fun QuackHeadLine2( * This component uses [QuackTypography.Large1] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -241,23 +230,23 @@ public fun QuackHeadLine2( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackLarge1( - modifier: Modifier = sugar(), +fun QuackLarge1( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Large1, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -267,10 +256,10 @@ public fun QuackLarge1( /** * 텍스트를 그리는 기본적인 컴포저블입니다. * - * This component uses [QuackTypography.Subtitle] as the token value for `typography`. + * This component uses [QuackTypography.Quote] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -280,23 +269,62 @@ public fun QuackLarge1( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackSubtitle( - modifier: Modifier = sugar(), +fun QuackQuote( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( + text = text, + typography = QuackTypography.Quote, modifier = modifier, + singleLine = singleLine, + softWrap = softWrap, + overflow = overflow, + ) +} + +/** + * 텍스트를 그리는 기본적인 컴포저블입니다. + * + * This component uses [QuackTypography.Subtitle] as the token value for `typography`. + * + * This document was automatically generated by [QuackText]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param text 그릴 텍스트 + * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 + * [softWrap] 및 [overflow]에 따라 잘립니다. + * @param softWrap 텍스트에 softwrap break를 적용할지 여부. `false`이면 텍스트 글리프가 + * 가로 공간이 무제한인 것처럼 배치됩니다. 또한 [overflow] 및 [TextAlign]에 예기치 않은 + * 효과가 발생할 수 있습니다. + * @param overflow 시각적 overflow를 처리하는 방법 + * + * @sample team.duckie.quackquack.ui.sample.SampleTest + */ +@Casa +@Composable +@NonRestartableComposable +@SugarRefer("team.duckie.quackquack.ui.QuackText") +fun QuackSubtitle( + @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), + singleLine: Boolean = sugar(), + softWrap: Boolean = sugar(), + overflow: TextOverflow = sugar(), +) { + QuackText( text = text, typography = QuackTypography.Subtitle, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -309,7 +337,7 @@ public fun QuackSubtitle( * This component uses [QuackTypography.Subtitle2] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -319,23 +347,23 @@ public fun QuackSubtitle( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackSubtitle2( - modifier: Modifier = sugar(), +fun QuackSubtitle2( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Subtitle2, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -348,7 +376,7 @@ public fun QuackSubtitle2( * This component uses [QuackTypography.Title1] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -358,23 +386,23 @@ public fun QuackSubtitle2( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackTitle1( - modifier: Modifier = sugar(), +fun QuackTitle1( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Title1, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, @@ -387,7 +415,7 @@ public fun QuackTitle1( * This component uses [QuackTypography.Title2] as the token value for `typography`. * * This document was automatically generated by [QuackText]. - * If any contents are broken, please check the original document. + * If any contents are broken or wanna see the entire contents, please check the original document. * * @param text 그릴 텍스트 * @param singleLine 텍스트가 한 줄로 그려질 지 여부. 텍스트가 주어진 줄 수를 초과하면 @@ -397,23 +425,23 @@ public fun QuackTitle1( * 효과가 발생할 수 있습니다. * @param overflow 시각적 overflow를 처리하는 방법 * - * @sample team.duckie.quackquack.ui.sample.SampleTest + * @sample team.duckie.quackquack.ui.sample.SampleTest */ @Casa @Composable @NonRestartableComposable @SugarRefer("team.duckie.quackquack.ui.QuackText") -public fun QuackTitle2( - modifier: Modifier = sugar(), +fun QuackTitle2( @CasaValue("\"QuackText\"") text: String, + modifier: Modifier = sugar(), singleLine: Boolean = sugar(), softWrap: Boolean = sugar(), overflow: TextOverflow = sugar(), -): Unit { +) { QuackText( - modifier = modifier, text = text, typography = QuackTypography.Title2, + modifier = modifier, singleLine = singleLine, softWrap = softWrap, overflow = overflow, diff --git a/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/textfield.kt b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/textfield.kt new file mode 100644 index 000000000..d755481ef --- /dev/null +++ b/ui-sugar/src/main/kotlin/team/duckie/quackquack/ui/sugar/textfield.kt @@ -0,0 +1,1127 @@ +// This file was automatically generated by sugar-core. +// Do not modify it manually. +@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) +@file:SugarGeneratedFile + +package team.duckie.quackquack.ui.sugar + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.VisualTransformation +import team.duckie.quackquack.casa.annotation.Casa +import team.duckie.quackquack.casa.annotation.CasaValue +import team.duckie.quackquack.casa.annotation.SugarGeneratorUsage +import team.duckie.quackquack.sugar.material.SugarCompilerApi +import team.duckie.quackquack.sugar.material.SugarGeneratedFile +import team.duckie.quackquack.sugar.material.SugarRefer +import team.duckie.quackquack.sugar.material.sugar +import team.duckie.quackquack.ui.QuackDefaultTextField +import team.duckie.quackquack.ui.QuackFilledTextField +import team.duckie.quackquack.ui.QuackTextFieldStyle +import team.duckie.quackquack.ui.TextFieldPlaceholderStrategy +import team.duckie.quackquack.ui.TextFieldValidationLabelVisibilityStrategy +import team.duckie.quackquack.ui.TextFieldValidationState +import team.duckie.quackquack.ui.optin.ExperimentalDesignToken +import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.Default] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackDefaultDefaultTextField( + @CasaValue("\"QuackDefaultTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.Default, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.DefaultLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackDefaultLargeDefaultTextField( + @CasaValue("\"QuackDefaultTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.DefaultLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.FilledLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackFilledLargeDefaultTextField( + @CasaValue("\"QuackDefaultTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.FilledFlat] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackFilledFlatDefaultTextField( + @CasaValue("\"QuackDefaultTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledFlat, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.Default] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackDefaultDefaultTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.Default, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.DefaultLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackDefaultLargeDefaultTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.DefaultLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.FilledLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackFilledLargeDefaultTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * Default 텍스트 필드를 그립니다. + * + * This component uses [QuackTextFieldStyle.FilledFlat] as the token value for `style`. + * + * This document was automatically generated by [QuackDefaultTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + * + * @param value 텍스트 필드에 입력된 글자 + * @param onValueChange 입력 서비스가 글자를 업데이트할 때 호출되는 콜백입니다. 업데이트된 글자는 콜백의 매개변수로 제공됩니다. + * @param enabled 텍스트 필드의 활성화 상태를 제어합니다. false면 텍스트 필드를 편집하거나 포커스를 맞출 수 없으며 + * 텍스트 필드에 글자를 입력할 수 없습니다. + * @param readOnly 텍스트 필드의 편집 가능한 상태를 제어합니다. true면 텍스트 필드를 수정할 수 없지만 사용자가 포커스를 맞추고 + * 텍스트를 복사할 수 있습니다. 읽기 전용 텍스트 필드는 일반적으로 사용자가 편집할 수 없는 미리 채워진 양식을 표시하는 데 사용됩니다. + * @param placeholderText 입력된 글자가 없을 때 표시할 대체 글자 + * @param placeholderStrategy [placeholderText]의 시각적 정책. 자세한 정보는 [TextFieldPlaceholderStrategy] 문서를 + * 참고하세요. + * @param keyboardOptions 키보드 유형 및 [ImeAction]과 같은 구성이 포함된 소프트웨어 키보드 옵션입니다. + * @param keyboardActions 입력 서비스가 IME 액션을 방출하면 해당 콜백이 호출됩니다. 이 IME 액션은 [KeyboardOptions.imeAction]에 + * 지정한 것과 다를 수 있습니다. + * @param singleLine true로 설정하면 이 텍스트 필드가 여러 줄로 줄 바꿈되지 않고 가로로 스크롤되는 단일 텍스트 필드가 됩니다. + * 키보드에 [ImeAction]으로 return 키가 표시되지 않도록 알려줍니다. [maxLines] 및 [minLines]는 모두 자동으로 1로 설정되므로 무시됩니다. + * @param minLines 텍스트 필드에 표시하기 위한 최소 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param maxLines 텍스트 필드에 표시될 수 있는 최대 줄 수입니다. `1 <= minLines <= maxLines`가 요구됩니다. [singleLine]이 + * true면 + * 이 옵션은 무시됩니다. + * @param visualTransformation [value]의 시각적 표현을 변경할 수 있는 필터입니다. 기본적으로 적용되지 않습니다. + * @param onTextLayout 새 텍스트 레이아웃이 계산될 때 실행되는 콜백입니다. 콜백이 제공하는 [TextLayoutResult] 객체에는 단락 정보, + * 텍스트의 크기, 기준선 및 기타 세부 정보가 포함됩니다. 콜백은 텍스트에 장식이나 기능을 추가하는 데 사용할 수 있습니다. + * 예를 들어 텍스트 주위에 커서나 선택 영역을 그리는 데 사용할 수 있습니다. + * @param validationState 텍스트 필드의 검증 결과. 자세한 정보는 [TextFieldValidationState] 문서를 참고하세요. + * 만약 [검증 결과 문구][TextFieldValidationState.WithLabel]를 제공하고 [defaultTextFieldIndicator] 데코레이터가 사용됐을 + * 경우, + * 인디케이터의 방향이 [Bottom][VerticalDirection.Bottom]이여야 합니다. + * @Param validationLabelVisibilityStrategy [validationState]를 설명할 문구가 제공됐다면 해당 문구의 시각적 정책. + * 자세한 내용은 [TextFieldValidationLabelVisibilityStrategy] 문서를 참고하세요. + * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 + * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackDefaultTextField") +fun QuackFilledFlatDefaultTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + validationState: TextFieldValidationState = sugar(), + validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackDefaultTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledFlat, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + validationState = validationState, + validationLabelVisibilityStrategy = validationLabelVisibilityStrategy, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.Default] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackDefaultFilledTextField( + @CasaValue("\"QuackFilledTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.Default, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.DefaultLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackDefaultLargeFilledTextField( + @CasaValue("\"QuackFilledTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.DefaultLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.FilledLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackFilledLargeFilledTextField( + @CasaValue("\"QuackFilledTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.FilledFlat] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackFilledFlatFilledTextField( + @CasaValue("\"QuackFilledTextFieldPreview\"") `value`: String, + @CasaValue("{}") onValueChange: (P0: String) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledFlat, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.Default] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackDefaultFilledTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.Default, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.DefaultLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackDefaultLargeFilledTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.DefaultLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.FilledLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackFilledLargeFilledTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledLarge, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} + +/** + * This component uses [QuackTextFieldStyle.FilledFlat] as the token value for `style`. + * + * This document was automatically generated by [QuackFilledTextField]. + * If any contents are broken or wanna see the entire contents, please check the original document. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalDesignToken +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackFilledTextField") +fun QuackFilledFlatFilledTextField( + `value`: TextFieldValue, + onValueChange: (P0: TextFieldValue) -> Unit, + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + readOnly: Boolean = sugar(), + placeholderText: String? = sugar(), + placeholderStrategy: TextFieldPlaceholderStrategy = sugar(), + keyboardOptions: KeyboardOptions = sugar(), + keyboardActions: KeyboardActions = sugar(), + singleLine: Boolean = sugar(), + minLines: Int = sugar(), + maxLines: Int = sugar(), + visualTransformation: VisualTransformation = sugar(), + onTextLayout: (P0: TextLayoutResult) -> Unit = sugar(), + interactionSource: MutableInteractionSource = sugar(), +) { + QuackFilledTextField( + value = value, + onValueChange = onValueChange, + style = QuackTextFieldStyle.FilledFlat, + modifier = modifier, + enabled = enabled, + readOnly = readOnly, + placeholderText = placeholderText, + placeholderStrategy = placeholderStrategy, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + minLines = minLines, + maxLines = maxLines, + visualTransformation = visualTransformation, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + ) +} diff --git a/ui-sugar/version.txt b/ui-sugar/version.txt new file mode 100644 index 000000000..0cc5bc23c --- /dev/null +++ b/ui-sugar/version.txt @@ -0,0 +1 @@ +0.1.0-2.0.0-alpha10 diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 26197c211..9ff288ae7 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -5,7 +5,7 @@ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE */ -@file:Suppress("UnstableApiUsage", "INLINE_FROM_HIGHER_PLATFORM") +@file:Suppress("UnstableApiUsage") import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME as kotlinCompilerPlugin import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -24,16 +24,12 @@ plugins { } tasks.withType { - val sugarProcessorKotlinCompilerPluginId = "team.duckie.quackquack.sugar.processor" - val sugarPath = "$projectDir/src/main/kotlin/team/duckie/quackquack/ui/sugar" + val sugarCorePluginId = "team.duckie.quackquack.sugar.core" + val sugarPath = "${projects.uiSugar.dependencyProject.projectDir}/src/main/kotlin/team/duckie/quackquack/ui/sugar" kotlinOptions { freeCompilerArgs = freeCompilerArgs + listOf( "-P", - "plugin:$sugarProcessorKotlinCompilerPluginId:sugarPath=$sugarPath", - ) - freeCompilerArgs = freeCompilerArgs + listOf( - "-P", - "plugin:$sugarProcessorKotlinCompilerPluginId:poet=$sugarPoet", + "plugin:$sugarCorePluginId:sugarPath=$sugarPath", ) } } @@ -53,7 +49,6 @@ android { } ksp { - arg("AidePath", "$rootDir/aide/src/main/kotlin/team/duckie/quackquack/aide/rule") arg("CasaPath", "$rootDir/catalog/src/main/kotlin/team/duckie/quackquack/catalog") } @@ -99,12 +94,10 @@ dependencies { libs.test.kotest.assertion.core, ) - kotlinCompilerPlugin(projects.sugarProcessor.orArtifact()) + kotlinCompilerPlugin(projects.sugarCompiler.orArtifact()) safeRunWithinDevelopmentMode { - ksps( - // TODO: projects.casaProcessor, - ) - // kotlinCompilerPlugin(projects.docusaurusIntegration) + // TODO: ksp(projects.casaProcessor) + kotlinCompilerPlugin(projects.sugarCore) } } diff --git a/ui/report/compose-metrics/ui_debug-module.json b/ui/report/compose-metrics/ui_debug-module.json index 495f09229..3e99016c0 100644 --- a/ui/report/compose-metrics/ui_debug-module.json +++ b/ui/report/compose-metrics/ui_debug-module.json @@ -1,25 +1,25 @@ { - "skippableComposables": 2, - "restartableComposables": 5, - "readonlyComposables": 0, - "totalComposables": 7, - "restartGroups": 5, - "totalGroups": 8, - "staticArguments": 6, - "certainArguments": 16, - "knownStableArguments": 76, - "knownUnstableArguments": 6, + "skippableComposables": 8, + "restartableComposables": 14, + "readonlyComposables": 1, + "totalComposables": 30, + "restartGroups": 14, + "totalGroups": 44, + "staticArguments": 32, + "certainArguments": 158, + "knownStableArguments": 486, + "knownUnstableArguments": 32, "unknownStableArguments": 0, - "totalArguments": 82, - "markedStableClasses": 14, - "inferredStableClasses": 0, + "totalArguments": 518, + "markedStableClasses": 18, + "inferredStableClasses": 11, "inferredUnstableClasses": 0, "inferredUncertainClasses": 0, - "effectivelyStableClasses": 14, - "totalClasses": 14, - "memoizedLambdas": 0, - "singletonLambdas": 0, + "effectivelyStableClasses": 29, + "totalClasses": 29, + "memoizedLambdas": 23, + "singletonLambdas": 5, "singletonComposableLambdas": 0, - "composableLambdas": 0, - "totalLambdas": 2 + "composableLambdas": 3, + "totalLambdas": 35 } \ No newline at end of file diff --git a/ui/report/compose-metrics/ui_debugUnitTest-module.json b/ui/report/compose-metrics/ui_debugUnitTest-module.json index bfec53e19..09dd80b45 100644 --- a/ui/report/compose-metrics/ui_debugUnitTest-module.json +++ b/ui/report/compose-metrics/ui_debugUnitTest-module.json @@ -5,12 +5,12 @@ "totalComposables": 140, "restartGroups": 140, "totalGroups": 140, - "staticArguments": 454, + "staticArguments": 460, "certainArguments": 0, - "knownStableArguments": 1767, + "knownStableArguments": 1773, "knownUnstableArguments": 0, "unknownStableArguments": 0, - "totalArguments": 1767, + "totalArguments": 1773, "markedStableClasses": 0, "inferredStableClasses": 2, "inferredUnstableClasses": 1, diff --git a/ui/report/compose-metrics/ui_release-module.json b/ui/report/compose-metrics/ui_release-module.json index d3920a71a..3e99016c0 100644 --- a/ui/report/compose-metrics/ui_release-module.json +++ b/ui/report/compose-metrics/ui_release-module.json @@ -2,15 +2,15 @@ "skippableComposables": 8, "restartableComposables": 14, "readonlyComposables": 1, - "totalComposables": 52, + "totalComposables": 30, "restartGroups": 14, - "totalGroups": 66, - "staticArguments": 54, - "certainArguments": 268, - "knownStableArguments": 618, + "totalGroups": 44, + "staticArguments": 32, + "certainArguments": 158, + "knownStableArguments": 486, "knownUnstableArguments": 32, "unknownStableArguments": 0, - "totalArguments": 650, + "totalArguments": 518, "markedStableClasses": 18, "inferredStableClasses": 11, "inferredUnstableClasses": 0, diff --git a/ui/report/compose-metrics/ui_releaseUnitTest-module.json b/ui/report/compose-metrics/ui_releaseUnitTest-module.json index bfec53e19..09dd80b45 100644 --- a/ui/report/compose-metrics/ui_releaseUnitTest-module.json +++ b/ui/report/compose-metrics/ui_releaseUnitTest-module.json @@ -5,12 +5,12 @@ "totalComposables": 140, "restartGroups": 140, "totalGroups": 140, - "staticArguments": 454, + "staticArguments": 460, "certainArguments": 0, - "knownStableArguments": 1767, + "knownStableArguments": 1773, "knownUnstableArguments": 0, "unknownStableArguments": 0, - "totalArguments": 1767, + "totalArguments": 1773, "markedStableClasses": 0, "inferredStableClasses": 2, "inferredUnstableClasses": 1, diff --git a/ui/report/compose-reports/ui_debug-classes.txt b/ui/report/compose-reports/ui_debug-classes.txt index 788c6814a..f0c83eedd 100644 --- a/ui/report/compose-reports/ui_debug-classes.txt +++ b/ui/report/compose-reports/ui_debug-classes.txt @@ -80,6 +80,21 @@ stable class QuackSecondaryRoundSmallButtonDefaults { stable var typography: QuackTypography stable var disabledTypography: QuackTypography } +stable class QuackSwitchColors { + stable val track: QuackColor + stable val disableTrack: QuackColor + stable val thumb: QuackColor + stable val thumbStroke: QuackColor + stable val disableThumb: QuackColor + stable val disableThumbStroke: QuackColor +} +stable class QuackTabColors { + stable val background: QuackColor + stable val underline: QuackColor + stable val indicate: QuackColor + stable val contentColor: QuackColor + stable val disableContentColor: QuackColor +} stable class QuackTagColors { stable val backgroundColor: QuackColor stable val unselectedBackgroundColor: QuackColor @@ -127,3 +142,75 @@ stable class QuackGrayscaleOutlinedTagDefaults { stable var typography: QuackTypography stable var unselectedTypography: QuackTypography } +stable class Success { + stable val label: String? + = Stable +} +stable class Error { + stable val label: String? + = Stable +} +stable class Default { + = Stable +} +stable class TextFieldValidationState { +} +stable class Invisible { + stable val baselineLabel: String + stable val baselineTypography: QuackTypography? + = Stable +} +stable class Gone { + = Stable +} +stable class TextFieldValidationLabelVisibilityStrategy { +} +stable class TextFieldColors { + stable val backgroundColor: QuackColor + stable val contentColor: QuackColor + stable val placeholderColor: QuackColor + stable val errorColor: QuackColor + stable val successColor: QuackColor + = Stable +} +stable class TextFieldColors { + stable val backgroundColor: QuackColor? + stable val backgroundColorGetter: Function2<@[ParameterName(name = 'text')] String, @[ParameterName(name = 'focusInteraction')] FocusInteraction?, QuackColor>? + stable val contentColor: QuackColor + stable val placeholderColor: QuackColor + = Stable +} +stable class QuackDefaultTextFieldDefaults { + stable var colors: TextFieldColors + stable var contentPadding: PaddingValues + stable var contentSpacedBy: Dp + stable var validationLabelAndIndicatorSpacedBy: Dp + stable var typography: QuackTypography + stable val validationLabelTypography: QuackTypography + = Stable +} +stable class QuackDefaultLargeTextFieldDefaults { + stable var colors: TextFieldColors + stable var contentPadding: PaddingValues + stable var contentSpacedBy: Dp + stable var validationLabelAndIndicatorSpacedBy: Dp + stable var typography: QuackTypography + stable val validationLabelTypography: QuackTypography + = Stable +} +stable class QuackFilledLargeTextFieldDefaults { + stable var radius: Dp + stable var colors: TextFieldColors + stable var contentPadding: PaddingValues + stable var contentSpacedBy: Dp + stable var typography: QuackTypography + = Stable +} +stable class QuackFilledFlatTextFieldDefaults { + stable var radius: Dp + stable var colors: TextFieldColors + stable var contentPadding: PaddingValues + stable var contentSpacedBy: Dp + stable var typography: QuackTypography + = Stable +} diff --git a/ui/report/compose-reports/ui_debug-composables.csv b/ui/report/compose-reports/ui_debug-composables.csv index c98d0f702..ba45f167c 100644 --- a/ui/report/compose-reports/ui_debug-composables.csv +++ b/ui/report/compose-reports/ui_debug-composables.csv @@ -1,5 +1,22 @@ package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls, team.duckie.quackquack.ui.QuackButton,QuackButton,1,0,0,0,0,0,0,0,1,6, team.duckie.quackquack.ui.QuackBaseButton,QuackBaseButton,1,1,1,0,0,0,0,0,1,2, +team.duckie.quackquack.ui.QuackIcon,QuackIcon,1,0,0,0,0,0,0,0,2,3, +team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,1,1, +team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,2,3, +team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,2,6, +team.duckie.quackquack.ui.QuackSwitch,QuackSwitch,1,1,1,0,0,0,0,0,1,8, +team.duckie.quackquack.ui.QuackTab,QuackTab,1,1,1,0,0,0,1,0,3,11, team.duckie.quackquack.ui.QuackTag,QuackTag,1,0,0,0,0,0,0,0,1,6, team.duckie.quackquack.ui.QuackBaseTag,QuackBaseTag,1,1,1,0,0,0,0,0,1,2, +team.duckie.quackquack.ui.QuackText,QuackText,1,1,1,0,0,0,0,0,4,11, +team.duckie.quackquack.ui.ClickableText,ClickableText,1,1,1,0,0,0,0,0,1,4, +team.duckie.quackquack.ui.rememberSpanAnnotatedString,rememberSpanAnnotatedString,1,0,0,0,0,0,0,0,1,1, +team.duckie.quackquack.ui.QuackDefaultTextField,QuackDefaultTextField,1,0,0,0,0,0,0,0,1,7, +team.duckie.quackquack.ui.QuackDefaultTextField,QuackDefaultTextField,1,0,0,0,0,0,0,0,1,10, +team.duckie.quackquack.ui.QuackFilledTextField,QuackFilledTextField,1,0,0,0,0,0,0,0,1,7, +team.duckie.quackquack.ui.QuackFilledTextField,QuackFilledTextField,1,0,0,0,0,0,0,0,1,10, +team.duckie.quackquack.ui.QuackBaseDefaultTextField,QuackBaseDefaultTextField,1,1,1,0,0,0,0,0,1,27, +team.duckie.quackquack.ui.QuackOutlinedTextField,QuackOutlinedTextField,1,0,0,0,0,0,0,0,1,0, +team.duckie.quackquack.ui.util.rememberLtrTextMeasurer,rememberLtrTextMeasurer,1,0,0,0,0,0,0,0,1,3, +team.duckie.quackquack.ui.util.currentFontScale,currentFontScale,1,0,0,1,1,0,0,0,1,1, diff --git a/ui/report/compose-reports/ui_debug-composables.txt b/ui/report/compose-reports/ui_debug-composables.txt index 75270c240..37845ed79 100644 --- a/ui/report/compose-reports/ui_debug-composables.txt +++ b/ui/report/compose-reports/ui_debug-composables.txt @@ -22,6 +22,62 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackBase stable trailingIcon: ImageVector? stable onClick: Function0? ) +scheme("[androidx.compose.ui.UiComposable]") fun QuackIcon( + stable icon: ImageVector + stable modifier: Modifier? = @static Companion + stable size: Dp = @static 24.dp + stable tint: QuackColor = @static Companion.Unspecified + stable contentScale: ContentScale? = @static Companion.Fit + stable contentDescription: String? = @static null +) +scheme("[androidx.compose.ui.UiComposable]") fun QuackImage( + stable src: ImageVector + stable modifier: Modifier? = @static Companion + stable tint: QuackColor = @static Companion.Unspecified + stable contentScale: ContentScale? = @static Companion.Fit +) +scheme("[androidx.compose.ui.UiComposable]") fun QuackImage( + stable src: Int + stable modifier: Modifier? = @static Companion + stable tint: QuackColor = @static Companion.Unspecified + stable contentScale: ContentScale? = @static Companion.Fit + stable contentDescription: String? = @static null +) +fun QuackImage( + unstable src: Any? + stable modifier: Modifier? = @static Companion + stable tint: QuackColor = @static Companion.Unspecified + stable contentScale: ContentScale? = @static Companion.Fit + stable contentDescription: String? = @static null +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackSwitch( + stable enabled: Boolean + stable colors: QuackSwitchColors? = @static Companion.defaultSwitchColors + stable modifier: Modifier? = @static Companion + stable onClick: Function0 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackTab( + stable index: Int + stable colors: QuackTabColors? = @static Companion.defaultTabColors() + indicatorStartXOffsetAnimatable: Animatable? = @dynamic remember({ + Animatable ( + initialValue = QuackTabIndicatorXOffsetInitialValue , + typeConverter = Companion . VectorConverter , + label = "QuackTabIndicatorStartXOffset" + ) +} +, $composer, 0) + indicatorEndXOffsetAnimatable: Animatable? = @dynamic remember({ + Animatable ( + initialValue = QuackTabIndicatorXOffsetInitialValue , + typeConverter = Companion . VectorConverter , + label = "QuackTabIndicatorEndXOffset" + ) +} +, $composer, 0) + stable modifier: Modifier? = @static Companion + stable content: @[ExtensionFunctionType] Function1 +) scheme("[androidx.compose.ui.UiComposable]") fun QuackTag( stable text: String stable style: QuackTagStyle @@ -47,3 +103,201 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackBase stable trailingIconOnClick: Function0? stable onClick: Function0? ) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackText( + stable text: String + stable typography: QuackTypography + stable modifier: Modifier? = @static Companion + stable singleLine: Boolean = @static false + stable softWrap: Boolean = @static true + stable overflow: TextOverflow = @static Companion.Ellipsis +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ClickableText( + stable modifier: Modifier + stable text: String + stable highlightData: TextHighlightData + stable style: TextStyle + stable softWrap: Boolean + stable overflow: TextOverflow + stable maxLines: Int +) +fun rememberSpanAnnotatedString( + stable text: String + unstable spanTexts: List + stable spanStyle: SpanStyle + unstable annotationTexts: List +): AnnotatedString +fun QuackDefaultTextField( + stable value: String + stable onValueChange: Function1<@[ParameterName(name = 'value')] String, Unit> + stable style: QuackTextFieldStyle + stable modifier: Modifier? = @static Companion + stable enabled: Boolean = @static true + stable readOnly: Boolean = @static false + stable placeholderText: String? = @static null + stable placeholderStrategy: TextFieldPlaceholderStrategy? = @static TextFieldPlaceholderStrategy.Hidable + stable keyboardOptions: KeyboardOptions? = @static Companion.Default + stable keyboardActions: KeyboardActions? = @static Companion.Default + stable singleLine: Boolean = @static false + stable minLines: Int = @static 1 + stable maxLines: Int = @dynamic if (singleLine) { + 1 +} else { + Companion . MAX_VALUE +} + + stable visualTransformation: VisualTransformation? = @static Companion.None + stable onTextLayout: Function1<@[ParameterName(name = 'layoutResult')] TextLayoutResult, Unit>? = @static { it: TextLayoutResult -> + +} + + stable validationState: TextFieldValidationState? = @static Default + stable validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy? = @static Gone + stable interactionSource: MutableInteractionSource? = @static remember({ + MutableInteractionSource ( ) +} +, $composer, 0) +) +scheme("[androidx.compose.ui.UiComposable]") fun QuackDefaultTextField( + stable value: TextFieldValue + stable onValueChange: Function1<@[ParameterName(name = 'value')] TextFieldValue, Unit> + stable style: QuackTextFieldStyle + stable modifier: Modifier? = @static Companion + stable enabled: Boolean = @static true + stable readOnly: Boolean = @static false + stable placeholderText: String? = @static null + stable placeholderStrategy: TextFieldPlaceholderStrategy? = @static TextFieldPlaceholderStrategy.Hidable + stable keyboardOptions: KeyboardOptions? = @static Companion.Default + stable keyboardActions: KeyboardActions? = @static Companion.Default + stable singleLine: Boolean = @static false + stable minLines: Int = @static 1 + stable maxLines: Int = @dynamic if (singleLine) { + 1 +} else { + Companion . MAX_VALUE +} + + stable visualTransformation: VisualTransformation? = @static Companion.None + stable onTextLayout: Function1<@[ParameterName(name = 'layoutResult')] TextLayoutResult, Unit>? = @static { it: TextLayoutResult -> + +} + + stable validationState: TextFieldValidationState? = @static Default + stable validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy? = @static Gone + stable interactionSource: MutableInteractionSource? = @static remember({ + MutableInteractionSource ( ) +} +, $composer, 0) +) +fun QuackFilledTextField( + stable value: String + stable onValueChange: Function1<@[ParameterName(name = 'value')] String, Unit> + stable style: QuackTextFieldStyle + stable modifier: Modifier? = @static Companion + stable enabled: Boolean = @static true + stable readOnly: Boolean = @static false + stable placeholderText: String? = @static null + stable placeholderStrategy: TextFieldPlaceholderStrategy? = @static TextFieldPlaceholderStrategy.Hidable + stable keyboardOptions: KeyboardOptions? = @static Companion.Default + stable keyboardActions: KeyboardActions? = @static Companion.Default + stable singleLine: Boolean = @static false + stable minLines: Int = @static 1 + stable maxLines: Int = @dynamic if (singleLine) { + 1 +} else { + Companion . MAX_VALUE +} + + stable visualTransformation: VisualTransformation? = @static Companion.None + stable onTextLayout: Function1<@[ParameterName(name = 'layoutResult')] TextLayoutResult, Unit>? = @static { it: TextLayoutResult -> + +} + + stable interactionSource: MutableInteractionSource? = @static remember({ + MutableInteractionSource ( ) +} +, $composer, 0) +) +scheme("[androidx.compose.ui.UiComposable]") fun QuackFilledTextField( + stable value: TextFieldValue + stable onValueChange: Function1<@[ParameterName(name = 'value')] TextFieldValue, Unit> + stable style: QuackTextFieldStyle + stable modifier: Modifier? = @static Companion + stable enabled: Boolean = @static true + stable readOnly: Boolean = @static false + stable placeholderText: String? = @static null + stable placeholderStrategy: TextFieldPlaceholderStrategy? = @static TextFieldPlaceholderStrategy.Hidable + stable keyboardOptions: KeyboardOptions? = @static Companion.Default + stable keyboardActions: KeyboardActions? = @static Companion.Default + stable singleLine: Boolean = @static false + stable minLines: Int = @static 1 + stable maxLines: Int = @dynamic if (singleLine) { + 1 +} else { + Companion . MAX_VALUE +} + + stable visualTransformation: VisualTransformation? = @static Companion.None + stable onTextLayout: Function1<@[ParameterName(name = 'layoutResult')] TextLayoutResult, Unit>? = @static { it: TextLayoutResult -> + +} + + stable interactionSource: MutableInteractionSource? = @static remember({ + MutableInteractionSource ( ) +} +, $composer, 0) +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackBaseDefaultTextField( + stable value: TextFieldValue + stable onValueChange: Function1<@[ParameterName(name = 'value')] TextFieldValue, Unit> + stable modifier: Modifier + stable enabled: Boolean + stable readOnly: Boolean + stable placeholderText: String? + stable placeholderStrategy: TextFieldPlaceholderStrategy + stable keyboardOptions: KeyboardOptions + stable keyboardActions: KeyboardActions + stable singleLine: Boolean + stable minLines: Int + stable maxLines: Int + stable visualTransformation: VisualTransformation + stable onTextLayout: Function1<@[ParameterName(name = 'layoutResult')] TextLayoutResult, Unit> + stable validationState: TextFieldValidationState? + stable validationLabelVisibilityStrategy: TextFieldValidationLabelVisibilityStrategy? + stable interactionSource: MutableInteractionSource + stable backgroundColor: QuackColor + stable contentPadding: PaddingValues? + stable contentSpacedBy: Dp + stable validationLabelAndIndicatorSpacedBy: Dp? + stable typography: QuackTypography + stable placeholderTypography: QuackTypography + stable errorTypography: QuackTypography? + stable successTypography: QuackTypography? + stable textFieldType: QuackTextFieldType + stable leadingIcon: ImageVector? + stable leadingIconSize: Dp? + stable leadingIconTint: QuackColor? + stable leadingIconContentScale: ContentScale? + stable leadingIconContentDescription: String? + stable leadingIconOnClick: Function0? + stable trailingIcon: ImageVector? + stable trailingIconSize: Dp? + stable trailingIconTint: QuackColor? + stable trailingIconContentScale: ContentScale? + stable trailingIconContentDescription: String? + stable trailingIconOnClick: Function0? + stable indicatorThickness: Dp? + stable indicatorColor: QuackColor? + stable indicatorDirection: VerticalDirection? + stable counterBaseColor: QuackColor? + stable counterHighlightColor: QuackColor? + stable counterTypography: QuackTypography? + stable counterBaseAndHighlightGap: Dp? + stable counterMaxLength: Int? +) +fun QuackOutlinedTextField() +fun rememberLtrTextMeasurer( + stable cacheSize: Int = @static 8 +): TextMeasurer +readonly inline fun currentFontScale( + stable content: Function1<@[ParameterName(name = 'fontScale')] Float, T> +): T diff --git a/ui/report/compose-reports/ui_release-composables.csv b/ui/report/compose-reports/ui_release-composables.csv index a8f435b64..ba45f167c 100644 --- a/ui/report/compose-reports/ui_release-composables.csv +++ b/ui/report/compose-reports/ui_release-composables.csv @@ -5,28 +5,6 @@ team.duckie.quackquack.ui.QuackIcon,QuackIcon,1,0,0,0,0,0,0,0,2,3, team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,1,1, team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,2,3, team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,2,6, -team.duckie.quackquack.ui.sugar.QuackPrimaryLargeButton,QuackPrimaryLargeButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackSecondaryLargeButton,QuackSecondaryLargeButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackMediumButton,QuackMediumButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackPrimaryFilledSmallButton,QuackPrimaryFilledSmallButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackPrimaryOutlinedSmallButton,QuackPrimaryOutlinedSmallButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackPrimaryOutlinedRoundSmallButton,QuackPrimaryOutlinedRoundSmallButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackSecondarySmallButton,QuackSecondarySmallButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackSecondaryRoundSmallButton,QuackSecondaryRoundSmallButton,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackOutlinedTag,QuackOutlinedTag,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackFilledTag,QuackFilledTag,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackGrayscaleFlatTag,QuackGrayscaleFlatTag,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackGrayscaleOutlinedTag,QuackGrayscaleOutlinedTag,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackBody1,QuackBody1,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackBody2,QuackBody2,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackBody3,QuackBody3,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackHeadLine1,QuackHeadLine1,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackHeadLine2,QuackHeadLine2,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackLarge1,QuackLarge1,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackSubtitle,QuackSubtitle,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackSubtitle2,QuackSubtitle2,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackTitle1,QuackTitle1,1,0,0,0,0,0,0,0,1,1, -team.duckie.quackquack.ui.sugar.QuackTitle2,QuackTitle2,1,0,0,0,0,0,0,0,1,1, team.duckie.quackquack.ui.QuackSwitch,QuackSwitch,1,1,1,0,0,0,0,0,1,8, team.duckie.quackquack.ui.QuackTab,QuackTab,1,1,1,0,0,0,1,0,3,11, team.duckie.quackquack.ui.QuackTag,QuackTag,1,0,0,0,0,0,0,0,1,6, diff --git a/ui/report/compose-reports/ui_release-composables.txt b/ui/report/compose-reports/ui_release-composables.txt index 1afe56d80..37845ed79 100644 --- a/ui/report/compose-reports/ui_release-composables.txt +++ b/ui/report/compose-reports/ui_release-composables.txt @@ -50,160 +50,6 @@ fun QuackImage( stable contentScale: ContentScale? = @static Companion.Fit stable contentDescription: String? = @static null ) -scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryLargeButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackSecondaryLargeButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackMediumButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryFilledSmallButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryOutlinedSmallButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryOutlinedRoundSmallButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackSecondarySmallButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackSecondaryRoundSmallButton( - stable modifier: Modifier? = @static Companion - stable enabled: Boolean = @static true - stable text: String - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -fun QuackOutlinedTag( - stable text: String - stable modifier: Modifier? = @static Companion - stable selected: Boolean = @static true - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -fun QuackFilledTag( - stable text: String - stable modifier: Modifier? = @static Companion - stable selected: Boolean = @static true - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -fun QuackGrayscaleFlatTag( - stable text: String - stable modifier: Modifier? = @static Companion - stable selected: Boolean = @static true - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -fun QuackGrayscaleOutlinedTag( - stable text: String - stable modifier: Modifier? = @static Companion - stable selected: Boolean = @static true - stable rippleEnabled: Boolean = @static true - stable onClick: Function0 -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackBody1( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackBody2( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackBody3( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackHeadLine1( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackHeadLine2( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackLarge1( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackSubtitle( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackSubtitle2( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackTitle1( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) -scheme("[androidx.compose.ui.UiComposable]") fun QuackTitle2( - stable modifier: Modifier? = @static Companion - stable text: String - stable singleLine: Boolean = @static false - stable softWrap: Boolean = @static true - stable overflow: TextOverflow = @static Companion.Ellipsis -) restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackSwitch( stable enabled: Boolean stable colors: QuackSwitchColors? = @static Companion.defaultSwitchColors diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt index a515804b3..ffc2aa000 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt @@ -49,8 +49,8 @@ import team.duckie.quackquack.material.QuackTypography import team.duckie.quackquack.material.quackSurface import team.duckie.quackquack.runtime.QuackDataModifierModel import team.duckie.quackquack.runtime.quackMaterializeOf -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable import team.duckie.quackquack.ui.plugin.interceptor.rememberInterceptedStyleSafely import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi import team.duckie.quackquack.ui.util.QuackDsl @@ -758,6 +758,7 @@ public fun Modifier.icons( * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. */ +@Sugarable @MustBeTested(passed = true) @Composable @NonRestartableComposable @@ -868,7 +869,6 @@ private const val TrailingIconLayoutId = "QuackBaseButtonTrailingIcon" * * 이 컴포넌트는 [QuackButtonStyle]의 필드를 개별 인자로 받습니다. */ -@NoSugar @Composable public fun QuackBaseButton( modifier: Modifier, diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/icon.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/icon.kt index 926efd97e..84bf9778f 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/icon.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/icon.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import team.duckie.quackquack.material.QuackColor import team.duckie.quackquack.material.icon.QuackIcon -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.ui.util.fontScaleAwareIconSize import team.duckie.quackquack.util.applyIf @@ -38,7 +37,6 @@ import team.duckie.quackquack.util.applyIf * @param contentScale [icon]에 적용할 [contentScale][ContentScale] * @param contentDescription [icon]을 설명하는 문구. 접근성 서비스에 사용됩니다. */ -@NoSugar @NonRestartableComposable @Composable public fun QuackIcon( diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/image.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/image.kt index 7d4287ee3..c927f4f79 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/image.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/image.kt @@ -27,7 +27,6 @@ import coil.ImageLoader import coil.compose.AsyncImage import coil.compose.LocalImageLoader import team.duckie.quackquack.material.QuackColor -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.ui.plugin.EmptyQuackPlugins import team.duckie.quackquack.ui.plugin.LocalQuackPlugins import team.duckie.quackquack.ui.plugin.QuackPluginLocal @@ -51,7 +50,6 @@ import team.duckie.quackquack.util.modifier.getElementByTypeOrNull replaceWith = ReplaceWith("QuackIcon"), level = DeprecationLevel.ERROR, ) -@NoSugar @NonRestartableComposable @Composable public fun QuackImage( @@ -76,7 +74,6 @@ public fun QuackImage( * @param contentScale drawable 리소스에 적용할 [contentScale][ContentScale] * @param contentDescription 접근성 서비스에서 이 이미지가 무엇을 나타내는지 설명할 문구 */ -@NoSugar @NonRestartableComposable @Composable public fun QuackImage( @@ -116,7 +113,6 @@ public fun QuackImage( * @param contentScale 이미지 리소스에 적용할 [contentScale][ContentScale] * @param contentDescription 접근성 서비스에서 이 이미지가 무엇을 나타내는지 설명할 문구 */ -@NoSugar @NonRestartableComposable @Composable public fun QuackImage( diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt deleted file mode 100644 index 90c87ea42..000000000 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt +++ /dev/null @@ -1,760 +0,0 @@ -// This file was automatically generated by sugar-processor. -// Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") -@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) -@file:SugarGeneratedFile - -package team.duckie.quackquack.ui.sugar - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.NonRestartableComposable -import androidx.compose.ui.Modifier -import kotlin.Boolean -import kotlin.Function0 -import kotlin.OptIn -import kotlin.String -import kotlin.Suppress -import kotlin.Unit -import team.duckie.quackquack.casa.`annotation`.Casa -import team.duckie.quackquack.casa.`annotation`.CasaValue -import team.duckie.quackquack.casa.`annotation`.SugarGeneratorUsage -import team.duckie.quackquack.sugar.material.SugarCompilerApi -import team.duckie.quackquack.sugar.material.SugarGeneratedFile -import team.duckie.quackquack.sugar.material.SugarRefer -import team.duckie.quackquack.sugar.material.sugar -import team.duckie.quackquack.ui.QuackButton -import team.duckie.quackquack.ui.QuackButtonStyle -import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.PrimaryLarge] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackPrimaryLargeButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.PrimaryLarge, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.SecondaryLarge] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackSecondaryLargeButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.SecondaryLarge, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.Medium] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackMediumButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.Medium, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.PrimaryFilledSmall] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackPrimaryFilledSmallButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.PrimaryFilledSmall, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.PrimaryOutlinedSmall] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackPrimaryOutlinedSmallButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.PrimaryOutlinedSmall, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.PrimaryOutlinedRoundSmall] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackPrimaryOutlinedRoundSmallButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.PrimaryOutlinedRoundSmall, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.SecondarySmall] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackSecondarySmallButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.SecondarySmall, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 버튼을 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 - * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackButtonStyle.contentPadding]이 - * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 배치 정책 - * - * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 - * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, - * [contentPadding][QuackButtonStyle.contentPadding]으로 - * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 - * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 - * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. - * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 - * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 - * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. - * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 - * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 - * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 - * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, - * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 - * 위해 이 정책이 사용됩니다. - * - * ### 사용 가능 데코레이터 - * - * | style | [icons][Modifier.icons] | description - * | - * | :-------------------------------: | :---------------------: | - * :-----------------------------------------------------: | - * | [Large][QuackLargeButtonStyle] | ⭕ | - * | - * | [Medium][QuackMediumButtonStyle] | ⭕ | - * | - * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. - * | - * - * This component uses [QuackButtonStyle.SecondaryRoundSmall] as the token value for `style`. - * - * This document was automatically generated by [QuackButton]. - * If any contents are broken, please check the original document. - * - * @param enabled 활성화 상태 여부 - * @param text 중앙에 표시할 텍스트 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackSecondaryRoundSmallButton( - modifier: Modifier = sugar(), - enabled: Boolean = sugar(), - @CasaValue("\"QuackButton is experimental\"") text: String, - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackButton( - modifier = modifier, - enabled = enabled, - style = QuackButtonStyle.SecondaryRoundSmall, - text = text, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt deleted file mode 100644 index 71f02684d..000000000 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/tag.kt +++ /dev/null @@ -1,368 +0,0 @@ -// This file was automatically generated by sugar-processor. -// Do not modify it manually. -// @formatter:off -@file:Suppress("NoConsecutiveBlankLines", "PackageDirectoryMismatch", "Wrapping", - "TrailingCommaOnCallSite", "ArgumentListWrapping", "RedundantVisibilityModifier", - "UnusedImport", "NoUnusedImports", "SpacingAroundParens", "Indentation", "NoUnitReturn", - "RedundantUnitReturnType", "ModifierParameter", "KDocUnresolvedReference", "NoTrailingSpaces", - "NoMultipleSpaces", "ktlint") -@file:OptIn(SugarCompilerApi::class, SugarGeneratorUsage::class) -@file:SugarGeneratedFile - -package team.duckie.quackquack.ui.sugar - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.NonRestartableComposable -import androidx.compose.ui.Modifier -import kotlin.Boolean -import kotlin.Function0 -import kotlin.OptIn -import kotlin.String -import kotlin.Suppress -import kotlin.Unit -import team.duckie.quackquack.casa.`annotation`.Casa -import team.duckie.quackquack.casa.`annotation`.CasaValue -import team.duckie.quackquack.casa.`annotation`.SugarGeneratorUsage -import team.duckie.quackquack.sugar.material.SugarCompilerApi -import team.duckie.quackquack.sugar.material.SugarGeneratedFile -import team.duckie.quackquack.sugar.material.SugarRefer -import team.duckie.quackquack.sugar.material.sugar -import team.duckie.quackquack.ui.QuackTag -import team.duckie.quackquack.ui.QuackTagStyle -import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi - -/** - * 태그를 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [태그의 스타일][QuackTagStyle]에서 [contentPadding][QuackTagStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 태그의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackTagStyle.contentPadding]은 태그의 - * 텍스트와 후행 아이콘을 기준으로 적용됩니다. 태그 컴포넌트는 [trailingIcon][Modifier.trailingIcon] 데코레이터로 - * 후행 아이콘을 추가할 수 있고, 후행 아이콘 여부에 따라 패딩 정책이 결정됩니다. 후행 아이콘이 있다면 세로와 - * 가로에 따라 패딩을 적용하는 방식이 달라집니다. 세로의 경우는 태그 텍스트를 기준으로 적용되고, 가로의 경우는 - * 후행 아이콘의 터치 영역을 증가시키는 식으로 적용됩니다. 기본적으로 후행 아이콘은 16px의 사이즈를 갖습니다. - * 유저 입장에서 16px의 터치 영역은 좋은 경험을 제공하지 못할 것으로 예상하여, [전체 가로 패딩][QuackPadding.vertical]의 - * 오른쪽 영역을 후행 아이콘의 오른쪽 패딩으로 적용합니다. 이때, [전체 가로 패딩][QuackPadding.vertical]의 오른쪽 - * 영역을 그대로 적용하는 게 아니라 해당 값에서 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]을 뺀 - * 값을 적용합니다. 이는 디자인 가이드라인에 의거합니다. 그리고 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]의 - * 반을 후행 아이콘의 왼쪽 패딩으로 적용합니다. [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy] 반의 - * 나머지 부분은 태그 텍스트의 오른쪽 패딩으로 적용됩니다. 후행 아이콘이 없다면 단순히 태그 텍스트를 기준으로 패딩이 - * 적용됩니다. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackTagStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackTagStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackTagStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackTagStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackTagStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackTagStyle.contentPadding]이 - * 무시되고 태그의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackTagStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackTagStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 사용 가능 데코레이터 - * - * | style | [trailingIcon][Modifier.trailingIcon] - * | description | - * |:------------------------------------------------------:|:-------------------------------------:|:----------------------------------:| - * | [Outlined][QuackOutlinedTagDefaults] | ⭕ - * | | - * | [Filled][QuackFilledTagDefaults] | ⭕ - * | | - * | [GrayscaleFlat][QuackGrayscaleFlatTagDefaults] | ❌ - * | 태그의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. | - * | [GrayscaleOutlined][QuackGrayscaleOutlinedTagDefaults] | ⭕ - * | | - * - * This component uses [QuackTagStyle.Outlined] as the token value for `style`. - * - * This document was automatically generated by [QuackTag]. - * If any contents are broken, please check the original document. - * - * @param text 중앙에 표시할 텍스트 - * @param selected 선택 상태 여부 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 - * 항상 클릭 가능합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackTag") -public fun QuackOutlinedTag( - @CasaValue("\"QuackTagPreview\"") text: String, - modifier: Modifier = sugar(), - selected: Boolean = sugar(), - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackTag( - text = text, - style = QuackTagStyle.Outlined, - modifier = modifier, - selected = selected, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 태그를 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [태그의 스타일][QuackTagStyle]에서 [contentPadding][QuackTagStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 태그의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackTagStyle.contentPadding]은 태그의 - * 텍스트와 후행 아이콘을 기준으로 적용됩니다. 태그 컴포넌트는 [trailingIcon][Modifier.trailingIcon] 데코레이터로 - * 후행 아이콘을 추가할 수 있고, 후행 아이콘 여부에 따라 패딩 정책이 결정됩니다. 후행 아이콘이 있다면 세로와 - * 가로에 따라 패딩을 적용하는 방식이 달라집니다. 세로의 경우는 태그 텍스트를 기준으로 적용되고, 가로의 경우는 - * 후행 아이콘의 터치 영역을 증가시키는 식으로 적용됩니다. 기본적으로 후행 아이콘은 16px의 사이즈를 갖습니다. - * 유저 입장에서 16px의 터치 영역은 좋은 경험을 제공하지 못할 것으로 예상하여, [전체 가로 패딩][QuackPadding.vertical]의 - * 오른쪽 영역을 후행 아이콘의 오른쪽 패딩으로 적용합니다. 이때, [전체 가로 패딩][QuackPadding.vertical]의 오른쪽 - * 영역을 그대로 적용하는 게 아니라 해당 값에서 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]을 뺀 - * 값을 적용합니다. 이는 디자인 가이드라인에 의거합니다. 그리고 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]의 - * 반을 후행 아이콘의 왼쪽 패딩으로 적용합니다. [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy] 반의 - * 나머지 부분은 태그 텍스트의 오른쪽 패딩으로 적용됩니다. 후행 아이콘이 없다면 단순히 태그 텍스트를 기준으로 패딩이 - * 적용됩니다. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackTagStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackTagStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackTagStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackTagStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackTagStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackTagStyle.contentPadding]이 - * 무시되고 태그의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackTagStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackTagStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 사용 가능 데코레이터 - * - * | style | [trailingIcon][Modifier.trailingIcon] - * | description | - * |:------------------------------------------------------:|:-------------------------------------:|:----------------------------------:| - * | [Outlined][QuackOutlinedTagDefaults] | ⭕ - * | | - * | [Filled][QuackFilledTagDefaults] | ⭕ - * | | - * | [GrayscaleFlat][QuackGrayscaleFlatTagDefaults] | ❌ - * | 태그의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. | - * | [GrayscaleOutlined][QuackGrayscaleOutlinedTagDefaults] | ⭕ - * | | - * - * This component uses [QuackTagStyle.Filled] as the token value for `style`. - * - * This document was automatically generated by [QuackTag]. - * If any contents are broken, please check the original document. - * - * @param text 중앙에 표시할 텍스트 - * @param selected 선택 상태 여부 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 - * 항상 클릭 가능합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackTag") -public fun QuackFilledTag( - @CasaValue("\"QuackTagPreview\"") text: String, - modifier: Modifier = sugar(), - selected: Boolean = sugar(), - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackTag( - text = text, - style = QuackTagStyle.Filled, - modifier = modifier, - selected = selected, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 태그를 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [태그의 스타일][QuackTagStyle]에서 [contentPadding][QuackTagStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 태그의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackTagStyle.contentPadding]은 태그의 - * 텍스트와 후행 아이콘을 기준으로 적용됩니다. 태그 컴포넌트는 [trailingIcon][Modifier.trailingIcon] 데코레이터로 - * 후행 아이콘을 추가할 수 있고, 후행 아이콘 여부에 따라 패딩 정책이 결정됩니다. 후행 아이콘이 있다면 세로와 - * 가로에 따라 패딩을 적용하는 방식이 달라집니다. 세로의 경우는 태그 텍스트를 기준으로 적용되고, 가로의 경우는 - * 후행 아이콘의 터치 영역을 증가시키는 식으로 적용됩니다. 기본적으로 후행 아이콘은 16px의 사이즈를 갖습니다. - * 유저 입장에서 16px의 터치 영역은 좋은 경험을 제공하지 못할 것으로 예상하여, [전체 가로 패딩][QuackPadding.vertical]의 - * 오른쪽 영역을 후행 아이콘의 오른쪽 패딩으로 적용합니다. 이때, [전체 가로 패딩][QuackPadding.vertical]의 오른쪽 - * 영역을 그대로 적용하는 게 아니라 해당 값에서 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]을 뺀 - * 값을 적용합니다. 이는 디자인 가이드라인에 의거합니다. 그리고 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]의 - * 반을 후행 아이콘의 왼쪽 패딩으로 적용합니다. [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy] 반의 - * 나머지 부분은 태그 텍스트의 오른쪽 패딩으로 적용됩니다. 후행 아이콘이 없다면 단순히 태그 텍스트를 기준으로 패딩이 - * 적용됩니다. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackTagStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackTagStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackTagStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackTagStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackTagStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackTagStyle.contentPadding]이 - * 무시되고 태그의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackTagStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackTagStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 사용 가능 데코레이터 - * - * | style | [trailingIcon][Modifier.trailingIcon] - * | description | - * |:------------------------------------------------------:|:-------------------------------------:|:----------------------------------:| - * | [Outlined][QuackOutlinedTagDefaults] | ⭕ - * | | - * | [Filled][QuackFilledTagDefaults] | ⭕ - * | | - * | [GrayscaleFlat][QuackGrayscaleFlatTagDefaults] | ❌ - * | 태그의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. | - * | [GrayscaleOutlined][QuackGrayscaleOutlinedTagDefaults] | ⭕ - * | | - * - * This component uses [QuackTagStyle.GrayscaleFlat] as the token value for `style`. - * - * This document was automatically generated by [QuackTag]. - * If any contents are broken, please check the original document. - * - * @param text 중앙에 표시할 텍스트 - * @param selected 선택 상태 여부 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 - * 항상 클릭 가능합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackTag") -public fun QuackGrayscaleFlatTag( - @CasaValue("\"QuackTagPreview\"") text: String, - modifier: Modifier = sugar(), - selected: Boolean = sugar(), - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackTag( - text = text, - style = QuackTagStyle.GrayscaleFlat, - modifier = modifier, - selected = selected, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} - -/** - * 태그를 그립니다. - * - * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. - * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. - * - * ### 패딩 정책 - * - * 1. [태그의 스타일][QuackTagStyle]에서 [contentPadding][QuackTagStyle.contentPadding] 옵션을 - * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 - * 태그의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackTagStyle.contentPadding]은 태그의 - * 텍스트와 후행 아이콘을 기준으로 적용됩니다. 태그 컴포넌트는 [trailingIcon][Modifier.trailingIcon] 데코레이터로 - * 후행 아이콘을 추가할 수 있고, 후행 아이콘 여부에 따라 패딩 정책이 결정됩니다. 후행 아이콘이 있다면 세로와 - * 가로에 따라 패딩을 적용하는 방식이 달라집니다. 세로의 경우는 태그 텍스트를 기준으로 적용되고, 가로의 경우는 - * 후행 아이콘의 터치 영역을 증가시키는 식으로 적용됩니다. 기본적으로 후행 아이콘은 16px의 사이즈를 갖습니다. - * 유저 입장에서 16px의 터치 영역은 좋은 경험을 제공하지 못할 것으로 예상하여, [전체 가로 패딩][QuackPadding.vertical]의 - * 오른쪽 영역을 후행 아이콘의 오른쪽 패딩으로 적용합니다. 이때, [전체 가로 패딩][QuackPadding.vertical]의 오른쪽 - * 영역을 그대로 적용하는 게 아니라 해당 값에서 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]을 뺀 - * 값을 적용합니다. 이는 디자인 가이드라인에 의거합니다. 그리고 [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy]의 - * 반을 후행 아이콘의 왼쪽 패딩으로 적용합니다. [텍스트와 후행 아이콘 사이 공간][QuackTagStyle.iconSpacedBy] 반의 - * 나머지 부분은 태그 텍스트의 오른쪽 패딩으로 적용됩니다. 후행 아이콘이 없다면 단순히 태그 텍스트를 기준으로 패딩이 - * 적용됩니다. - * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackTagStyle.contentPadding] - * 옵션은 무시됩니다. [contentPadding][QuackTagStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 - * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackTagStyle.contentPadding]을 - * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 - * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackTagStyle.contentPadding] - * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, - * [contentPadding][QuackTagStyle.contentPadding]으로 - * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 - * [contentPadding][QuackTagStyle.contentPadding]이 - * 무시되고 태그의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 - * [contentPadding][QuackTagStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. - * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 - * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackTagStyle.contentPadding] 무시 - * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) - * - * ### 사용 가능 데코레이터 - * - * | style | [trailingIcon][Modifier.trailingIcon] - * | description | - * |:------------------------------------------------------:|:-------------------------------------:|:----------------------------------:| - * | [Outlined][QuackOutlinedTagDefaults] | ⭕ - * | | - * | [Filled][QuackFilledTagDefaults] | ⭕ - * | | - * | [GrayscaleFlat][QuackGrayscaleFlatTagDefaults] | ❌ - * | 태그의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. | - * | [GrayscaleOutlined][QuackGrayscaleOutlinedTagDefaults] | ⭕ - * | | - * - * This component uses [QuackTagStyle.GrayscaleOutlined] as the token value for `style`. - * - * This document was automatically generated by [QuackTag]. - * If any contents are broken, please check the original document. - * - * @param text 중앙에 표시할 텍스트 - * @param selected 선택 상태 여부 - * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 - * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 - * 항상 클릭 가능합니다. - */ -@Casa -@Composable -@NonRestartableComposable -@ExperimentalQuackQuackApi -@SugarRefer("team.duckie.quackquack.ui.QuackTag") -public fun QuackGrayscaleOutlinedTag( - @CasaValue("\"QuackTagPreview\"") text: String, - modifier: Modifier = sugar(), - selected: Boolean = sugar(), - rippleEnabled: Boolean = sugar(), - @CasaValue("{}") onClick: () -> Unit, -): Unit { - QuackTag( - text = text, - style = QuackTagStyle.GrayscaleOutlined, - modifier = modifier, - selected = selected, - rippleEnabled = rippleEnabled, - onClick = onClick, - ) -} diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/switch.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/switch.kt index 3f8b4a0e1..c0160ec70 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/switch.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/switch.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import team.duckie.quackquack.material.QuackColor import team.duckie.quackquack.material.quackClickable -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.ui.plugin.interceptor.rememberInterceptedStyleSafely import team.duckie.quackquack.ui.util.onDrawFront @@ -126,7 +125,6 @@ private val colorTweenSpec = tween(durationMillis = AnimationMillis) * @param onClick 스위치가 클릭됐을 때 실행할 람다식. 토글은 stable 릴리스 전에 지원될 예정입니다. */ // TODO(impl): anchored-draggable 지원 (toggle) -@NoSugar @Composable public fun QuackSwitch( enabled: Boolean, diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/tab.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/tab.kt index 1a05c0452..49b973c2b 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/tab.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/tab.kt @@ -48,7 +48,6 @@ import team.duckie.quackquack.animation.animatedQuackTypographyAsState import team.duckie.quackquack.material.QuackColor import team.duckie.quackquack.material.QuackTypography import team.duckie.quackquack.material.quackClickable -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.ui.plugin.interceptor.rememberInterceptedStyleSafely import team.duckie.quackquack.ui.util.fastFilterById import team.duckie.quackquack.ui.util.onDrawFront @@ -205,7 +204,6 @@ private val tabSnapSpec = snap() * * 자세한 내용은 [QuackTabScope]를 참고하세요. */ -@NoSugar @Composable public fun QuackTab( index: Int, diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/tag.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/tag.kt index e92df7ce2..7f0d8561f 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/tag.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/tag.kt @@ -52,8 +52,8 @@ import team.duckie.quackquack.material.quackClickable import team.duckie.quackquack.material.quackSurface import team.duckie.quackquack.runtime.QuackDataModifierModel import team.duckie.quackquack.runtime.quackMaterializeOf -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable import team.duckie.quackquack.ui.plugin.interceptor.rememberInterceptedStyleSafely import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi import team.duckie.quackquack.ui.util.QuackDsl @@ -542,6 +542,7 @@ internal object QuackTagErrors { * @param onClick 클릭했을 때 실행할 람다식. 태그는 토글이 자유로워야 하므로 [selected]와 관계 없이 * 항상 클릭 가능합니다. */ +@Sugarable @ExperimentalQuackQuackApi @NonRestartableComposable @Composable @@ -648,7 +649,6 @@ private const val FakeTrailingIconLayoutId = "QuackBaseTagFakeTrailingIcon" * 이 컴포넌트는 [QuackTagStyle]의 필드를 개별 인자로 받습니다. */ @ExperimentalQuackQuackApi -@NoSugar @Composable public fun QuackBaseTag( modifier: Modifier, diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/text.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/text.kt index f96f198d3..7bc457dda 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/text.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/text.kt @@ -39,6 +39,7 @@ import team.duckie.quackquack.runtime.quackComposed import team.duckie.quackquack.runtime.quackMaterializeOf import team.duckie.quackquack.sugar.material.SugarName import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable import team.duckie.quackquack.ui.plugin.interceptor.rememberInterceptedStyleSafely import team.duckie.quackquack.ui.util.asLoose import team.duckie.quackquack.ui.util.rememberLtrTextMeasurer @@ -166,6 +167,7 @@ internal object QuackTextErrors { * * @sample team.duckie.quackquack.ui.sample.SampleTest */ +@Sugarable @SugarName(SugarName.PREFIX_NAME + SugarName.TOKEN_NAME) @Composable public fun QuackText( diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/textfield.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/textfield.kt index 7314f1bb5..0c8950a18 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/textfield.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/textfield.kt @@ -84,8 +84,8 @@ import team.duckie.quackquack.material.quackSurface import team.duckie.quackquack.material.theme.LocalQuackTextFieldTheme import team.duckie.quackquack.runtime.QuackDataModifierModel import team.duckie.quackquack.runtime.quackMaterializeOf -import team.duckie.quackquack.sugar.material.NoSugar import team.duckie.quackquack.sugar.material.SugarToken +import team.duckie.quackquack.sugar.material.Sugarable import team.duckie.quackquack.ui.optin.ExperimentalDesignToken import team.duckie.quackquack.ui.plugin.interceptor.rememberInterceptedStyleSafely import team.duckie.quackquack.ui.token.HorizontalDirection @@ -876,6 +876,7 @@ public fun Modifier.counter( * @param interactionSource 이 텍스트 필드의 인터랙션 스트림을 나타내는 변경 가능한 인터랙션 소스입니다. 인터랙션을 관찰하고 * 다른 인터랙션에서 이 텍스트 필드의 모양/동작을 커스터마이징하려면 자신만의 변경 가능한 인터랙션 소스를 생성하여 전달할 수 있습니다. */ +@Sugarable @ExperimentalDesignToken @ExperimentalQuackQuackApi @NonRestartableComposable @@ -1023,7 +1024,7 @@ public fun