Skip to main content

dioxus_ui_system/atoms/
icon.rs

1//! Icon atom component
2//!
3//! SVG icon component with theme integration and sizing options.
4
5use crate::styles::Style;
6use crate::theme::tokens::Color;
7use crate::theme::{use_style, use_theme};
8use dioxus::prelude::*;
9
10/// Icon sizes
11#[derive(Default, Copy, Clone, PartialEq)]
12pub enum IconSize {
13    ExtraSmall, // 12px
14    Small,      // 16px
15    #[default]
16    Medium, // 20px
17    Large,      // 24px
18    ExtraLarge, // 32px
19}
20
21impl IconSize {
22    pub fn to_px(&self) -> u16 {
23        match self {
24            IconSize::ExtraSmall => 12,
25            IconSize::Small => 16,
26            IconSize::Medium => 20,
27            IconSize::Large => 24,
28            IconSize::ExtraLarge => 32,
29        }
30    }
31}
32
33/// Icon color options
34#[derive(Default, Clone, PartialEq)]
35pub enum IconColor {
36    #[default]
37    Current, // Uses currentColor (inherits from parent)
38    Primary,
39    Secondary,
40    Muted,
41    Destructive,
42    Success,
43    Warning,
44    Inverse,
45    Custom(Color),
46}
47
48/// Icon properties
49#[derive(Props, Clone, PartialEq)]
50pub struct IconProps {
51    /// SVG path data or icon name
52    pub name: String,
53    /// Icon size
54    #[props(default)]
55    pub size: IconSize,
56    /// Icon color
57    #[props(default)]
58    pub color: IconColor,
59    /// Custom inline styles
60    #[props(default)]
61    pub style: Option<String>,
62    /// Custom class name
63    #[props(default)]
64    pub class: Option<String>,
65    /// Accessibility label
66    #[props(default)]
67    pub aria_label: Option<String>,
68    /// Flip horizontally
69    #[props(default)]
70    pub flip_h: bool,
71    /// Flip vertically
72    #[props(default)]
73    pub flip_v: bool,
74    /// Rotate degrees (0, 90, 180, 270)
75    #[props(default)]
76    pub rotate: u16,
77}
78
79/// Icon atom component
80///
81/// Renders an SVG icon. The icon name can be:
82/// - A preset icon name (e.g., "check", "x", "arrow-right")
83/// - Raw SVG path data
84///
85/// # Example
86/// ```rust,ignore
87/// use dioxus_ui_system::atoms::{Icon, IconSize, IconColor};
88///
89/// rsx! {
90///     Icon {
91///         name: "check".to_string(),
92///         size: IconSize::Medium,
93///         color: IconColor::Success,
94///     }
95/// }
96/// ```
97#[component]
98pub fn Icon(props: IconProps) -> Element {
99    let _theme = use_theme();
100
101    let size = props.size.clone();
102    let color = props.color.clone();
103    let flip_h = props.flip_h;
104    let flip_v = props.flip_v;
105    let rotate = props.rotate;
106
107    // Memoized styles
108    let style = use_style(move |t| {
109        let base = Style::new()
110            .inline_flex()
111            .items_center()
112            .justify_center()
113            .w_px(size.to_px())
114            .h_px(size.to_px())
115            .transition("color 150ms ease");
116
117        // Apply color
118        let base = match &color {
119            IconColor::Current => base,
120            IconColor::Primary => base.text_color(&t.colors.primary),
121            IconColor::Secondary => base.text_color(&t.colors.secondary_foreground),
122            IconColor::Muted => base.text_color(&t.colors.muted_foreground),
123            IconColor::Destructive => base.text_color(&t.colors.destructive),
124            IconColor::Success => base.text_color(&t.colors.success),
125            IconColor::Warning => base.text_color(&t.colors.warning),
126            IconColor::Inverse => base.text_color(&t.colors.background),
127            IconColor::Custom(c) => base.text_color(c),
128        };
129
130        // Transforms
131        let mut transform = String::new();
132
133        if flip_h {
134            transform.push_str("scaleX(-1) ");
135        }
136        if flip_v {
137            transform.push_str("scaleY(-1) ");
138        }
139        if rotate != 0 {
140            transform.push_str(&format!("rotate({}deg)", rotate));
141        }
142
143        if !transform.is_empty() {
144            Style {
145                transform: Some(transform.trim().to_string()),
146                ..base
147            }
148            .build()
149        } else {
150            base.build()
151        }
152    });
153
154    // Combine with custom styles
155    let final_style = if let Some(custom) = &props.style {
156        format!("{} {}", style(), custom)
157    } else {
158        style()
159    };
160
161    let class = props.class.clone().unwrap_or_default();
162    let aria_label = props.aria_label.clone();
163
164    // Get SVG content
165    let svg_content = get_icon_svg(&props.name);
166    let px = size.to_px();
167    let view_box = get_icon_viewbox(&props.name);
168
169    rsx! {
170        svg {
171            style: "{final_style}",
172            class: "{class}",
173            width: "{px}",
174            height: "{px}",
175            view_box: "{view_box}",
176            fill: "currentColor",
177            role: if aria_label.is_some() { "img" } else { "presentation" },
178            dangerous_inner_html: "{svg_content}",
179        }
180    }
181}
182
183/// Get SVG path data for preset icons
184fn get_icon_svg(name: &str) -> String {
185    match name {
186        // Common icons
187        "check" => r#"<path d="M20 6L9 17l-5-5" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
188        "x" | "close" => r#"<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
189        "plus" => r#"<path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
190        "minus" => r#"<path d="M5 12h14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
191        "arrow-left" => r#"<path d="M19 12H5M12 19l-7-7 7-7" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
192        "arrow-right" => r#"<path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
193        "arrow-up" => r#"<path d="M12 19V5M5 12l7-7 7 7" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
194        "arrow-down" => r#"<path d="M12 5v14M19 12l-7 7-7-7" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
195        "chevron-left" => r#"<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
196        "chevron-right" => r#"<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
197        "chevron-up" => r#"<path d="M18 15l-6-6-6 6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
198        "chevron-down" => r#"<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
199        "menu" => r#"<path d="M3 12h18M3 6h18M3 18h18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
200        "search" => r#"<circle cx="11" cy="11" r="8" stroke="currentColor" stroke-width="2" fill="none"/><path d="M21 21l-4.35-4.35" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
201        "user" => r#"<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2" fill="none"/>"#,
202        "settings" => r#"<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" fill="none"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
203        "home" => r#"<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><polyline points="9 22 9 12 15 12 15 22" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
204        "bell" => r#"<path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M13.73 21a2 2 0 01-3.46 0" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
205        "heart" => r#"<path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
206        "star" => r#"<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
207        "trash" => r#"<polyline points="3 6 5 6 21 6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
208        "edit" => r#"<path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
209        "copy" => r#"<rect x="9" y="9" width="13" height="13" rx="2" ry="2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
210        "external-link" => r#"<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><polyline points="15 3 21 3 21 9" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="10" y1="14" x2="21" y2="3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
211        "loading" | "spinner" => r#"<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
212        "info" => r#"<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="16" x2="12" y2="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="8" x2="12.01" y2="8" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
213        "warning" | "alert" => r#"<path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="9" x2="12" y2="13" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="17" x2="12.01" y2="17" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
214        "error" | "alert-circle" => r#"<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="15" y1="9" x2="9" y2="15" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="9" y1="9" x2="15" y2="15" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
215        "moon" => r#"<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
216        "sun" => r#"<circle cx="12" cy="12" r="5" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="1" x2="12" y2="3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="21" x2="12" y2="23" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="1" y1="12" x2="3" y2="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="21" y1="12" x2="23" y2="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
217        // Layout icons
218        "book" => r#"<path d="M4 19.5A2.5 2.5 0 016.5 17H20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
219        "layout" => r#"<rect x="3" y="3" width="18" height="18" rx="2" ry="2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="3" y1="9" x2="21" y2="9" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="9" y1="21" x2="9" y2="9" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
220        "sidebar" => r#"<rect x="3" y="3" width="18" height="18" rx="2" ry="2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="9" y1="3" x2="9" y2="21" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
221        "maximize" => r#"<path d="M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
222        "box" => r#"<path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><polyline points="3.27 6.96 12 12.01 20.73 6.96" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="22.08" x2="12" y2="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
223        "smartphone" => r#"<rect x="5" y="2" width="14" height="20" rx="2" ry="2" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="18" x2="12.01" y2="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
224        "palette" => r#"<circle cx="13.5" cy="6.5" r=".5" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><circle cx="17.5" cy="10.5" r=".5" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><circle cx="8.5" cy="7.5" r=".5" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><circle cx="6.5" cy="12.5" r=".5" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.042a1.66 1.66 0 011.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.01 17.461 2 12 2z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
225        "share" => r#"<path d="M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><polyline points="16 6 12 2 8 6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="2" x2="12" y2="15" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
226        "x-circle" => r#"<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="15" y1="9" x2="9" y2="15" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><line x1="9" y1="9" x2="15" y2="15" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
227        "play-circle" => r#"<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><polygon points="10 8 16 12 10 16 10 8" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>"#,
228        // Default: return the name as raw SVG path data
229        _ => name,
230    }.to_string()
231}
232
233/// Get viewBox for preset icons
234fn get_icon_viewbox(name: &str) -> &'static str {
235    match name {
236        "check" | "x" | "close" | "plus" | "minus" | "arrow-left" | "arrow-right" | "arrow-up"
237        | "arrow-down" | "chevron-left" | "chevron-right" | "chevron-up" | "chevron-down"
238        | "menu" | "search" | "user" | "settings" | "home" | "bell" | "heart" | "star"
239        | "trash" | "edit" | "copy" | "external-link" | "loading" | "spinner" | "info"
240        | "warning" | "alert" | "error" | "alert-circle" | "moon" | "sun" | "book" | "layout"
241        | "sidebar" | "maximize" | "box" | "smartphone" | "palette" => "0 0 24 24",
242        // Default
243        _ => "0 0 24 24",
244    }
245}
246
247/// Icon button component (combines Icon with Button behavior)
248#[derive(Props, Clone, PartialEq)]
249pub struct IconButtonProps {
250    pub icon: String,
251    #[props(default)]
252    pub size: IconSize,
253    #[props(default)]
254    pub color: IconColor,
255    #[props(default)]
256    pub disabled: bool,
257    #[props(default)]
258    pub onclick: Option<EventHandler<MouseEvent>>,
259    #[props(default)]
260    pub aria_label: String,
261    #[props(default)]
262    pub style: Option<String>,
263    #[props(default)]
264    pub class: Option<String>,
265}
266
267#[component]
268pub fn IconButton(props: IconButtonProps) -> Element {
269    let class = format!("icon-button {}", props.class.clone().unwrap_or_default());
270
271    rsx! {
272        button {
273            class: "{class}",
274            style: props.style.clone().unwrap_or_default(),
275            disabled: props.disabled,
276            aria_label: props.aria_label.clone(),
277            onclick: move |e| {
278                if let Some(handler) = &props.onclick {
279                    if !props.disabled {
280                        handler.call(e);
281                    }
282                }
283            },
284            Icon {
285                name: props.icon.clone(),
286                size: props.size.clone(),
287                color: props.color.clone(),
288            }
289        }
290    }
291}