Enterprise data table with three view modes — table, card, and list. Configurable columns, context menus, status pills, priority badges, segmented progress bars, and full theme customization.
If your users are in a comma-decimal region, wrap the numbers
This chart draws with an SVG built by string concatenation, and Power Fx & converts numbers using the viewer’s locale. In regions that write 123,45 rather than 123.45, SVG reads that comma as a separator between two numbers and the the progress bar segment widths silently fail to draw. Axes and labels still render, because whole numbers are unaffected.
The fix is to format explicitly at the point of concatenation. In this component that means the progress bar segment widths:
- "<rect x='0' y='0' width='" & segW & "'+ "<rect x='0' y='0' width='" & Text(segW, "[$-en-US]0.##") & "'Do not repair the finished string with Substitute(s, ",", "."). That also rewrites thousands separators in your labels, turning 1,420 into 1.420, and breaks comma-separated font stacks.
Copy the YAML component definition and import into Power Apps Studio.
# In Power Apps Studio:
1. Components → New component → Import from code
2. Paste YAML definition
3. Component appears in library
4. Configure Items, Columns, and theme propertiescmpMyTable.Items: YourDataSource
cmpMyTable.Width: Parent.Width
cmpMyTable.Height: 600
cmpMyTable.ViewMode: "table" // "table", "card", or "list"// Enable view toggle (Table / Card / List)
cmpMyTable.ShowViewToggle: true
cmpMyTable.ViewMode: "table" // Default view
// React to view changes
cmpMyTable.OnViewChange:
Notify(
"Switched to " & cmpMyTable.SelectedViewMode & " view",
NotificationType.Information
)
// Read current view
Set(varCurrentView, cmpMyTable.SelectedViewMode)cmpMyTable.EnableMenu: true
// Note: ItemIconSvg / ItemIconColor are reserved and not currently rendered
// (the native TabList menu shows text labels only)
cmpMyTable.ContextMenuItems: Table(
{ItemKey: "view", ItemDisplayName: "View", ItemIconSvg: "<svg .../>", ItemIconColor: "60,60,60,1", ItemEnabled: true, ItemVisible: true},
{ItemKey: "edit", ItemDisplayName: "Edit", ItemIconSvg: "<svg .../>", ItemIconColor: "60,60,60,1", ItemEnabled: true, ItemVisible: true},
{ItemKey: "delete", ItemDisplayName: "Delete", ItemIconSvg: "<svg .../>", ItemIconColor: "248,113,113,1", ItemEnabled: true, ItemVisible: true}
)
cmpMyTable.OnMenuItemSelect:
Switch(
cmpMyTable.SelectedMenuItem.ItemKey,
"view", Navigate(ViewScreen, None, {Item: cmpMyTable.SelectedItem}),
"edit", Navigate(EditScreen, None, {Item: cmpMyTable.SelectedItem}),
"delete", Remove(DataSource, cmpMyTable.SelectedItem)
)// StatusConfig is a Table — add any status row and the default catches the rest
cmpMyTable.StatusConfig: Table(
{StatusKey: "in progress", Background: "219,234,254,1", TextColor: "30,64,175,1"},
{StatusKey: "completed", Background: "212,244,221,1", TextColor: "15,81,50,1"},
{StatusKey: "on hold", Background: "254,243,199,1", TextColor: "146,64,14,1"},
{StatusKey: "not started", Background: "243,244,246,1", TextColor: "75,85,99,1"},
{StatusKey: "default", Background: "240,240,240,1", TextColor: "96,96,96,1"}
)
// PriorityConfig follows the same pattern
cmpMyTable.PriorityConfig: Table(
{PriorityKey: "high", Background: "254,226,226,1", TextColor: "185,28,28,1"},
{PriorityKey: "medium", Background: "254,243,199,1", TextColor: "146,64,14,1"},
{PriorityKey: "low", Background: "220,252,231,1", TextColor: "22,101,52,1"},
{PriorityKey: "default", Background: "243,244,246,1", TextColor: "75,85,99,1"}
)// SVG-based segmented progress bar (up to 10 segments)
// Requires CompletedSteps (number) and TotalSteps (number) in your data
cmpMyTable.ProgressActiveColor: "2563EB" // hex without #
cmpMyTable.ProgressInactiveColor: "E2E8F0" // hex without #cmpMyTable.ThemeConfig: {
container: {background: "255,255,255,1", border: "224,224,224,1", borderThickness: 1, radius: 12},
header: {background: "249,250,251,1", border: "224,224,224,1", borderThickness: 2, textColor: "96,96,96,1", fontSize: 11, fontWeight: "Semibold", height: 50},
row: {background: "0,0,0,0", hoverBackground: "248,249,250,0.5", borderColor: "240,240,240,1", borderThickness: 1, height: 60},
card: {background: "255,255,255,1", border: "229,231,235,1", borderThickness: 1, radius: 12, height: 300, gap: 16},
list: {background: "255,255,255,1", border: "229,231,235,1", divider: "243,244,246,1", text: "17,24,39,1", textMuted: "107,114,128,1", radius: 12},
text: {primary: "32,32,32,1", secondary: "96,96,96,1", accent: "37,99,235,1", fontFamily: "Segoe UI", fontSizeNormal: 12, fontSizeSmall: 11},
spacing: {paddingLeft: 20, columnGap: 10}
}| Property | Type | Default | Description |
|---|---|---|---|
Items | Table | Sample data | Data source. Progress columns require CompletedSteps and TotalSteps number fields. |
ViewMode | Text | "table" | Initial view mode: "table", "card", or "list". |
ShowViewToggle | Boolean | true | Show the 3-button Table / Card / List toggle. |
ShowHeader | Boolean | true | Show column headers in table view. |
EnableMenu | Boolean | true | Enable the context menu (⋮) button on rows. |
EnableHover | Boolean | true | Enable row hover highlight effects. |
ContextMenuItems | Table | View, Edit, Delete | Menu items: ItemKey, ItemDisplayName, ItemEnabled, ItemVisible. Note: ItemIconSvg and ItemIconColor are reserved and not currently rendered — the native TabList menu shows text labels only (no custom icons or per-item colors). |
StatusConfig | Table | 5 statuses | Table with {StatusKey, Background, TextColor} rows. LookUp matches Lower(StatusKey) to Lower(ThisItem.Status); the "default" row is the fallback. |
PriorityConfig | Table | 4 priorities | Table with {PriorityKey, Background, TextColor} rows. LookUp matches Lower(PriorityKey) to Lower(ThisItem.Priority); the "default" row is the fallback. |
ProgressActiveColor | Text | "2563EB" | Hex color (without #) for completed progress segments. |
ProgressInactiveColor | Text | "E2E8F0" | Hex color (without #) for incomplete progress segments. |
CharWidths | Table | Segoe UI Semibold | Character width lookup table used to size list-view tag pills precisely. |
ThemeConfig | Record | Light theme | Full theme: container, header, row, card, list, text, and spacing configuration. |
| Property | Type | Description |
|---|---|---|
SelectedItem | Record | Currently selected row/card/list item. |
SelectedMenuItem | Record | Last selected context menu item (ItemKey, ItemDisplayName, etc.). |
SelectedViewMode | Text | Current active view mode: "table", "card", or "list". |
| Event | Description |
|---|---|
OnRowSelect | Fires when a row, card, or list item is tapped. SelectedItem is populated before this fires. |
OnMenuItemSelect | Fires when a context menu item is chosen. SelectedMenuItem and SelectedItem both populated. |
OnViewChange | Fires when the user switches between table, card, and list views. |
Table view renders rows in a horizontal auto-layout gallery with configurable header. Card view renders responsive cards using WrapCount (1 column under 600px, 2 under 1024px, 3 above). List view renders compact 76px rows with a status-colored icon box, title/ID stack, auto-sized tag pills, and a chevron. All three views share the same Items source and context menus.
List view status and priority tags use CharWidths — a character-level width lookup table for Segoe UI Semibold — to calculate exact pill widths via Sum(AddColumns(Split(...), ...), _w). This eliminates fixed-width truncation without requiring PowerFx MeasureText.
Built with the TabList modern control for reliable flyout behavior. Menu items support custom SVG icons, RGBA color strings, and individual enabled/visible flags.
Segmented SVG progress bars render dynamically from CompletedSteps and TotalSteps fields. Table view supports up to 5 segments; card view supports up to 10. Colors are hex strings via ProgressActiveColor / ProgressInactiveColor.
All colors use RGBA string format ("R,G,B,A") parsed at runtime with Split() and Value(). Status and priority badges resolve colors via LookUp(StatusConfig, Lower(StatusKey) = Lower(Text(ThisItem.Status))) with a Coalesce fallback to the default row — no hardcoded Switch blocks, fully data-driven.
Table rows use an invisible button overlay for tap detection, offset past the actions column to avoid interfering with the context menu. List rows use a full-width button overlay. On select, varCT_SelectedItem is set then OnRowSelect fires.
OnMenuItemSelect:
Switch(
Self.SelectedMenuItem.ItemKey,
"view",
Navigate(DetailScreen, None, {
Record: Self.SelectedItem
}),
"edit",
Set(varEditMode, true);
Set(varEditRecord, Self.SelectedItem),
"delete",
Remove(DataSource, Self.SelectedItem)
)Status
Priority
Progress
// Colors resolve via LookUp — case-insensitive,
// "default" row catches any unmatched value
With(
{_m: Coalesce(
LookUp(StatusConfig,
Lower(StatusKey) = Lower(Text(ThisItem.Status))
),
LookUp(StatusConfig, StatusKey = "default")
)},
With({p: Split(_m.Background, ",")},
RGBA(Value(Index(p,1).Value),
Value(Index(p,2).Value),
Value(Index(p,3).Value),
Value(Index(p,4).Value))
)
)
// Progress reads from data fields:
// ThisItem.CompletedSteps
// ThisItem.TotalSteps// Dark-mode list theme
ThemeConfig: {
list: {
background: "17,24,39,1",
border: "31,41,55,1",
divider: "31,41,55,1",
text: "255,255,255,1",
textMuted: "156,163,175,1",
radius: 12
}
}One email when something new ships. Unsubscribe anytime.
Use the toolbar to format · or type markdown directly