Skip to content
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
36 changes: 36 additions & 0 deletions design/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,42 @@ package com.urlaunched.android.design.ui.textfield {

}

package com.urlaunched.android.design.ui.textfield.hashtags {

public abstract class Delegate implements java.io.Closeable {
ctor public Delegate(kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
method public void close();
method public final error.NonExistentClass! getDelegateScope();
property public final error.NonExistentClass! delegateScope;
}

public final class HashtagsContainerKt {
method @androidx.compose.runtime.Composable public static void HashtagsContainer(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Modifier innerFieldModifier, optional androidx.compose.ui.text.input.TextFieldValue value, optional String? label, String? placeHolder, optional String? error, optional boolean enabled, optional boolean readOnly, optional com.urlaunched.android.design.ui.textfield.models.TextFieldBorderConfig borderConfig, optional com.urlaunched.android.design.ui.textfield.models.TextFieldBackgroundConfig backgroundConfig, optional com.urlaunched.android.design.ui.textfield.models.TextFieldInputTextConfig inputTextConfig, optional com.urlaunched.android.design.ui.textfield.models.TextFieldInputPlaceholderTextConfig inputPlaceholderTextConfig, optional com.urlaunched.android.design.ui.textfield.models.TextFieldTopLabelConfig topLabelConfig, optional com.urlaunched.android.design.ui.textfield.models.TextFieldsSpacerConfig textFieldsSpacerConfig, optional long selectionHandleColor, optional long selectionBackgroundColor, optional androidx.compose.ui.graphics.Brush cursorBrush, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional boolean collapseLabel, optional androidx.compose.ui.unit.Dp? textFieldHeight, optional androidx.compose.foundation.layout.PaddingValues innerPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? labelIcon, optional boolean trailingIconAlwaysShown, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange);
}

public interface HashtagsDelegate {
method public kotlinx.coroutines.flow.StateFlow<com.urlaunched.android.design.ui.textfield.hashtags.HashtagsDelegateState> getUiHashtagsStage();
method public void setCurrentHashtags(androidx.compose.ui.text.input.TextFieldValue newValue);
property public abstract kotlinx.coroutines.flow.StateFlow<com.urlaunched.android.design.ui.textfield.hashtags.HashtagsDelegateState> uiHashtagsStage;
}

public final class HashtagsDelegateImpl extends com.urlaunched.android.design.ui.textfield.hashtags.Delegate implements com.urlaunched.android.design.ui.textfield.hashtags.HashtagsDelegate {
ctor public HashtagsDelegateImpl(kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
method public kotlinx.coroutines.flow.StateFlow<com.urlaunched.android.design.ui.textfield.hashtags.HashtagsDelegateState> getUiHashtagsStage();
method public void setCurrentHashtags(androidx.compose.ui.text.input.TextFieldValue newValue);
property public kotlinx.coroutines.flow.StateFlow<com.urlaunched.android.design.ui.textfield.hashtags.HashtagsDelegateState> uiHashtagsStage;
}

public final class HashtagsDelegateState {
ctor public HashtagsDelegateState(androidx.compose.ui.text.input.TextFieldValue currentHashtagsText);
method public androidx.compose.ui.text.input.TextFieldValue component1();
method public com.urlaunched.android.design.ui.textfield.hashtags.HashtagsDelegateState copy(androidx.compose.ui.text.input.TextFieldValue currentHashtagsText);
method public androidx.compose.ui.text.input.TextFieldValue getCurrentHashtagsText();
property public final androidx.compose.ui.text.input.TextFieldValue currentHashtagsText;
}

}

package com.urlaunched.android.design.ui.textfield.models {

public final class TextFieldBackgroundConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.urlaunched.android.design.ui.textfield.hashtags

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import java.io.Closeable

abstract class Delegate(coroutineDispatcher: CoroutineDispatcher) : Closeable {
private val delegateScope = CoroutineScope(coroutineDispatcher + SupervisorJob())

override fun close() {
delegateScope.cancel()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.urlaunched.android.design.ui.textfield.hashtags

import androidx.compose.ui.text.input.TextFieldValue
import kotlinx.coroutines.flow.StateFlow

interface HashtagsDelegate {
val uiHashtagsStage: StateFlow<HashtagsDelegateState>

fun setCurrentHashtags(newValue: TextFieldValue)
}

data class HashtagsDelegateState(
val currentHashtagsText: TextFieldValue
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.urlaunched.android.design.ui.textfield.hashtags

import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update

class HashtagsDelegateImpl(
private val coroutineDispatcher: CoroutineDispatcher
) : Delegate(coroutineDispatcher), HashtagsDelegate {
private val _uiState = MutableStateFlow(HashtagsDelegateState(currentHashtagsText = TextFieldValue()))

override val uiHashtagsStage: StateFlow<HashtagsDelegateState> = _uiState

override fun setCurrentHashtags(newValue: TextFieldValue) {
val rawText = newValue.text
val selection = newValue.selection
val previousText = _uiState.value.currentHashtagsText.text

val filteredText = rawText.filter { it.isLetter() || it.isDigit() || it == ' ' || it == '#' }

val updatedText = buildString {
if (previousText.isEmpty() && filteredText.isNotEmpty()) {
append("#")
append(filteredText)
} else if (filteredText.isEmpty()) {
append("")
} else if (filteredText.lastOrNull() == ' ' && previousText.length <= filteredText.length) {
if (previousText.lastOrNull() != '#') {
append(filteredText.dropLast(1))
append(" #")
} else {
append(previousText)
}
} else {
append(filteredText)
}
}

val cursorPos = when {
previousText.isEmpty() && filteredText.isNotEmpty() -> {
updatedText.length
}

updatedText.length > filteredText.length -> {
updatedText.length
}

updatedText.length < filteredText.length -> {
selection.start - (filteredText.length - updatedText.length)
}

filteredText.lastOrNull() == ' ' && previousText.lastOrNull() == '#' -> {
updatedText.length
}

else -> {
selection.start
}
}.coerceIn(0, updatedText.length)

_uiState.update {
it.copy(currentHashtagsText = TextFieldValue(updatedText, TextRange(cursorPos)))
}
}
}
Loading