Skip to content

Commit 5d97396

Browse files
committed
Roughly fix the select components, enhance ExposedDropdownMenu and Select components with unified behavior and additional parameters, and extract common IMdItemScope.contentFromComponents functions
The `selection` state in `Material3` in the demo doesn't sync properly to the 2 select components yet. The corresponding commit: huanshankeji/compose-html-material@6e0c258 Reviewed locally with Copilot and improved.
1 parent 6e170ef commit 5d97396

File tree

16 files changed

+523
-225
lines changed

16 files changed

+523
-225
lines changed

buildSrc/src/main/kotlin/common-conventions.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@ kotlin {
5858

5959
compilerOptions {
6060
freeCompilerArgs.add("-Xexpect-actual-classes")
61+
optIn.add("com.huanshankeji.compose.ExperimentalApi")
6162
}
6263
}

demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -202,52 +202,84 @@ fun Material3(/*modifier: Modifier = Modifier*/
202202
)
203203
}
204204

205+
@Composable
206+
fun SelectMenuContent(selection: Selection?, setSelection: (Selection?) -> Unit, close: () -> Unit) =
207+
(listOf(null) + Selection.entries).forEach {
208+
SelectOptionWithMaterialIcons(
209+
{ modifier -> it?.let { Text(it.name, modifier) } },
210+
{
211+
setSelection(it)
212+
close()
213+
},
214+
it == selection,
215+
leadingIcon = Icons.Filled.Add,
216+
trailingIcon = Icons.Filled.Remove
217+
)
218+
}
219+
220+
205221
run {
206-
val (expanded, setExpanded) = remember { mutableStateOf(false) }
207-
val close = { setExpanded(false) }
208222
val (selection, setSelection) = remember { mutableStateOf<Selection?>(null) }
209-
ExposedDropdownMenuBoxWithTextField(
210-
expanded, setExpanded,
211-
textFieldArgs = ExposedDropdownMenuBoxTextFieldArgs(
212-
selection?.name ?: "", label = "Please select"
213-
),
214-
exposedDropdownMenuArgs = ExposedDropdownMenuArgs(expanded, close, close) {
223+
val value = selection?.name ?: ""
224+
Text("Selected: $value")
225+
val label = "Please select"
226+
run {
227+
val (expanded, setExpanded) = remember { mutableStateOf(false) }
228+
val close = { setExpanded(false) }
229+
ExposedDropdownMenuBoxWithTextField(
230+
expanded, setExpanded,
231+
textFieldArgs = ExposedDropdownMenuBoxTextFieldArgs(value, label = label),
232+
exposedDropdownMenuArgs = ExposedDropdownMenuArgs(expanded, close, close) {
233+
DropdownMenuContent(setSelection, close)
234+
}
235+
)
236+
}
237+
run {
238+
val (expanded, setExpanded) = remember { mutableStateOf(false) }
239+
val close = { setExpanded(false) }
240+
ExposedDropdownMenuBoxWithOutlinedTextField(
241+
expanded, setExpanded,
242+
textFieldArgs = ExposedDropdownMenuBoxTextFieldArgs(value, label = label),
243+
exposedDropdownMenuArgs = ExposedDropdownMenuArgs(expanded, close, close) {
244+
DropdownMenuContent(setSelection, close)
245+
}
246+
)
247+
}
248+
249+
run {
250+
val (expanded, setExpanded) = remember { mutableStateOf(false) }
251+
val close = { setExpanded(false) }
252+
FilledSelect(
253+
expanded, setExpanded,
254+
textFieldArgs = SelectTextFieldArgs(value, label = label),
255+
menuArgs = SelectMenuArgs(expanded, close, close) {
256+
SelectMenuContent(selection, setSelection, close)
257+
}
258+
)
259+
}
260+
run {
261+
val (expanded, setExpanded) = remember { mutableStateOf(false) }
262+
val close = { setExpanded(false) }
263+
OutlinedSelect(
264+
expanded, setExpanded,
265+
textFieldArgs = SelectTextFieldArgs(value, label = label),
266+
menuArgs = SelectMenuArgs(expanded, close, close) {
267+
SelectMenuContent(selection, setSelection, close)
268+
}
269+
)
270+
}
271+
272+
DropdownMenuBox {
273+
var expanded by remember { mutableStateOf(false) }
274+
val close = { expanded = false }
275+
IconButton({ expanded = true }, Modifier.menuAnchorJsDom()) {
276+
Icon(Icons.Filled.ArrowDropDown, "Please select")
277+
}
278+
DropdownMenu(expanded, close, close) {
215279
DropdownMenuContent(setSelection, close)
216280
}
217-
)
218-
}
219-
DropdownMenuBox {
220-
var expanded by remember { mutableStateOf(false) }
221-
val close = { expanded = false }
222-
val (_, setSelection) = remember { mutableStateOf<Selection?>(null) }
223-
IconButton({ expanded = true }, Modifier.menuAnchorJsDom()) {
224-
Icon(Icons.Filled.ArrowDropDown, "Please select")
225-
}
226-
DropdownMenu(expanded, close, close) {
227-
DropdownMenuContent(setSelection, close)
228281
}
229282
}
230-
// Select components
231-
var selectedValue by remember { mutableStateOf("Option 1") }
232-
Text("Selected: $selectedValue")
233-
FilledSelect(
234-
value = selectedValue,
235-
onValueChange = { selectedValue = it },
236-
label = "Choose option"
237-
) {
238-
SelectOption("Option 1", "Option 1")
239-
SelectOption("Option 2", "Option 2")
240-
SelectOption("Option 3", "Option 3")
241-
}
242-
OutlinedSelect(
243-
value = selectedValue,
244-
onValueChange = { selectedValue = it },
245-
label = "Choose option"
246-
) {
247-
SelectOption("Option 1", "Option 1")
248-
SelectOption("Option 2", "Option 2")
249-
SelectOption("Option 3", "Option 3")
250-
}
251283

252284
LinearProgressIndicator()
253285
LinearProgressIndicator({ 0.5f })

material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Badge.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import com.huanshankeji.compose.ui.Modifier
1414
@Deprecated(
1515
"This component is not displayed correctly on JS DOM. " +
1616
"It seems to be displayed with absolute position. " +
17-
"See https://github.com/material-components/material-web/blob/main/labs/badge/internal/_badge.scss#L40."
17+
"See https://github.com/material-components/material-web/blob/516cbc02bf770b7c3c5c6b546f1e5d81939b9f23/labs/badge/internal/_badge.scss#L40 ."
1818
)
1919
@Composable
2020
expect fun Badge(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.huanshankeji.compose.material3
2+
3+
import androidx.compose.runtime.Composable
4+
import com.huanshankeji.compose.ui.Modifier
5+
6+
/**
7+
* Corresponds to the slots of Material Web `md-item`.
8+
*
9+
* @param start corresponds to `leadingIcon` in Compose UI.
10+
* @param end corresponds to `trailingIcon` in Compose UI.
11+
*/
12+
interface ItemComponents {
13+
val headline: @Composable (Modifier) -> Unit
14+
val start: @Composable ((Modifier) -> Unit)?
15+
val end: @Composable ((Modifier) -> Unit)?
16+
val supportingText: @Composable ((Modifier) -> Unit)?
17+
val trailingSupportingText: @Composable ((Modifier) -> Unit)?
18+
val container: @Composable ((Modifier) -> Unit)?
19+
val overline: @Composable ((Modifier) -> Unit)?
20+
}

material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.kt

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.huanshankeji.compose.material3.ext
22

33
import androidx.compose.runtime.Composable
4+
import com.huanshankeji.compose.ExperimentalApi
45
import com.huanshankeji.compose.ui.Modifier
56

67
// still in the `ext` package because `content` doesn't take a `ColumnScope` receiver
@@ -35,14 +36,13 @@ expect class ExposedDropdownMenuBoxScope {
3536
onCloseJsDom: () -> Unit,
3637
modifier: Modifier = Modifier,
3738
//scrollState: ScrollState = rememberScrollState(),
39+
matchAnchorWidthComposeUi: Boolean = true,
3840
content: @Composable /*ColumnScope.*/() -> Unit
3941
)
4042

4143
//TODO for `DropdownMenuItem`: `contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding`
4244
}
4345

44-
45-
// TODO rename to `ExposedDropdownMenuBoxWithFilledTextField`
4646
@Composable
4747
fun ExposedDropdownMenuBoxWithTextField(
4848
expanded: Boolean,
@@ -57,18 +57,74 @@ fun ExposedDropdownMenuBoxWithTextField(
5757
ExposedDropdownMenuBoxTextField(expanded, textFieldArgs)
5858
with(exposedDropdownMenuArgs) {
5959
ExposedDropdownMenu(
60-
this.expanded, onDismissRequestComposeUi, onCloseJsDom, this.modifier, content
60+
this.expanded,
61+
onDismissRequestComposeUi,
62+
onCloseJsDom,
63+
this.modifier,
64+
matchAnchorWidthComposeUi,
65+
content
66+
)
67+
}
68+
}
69+
70+
/**
71+
* An alias of [ExposedDropdownMenuBoxWithTextField].
72+
*/
73+
@Composable
74+
fun ExposedDropdownMenuBoxWithFilledTextField(
75+
expanded: Boolean,
76+
onExpandedChange: (Boolean) -> Unit,
77+
modifier: Modifier = Modifier,
78+
textFieldArgs: ExposedDropdownMenuBoxTextFieldArgs,
79+
//scrollState: ScrollState = rememberScrollState(),
80+
exposedDropdownMenuArgs: ExposedDropdownMenuArgs
81+
) =
82+
ExposedDropdownMenuBoxWithTextField(expanded, onExpandedChange, modifier, textFieldArgs, exposedDropdownMenuArgs)
83+
84+
@Composable
85+
fun ExposedDropdownMenuBoxWithOutlinedTextField(
86+
expanded: Boolean,
87+
onExpandedChange: (Boolean) -> Unit,
88+
modifier: Modifier = Modifier,
89+
textFieldArgs: ExposedDropdownMenuBoxTextFieldArgs,
90+
//scrollState: ScrollState = rememberScrollState(),
91+
exposedDropdownMenuArgs: ExposedDropdownMenuArgs
92+
) =
93+
// adapted from the examples in https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#ExposedDropdownMenuBox(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1)
94+
ExposedDropdownMenuBox(expanded, onExpandedChange, modifier) {
95+
ExposedDropdownMenuBoxOutlinedTextField(expanded, textFieldArgs)
96+
with(exposedDropdownMenuArgs) {
97+
ExposedDropdownMenu(
98+
this.expanded,
99+
onDismissRequestComposeUi,
100+
onCloseJsDom,
101+
this.modifier,
102+
matchAnchorWidthComposeUi,
103+
content
61104
)
62105
}
63106
}
64107

108+
@ExperimentalApi
109+
interface ICommonExposedDropdownMenuBoxTextFieldArgs {
110+
//val value: String
111+
//val onValueChange: (String) -> Unit
112+
val enabled: Boolean
113+
val label: String
114+
val supportingText: String?
115+
val isError: Boolean
116+
}
117+
65118
class ExposedDropdownMenuBoxTextFieldArgs(
66119
val value: String,
67120
val onValueChange: (String) -> Unit = {}, // pass this only when specifying custom values in the text field
121+
override val enabled: Boolean = true,
68122
val readOnly: Boolean = true,
69123
val singleLine: Boolean = true,
70-
val label: String
71-
)
124+
override val label: String,
125+
override val supportingText: String? = null,
126+
override val isError: Boolean = false,
127+
) : ICommonExposedDropdownMenuBoxTextFieldArgs
72128

73129
@Composable
74130
expect fun ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxTextField(
@@ -77,10 +133,27 @@ expect fun ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxTextField(
77133
)
78134
// TODO Set `menuAnchor` on the icon too when the text field is editable and `MenuAnchorType.SecondaryEditable` is supported. See https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#ExposedDropdownMenuBox(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1).
79135

136+
@Composable
137+
expect fun ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxOutlinedTextField(
138+
expanded: Boolean,
139+
args: ExposedDropdownMenuBoxTextFieldArgs
140+
)
141+
142+
interface ICommonExposedDropdownMenuArgs {
143+
//val expanded: Boolean
144+
val onDismissRequestComposeUi: () -> Unit
145+
val onCloseJsDom: () -> Unit
146+
147+
//val modifier: Modifier
148+
val content: @Composable /*ColumnScope.*/(() -> Unit)
149+
}
150+
80151
class ExposedDropdownMenuArgs(
152+
// TODO Consider merging this with the `expanded` in `ExposedDropdownMenuBoxScope` to avoid potential confusion.
81153
val expanded: Boolean,
82-
val onDismissRequestComposeUi: () -> Unit,
83-
val onCloseJsDom: () -> Unit,
154+
override val onDismissRequestComposeUi: () -> Unit,
155+
override val onCloseJsDom: () -> Unit,
84156
val modifier: Modifier = Modifier,
85-
val content: @Composable /*ColumnScope.*/() -> Unit
86-
)
157+
val matchAnchorWidthComposeUi: Boolean = true,
158+
override val content: @Composable /*ColumnScope.*/() -> Unit
159+
) : ICommonExposedDropdownMenuArgs

material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ expect class DropdownMenuBoxScope {
4747
expect fun DropdownMenuBox(content: @Composable DropdownMenuBoxScope.() -> Unit)
4848

4949
/**
50+
* @param text corresponds to the `headline` slot on JS DOM in Material Web.
5051
* @param keepOpenJsDom set to `true` for completely consistent behavior on JS to `androidx.compose`. However, if you set the `expanded` state to false in [onClick], doing this is unnecessary.
5152
*/
5253
@Composable
@@ -58,6 +59,7 @@ expect fun DropdownMenuItem(
5859
trailingIcon: @Composable ((Modifier) -> Unit)? = null,
5960
enabled: Boolean = true,
6061
keepOpenJsDom: Boolean = false
62+
//selectedJsDom: Boolean = false
6163
)
6264

6365
@Composable

0 commit comments

Comments
 (0)