use pathland_core::{component_type, role, text_style};
pub struct RoleTag {
pub role: u8,
pub tag: &'static str,
pub aria: &'static str,
}
pub const ROLE_TAGS: &[RoleTag] = &[
RoleTag { role: role::HEADER, tag: "h2", aria: "heading" },
RoleTag { role: role::PARAGRAPH, tag: "p", aria: "paragraph" },
RoleTag { role: role::LIST, tag: "ul", aria: "list" },
RoleTag { role: role::LIST_ITEM, tag: "li", aria: "listitem" },
RoleTag { role: role::SUMMARY, tag: "section", aria: "region" },
RoleTag { role: role::BANNER, tag: "header", aria: "banner" },
RoleTag { role: role::NAVIGATION, tag: "nav", aria: "navigation" },
RoleTag { role: role::MAIN, tag: "main", aria: "main" },
RoleTag { role: role::CONTENT_INFO, tag: "footer", aria: "contentinfo" },
RoleTag { role: role::COMPLEMENTARY, tag: "aside", aria: "complementary" },
RoleTag { role: role::ARTICLE, tag: "article", aria: "article" },
RoleTag { role: role::SECTION, tag: "section", aria: "region" },
RoleTag { role: role::SEARCH, tag: "search", aria: "search" },
];
pub const HEADING_STYLES: &[(u8, u8)] = &[
(text_style::LARGE_TITLE, 1),
(text_style::TITLE, 2),
(text_style::TITLE2, 3),
(text_style::TITLE3, 4),
(text_style::HEADLINE, 5),
];
#[must_use]
pub fn heading_level(text_style: u8) -> Option<u8> {
HEADING_STYLES.iter().find(|(s, _)| *s == text_style).map(|(_, level)| *level)
}
#[must_use]
pub fn heading_tag(level: u8) -> &'static str {
match level {
1 => "h1",
2 => "h2",
3 => "h3",
4 => "h4",
_ => "h5",
}
}
#[must_use]
pub fn text_tag(kind: ShellKind, role: u8, text_style: Option<u8>) -> Option<&'static str> {
if kind == ShellKind::Generic {
if let Some(level) = text_style.and_then(heading_level) {
return Some(heading_tag(level));
}
}
semantic_tag(kind, role)
}
pub const GENERIC_COMPONENTS: &[u16] = &[
component_type::TEXT,
component_type::VSTACK,
component_type::HSTACK,
component_type::LAZY_VSTACK,
component_type::LAZY_HSTACK,
component_type::ZSTACK,
component_type::GRID,
component_type::LAZY_VGRID,
component_type::LAZY_HGRID,
component_type::SCROLLVIEW,
component_type::COLOR,
component_type::SHAPE,
component_type::SPACER,
];
pub const NATIVE_ROLE_COMPONENTS: &[u16] = &[
component_type::BUTTON,
component_type::TEXT_FIELD,
component_type::TEXT_EDITOR,
component_type::SLIDER,
component_type::TOGGLE,
component_type::IMAGE,
component_type::VIDEO,
component_type::PROGRESS_VIEW,
component_type::GAUGE,
component_type::DATE_PICKER,
component_type::COLOR_PICKER,
component_type::PICKER,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShellKind {
Generic,
Native,
Other,
}
#[must_use]
pub fn shell_kind(component: u16) -> ShellKind {
if GENERIC_COMPONENTS.contains(&component) {
ShellKind::Generic
} else if NATIVE_ROLE_COMPONENTS.contains(&component) {
ShellKind::Native
} else {
ShellKind::Other
}
}
#[must_use]
pub fn semantic_tag(kind: ShellKind, role: u8) -> Option<&'static str> {
if kind != ShellKind::Generic {
return None;
}
ROLE_TAGS.iter().find(|r| r.role == role).map(|r| r.tag)
}
#[must_use]
pub fn aria_role(kind: ShellKind, role: u8) -> Option<&'static str> {
if kind == ShellKind::Native {
return None;
}
if let Some(tag) = ROLE_TAGS.iter().find(|r| r.role == role) {
return if kind == ShellKind::Generic { None } else { Some(tag.aria) };
}
None
}