patternfly_yew/components/
truncate.rs1use yew::html::IntoPropValue;
2use yew::prelude::*;
3
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub enum TruncateContent {
6 Default(String),
7 Middle(String, String),
8 Start(String),
9}
10
11impl TruncateContent {
12 pub fn middle<S: Into<String>, E: Into<String>>(start: S, end: E) -> Self {
13 Self::Middle(start.into(), end.into())
14 }
15
16 pub fn start<S: Into<String>>(start: S) -> Self {
17 Self::Start(start.into())
18 }
19}
20
21impl From<String> for TruncateContent {
22 fn from(value: String) -> Self {
23 Self::Default(value)
24 }
25}
26
27impl From<&str> for TruncateContent {
28 fn from(value: &str) -> Self {
29 Self::Default(value.to_string())
30 }
31}
32
33impl IntoPropValue<TruncateContent> for String {
34 fn into_prop_value(self) -> TruncateContent {
35 TruncateContent::Default(self)
36 }
37}
38
39impl IntoPropValue<TruncateContent> for &str {
40 fn into_prop_value(self) -> TruncateContent {
41 TruncateContent::Default(self.to_string())
42 }
43}
44
45pub trait IntoTruncateContent {
47 fn truncate_start(self) -> TruncateContent;
49
50 fn truncate_before(self, num: usize) -> TruncateContent;
52}
53
54impl<T: ToString> IntoTruncateContent for T {
55 fn truncate_start(self) -> TruncateContent {
56 TruncateContent::Start(self.to_string())
57 }
58
59 fn truncate_before(self, num: usize) -> TruncateContent {
78 let s = self.to_string();
79 let len = s.len();
80
81 if num == 0 {
82 return TruncateContent::Default(s);
83 }
84
85 if num > len {
86 return TruncateContent::Start(s);
87 }
88
89 let mut end = len - num;
90 loop {
91 if end == 0 {
92 return TruncateContent::Start(s);
93 }
94
95 if s.is_char_boundary(end) {
96 break;
97 }
98
99 end -= 1;
101 }
102
103 let (start, end) = s.split_at(end);
104 TruncateContent::Middle(start.to_string(), end.to_string())
105 }
106}
107
108#[derive(PartialEq, Properties)]
110pub struct TruncateProperties {
111 pub content: TruncateContent,
112
113 #[prop_or_default]
114 pub id: Option<AttrValue>,
115 #[prop_or_default]
116 pub style: Option<AttrValue>,
117 #[prop_or_default]
118 pub class: Classes,
119 #[prop_or_default]
120 pub start_class: Classes,
121 #[prop_or_default]
122 pub end_class: Classes,
123}
124
125#[function_component(Truncate)]
135pub fn truncate(props: &TruncateProperties) -> Html {
136 let class = classes!("pf-v5-c-truncate", props.class.clone());
137 let start_class = classes!("pf-v5-c-truncate__start", props.start_class.clone());
138 let end_class = classes!("pf-v5-c-truncate__end", props.end_class.clone());
139
140 html!(
141 <span
142 {class}
143 style={props.style.clone()}
144 id={props.id.clone()}
145 >
146 {
147 match &props.content {
148 TruncateContent::Default(value) => html!(
149 <span class={start_class}>{ &value }</span>
150 ),
151 TruncateContent::Middle(start, end) => html!(<>
152 <span class={start_class}>{ &start }</span>
153 <span class={end_class}>{ &end }</span>
154 </>),
155 TruncateContent::Start(value) => html!(<>
156 <span class={end_class}>{ &value }{ "\u{200E}" }</span>
157 </>),
158 }
159 }
160 </span>
161 )
162}
163
164#[cfg(test)]
165mod test {
166 use super::*;
167
168 #[test]
169 pub fn test_mid_basic() {
170 let content = "0123456789".truncate_before(5);
171 assert_eq!(
172 TruncateContent::Middle("01234".to_string(), "56789".to_string()),
173 content
174 );
175 }
176
177 #[test]
178 pub fn test_mid_empty() {
179 let content = "".truncate_before(5);
180 assert_eq!(TruncateContent::Start("".to_string()), content);
181 }
182
183 #[test]
184 pub fn test_mid_over() {
185 let content = "0123456789".truncate_before(20);
186 assert_eq!(TruncateContent::Start("0123456789".to_string()), content);
187 }
188
189 #[test]
190 pub fn test_mid_zero() {
191 let content = "0123456789".truncate_before(0);
192 assert_eq!(TruncateContent::Default("0123456789".to_string()), content);
193 }
194}