Skip to main content

husi-actions-dropdown-menu

Husi actions overflow menu conventions for Compose Material3 DropdownMenuPopup. Use this whenever editing or creating a DropdownMenuPopup inside CapsuleTopBar or CapsuleSearchTopBar actions, adding a more_vert actions menu, touching MenuDefaults.Label section headers, DropdownMenuGroup shapes, checked/selected menu rows, or improving the visual hierarchy of topbar action menus in composeApp/.

소스 정보

저장소
xchacha20-poly1305/husi
최근 소스 활동
2026년 9월 25일 14:09
감지된 SKILL.md 언어
영어
스타
1,858
포크
112

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
husi-actions-dropdown-menu
description
Husi actions overflow menu conventions for Compose Material3 DropdownMenuPopup. Use this whenever editing or creating a DropdownMenuPopup inside CapsuleTopBar or CapsuleSearchTopBar actions, adding a more_vert actions menu, touching MenuDefaults.Label section headers, DropdownMenuGroup shapes, checked/selected menu rows, or improving the visual hierarchy of topbar action menus in composeApp/.
# Husi Actions Dropdown Menus Use this skill when a topbar `actions = { ... }` block contains an overflow menu, usually a `more_vert` `SimpleIconButton` wrapped by `CapsuleActionButton`, and the menu is implemented with Material3 Expressive `DropdownMenuPopup`. If the task also changes topbar structure, first follow the `husi-topbar` skill. This skill only covers the overflow menu content and its visual hierarchy. ## Current house pattern Actions overflow menus should look like this: ```kotlin CapsuleActionButton { Box { SimpleIconButton( imageVector = vectorResource(Res.drawable.more_vert), contentDescription = stringResource(Res.string.more), onClick = { isOverflowMenuExpanded = true }, ) DropdownMenuPopup( expanded = isOverflowMenuExpanded, onDismissRequest = { isOverflowMenuExpanded = false }, ) { DropdownMenuGroup( shapes = MenuDefaults.groupShape(0, 1), ) { DropdownMenuSectionHeader(stringResource(Res.string.custom_config)) DropdownMenuItem( text = { Text(stringResource(Res.string.outbound)) }, onClick = { isOverflowMenuExpanded = false // action }, shape = MenuDefaults.itemShape(0, 2).shape, ) DropdownMenuItem( text = { Text(stringResource(Res.string.full)) }, onClick = { isOverflowMenuExpanded = false // action }, shape = MenuDefaults.itemShape(1, 2).shape, ) } } } } ``` Required pieces: - Wrap the overflow icon in `CapsuleActionButton`, and place the popup in the same `Box`. - Use `DropdownMenuPopup`, not the older plain `DropdownMenu`, for new topbar actions menus unless the surrounding screen already intentionally uses plain `DropdownMenu`. - Use `DropdownMenuGroup` for each visual group and separate groups with `Spacer(Modifier.height(MenuDefaults.GroupSpacing))`. - Use `MenuDefaults.groupShape(groupIndex, groupCount)` for grouped popup sections. ## Section headers Section headers in actions menus are labels, not actions. Do not create fake title rows like: ```kotlin DropdownMenuItem( text = { MenuDefaults.Label { Text(...) } }, onClick = {}, ) ``` That makes the title look and behave like a normal clickable menu row. It also adds misleading touch/ripple semantics. Use the shared helper instead: ```kotlin DropdownMenuSectionHeader(stringResource(Res.string.sort_mode)) ``` The helper lives at: `composeApp/src/commonMain/kotlin/fr/husi/compose/DropdownMenuSectionHeader.kt` It applies the current house style: - `MenuDefaults.Label` - full-width centered text - `MaterialTheme.typography.labelMedium` - `FontWeight.SemiBold` - `MaterialTheme.colorScheme.onSurfaceVariant` - `HorizontalDivider` with `MenuDefaults.HorizontalDividerPadding` ## Item shapes after headers Headers do not count as menu items for item shape indexing. Count only the actionable rows in that group. For plain click rows, Material3's API takes a single `Shape`, so use `.shape`: ```kotlin DropdownMenuItem( text = { Text(stringResource(Res.string.full)) }, onClick = { /* ... */ }, shape = MenuDefaults.itemShape(index, itemCount).shape, ) ``` For `selected` or `checked` rows, Material3's API takes `MenuItemShapes`, so use `shapes`: ```kotlin DropdownMenuItem( selected = sortMode == uiState.sortMode, onClick = { /* ... */ }, text = { Text(stringResource(text)) }, shapes = MenuDefaults.itemShape(index, itemCount), ) DropdownMenuItem( checked = sortMode == uiState.sortMode, onCheckedChange = { checked -> if (!checked) return@DropdownMenuItem // action }, text = { Text(stringResource(text)) }, shapes = MenuDefaults.itemShape(index, itemCount), ) ``` This distinction matters: using `shapes = ...` on a plain `onClick` item will not compile, while using `shape = MenuDefaults.itemShape(...).shape` preserves the grouped rounded-corner behavior. ## Checked filters For rows that toggle a boolean and show a checkbox, prefer making the row itself the action: ```kotlin DropdownMenuItem( text = { Text(stringResource(Res.string.connection_status_active)) }, onClick = { viewModel.setQueryActivate(!uiState.showActivate) }, leadingIcon = { Checkbox( checked = uiState.showActivate, onCheckedChange = null, ) }, shape = MenuDefaults.itemShape(0, 2).shape, ) ``` Use `onCheckedChange = null` on the checkbox when the containing row handles the click. This keeps the interaction target simple and avoids nested competing actions. ## Dismiss behavior Close the popup when the action navigates, opens another screen/dialog, or commits a one-shot selection: ```kotlin onClick = { isOverflowMenuExpanded = false onOpenConfigEditor(...) } ``` For non-destructive filter toggles that can be toggled repeatedly, leaving the menu open can be reasonable. Match the surrounding menu's behavior. ## Imports and local components Prefer Husi wrappers inside menu content: - `fr.husi.compose.material3.Text` - `fr.husi.compose.material3.Icon` - `fr.husi.compose.material3.Checkbox` - `fr.husi.compose.DropdownMenuSectionHeader` Use Compose resource helpers: - `stringResource(Res.string...)` - `vectorResource(Res.drawable...)` Avoid hardcoded colors or text sizes in menu rows. Use `MaterialTheme` roles in shared helpers only when the visual treatment is a deliberate app convention. ## Search and verification Before editing: ```bash rg -n "DropdownMenuPopup|MenuDefaults\\.Label|more_vert|actions\\s*=" composeApp/src/commonMain/kotlin/fr/husi -g '*.kt' ``` After editing: ```bash rg -n "MenuDefaults\\.Label" composeApp/src/commonMain/kotlin/fr/husi -g '*.kt' make test_gradle git diff --check -- composeApp/src/commonMain/kotlin/fr/husi ``` If the change touches Android-only UI or resources, also run: ```bash make apk_debug ``` Known reference implementations: - `composeApp/src/commonMain/kotlin/fr/husi/ui/RouteSettingsScreen.kt` - `composeApp/src/commonMain/kotlin/fr/husi/ui/dashboard/Dashboard.kt` - `composeApp/src/commonMain/kotlin/fr/husi/ui/profile/ProfileEditorScreen.kt` - `composeApp/src/commonMain/kotlin/fr/husi/compose/DropdownMenuSectionHeader.kt`
GitHub에서 보기