← Back to Index

Action Column Code-first, WI-2257 atom

Updated: 2026-09-07 09:40 ET

Audited: 2026-07-14 12:12 ET

Trailing action-button row — a small collection of icon buttons at the end of a list row.
Decision D53 (2026-07-14) — first build of "the shared action-column atom" named by D40 and the Saved Search Card spec but never previously built. Icon system corrected by D54 (2026-07-14). Wired to all 3 consumer pages by D55 (2026-07-14).
Not a fixed heart/bell/X trio. The original WI description guessed a fixed 3-icon set, but checking the 3 real hand-rolled consumers found 3 different action pairs, each with exactly 2 items: Recent Searches (heart-toggle "save" + "search again"), Saved Searches (static-heart "remove" + "search again"), Saved Flights (bell-toggle "alert" + "delete"). The atom is a generic Items collection, not a fixed set of named slots — each consumer supplies its own icon asset, label, and click behavior via ActionKey.
Icon is a typed asset enum, not a glyph string (D54). The first pass rendered Icon as raw text — ♡/♥/↻/✕ Unicode symbols (fine, they inherit CSS color) but the alert toggle used 🔔/🔕, a full-colour emoji that ignores color entirely and reads as an off-brand icon dropped into a flat design system. Icon is now ActionColumnIconAsset, masking in the same exported Figma vectors (wwwroot/icons/*.svg) used by alpa-top-nav-icon-* and the favorite heart elsewhere in the app.

Live CSS Preview

Recent Searches — heart-toggle (Accent when saved) + search-again (Highlight)
Saved Flights — bell-toggle (Accent when alerted, colour-only — no dedicated "off" asset) + delete (Accent, Compact)

Design Tokens

TokenValueMeaning
Base (Neutral)color: var(--alpa-gray), 20pxDefault, inactive state
Accent#c0392b (red)Saved / alerted / delete — an "active" or "destructive" action
Highlightvar(--alpa-blue)Secondary/info action (e.g. search again)
Brandvar(--alpa-navy)The filled "favorited" heart (Favorites list) — navy like every card heart, never the destructive red (AB#2408 / AB#2679). Same navy the swipe tray's --brand zone uses.
Compact14px icon (vs. 18px default)Smaller icon — scaffold's delete-icon sizing. Orthogonal to colour: any variant can be compact.
Icon assetsActionColumnIconAsset: Heart, HeartSelected, Notifications, SearchHistory, TrashExported Figma vectors (wwwroot/icons/*.svg), mask-imaged with currentColor — same technique as alpa-top-nav-icon-* (D54).

Layer Tree

└── .alpa-action-column (flex row, gap 8px)
    └── .alpa-action-column-btn × N (one per Items entry)
        └── .alpa-action-column-icon (masked SVG span, not text content)

ViewModel

└── ActionColumnViewModel : ComponentViewModel
    └── Items : ObservableCollection<ActionColumnItemViewModel>

└── ActionColumnItemViewModel : ComponentViewModel
    ├── ActionKey : string — caller-defined, identifies which action fired (not rendered)
    ├── Icon : ActionColumnIconAsset — Heart · HeartSelected · Notifications · SearchHistory · Trash (D54)
    ├── Title : string — accessible label / tooltip
    ├── Variant : ActionColumnItemVariant — Neutral · Accent · Highlight · Brand
    └── Compact : bool

Blazor Component API

Two inputs since AB#2679. Vm (ActionColumnViewModel, feed-produced, dispatched through OnItemClicked by ActionKey) is now optional; the preferred input is Actions — an IReadOnlyList<SwipeAction> whose records carry icon, title, variant and their own handler. The same list a SwipeRevealRow renders as a swipe tray renders here as a persistent button row, so the two are two renderings of one model. Actions wins when both are set; its buttons stop propagation and prevent default because every caller sits the row inside a navigating card or anchor. Presets (SwipeAction.Remove / ToggleSave / ToggleAlert / Unfavorite) are the catalogue — see docs/SWIPE-REVEAL-LIST-PLAN.md § 4 for the Favorites onboarding example.

Home: ALPAMobile.Presentation/Components/Library/ActionColumn.razor (migrated 2026-07-15, WI-2254/D61).

ParameterTypeRequiredDescription
VmActionColumnViewModelYesItems collection.
OnItemClickedEventCallback<ActionColumnItemViewModel>NoFires with the clicked item — switch on item.ActionKey to route behavior.

Razor Usage (actual — JumpseatRecentSearchesPage.razor)

<SavedSearchCard Vm="@BuildVm(search, i)"
                 OnActionClicked="@(item => OnActionClicked(item, search, i))" />

@code {
    private SavedSearchCardViewModel BuildVm(SavedSearch search, int index)
    {
        var saved = _savedIndices.Contains(index);
        return new SavedSearchCardViewModel
        {
            Origin = search.Origin, Destination = search.Destination,
            TripType = search.TripType, Date = search.Date,
            Actions = new ActionColumnViewModel
            {
                Items = [
                    new ActionColumnItemViewModel
                    {
                        ActionKey = "toggle-save",
                        Icon = saved ? ActionColumnIconAsset.HeartSelected : ActionColumnIconAsset.Heart,
                        Title = saved ? "Saved" : "Save search",
                        Variant = saved ? ActionColumnItemVariant.Accent : ActionColumnItemVariant.Neutral,
                    },
                    new ActionColumnItemViewModel
                    {
                        ActionKey = "search-again",
                        Icon = ActionColumnIconAsset.SearchHistory,
                        Title = "Search again",
                        Variant = ActionColumnItemVariant.Highlight,
                    },
                ],
            },
        };
    }
}

In practice ActionColumn is always composed inside a domain control (Saved Search Card / Saved Flight Card, D55) rather than used bare — the example above shows the real call site.

CSS Class Reference (actual)

Classes live in ALPAMobile/wwwroot/css/alpa-components.css, renamed from .alpa-saved-card-action-btn* (previously duplicated verbatim as .alpa-saved-flight-card-actions, removed by D55 once the page composed <ActionColumn> directly).

ClassScopeKey rules
.alpa-action-columnRow containerdisplay:flex; gap:8px; flex-shrink:0
.alpa-action-column-btnBase buttonbackground:transparent; border:none; color:var(--alpa-gray)
.alpa-action-column-btn--accentVariantcolor:#c0392b
.alpa-action-column-btn--highlightVariantcolor:var(--alpa-blue)
.alpa-action-column-btn--brandVariantcolor:var(--alpa-navy) — renamed from --favorited (AB#2679) so it maps 1:1 onto ActionColumnItemVariant.Brand
.alpa-action-column-iconMasked icon spanwidth/height:20px (14px compact); background-color:currentColor; mask:var(--action-column-icon) center / 18px 18px no-repeat (12px compact) — fixed size, not contain (D54)
.alpa-action-column-icon--heart / --heart-selected / --notifications / --search-history / --trashIcon assetSets --action-column-icon to the matching wwwroot/icons/*.svg

Developer Notes