Skip to content
This repository has been archived by the owner on Jun 14, 2023. It is now read-only.

Refactored + Refined the design system #104

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,21 +140,28 @@ We would endlessly like to thank the following contributors
<sub><b>Evans Chepsiror</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/GibsonRuitiari">
<img src="https://avatars.githubusercontent.com/u/88240355?v=4" width="100;" alt="GibsonRuitiari"/>
<br />
<sub><b>8BitsLives .❤️</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tamzi">
<img src="https://avatars.githubusercontent.com/u/3008932?v=4" width="100;" alt="tamzi"/>
<br />
<sub><b>Frank Tamre</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/michaelbukachi">
<img src="https://avatars.githubusercontent.com/u/10145850?v=4" width="100;" alt="michaelbukachi"/>
<br />
<sub><b>Michael Bukachi</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/keithchad">
<img src="https://avatars.githubusercontent.com/u/63049827?v=4" width="100;" alt="keithchad"/>
Expand Down Expand Up @@ -189,13 +196,6 @@ We would endlessly like to thank the following contributors
<br />
<sub><b>Beatrice Kinya</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/GibsonRuitiari">
<img src="https://avatars.githubusercontent.com/u/88240355?v=4" width="100;" alt="GibsonRuitiari"/>
<br />
<sub><b>8BitsLives .❤️</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
Expand Down
251 changes: 251 additions & 0 deletions chai/src/main/java/com/droidconke/chai/atoms/Color.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@
*/
package com.droidconke.chai.atoms

import androidx.compose.animation.core.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.colorspace.ColorSpace
import androidx.compose.ui.graphics.colorspace.ColorSpaces
import com.droidconke.chai.utils.chaiAnimationSpec
import kotlin.math.pow
import kotlin.reflect.KProperty

/**
* these are the Primary colours from Chai's Design spec document
Expand Down Expand Up @@ -43,6 +50,250 @@ val ChaiDarkerGrey = Color(0xFF191d1d)
val ChaiCoal = Color(0xFF20201E)
val ChaiBlack = Color(0xFF000000)

/**
* Defines the colors to be used by the app
* This an abstraction that contains all the colors (neutrals,primary & secondary)
* needed by the application
* A normal class, instead of data class, is used to prevent changing of the colors via copy
* as that would lead to breaking of the intended app design system
* Note: The color definition ought to be in Hex Color format
* * Example usage see ChaiDemo
* @param value the color value given by the client, the value is of a type [Color]
*/
@Immutable // since we are using an internal constructor, values read from [ChaiColor] won't change after an instance is constructed
@JvmInline
value class ChaiColor internal constructor(val value: Color) {
/**
* Changes the alpha of the ChaiColor
* @param alpha alpha in form float to be applied to current ChaiColor
* @return an instance of ChaiColor with modified alpha value
*/
fun changeAlpha(alpha: Float) = ChaiColor(value.copy(alpha = alpha))

companion object {
// should not be used in real components but,
// can be used as a base value for ChaiColor
@Stable
internal val Unspecified = ChaiColor(value = Color.Unspecified)

/* these are the Primary colours from Chai's Design spec document*/
@Stable
val ChaiBlue = ChaiColor(value = Color(0xFF000CEB))
@Stable
val ChaiWhite = ChaiColor(value = Color(0xFFFFFFFF))

/* these are the Secondary colours from Chai's Design spec document */
@Stable
val ChaiRed = ChaiColor(value = Color(0xFFFF6E4D))
@Stable
val ChaiTeal = ChaiColor(value = Color(0xFF00e2c3))
@Stable
val ChaiFadedLime = ChaiColor(value = Color(0xFF7de1c3))

/* these are the Neutrals from the Chai's Design spec document */
@Stable
val ChaiLightGrey = ChaiColor(value = Color(0xFFF5F5F5))
@Stable
val ChaiGrey = ChaiColor(value = Color(0xFFB1B1B1))
@Stable
val ChaiDarkGrey = ChaiColor(value = Color(0xFF5A5A5A))
@Stable
val ChaiDarkerGrey = ChaiColor(value = Color(0xFF191d1d))
@Stable
val ChaiCoal = ChaiColor(value = Color(0xFF20201E))
@Stable
val ChaiBlack = ChaiColor(value = Color(0xFF000000))

/**
* Typically colors are 3 dimensional planes i.e x,y,z
* with the x being red, y being green and z blue
* The three combined create a [ColorSpace] i.e a unique representation
* of colors that can be created by combining the three pigments
* To convert one color to the other, a mapping of these color spaces is needed
* from [xyz] plane to another [xyz] plane.
* for comprehensive explanation see [full_explanation] (https://en.wikipedia.org/wiki/Color_space)
* note: adapted from aosp
*/
private fun multiplyColumn(
column: Int,
x: Float,
y: Float,
z: Float,
matrix: FloatArray
) = x * matrix[column] + y * matrix[3 + column] + z * matrix[6 + column]
private val M1 = floatArrayOf(
0.80405736f,
0.026893456f,
0.04586542f,
0.3188387f,
0.9319606f,
0.26299807f,
-0.11419419f,
0.05105356f,
0.83999807f,
)

private val InverseM1 = floatArrayOf(
1.2485008f,
-0.032856926f,
-0.057883114f,
-0.48331892f,
1.1044513f,
-0.3194066f,
0.19910365f,
-0.07159331f,
1.202023f,
)
internal val VectorConverter: (colorSpace: ColorSpace) -> TwoWayConverter<ChaiColor, AnimationVector4D> =
{ colorSpace ->
TwoWayConverter(
convertToVector = { chaiColor ->
val color by chaiColor
val colorXyz = color.convert(
colorSpace = ColorSpaces.CieXyz,
)

val x = colorXyz.red
val y = colorXyz.green
val z = colorXyz.blue

val l = multiplyColumn(
column = 0,
x = x,
y = y,
z = z,
matrix = M1,
).pow(
x = 1f / 3f,
)
val a = multiplyColumn(
column = 1,
x = x,
y = y,
z = z,
matrix = M1,
).pow(
x = 1f / 3f,
)
val b = multiplyColumn(
column = 2,
x = x,
y = y,
z = z,
matrix = M1,
).pow(
x = 1f / 3f,
)

AnimationVector4D(
v1 = color.alpha,
v2 = l,
v3 = a,
v4 = b,
)
},
convertFromVector = { vector ->
val l = vector.v2.pow(
x = 3f,
)
val a = vector.v3.pow(
x = 3f,
)
val b = vector.v4.pow(
x = 3f,
)

val x = multiplyColumn(
column = 0,
x = l,
y = a,
z = b,
matrix = InverseM1,
)
val y = multiplyColumn(
column = 1,
x = l,
y = a,
z = b,
matrix = InverseM1,
)
val z = multiplyColumn(
column = 2,
x = l,
y = a,
z = b,
matrix = InverseM1,
)

val colorXyz = Color(
alpha = vector.v1.coerceIn(
minimumValue = 0f,
maximumValue = 1f,
),
red = x.coerceIn(
minimumValue = -2f,
maximumValue = 2f,
),
green = y.coerceIn(
minimumValue = -2f,
maximumValue = 2f,
),
blue = z.coerceIn(
minimumValue = -2f,
maximumValue = 2f,
),
colorSpace = ColorSpaces.CieXyz,
)

ChaiColor(
value = colorXyz.convert(
colorSpace = colorSpace,
),
)
}
)
}
}
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
}

/**
* Constructs a color animation spec using [tween]
* The duration millis is divided by 2 since color animation
* more often than not will depend on other animations,
* so it needs to run and complete before other animations to ensure a
* smooth transition
* @receiver animation spec to be used, ideally should be an instance of [TweenSpec]
* @return a new instance of animation spec whose duration is divided by 2
*/
private fun <T> AnimationSpec<T>.toColorSpec(): AnimationSpec<T> {
val tweenSpec = this as TweenSpec<T> ?: return this
return tween(
durationMillis = tweenSpec.durationMillis / 2,
delayMillis = tweenSpec.delay, easing = tweenSpec.easing
)
}
/**
* Animates [ChaiColor] changes from one color to the other
* @param targetValue an instance of [ChaiColor]
* @param animationSpec animationSpec to be used when color change is detected/happens
* @return state object of type [ChaiColor]
*/
@Composable
internal fun animateChaiColorAsState(
targetValue: ChaiColor,
animationSpec: AnimationSpec<ChaiColor> = chaiAnimationSpec()
): State<ChaiColor> {
val converter = remember(key1 = targetValue.value.colorSpace) {
(ChaiColor.VectorConverter)(targetValue.value.colorSpace)
}
return animateValueAsState(
targetValue = targetValue,
typeConverter = converter,
animationSpec = animationSpec.toColorSpec(),
finishedListener = null
)
}
/**
* TOBE Replaced
*/
Expand Down
Loading