Skip to content

Commit

Permalink
Supplementing documentation (#662)
Browse files Browse the repository at this point in the history
* Change how to manage the sidebar

* Cleanup

* Cleanup

* Reorganize documentation approach

* Supplemental documentation

* Write button documents

* Write missing documents

* Remove unused modules from indexing in AS
  • Loading branch information
jisungbin committed May 6, 2023
1 parent 3314f9a commit 73a5821
Show file tree
Hide file tree
Showing 56 changed files with 810 additions and 320 deletions.
7 changes: 6 additions & 1 deletion build-logic/src/main/kotlin/ProjectOrArtifact.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

import org.gradle.api.artifacts.ProjectDependency

// TODO: 문서화
/**
* 로컬 모듈을 publishing된 아티팩트로 변환합니다.
* 해당 모듈이 maven-central에 publishing 되어 있다고 가정합니다.
*
* @see useArtifact
*/
fun ProjectDependency.orArtifact(): Any {
return if (useArtifact) {
val artifact = ArtifactConfig.of(dependencyProject)
Expand Down
35 changes: 34 additions & 1 deletion build-logic/src/main/kotlin/ProjectOrArtifactConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,38 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/

// TODO: 문서화
import org.gradle.api.artifacts.ProjectDependency

/**
* 로컬 모듈 의존성을 가지는 모듈을 publishing하면 해당 아티팩트를
* 다운받았을 때 아티팩트 내부에서 참조하고 있는 로컬 모듈을 불러오지 못해
* 싱크 에러가 발생합니다. 예를 들어 다음과 같은 상황에 해당합니다.
*
* ```
* // file:build.gradle
*
* dependencies {
* implementation projects.util.common // local-module
* implementation projects.compose.ui // local-module
* }
* ```
*
* 이를 대응하기 위해 [useArtifact]가 true라면 로컬 모듈을
* publishing된 아티팩트로 변환하여 참조합니다.
*
* ```
* // file:build.gradle
*
* dependencies {
* implementation 'team.duckie.quackquack.util:util-common:$version'
* implementation 'team.duckie.quackquack.compose:compose-ui:$version'
* }
* ```
*
* 즉, 모듈을 안전하게 publishing하기 위해선 publishing 전에 먼저 [useArtifact]를
* true로 설정해야 합니다.
*
* 로컬 모듈을 아티팩트로 변환하는 로직은 [ProjectDependency.orArtifact]를
* 참고하세요.
*/
const val useArtifact = false
2 changes: 1 addition & 1 deletion build-logic/src/main/kotlin/SugarPoetConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/

// TODO: 문서화
// TODO: 작동 방식 변경 (gradle task)
const val sugarPoet = true
2 changes: 1 addition & 1 deletion build-logic/src/main/kotlin/internal/versioning.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ package internal
import java.io.File
import org.gradle.api.Project

// TODO: 문서화
// TODO: 테스트 방식 변경
internal var projectTestingMode = false

internal fun Project.parseArtifactVersion(): String {
Expand Down
7 changes: 7 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ plugins {
alias(libs.plugins.kotlin.ktlint)
alias(libs.plugins.gradle.dependency.handler.extensions)
alias(libs.plugins.gradle.dependency.graph)
id("idea")
}

private val quackquackColor = "#36bcf5"
Expand Down Expand Up @@ -45,6 +46,12 @@ tasks.matching { task ->
notCompatibleWithConfigurationCache("https://github.com/jisungbin/dependency-graph-plugin/issues/8")
}

idea {
module {
excludeDirs = excludeDirs + file("website")
}
}

buildscript {
repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

// TODO: 문서화
/**
* 컴포넌트에 주어진 디자인 스팩을 적용합니다.
*/
@Stable
public fun Modifier.quackSurface(
shape: Shape = RectangleShape,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ public fun Modifier.quackComposed(factory: @Composable Modifier.() -> Modifier):
return then(QuackComposedModifier(factory))
}

// TODO: 문서화 개선 (key, fqn 인자 역할 설명, inspectableInfo 사용 불가 안내)
/**
* [Modifier.composed]의 꽥꽥 버전을 구현합니다.
*
Expand Down
138 changes: 135 additions & 3 deletions ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
package team.duckie.quackquack.ui

import android.annotation.SuppressLint
import androidx.annotation.RestrictTo
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
Expand All @@ -26,6 +29,8 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.LayoutModifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
import androidx.compose.ui.platform.testTag
Expand Down Expand Up @@ -56,40 +61,63 @@ import team.duckie.quackquack.ui.util.wrappedDebugInspectable
import team.duckie.quackquack.util.MustBeTested
import team.duckie.quackquack.util.fastFirstIsInstanceOrNull

/**
* 버튼의 디자인 스팩을 나타냅니다.
*
* @see QuackButtonStyle
*/
@QuackDsl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public interface ButtonStyleMarker

/**
* 기본으로 제공되는 [ButtonStyleMarker]의 스팩들에서 공통되는 필드를 나타냅니다.
*/
@Immutable
public interface QuackButtonStyle<T : ButtonStyleMarker> {
/** 사용할 색상들 */
public val colors: QuackButtonColors

/** 모서리의 둥글기 정도 */
public val radius: Dp

/** 컨텐츠 주변에 들어갈 패딩 */
public val contentPadding: QuackPadding

/** 배치되는 아이콘과 텍스트 사이의 간격 */
public val iconSpacedBy: Dp

/** 테두리의 굵기 */
public val borderThickness: Dp

/** 활성화 상태에서 표시될 텍스트의 타이포그래피 */
public val typography: QuackTypography

/** 비활성화 상태에서 표시될 텍스트의 타이포그래피 */
public val disabledTypography: QuackTypography

/** 필드에 mutation을 적용하는 람다 */
@Stable
public operator fun invoke(styleBuilder: T.() -> Unit): T

public companion object {
/** 기본으로 정의된 스팩 중 [QuackLargeButtonDefaults]를 가져옵니다. */
@Stable
public val Large: QuackButtonStyle<QuackLargeButtonDefaults> get() = QuackLargeButtonDefaults()

/** 기본으로 정의된 스팩 중 [QuackMediumButtonDefaults]를 가져옵니다. */
@Stable
public val Medium: QuackButtonStyle<QuackMediumButtonDefaults> get() = QuackMediumButtonDefaults()

/** 기본으로 정의된 스팩 중 [QuackSmallButtonDefaults]를 가져옵니다. */
// TODO(3): 데코레이터 모디파이어 사용 금지 린트 제공
@Stable
public val Small: QuackButtonStyle<QuackSmallButtonDefaults> get() = QuackSmallButtonDefaults()
}
}

/**
* [QuackButtonStyle]의 필드들을 [InspectorInfo]로 기록합니다.
*/
@SuppressLint("ModifierFactoryExtensionFunction")
@Stable
private fun QuackButtonStyle<*>.wrappedDebugInspectable(baseline: Modifier): Modifier =
Expand All @@ -104,6 +132,18 @@ private fun QuackButtonStyle<*>.wrappedDebugInspectable(baseline: Modifier): Mod
properties["disabledTypography"] = disabledTypography
}

/**
* 버튼에서 사용할 색상들을 정의합니다.
*
* @param backgroundColor 활성화 상태의 배경 색상
* @param disabledBackgroundColor 비활성화 상태의 배경 색상
* @param contentColor 활성화 상태의 컨텐츠 색상 (아이콘 색상은 [iconColor]로
* 관리되며, 컨텐츠라 하면 대부분 버튼의 텍스트를 의미합니다.)
* @param disabledBorderColor 비활성화 상태의 컨텐츠 색상 (아이콘 색상은
* [iconColor]로 관리되며, 컨텐츠라 하면 대부분 버튼의 텍스트를 의미합니다.)
* @param iconColor 활성화 상태에 관계 없이 항상 사용할 아이콘 색상
* @param rippleColor 활성화 상태에 관계 없이 항상 사용할 리플 색상
*/
@Immutable
public class QuackButtonColors internal constructor(
internal val backgroundColor: QuackColor,
Expand All @@ -115,6 +155,9 @@ public class QuackButtonColors internal constructor(
internal val iconColor: QuackColor,
internal val rippleColor: QuackColor,
) {
/**
* 기존 색상에서 일부 값만 변경하여 새로운 인스턴스를 반환합니다.
*/
@Stable
internal fun copy(
backgroundColor: QuackColor = this.backgroundColor,
Expand Down Expand Up @@ -182,6 +225,9 @@ public class QuackButtonColors internal constructor(
}
}

/**
* 꽥꽥 디자인 가이드의 `Buttons#LargeButtons`에 해당하는 디자인 스팩을 정의합니다.
*/
@Immutable
public class QuackLargeButtonDefaults internal constructor() :
QuackButtonStyle<QuackLargeButtonDefaults>, ButtonStyleMarker {
Expand Down Expand Up @@ -230,6 +276,9 @@ public class QuackLargeButtonDefaults internal constructor() :
override fun toString(): String = "QuackLargeButtonDefaults"
}

/**
* 꽥꽥 디자인 가이드의 `Buttons#MediumButton`에 해당하는 디자인 스팩을 정의합니다.
*/
@Immutable
public class QuackMediumButtonDefaults internal constructor() :
QuackButtonStyle<QuackMediumButtonDefaults>, ButtonStyleMarker {
Expand Down Expand Up @@ -270,6 +319,9 @@ public class QuackMediumButtonDefaults internal constructor() :
override fun toString(): String = "QuackMediumButtonDefaults"
}

/**
* 꽥꽥 디자인 가이드의 `Buttons#SmallButtons`에 해당하는 디자인 스팩을 정의합니다.
*/
@Immutable
public class QuackSmallButtonDefaults internal constructor() :
QuackButtonStyle<QuackSmallButtonDefaults>, ButtonStyleMarker {
Expand Down Expand Up @@ -317,12 +369,25 @@ public class QuackSmallButtonDefaults internal constructor() :
override fun toString(): String = "QuackSmallButtonDefaults"
}

/**
* 버튼의 아이콘 데코레이터 데이터를 저장합니다.
*
* @param leadingIcon 왼쪽에 배치될 아이콘 데이터
* @param trailingIcon 오른쪽에 배치될 아이콘 데이터
*/
@Stable
private data class ButtonIconData(
val leadingIcon: QuackIcon?,
val trailingIcon: QuackIcon?,
) : QuackDataModifierModel

/**
* 버튼에 아이콘을 추가합니다. 모든 아이콘은 버튼 텍스트를 기준으로
* 양옆에 배치됩니다.
*
* @param leadingIcon 왼쪽에 배치될 아이콘
* @param trailingIcon 오른쪽에 배치될 아이콘
*/
@DecorateModifier
@Stable
public fun Modifier.icons(
Expand All @@ -341,7 +406,67 @@ public fun Modifier.icons(
)
}

@MustBeTested(passed = false)
/**
* 버튼을 그립니다.
*
* - 이 컴포넌트는 자체의 패딩 정책을 구현합니다.
* - 이 컴포넌트는 자체의 배치 정책을 구현합니다.
* - [스타일][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][QuackLargeButtonDefaults] | ⭕ | |
* | [Medium][QuackMediumButtonDefaults] | ⭕ | |
* | [Small][QuackSmallButtonDefaults] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. |
*
* @param enabled 활성화 상태 여부
* @param style 적용할 스타일. 사전 정의 스타일로 [QuackButtonStyle.Large][QuackLargeButtonDefaults],
* [QuackButtonStyle.Medium][QuackMediumButtonDefaults], [QuackButtonStyle.Small][QuackSmallButtonDefaults]이 제공됩니다.
* @param rippleEnabled 클릭됐을 때 리플 애니메이션을 적용할지 여부
* @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다.
*/
@MustBeTested(passed = true)
@Composable
@NonRestartableComposable
@ExperimentalQuackQuackApi
Expand All @@ -358,6 +483,7 @@ public fun <T : ButtonStyleMarker> QuackButton(
) {
val isSmallButton = style is QuackSmallButtonDefaults
// TODO: 다른 경우로 사이즈를 지정하는 방법이 있을까?
// TODO(3): LayoutModifierNode 지원
var isSizeSpecified = false
val (composeModifier, quackDataModels) = currentComposer.quackMaterializeOf(modifier) { currentModifier ->
if (!isSizeSpecified) {
Expand Down Expand Up @@ -464,6 +590,12 @@ private const val TextLayoutId = "QuackBaseButtonText"
private const val LeadingIconLayoutId = "QuackBaseButtonLeadingIcon"
private const val TrailingIconLayoutId = "QuackBaseButtonTrailingIcon"

/**
* 고유한 배치 정책으로 버튼을 그립니다.
* 배치 정책의 자세한 정보는 [QuackButton] 문서를 참고하세요.
*
* 이 컴포넌트는 [QuackButtonStyle]의 필드를 개별 인자로 받습니다.
*/
@NoSugar
@Composable
public fun QuackBaseButton(
Expand Down
Loading

0 comments on commit 73a5821

Please sign in to comment.