1mod icons;
34
35use teksilo_core::signal::Signal;
36use teksilo_core::styles::{CheckboxVariant, TextInputVariant};
37use teksilo_core::widget::Widget;
38use teksilo_i18n::lit;
39use teksilo_preview::{
40 KnobOverrides, KnobSpec, KnobValues, PreviewVariant, SlottedChild, WidgetCatalog,
41 WidgetCategory, register_widget_catalog_at,
42};
43use teksilo_tokens::{BorderRole, SurfaceRole, TextRole, TextStyleRole};
44
45use crate::primitives::{
46 Center, Expand, FixedSize, Grid, HStack, IconWidget, Padding, Spacer, TextWidget, TrackSize,
47 VStack, ZStack,
48};
49#[allow(unused_imports)]
53use crate::primitives::{MaxSize, RectWidget};
54use crate::{
55 Accordion, Avatar, AvatarPresence, AvatarShape, AvatarSize, Badge, Breadcrumb, BreadcrumbItem,
56 Button, ButtonVariant, Card, Checkbox, ComboBox, FontPicker, GridSizing, GridView, GroupBox,
57 GroupHeader, IconButton, IconButtonSize, LanguageSwitcher, Link, ListView, MenuItem, MenuList,
58 Orientation, PaneDescriptor, Panel, ProgressBar, RadioButton, RadioGroup, RadioTile,
59 RadioTileGroup, ScrollArea, SegmentDisplay, SegmentOverflow, SegmentedControl, Slider,
60 Snackbar, SplitButton, Splitter, SplitterModel, StandardListItem, StandardTreeItem, StatusBar,
61 TabWidget, TextInput, TextScaleControl, ThemeSwitcher, TileLayout, Toggle, ToolBox, Toolbar,
62 TreeView,
63};
64
65fn button_variant(idx: usize) -> ButtonVariant {
71 match idx {
72 0 => ButtonVariant::Filled,
73 1 => ButtonVariant::Tinted,
74 2 => ButtonVariant::Outlined,
75 4 => ButtonVariant::Ghost,
76 5 => ButtonVariant::Link,
77 6 => ButtonVariant::Destructive,
78 _ => ButtonVariant::Plain,
79 }
80}
81
82fn checkbox_variant(idx: usize) -> CheckboxVariant {
84 match idx {
85 1 => CheckboxVariant::Rounded,
86 2 => CheckboxVariant::Circle,
87 _ => CheckboxVariant::Square,
88 }
89}
90
91fn text_input_variant(idx: usize) -> TextInputVariant {
93 match idx {
94 1 => TextInputVariant::Filled,
95 2 => TextInputVariant::Underline,
96 3 => TextInputVariant::Bare,
97 _ => TextInputVariant::Outlined,
98 }
99}
100
101impl WidgetCatalog for Button {
102 fn id() -> &'static str {
103 "button"
104 }
105 fn group() -> &'static str {
106 "Controls"
107 }
108 fn display_name() -> &'static str {
109 "Button"
110 }
111 fn knobs() -> KnobSpec {
112 KnobSpec::new()
113 .text("label", "Label", "Click me")
114 .ctor(0)
115 .enum_(
116 "variant",
117 "Variant",
118 "ButtonVariant",
119 &[
120 "Filled",
121 "Tinted",
122 "Outlined",
123 "Plain",
124 "Ghost",
125 "Link",
126 "Destructive",
127 ],
128 3,
129 )
130 .bool_("enabled", "Enabled", true)
131 .opt_text("tooltip", "Tooltip", None)
132 }
133 fn variants() -> Vec<PreviewVariant> {
134 vec![
135 PreviewVariant::defaults("default"),
136 PreviewVariant::knobs(
137 "primary",
138 KnobOverrides::new()
139 .enum_("variant", 0)
140 .text("label", "Save"),
141 ),
142 PreviewVariant::knobs(
143 "flat",
144 KnobOverrides::new()
145 .enum_("variant", 4)
146 .text("label", "More…"),
147 ),
148 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
149 PreviewVariant::knobs(
150 "with-tooltip",
151 KnobOverrides::new()
152 .text("label", "Help")
153 .opt_text("tooltip", Some("Open the help documentation")),
154 ),
155 ]
156 }
157 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
158 let label = knobs.text("label").get();
159 let variant = button_variant(knobs.enum_("variant").get());
160 let enabled = knobs.bool_("enabled").get();
161 let tooltip = knobs.opt_text("tooltip").get();
162 let mut b = Button::new(lit!(label)).variant(variant).enabled(enabled);
163 if let Some(t) = tooltip {
164 b = b.tooltip(lit!(t));
165 }
166 Box::new(b)
167 }
168 fn icon() -> Option<Box<dyn Widget>> {
169 Some(icons::button())
170 }
171}
172register_widget_catalog_at!("crates/teksilo-widgets/src/button.rs", Button);
173
174impl WidgetCatalog for VStack {
180 fn id() -> &'static str {
181 "vstack"
182 }
183 fn group() -> &'static str {
184 "Layout"
185 }
186 fn display_name() -> &'static str {
187 "VStack"
188 }
189 fn knobs() -> KnobSpec {
190 KnobSpec::new().f32_("spacing", "Spacing", 8.0, 0.0, 48.0)
191 }
192 fn variants() -> Vec<PreviewVariant> {
193 vec![PreviewVariant::defaults("default")]
194 }
195 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
196 Box::new(
197 VStack::new()
198 .spacing(knobs.f32_("spacing").get())
199 .child(sample_text("Item 1"))
200 .child(sample_text("Item 2"))
201 .child(sample_text("Item 3")),
202 )
203 }
204 fn icon() -> Option<Box<dyn Widget>> {
205 Some(icons::vstack())
206 }
207 fn category() -> WidgetCategory {
208 WidgetCategory::ContainerA
209 }
210 fn build_with_children(
211 _variant: &str,
212 knobs: &KnobValues,
213 children: Vec<SlottedChild>,
214 ) -> Box<dyn Widget> {
215 let mut s = VStack::new().spacing(knobs.f32_("spacing").get());
216 for c in children {
217 s = s.add_child(c.id);
218 }
219 Box::new(s)
220 }
221}
222register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/vstack.rs", VStack);
223
224impl WidgetCatalog for HStack {
225 fn id() -> &'static str {
226 "hstack"
227 }
228 fn group() -> &'static str {
229 "Layout"
230 }
231 fn display_name() -> &'static str {
232 "HStack"
233 }
234 fn knobs() -> KnobSpec {
235 KnobSpec::new().f32_("spacing", "Spacing", 8.0, 0.0, 48.0)
236 }
237 fn variants() -> Vec<PreviewVariant> {
238 vec![PreviewVariant::defaults("default")]
239 }
240 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
241 Box::new(
242 HStack::new()
243 .spacing(knobs.f32_("spacing").get())
244 .child(sample_text("A"))
245 .child(sample_text("B"))
246 .child(sample_text("C")),
247 )
248 }
249 fn icon() -> Option<Box<dyn Widget>> {
250 Some(icons::hstack())
251 }
252 fn category() -> WidgetCategory {
253 WidgetCategory::ContainerA
254 }
255 fn build_with_children(
256 _variant: &str,
257 knobs: &KnobValues,
258 children: Vec<SlottedChild>,
259 ) -> Box<dyn Widget> {
260 let mut s = HStack::new().spacing(knobs.f32_("spacing").get());
261 for c in children {
262 s = s.add_child(c.id);
263 }
264 Box::new(s)
265 }
266}
267register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/hstack.rs", HStack);
268
269impl WidgetCatalog for ZStack {
270 fn id() -> &'static str {
271 "zstack"
272 }
273 fn group() -> &'static str {
274 "Layout"
275 }
276 fn display_name() -> &'static str {
277 "ZStack"
278 }
279 fn variants() -> Vec<PreviewVariant> {
280 vec![PreviewVariant::defaults("default")]
281 }
282 fn build(_variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
283 Box::new(
284 ZStack::new()
285 .child(RectWidget::new().background(SurfaceRole::Raised))
286 .child(Center::new().child(sample_text("ZStack"))),
287 )
288 }
289 fn icon() -> Option<Box<dyn Widget>> {
290 Some(icons::zstack())
291 }
292 fn category() -> WidgetCategory {
293 WidgetCategory::ContainerA
294 }
295 fn build_with_children(
296 _variant: &str,
297 _knobs: &KnobValues,
298 children: Vec<SlottedChild>,
299 ) -> Box<dyn Widget> {
300 let mut s = ZStack::new();
301 for c in children {
302 s = s.add_child(c.id);
303 }
304 Box::new(s)
305 }
306}
307register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/zstack.rs", ZStack);
308
309impl WidgetCatalog for Grid {
310 fn id() -> &'static str {
311 "grid"
312 }
313 fn group() -> &'static str {
314 "Layout"
315 }
316 fn display_name() -> &'static str {
317 "Grid"
318 }
319 fn knobs() -> KnobSpec {
320 KnobSpec::new()
321 .choice("columns", "Columns", &["1", "2", "3", "4"], 1)
322 .f32_("gap", "Gap", 8.0, 0.0, 32.0)
323 }
324 fn variants() -> Vec<PreviewVariant> {
325 vec![PreviewVariant::defaults("default")]
326 }
327 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
328 let cols = knobs.choice("columns").get() + 1;
329 let gap = knobs.f32_("gap").get();
330 let mut g = Grid::new()
331 .columns(vec![TrackSize::Fractional(1.0); cols])
332 .column_gap(gap)
333 .row_gap(gap);
334 for i in 1..=6 {
335 g = g.child(sample_text(&format!("{i}")));
336 }
337 Box::new(g)
338 }
339 fn icon() -> Option<Box<dyn Widget>> {
340 Some(icons::grid())
341 }
342 fn category() -> WidgetCategory {
343 WidgetCategory::ContainerA
344 }
345 fn build_with_children(
346 _variant: &str,
347 knobs: &KnobValues,
348 children: Vec<SlottedChild>,
349 ) -> Box<dyn Widget> {
350 let cols = knobs.choice("columns").get() + 1;
351 let gap = knobs.f32_("gap").get();
352 let mut g = Grid::new()
353 .columns(vec![TrackSize::Fractional(1.0); cols])
354 .column_gap(gap)
355 .row_gap(gap);
356 for c in children {
357 g = g.add_child(c.id);
358 }
359 Box::new(g)
360 }
361}
362register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/grid.rs", Grid);
363
364impl WidgetCatalog for Padding {
365 fn id() -> &'static str {
366 "padding"
367 }
368 fn group() -> &'static str {
369 "Layout"
370 }
371 fn display_name() -> &'static str {
372 "Padding"
373 }
374 fn knobs() -> KnobSpec {
375 KnobSpec::new().f32_("amount", "Amount", 16.0, 0.0, 48.0)
376 }
377 fn variants() -> Vec<PreviewVariant> {
378 vec![PreviewVariant::defaults("default")]
379 }
380 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
381 Box::new(Padding::uniform(knobs.f32_("amount").get()).child(sample_text("Padded content")))
382 }
383 fn icon() -> Option<Box<dyn Widget>> {
384 Some(icons::padding())
385 }
386 fn category() -> WidgetCategory {
387 WidgetCategory::ContainerA
388 }
389 fn build_with_children(
390 _variant: &str,
391 knobs: &KnobValues,
392 children: Vec<SlottedChild>,
393 ) -> Box<dyn Widget> {
394 let mut p = Padding::uniform(knobs.f32_("amount").get());
395 if let Some(c) = children.into_iter().next() {
396 p = p.child_id(c.id);
397 }
398 Box::new(p)
399 }
400}
401register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/padding.rs", Padding);
402
403impl WidgetCatalog for Expand {
404 fn id() -> &'static str {
405 "expand"
406 }
407 fn group() -> &'static str {
408 "Layout"
409 }
410 fn display_name() -> &'static str {
411 "Expand"
412 }
413 fn knobs() -> KnobSpec {
414 KnobSpec::new().f32_("flex", "Flex", 1.0, 0.0, 4.0)
415 }
416 fn variants() -> Vec<PreviewVariant> {
417 vec![PreviewVariant::defaults("default")]
418 }
419 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
420 Box::new(
421 FixedSize::new().width(220.0_f32).height(60.0_f32).child(
422 Expand::new()
423 .flex(knobs.f32_("flex").get())
424 .child(RectWidget::new().background(SurfaceRole::AccentSubtle)),
425 ),
426 )
427 }
428 fn icon() -> Option<Box<dyn Widget>> {
429 Some(icons::expand())
430 }
431 fn category() -> WidgetCategory {
432 WidgetCategory::ContainerA
433 }
434 fn build_with_children(
435 _variant: &str,
436 knobs: &KnobValues,
437 children: Vec<SlottedChild>,
438 ) -> Box<dyn Widget> {
439 let mut e = Expand::new().flex(knobs.f32_("flex").get());
440 if let Some(c) = children.into_iter().next() {
441 e = e.child_id(c.id);
442 }
443 Box::new(e)
444 }
445}
446register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/expand.rs", Expand);
447
448impl WidgetCatalog for Center {
449 fn id() -> &'static str {
450 "center"
451 }
452 fn group() -> &'static str {
453 "Layout"
454 }
455 fn display_name() -> &'static str {
456 "Center"
457 }
458 fn variants() -> Vec<PreviewVariant> {
459 vec![PreviewVariant::defaults("default")]
460 }
461 fn build(_variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
462 Box::new(
463 FixedSize::new()
464 .width(200.0_f32)
465 .height(80.0_f32)
466 .child(Center::new().child(sample_text("Centered"))),
467 )
468 }
469 fn icon() -> Option<Box<dyn Widget>> {
470 Some(icons::center())
471 }
472 fn category() -> WidgetCategory {
473 WidgetCategory::ContainerA
474 }
475 fn build_with_children(
476 _variant: &str,
477 _knobs: &KnobValues,
478 children: Vec<SlottedChild>,
479 ) -> Box<dyn Widget> {
480 let mut c0 = Center::new();
481 if let Some(c) = children.into_iter().next() {
482 c0 = c0.child_id(c.id);
483 }
484 Box::new(c0)
485 }
486}
487register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/center.rs", Center);
488
489impl WidgetCatalog for Spacer {
490 fn id() -> &'static str {
491 "spacer"
492 }
493 fn group() -> &'static str {
494 "Layout"
495 }
496 fn display_name() -> &'static str {
497 "Spacer"
498 }
499 fn variants() -> Vec<PreviewVariant> {
500 vec![PreviewVariant::defaults("default")]
501 }
502 fn build(_variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
503 Box::new(
504 HStack::new()
505 .child(sample_text("L"))
506 .child(Spacer::new())
507 .child(sample_text("R")),
508 )
509 }
510 fn icon() -> Option<Box<dyn Widget>> {
511 Some(icons::spacer())
512 }
513}
514register_widget_catalog_at!("crates/teksilo-widgets/src/primitives/spacer.rs", Spacer);
515
516impl WidgetCatalog for TextWidget {
517 fn id() -> &'static str {
518 "text_widget"
519 }
520 fn group() -> &'static str {
521 "Display"
522 }
523 fn display_name() -> &'static str {
524 "TextWidget"
529 }
530 fn knobs() -> KnobSpec {
531 KnobSpec::new()
532 .text("text", "Text", "Label")
533 .ctor(0)
534 .text_role("color", "Color", TextRole::Primary)
535 .text_style("style", "Style", TextStyleRole::Body)
536 }
537 fn variants() -> Vec<PreviewVariant> {
538 vec![
539 PreviewVariant::defaults("default"),
540 PreviewVariant::knobs(
541 "secondary",
542 KnobOverrides::new().text_role("color", TextRole::Secondary),
543 ),
544 PreviewVariant::knobs(
545 "bold",
546 KnobOverrides::new().text_style("style", TextStyleRole::BodyBold),
547 ),
548 ]
549 }
550 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
551 Box::new(
552 TextWidget::new(lit!(knobs.text("text").get()))
553 .style(knobs.text_style("style").get())
554 .color(knobs.text_role("color").get()),
555 )
556 }
557 fn icon() -> Option<Box<dyn Widget>> {
558 Some(icons::text_widget())
559 }
560}
561register_widget_catalog_at!(
562 "crates/teksilo-widgets/src/primitives/text_widget.rs",
563 TextWidget
564);
565
566impl WidgetCatalog for TextInput {
567 fn id() -> &'static str {
568 "text_input"
569 }
570 fn group() -> &'static str {
571 "Controls"
572 }
573 fn display_name() -> &'static str {
574 "TextInput"
575 }
576 fn knobs() -> KnobSpec {
577 KnobSpec::new()
578 .text("value", "Value", "")
579 .ctor(0)
580 .enum_(
581 "variant",
582 "Variant",
583 "TextInputVariant",
584 &["Outlined", "Filled", "Underline", "Bare"],
585 0,
586 )
587 .text("placeholder", "Placeholder", "Type here…")
588 .bool_("enabled", "Enabled", true)
589 }
590 fn variants() -> Vec<PreviewVariant> {
591 vec![PreviewVariant::defaults("default")]
592 }
593 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
594 Box::new(
595 FixedSize::new().width(220.0_f32).child(
596 TextInput::new(knobs.text("value"))
597 .variant(text_input_variant(knobs.enum_("variant").get()))
598 .placeholder(lit!(knobs.text("placeholder").get()))
599 .enabled(knobs.bool_("enabled").get()),
600 ),
601 )
602 }
603 fn icon() -> Option<Box<dyn Widget>> {
604 Some(icons::text_input())
605 }
606}
607register_widget_catalog_at!("crates/teksilo-widgets/src/text_input.rs", TextInput);
608
609impl WidgetCatalog for TextScaleControl {
614 fn id() -> &'static str {
615 "text_scale_control"
616 }
617 fn group() -> &'static str {
618 "Accessibility"
619 }
620 fn display_name() -> &'static str {
621 "TextScaleControl"
622 }
623 fn variants() -> Vec<PreviewVariant> {
624 vec![PreviewVariant::defaults("default")]
625 }
626 fn build(_variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
627 Box::new(TextScaleControl::new(Signal::new(1.0_f32)).label(lit!("Text size")))
630 }
631}
632register_widget_catalog_at!(
633 "crates/teksilo-widgets/src/text_scale_control.rs",
634 TextScaleControl
635);
636
637impl WidgetCatalog for Checkbox {
642 fn id() -> &'static str {
643 "checkbox"
644 }
645 fn group() -> &'static str {
646 "Controls"
647 }
648 fn display_name() -> &'static str {
649 "Checkbox"
650 }
651 fn knobs() -> KnobSpec {
652 KnobSpec::new()
653 .bool_("checked", "Checked", false)
654 .ctor(0)
655 .enum_(
656 "variant",
657 "Variant",
658 "CheckboxVariant",
659 &["Square", "Rounded", "Circle"],
660 0,
661 )
662 .text("label", "Label", "Enable feature")
663 .bool_("enabled", "Enabled", true)
664 }
665 fn variants() -> Vec<PreviewVariant> {
666 vec![
667 PreviewVariant::defaults("unchecked"),
668 PreviewVariant::knobs("checked", KnobOverrides::new().bool_("checked", true)),
669 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
670 ]
671 }
672 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
673 let label = knobs.text("label").get();
674 let checked = knobs.bool_("checked");
675 let enabled = knobs.bool_("enabled").get();
676 Box::new(
677 Checkbox::new(checked)
678 .variant(checkbox_variant(knobs.enum_("variant").get()))
679 .label(lit!(label))
680 .enabled(enabled),
681 )
682 }
683 fn icon() -> Option<Box<dyn Widget>> {
684 Some(icons::checkbox())
685 }
686}
687register_widget_catalog_at!("crates/teksilo-widgets/src/checkbox.rs", Checkbox);
688
689impl WidgetCatalog for RadioButton {
694 fn id() -> &'static str {
695 "radio_button"
696 }
697 fn group() -> &'static str {
698 "Controls"
699 }
700 fn display_name() -> &'static str {
701 "RadioButton"
702 }
703 fn knobs() -> KnobSpec {
704 KnobSpec::new()
710 .choice("selected", "Selected", &["Yes", "No"], 1)
711 .opt_text("label", "Label", Some("Option"))
712 .bool_("enabled", "Enabled", true)
713 }
714 fn variants() -> Vec<PreviewVariant> {
715 vec![
716 PreviewVariant::defaults("unselected"),
717 PreviewVariant::knobs("selected", KnobOverrides::new().choice("selected", 0)),
718 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
719 PreviewVariant::knobs("no-label", KnobOverrides::new().opt_text("label", None)),
720 ]
721 }
722 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
723 let label = knobs.opt_text("label").get();
724 let enabled = knobs.bool_("enabled").get();
725 let mut r = RadioButton::new(0, knobs.choice("selected")).enabled(enabled);
726 if let Some(label) = label {
727 r = r.label(lit!(label));
728 }
729 Box::new(r)
730 }
731}
732register_widget_catalog_at!("crates/teksilo-widgets/src/radio_button.rs", RadioButton);
733
734impl WidgetCatalog for Toggle {
739 fn id() -> &'static str {
740 "toggle"
741 }
742 fn group() -> &'static str {
743 "Controls"
744 }
745 fn display_name() -> &'static str {
746 "Toggle"
747 }
748 fn knobs() -> KnobSpec {
749 KnobSpec::new()
753 .bool_("on", "On", false)
754 .text("label", "Label", "Enable feature")
755 .bool_("enabled", "Enabled", true)
756 }
757 fn variants() -> Vec<PreviewVariant> {
758 vec![
759 PreviewVariant::defaults("off"),
760 PreviewVariant::knobs("on", KnobOverrides::new().bool_("on", true)),
761 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
762 ]
763 }
764 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
765 let on = knobs.bool_("on");
766 let label = knobs.text("label").get();
767 let enabled = knobs.bool_("enabled").get();
768 Box::new(Toggle::new(on).label(lit!(label)).enabled(enabled))
769 }
770 fn icon() -> Option<Box<dyn Widget>> {
771 Some(icons::toggle())
772 }
773}
774register_widget_catalog_at!("crates/teksilo-widgets/src/toggle.rs", Toggle);
775
776impl WidgetCatalog for Slider {
781 fn id() -> &'static str {
782 "slider"
783 }
784 fn group() -> &'static str {
785 "Controls"
786 }
787 fn display_name() -> &'static str {
788 "Slider"
789 }
790 fn knobs() -> KnobSpec {
791 KnobSpec::new()
792 .f32_("value", "Value", 0.5, 0.0, 1.0)
793 .ctor(0)
794 .enum_(
795 "orientation",
796 "Orientation",
797 "Orientation",
798 &["Horizontal", "Vertical"],
799 0,
800 )
801 .f32_step("step", "Step (0 = continuous)", 0.0, 0.0, 0.5, 0.05)
802 .bool_("enabled", "Enabled", true)
803 .opt_text("label", "Label", None)
804 }
805 fn variants() -> Vec<PreviewVariant> {
806 vec![
807 PreviewVariant::defaults("default"),
808 PreviewVariant::knobs("min", KnobOverrides::new().f32_("value", 0.0)),
809 PreviewVariant::knobs("max", KnobOverrides::new().f32_("value", 1.0)),
810 PreviewVariant::knobs(
811 "stepped",
812 KnobOverrides::new().f32_("value", 0.5).f32_("step", 0.1),
813 ),
814 PreviewVariant::knobs("vertical", KnobOverrides::new().enum_("orientation", 1)),
815 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
816 PreviewVariant::knobs(
817 "with-label",
818 KnobOverrides::new().opt_text("label", Some("Volume")),
819 ),
820 ]
821 }
822 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
823 use teksilo_tokens::Orientation;
824 let orient = match knobs.enum_("orientation").get() {
825 1 => Orientation::Vertical,
826 _ => Orientation::Horizontal,
827 };
828 let step = knobs.f32_("step").get();
829 let enabled = knobs.bool_("enabled").get();
830 let label = knobs.opt_text("label").get();
831 let mut s = Slider::new(knobs.f32_("value"), 0.0, 1.0)
832 .orientation(orient)
833 .enabled(enabled);
834 if step > 0.0 {
835 s = s.step(step);
836 }
837 if let Some(label) = label {
838 s = s.label(lit!(label));
839 }
840 let widget: Box<dyn Widget> = if matches!(orient, Orientation::Vertical) {
842 Box::new(FixedSize::new().height(160.0_f32).child(s))
843 } else {
844 Box::new(s)
845 };
846 widget
847 }
848 fn icon() -> Option<Box<dyn Widget>> {
849 Some(icons::slider())
850 }
851}
852register_widget_catalog_at!("crates/teksilo-widgets/src/slider.rs", Slider);
853
854impl WidgetCatalog for ProgressBar {
859 fn id() -> &'static str {
860 "progress_bar"
861 }
862 fn group() -> &'static str {
863 "Controls"
864 }
865 fn display_name() -> &'static str {
866 "ProgressBar"
867 }
868 fn knobs() -> KnobSpec {
869 KnobSpec::new()
870 .f32_("value", "Value", 0.4, 0.0, 1.0)
871 .bool_("indeterminate", "Indeterminate", false)
872 .choice("orientation", "Orientation", &["Horizontal", "Vertical"], 0)
873 .opt_text("label", "Label", None)
874 }
875 fn variants() -> Vec<PreviewVariant> {
876 vec![
877 PreviewVariant::defaults("determinate"),
878 PreviewVariant::knobs("empty", KnobOverrides::new().f32_("value", 0.0)),
879 PreviewVariant::knobs("full", KnobOverrides::new().f32_("value", 1.0)),
880 PreviewVariant::knobs(
881 "indeterminate",
882 KnobOverrides::new().bool_("indeterminate", true),
883 ),
884 PreviewVariant::knobs("vertical", KnobOverrides::new().choice("orientation", 1)),
885 PreviewVariant::knobs(
886 "with-label",
887 KnobOverrides::new()
888 .f32_("value", 0.7)
889 .opt_text("label", Some("Uploading…")),
890 ),
891 ]
892 }
893 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
894 use teksilo_tokens::Orientation;
895 let indeterminate = knobs.bool_("indeterminate").get();
896 let orient = match knobs.choice("orientation").get() {
897 1 => Orientation::Vertical,
898 _ => Orientation::Horizontal,
899 };
900 let label = knobs.opt_text("label").get();
901 let mut bar = if indeterminate {
902 ProgressBar::indeterminate()
903 } else {
904 ProgressBar::new(knobs.f32_("value").get())
905 };
906 bar = bar.orientation(orient);
907 if let Some(label) = label {
908 bar = bar.label(lit!(label));
909 }
910 let widget: Box<dyn Widget> = if matches!(orient, Orientation::Vertical) {
911 Box::new(FixedSize::new().height(160.0_f32).child(bar))
912 } else {
913 Box::new(bar)
914 };
915 widget
916 }
917}
918register_widget_catalog_at!("crates/teksilo-widgets/src/progress_bar.rs", ProgressBar);
919
920impl WidgetCatalog for Badge {
925 fn id() -> &'static str {
926 "badge"
927 }
928 fn group() -> &'static str {
929 "Controls"
930 }
931 fn display_name() -> &'static str {
932 "Badge"
933 }
934 fn knobs() -> KnobSpec {
935 KnobSpec::new()
936 .text("label", "Label", "NEW")
937 .surface_role("background", "Background", SurfaceRole::Accent)
938 .text_role("text_role", "Text colour", TextRole::OnAccent)
939 }
940 fn variants() -> Vec<PreviewVariant> {
941 vec![
942 PreviewVariant::defaults("accent"),
943 PreviewVariant::knobs("long", KnobOverrides::new().text("label", "EXPERIMENTAL")),
944 PreviewVariant::knobs("short", KnobOverrides::new().text("label", "•")),
945 PreviewVariant::knobs(
946 "success",
947 KnobOverrides::new()
948 .text("label", "OK")
949 .surface_role("background", SurfaceRole::StatusSuccess)
950 .text_role("text_role", TextRole::Success),
951 ),
952 PreviewVariant::knobs(
953 "warning",
954 KnobOverrides::new()
955 .text("label", "BETA")
956 .surface_role("background", SurfaceRole::StatusWarning)
957 .text_role("text_role", TextRole::Warning),
958 ),
959 PreviewVariant::knobs(
960 "error",
961 KnobOverrides::new()
962 .text("label", "ERR")
963 .surface_role("background", SurfaceRole::StatusError)
964 .text_role("text_role", TextRole::Error),
965 ),
966 ]
967 }
968 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
969 let bg = knobs.surface_role("background");
970 let fg = knobs.text_role("text_role");
971 Box::new(
972 Badge::new(lit!(knobs.text("label").get()))
973 .background(bg)
974 .text_role(fg),
975 )
976 }
977}
978register_widget_catalog_at!("crates/teksilo-widgets/src/badge.rs", Badge);
979
980impl WidgetCatalog for Avatar {
985 fn id() -> &'static str {
986 "avatar"
987 }
988 fn group() -> &'static str {
989 "Controls"
990 }
991 fn display_name() -> &'static str {
992 "Avatar"
993 }
994 fn knobs() -> KnobSpec {
995 KnobSpec::new()
996 .text("name", "Name", "Jane Doe")
998 .opt_text("initials_override", "Initials override", None)
1001 .choice(
1002 "size",
1003 "Size",
1004 &["Small (24)", "Medium (32)", "Large (48)", "XLarge (64)"],
1005 1,
1006 )
1007 .choice("shape", "Shape", &["Circle", "RoundedSquare", "Square"], 0)
1008 .choice(
1009 "presence",
1010 "Presence",
1011 &["None", "Online", "Offline", "Away", "Busy"],
1012 0,
1013 )
1014 .choice(
1015 "presence_corner",
1016 "Presence corner",
1017 &[
1018 "BottomTrailing",
1019 "BottomLeading",
1020 "TopTrailing",
1021 "TopLeading",
1022 ],
1023 0,
1024 )
1025 .bool_("border", "Show ring", false)
1026 .bool_("clickable", "Clickable", false)
1027 }
1028 fn variants() -> Vec<PreviewVariant> {
1029 vec![
1030 PreviewVariant::defaults("default"),
1031 PreviewVariant::knobs(
1032 "small",
1033 KnobOverrides::new().choice("size", 0).text("name", "AB"),
1034 ),
1035 PreviewVariant::knobs(
1036 "large",
1037 KnobOverrides::new()
1038 .choice("size", 2)
1039 .text("name", "Sherlock Holmes"),
1040 ),
1041 PreviewVariant::knobs(
1042 "xlarge",
1043 KnobOverrides::new()
1044 .choice("size", 3)
1045 .text("name", "Marie Curie"),
1046 ),
1047 PreviewVariant::knobs(
1048 "rounded-square",
1049 KnobOverrides::new()
1050 .choice("shape", 1)
1051 .text("name", "Project X"),
1052 ),
1053 PreviewVariant::knobs("online", KnobOverrides::new().choice("presence", 1)),
1054 PreviewVariant::knobs("away", KnobOverrides::new().choice("presence", 3)),
1055 PreviewVariant::knobs("busy", KnobOverrides::new().choice("presence", 4)),
1056 PreviewVariant::knobs("with-ring", KnobOverrides::new().bool_("border", true)),
1057 PreviewVariant::knobs("clickable", KnobOverrides::new().bool_("clickable", true)),
1058 PreviewVariant::knobs("single-letter", KnobOverrides::new().text("name", "Cher")),
1059 PreviewVariant::knobs(
1060 "email-derived",
1061 KnobOverrides::new().text("name", "jane.doe@example.com"),
1062 ),
1063 ]
1064 }
1065 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1066 let name = knobs.text("name").get();
1067 let size = match knobs.choice("size").get() {
1068 0 => AvatarSize::Small,
1069 2 => AvatarSize::Large,
1070 3 => AvatarSize::XLarge,
1071 _ => AvatarSize::Medium,
1072 };
1073 let shape = match knobs.choice("shape").get() {
1074 1 => AvatarShape::RoundedSquare,
1075 2 => AvatarShape::Square,
1076 _ => AvatarShape::Circle,
1077 };
1078 let presence = match knobs.choice("presence").get() {
1079 1 => Some(AvatarPresence::Online),
1080 2 => Some(AvatarPresence::Offline),
1081 3 => Some(AvatarPresence::Away),
1082 4 => Some(AvatarPresence::Busy),
1083 _ => None,
1084 };
1085 let presence_corner = match knobs.choice("presence_corner").get() {
1086 1 => crate::AvatarCorner::BottomLeading,
1087 2 => crate::AvatarCorner::TopTrailing,
1088 3 => crate::AvatarCorner::TopLeading,
1089 _ => crate::AvatarCorner::BottomTrailing,
1090 };
1091 let border = knobs.bool_("border").get();
1092 let clickable = knobs.bool_("clickable").get();
1093 let initials_override = knobs.opt_text("initials_override").get();
1094
1095 let mut a = if let Some(initials) = initials_override {
1096 Avatar::with_initials(lit!(&initials))
1097 } else {
1098 Avatar::with_name(lit!(&name))
1099 }
1100 .size(size)
1101 .shape(shape)
1102 .presence_corner(presence_corner);
1103
1104 if let Some(p) = presence {
1105 a = a.presence(p);
1106 }
1107 if border {
1108 a = a.border(2.0);
1109 }
1110 if clickable {
1111 a = a.label(lit!("Open user menu")).on_activate_fn(|_ctx| {});
1112 }
1113 Box::new(a)
1114 }
1115}
1116register_widget_catalog_at!("crates/teksilo-widgets/src/avatar.rs", Avatar);
1117
1118impl WidgetCatalog for Link {
1123 fn id() -> &'static str {
1124 "link"
1125 }
1126 fn group() -> &'static str {
1127 "Controls"
1128 }
1129 fn display_name() -> &'static str {
1130 "Link"
1131 }
1132 fn knobs() -> KnobSpec {
1133 KnobSpec::new()
1134 .text("label", "Label", "Read more")
1135 .bool_("enabled", "Enabled", true)
1136 }
1137 fn variants() -> Vec<PreviewVariant> {
1138 vec![
1139 PreviewVariant::defaults("default"),
1140 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
1141 ]
1142 }
1143 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1144 Box::new(Link::new(lit!(knobs.text("label").get())).enabled(knobs.bool_("enabled").get()))
1145 }
1146}
1147register_widget_catalog_at!("crates/teksilo-widgets/src/link.rs", Link);
1148
1149fn segment_display(idx: usize) -> SegmentDisplay {
1155 match idx {
1156 1 => SegmentDisplay::Text,
1157 2 => SegmentDisplay::Icon,
1158 3 => SegmentDisplay::IconText,
1159 _ => SegmentDisplay::Auto,
1160 }
1161}
1162
1163fn segment_overflow(idx: usize) -> SegmentOverflow {
1165 match idx {
1166 1 => SegmentOverflow::Compress,
1167 _ => SegmentOverflow::Menu,
1168 }
1169}
1170
1171impl WidgetCatalog for SegmentedControl {
1172 fn id() -> &'static str {
1173 "segmented_control"
1174 }
1175 fn group() -> &'static str {
1176 "Controls"
1177 }
1178 fn display_name() -> &'static str {
1179 "SegmentedControl"
1180 }
1181 fn knobs() -> KnobSpec {
1182 KnobSpec::new()
1183 .choice("selected", "Selected", &["Day", "Week", "Month"], 0)
1184 .choice(
1185 "display",
1186 "Display",
1187 &["Auto", "Text", "Icon", "Icon+Text"],
1188 0,
1189 )
1190 .choice("overflow", "Overflow", &["Menu", "Compress"], 0)
1191 .bool_("enabled", "Enabled", true)
1192 }
1193 fn variants() -> Vec<PreviewVariant> {
1194 vec![
1195 PreviewVariant::defaults("default"),
1196 PreviewVariant::knobs("middle", KnobOverrides::new().choice("selected", 1)),
1197 PreviewVariant::knobs("icon-only", KnobOverrides::new().choice("display", 2)),
1198 PreviewVariant::knobs("compress", KnobOverrides::new().choice("overflow", 1)),
1199 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
1200 ]
1201 }
1202 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1203 Box::new(
1206 SegmentedControl::indexed(knobs.choice("selected"))
1207 .segments([lit!("Day"), lit!("Week"), lit!("Month")])
1208 .display(segment_display(knobs.choice("display").get()))
1209 .overflow(segment_overflow(knobs.choice("overflow").get()))
1210 .enabled(knobs.bool_("enabled").get()),
1211 )
1212 }
1213}
1214register_widget_catalog_at!(
1215 "crates/teksilo-widgets/src/segmented_control.rs",
1216 SegmentedControl
1217);
1218
1219impl WidgetCatalog for ComboBox<String> {
1224 fn id() -> &'static str {
1225 "combo_box"
1226 }
1227 fn group() -> &'static str {
1228 "Controls"
1229 }
1230 fn display_name() -> &'static str {
1231 "ComboBox"
1232 }
1233 fn knobs() -> KnobSpec {
1234 KnobSpec::new()
1235 .opt_text("selected", "Selected", Some("Apple"))
1236 .text("placeholder", "Placeholder", "Select a fruit…")
1237 .opt_text("label", "A11y label", Some("Fruit"))
1238 .bool_("enabled", "Enabled", true)
1239 }
1240 fn variants() -> Vec<PreviewVariant> {
1241 vec![
1242 PreviewVariant::defaults("with-selection"),
1243 PreviewVariant::knobs("empty", KnobOverrides::new().opt_text("selected", None)),
1244 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
1245 ]
1246 }
1247 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1248 let items = vec![
1249 "Apple".to_string(),
1250 "Banana".to_string(),
1251 "Cherry".to_string(),
1252 "Date".to_string(),
1253 "Elderberry".to_string(),
1254 ];
1255 let placeholder = knobs.text("placeholder").get();
1260 let enabled = knobs.bool_("enabled").get();
1261 let mut cb = ComboBox::new(items, knobs.opt_text("selected"))
1262 .placeholder(lit!(placeholder))
1263 .enabled(enabled);
1264 if let Some(label) = knobs.opt_text("label").get() {
1265 cb = cb.label(lit!(label));
1266 }
1267 Box::new(cb)
1268 }
1269 fn icon() -> Option<Box<dyn Widget>> {
1270 Some(icons::combo_box())
1271 }
1272}
1273register_widget_catalog_at!("crates/teksilo-widgets/src/combo_box.rs", ComboBox<String>);
1274
1275impl WidgetCatalog for FontPicker {
1280 fn id() -> &'static str {
1281 "font_picker"
1282 }
1283 fn group() -> &'static str {
1284 "Controls"
1285 }
1286 fn display_name() -> &'static str {
1287 "FontPicker"
1288 }
1289 fn knobs() -> KnobSpec {
1290 KnobSpec::new()
1291 .opt_text("selected", "Selected", Some("Georgia"))
1292 .opt_text("label", "A11y label", Some("Font"))
1293 .bool_("enabled", "Enabled", true)
1294 }
1295 fn variants() -> Vec<PreviewVariant> {
1296 vec![
1297 PreviewVariant::defaults("default"),
1298 PreviewVariant::knobs("empty", KnobOverrides::new().opt_text("selected", None)),
1299 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
1300 ]
1301 }
1302 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1303 let enabled = knobs.bool_("enabled").get();
1308 let mut fp = FontPicker::new(knobs.opt_text("selected"))
1309 .families([
1310 "Georgia",
1311 "Verdana",
1312 "Arial",
1313 "Courier New",
1314 "Times New Roman",
1315 "Trebuchet MS",
1316 ])
1317 .enabled(enabled);
1318 if let Some(label) = knobs.opt_text("label").get() {
1319 fp = fp.label(lit!(label));
1320 }
1321 Box::new(fp)
1322 }
1323 fn icon() -> Option<Box<dyn Widget>> {
1324 Some(icons::combo_box())
1325 }
1326}
1327register_widget_catalog_at!("crates/teksilo-widgets/src/font_picker.rs", FontPicker);
1328
1329impl WidgetCatalog for LanguageSwitcher {
1334 fn id() -> &'static str {
1335 "language_switcher"
1336 }
1337 fn group() -> &'static str {
1338 "Controls"
1339 }
1340 fn display_name() -> &'static str {
1341 "LanguageSwitcher"
1342 }
1343 fn knobs() -> KnobSpec {
1344 KnobSpec::new()
1345 }
1346 fn variants() -> Vec<PreviewVariant> {
1347 vec![PreviewVariant::defaults("default")]
1348 }
1349 fn build(_variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
1350 let locales = ["en-US", "fr-FR", "de-DE", "es-ES", "ar-SA", "ja-JP"]
1353 .iter()
1354 .map(|t| t.parse().expect("valid BCP-47 tag"))
1355 .collect();
1356 Box::new(LanguageSwitcher::new().locales(locales))
1357 }
1358 fn icon() -> Option<Box<dyn Widget>> {
1359 Some(icons::combo_box())
1360 }
1361}
1362register_widget_catalog_at!(
1363 "crates/teksilo-widgets/src/language_switcher.rs",
1364 LanguageSwitcher
1365);
1366
1367impl WidgetCatalog for ThemeSwitcher {
1372 fn id() -> &'static str {
1373 "theme_switcher"
1374 }
1375 fn group() -> &'static str {
1376 "Controls"
1377 }
1378 fn display_name() -> &'static str {
1379 "ThemeSwitcher"
1380 }
1381 fn knobs() -> KnobSpec {
1382 KnobSpec::new()
1383 }
1384 fn variants() -> Vec<PreviewVariant> {
1385 vec![PreviewVariant::defaults("default")]
1386 }
1387 fn build(_variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
1388 Box::new(ThemeSwitcher::new())
1390 }
1391 fn icon() -> Option<Box<dyn Widget>> {
1392 Some(icons::combo_box())
1393 }
1394}
1395register_widget_catalog_at!(
1396 "crates/teksilo-widgets/src/theme_switcher.rs",
1397 ThemeSwitcher
1398);
1399
1400impl WidgetCatalog for crate::primitives::Divider {
1405 fn id() -> &'static str {
1406 "divider"
1407 }
1408 fn group() -> &'static str {
1409 "Primitives"
1410 }
1411 fn display_name() -> &'static str {
1412 "Divider"
1413 }
1414 fn knobs() -> KnobSpec {
1415 KnobSpec::new()
1416 .choice("orientation", "Orientation", &["Horizontal", "Vertical"], 0)
1417 .f32_("thickness", "Thickness", 1.0, 0.5, 6.0)
1418 .border_role("color", "Colour", BorderRole::Divider)
1419 }
1420 fn variants() -> Vec<PreviewVariant> {
1421 vec![
1422 PreviewVariant::defaults("horizontal"),
1423 PreviewVariant::knobs("vertical", KnobOverrides::new().choice("orientation", 1)),
1424 PreviewVariant::knobs("thick", KnobOverrides::new().f32_("thickness", 4.0)),
1425 PreviewVariant::knobs(
1426 "strong",
1427 KnobOverrides::new().border_role("color", BorderRole::DividerStrong),
1428 ),
1429 PreviewVariant::knobs(
1430 "accent",
1431 KnobOverrides::new().border_role("color", BorderRole::Accent),
1432 ),
1433 ]
1434 }
1435 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1436 let orient = knobs.choice("orientation").get();
1437 let thickness = knobs.f32_("thickness").get();
1438 let role = knobs.border_role("color").get();
1439 let mut d = if orient == 1 {
1440 crate::primitives::Divider::vertical()
1441 } else {
1442 crate::primitives::Divider::horizontal()
1443 };
1444 d = d.thickness(thickness).color(role);
1445 let wrapped: Box<dyn Widget> = if orient == 1 {
1448 Box::new(
1449 crate::primitives::FixedSize::new()
1450 .height(120.0_f32)
1451 .child(d),
1452 )
1453 } else {
1454 Box::new(
1455 crate::primitives::FixedSize::new()
1456 .width(220.0_f32)
1457 .child(d),
1458 )
1459 };
1460 wrapped
1461 }
1462}
1463register_widget_catalog_at!(
1464 "crates/teksilo-widgets/src/primitives/divider.rs",
1465 crate::primitives::Divider
1466);
1467
1468impl WidgetCatalog for IconWidget {
1473 fn id() -> &'static str {
1474 "icon_widget"
1475 }
1476 fn group() -> &'static str {
1477 "Primitives"
1478 }
1479 fn display_name() -> &'static str {
1480 "IconWidget"
1481 }
1482 fn knobs() -> KnobSpec {
1483 KnobSpec::new()
1484 .f32_("size", "Size (dp)", 24.0, 12.0, 96.0)
1485 .choice("shape", "Shape", &["Square", "Circle", "Triangle"], 0)
1486 .text_role("color", "Colour", TextRole::Primary)
1487 }
1488 fn variants() -> Vec<PreviewVariant> {
1489 vec![
1490 PreviewVariant::defaults("square"),
1491 PreviewVariant::knobs("circle", KnobOverrides::new().choice("shape", 1)),
1492 PreviewVariant::knobs("triangle", KnobOverrides::new().choice("shape", 2)),
1493 PreviewVariant::knobs("large", KnobOverrides::new().f32_("size", 64.0)),
1494 PreviewVariant::knobs(
1495 "accent",
1496 KnobOverrides::new().text_role("color", TextRole::Accent),
1497 ),
1498 PreviewVariant::knobs(
1499 "error",
1500 KnobOverrides::new().text_role("color", TextRole::Error),
1501 ),
1502 ]
1503 }
1504 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1505 use teksilo_canvas::{Path, Point};
1506 let size = knobs.f32_("size").get();
1507 let shape = knobs.choice("shape").get();
1508 let mut path = Path::new();
1509 match shape {
1510 0 => {
1511 path.move_to(Point::new(2.0, 2.0));
1512 path.line_to(Point::new(size - 2.0, 2.0));
1513 path.line_to(Point::new(size - 2.0, size - 2.0));
1514 path.line_to(Point::new(2.0, size - 2.0));
1515 path.close();
1516 }
1517 1 => {
1518 let r = (size / 2.0) - 2.0;
1519 let c = Point::new(size / 2.0, size / 2.0);
1520 let k = 0.552_284_8 * r;
1522 path.move_to(Point::new(c.x + r, c.y));
1523 path.cubic_to(
1524 Point::new(c.x + r, c.y + k),
1525 Point::new(c.x + k, c.y + r),
1526 Point::new(c.x, c.y + r),
1527 );
1528 path.cubic_to(
1529 Point::new(c.x - k, c.y + r),
1530 Point::new(c.x - r, c.y + k),
1531 Point::new(c.x - r, c.y),
1532 );
1533 path.cubic_to(
1534 Point::new(c.x - r, c.y - k),
1535 Point::new(c.x - k, c.y - r),
1536 Point::new(c.x, c.y - r),
1537 );
1538 path.cubic_to(
1539 Point::new(c.x + k, c.y - r),
1540 Point::new(c.x + r, c.y - k),
1541 Point::new(c.x + r, c.y),
1542 );
1543 path.close();
1544 }
1545 _ => {
1546 path.move_to(Point::new(size / 2.0, 2.0));
1547 path.line_to(Point::new(size - 2.0, size - 2.0));
1548 path.line_to(Point::new(2.0, size - 2.0));
1549 path.close();
1550 }
1551 }
1552 let role = knobs.text_role("color").get();
1553 Box::new(IconWidget::from_path(path, size).color(role))
1554 }
1555}
1556register_widget_catalog_at!(
1557 "crates/teksilo-widgets/src/primitives/icon_widget.rs",
1558 IconWidget
1559);
1560
1561fn sample_text(label: &str) -> TextWidget {
1575 TextWidget::new(lit!(label))
1576 .style(TextStyleRole::Body)
1577 .color(TextRole::Primary)
1578}
1579
1580impl WidgetCatalog for Card {
1581 fn id() -> &'static str {
1582 "card"
1583 }
1584 fn group() -> &'static str {
1585 "Containers"
1586 }
1587 fn display_name() -> &'static str {
1588 "Card"
1589 }
1590 fn knobs() -> KnobSpec {
1591 KnobSpec::new()
1592 .text("title", "Title", "Card title")
1593 .text(
1594 "body",
1595 "Body",
1596 "Card body text. Cards group related controls into a labelled rectangular region.",
1597 )
1598 .bool_("show_header", "Show header", true)
1599 .bool_("show_footer", "Show footer", false)
1600 .surface_role("background", "Background", SurfaceRole::Main)
1601 .f32_("corner_radius", "Corner radius", 8.0, 0.0, 32.0)
1602 .f32_("padding", "Padding", 16.0, 0.0, 48.0)
1603 }
1604 fn variants() -> Vec<PreviewVariant> {
1605 vec![
1606 PreviewVariant::defaults("default"),
1607 PreviewVariant::knobs(
1608 "with-footer",
1609 KnobOverrides::new()
1610 .text("title", "Settings")
1611 .text("body", "Configure the application settings here.")
1612 .bool_("show_footer", true),
1613 ),
1614 PreviewVariant::knobs(
1615 "headerless",
1616 KnobOverrides::new().bool_("show_header", false),
1617 ),
1618 PreviewVariant::knobs(
1619 "raised",
1620 KnobOverrides::new().surface_role("background", SurfaceRole::Raised),
1621 ),
1622 ]
1623 }
1624 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1625 let mut card = Card::new()
1626 .background(knobs.surface_role("background"))
1627 .corner_radius(knobs.f32_("corner_radius").get())
1628 .padding(knobs.f32_("padding").get())
1629 .content(
1630 TextWidget::new(lit!(knobs.text("body").get()))
1631 .style(TextStyleRole::Body)
1632 .color(TextRole::Primary),
1633 );
1634 if knobs.bool_("show_header").get() {
1635 card = card.header(
1636 TextWidget::new(lit!(knobs.text("title").get()))
1637 .style(TextStyleRole::BodyBold)
1638 .color(TextRole::Primary),
1639 );
1640 }
1641 if knobs.bool_("show_footer").get() {
1642 card = card.footer(
1643 HStack::new()
1644 .spacing(8.0)
1645 .child(Spacer::new())
1646 .child(Button::new(lit!("Cancel")).variant(ButtonVariant::Plain))
1647 .child(Button::new(lit!("Save")).variant(ButtonVariant::Filled)),
1648 );
1649 }
1650 Box::new(card)
1651 }
1652 fn icon() -> Option<Box<dyn Widget>> {
1653 Some(icons::card())
1654 }
1655 fn category() -> WidgetCategory {
1656 WidgetCategory::ContainerB
1657 }
1658 fn slots() -> &'static [&'static str] {
1659 &["header", "content", "footer"]
1660 }
1661 fn build_with_children(
1662 _variant: &str,
1663 knobs: &KnobValues,
1664 children: Vec<SlottedChild>,
1665 ) -> Box<dyn Widget> {
1666 let mut card = Card::new()
1667 .background(knobs.surface_role("background"))
1668 .corner_radius(knobs.f32_("corner_radius").get())
1669 .padding(knobs.f32_("padding").get());
1670 for c in children {
1671 match c.slot.as_deref() {
1672 Some("header") => card = card.header_id(c.id),
1673 Some("footer") => card = card.footer_id(c.id),
1674 _ => card = card.content_id(c.id),
1675 }
1676 }
1677 Box::new(card)
1678 }
1679}
1680register_widget_catalog_at!("crates/teksilo-widgets/src/card.rs", Card);
1681
1682impl WidgetCatalog for Panel {
1687 fn id() -> &'static str {
1688 "panel"
1689 }
1690 fn group() -> &'static str {
1691 "Containers"
1692 }
1693 fn display_name() -> &'static str {
1694 "Panel"
1695 }
1696 fn knobs() -> KnobSpec {
1697 KnobSpec::new()
1698 .surface_role("background", "Background", SurfaceRole::Raised)
1699 .border_role("border_color", "Border colour", BorderRole::Default)
1700 .f32_("border_width", "Border width", 1.0, 0.0, 4.0)
1701 .f32_("corner_radius", "Corner radius", 6.0, 0.0, 32.0)
1702 .f32_("padding", "Padding", 16.0, 0.0, 48.0)
1703 .text("content", "Sample content", "Panel content")
1704 }
1705 fn variants() -> Vec<PreviewVariant> {
1706 vec![
1707 PreviewVariant::defaults("default"),
1708 PreviewVariant::knobs(
1709 "accent",
1710 KnobOverrides::new()
1711 .surface_role("background", SurfaceRole::AccentSubtle)
1712 .border_role("border_color", BorderRole::Accent)
1713 .text("content", "Accent panel"),
1714 ),
1715 PreviewVariant::knobs(
1716 "sunken",
1717 KnobOverrides::new()
1718 .surface_role("background", SurfaceRole::Sunken)
1719 .text("content", "Sunken panel"),
1720 ),
1721 PreviewVariant::knobs("no-border", KnobOverrides::new().f32_("border_width", 0.0)),
1722 PreviewVariant::knobs("rounded", KnobOverrides::new().f32_("corner_radius", 16.0)),
1723 ]
1724 }
1725 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1726 Box::new(
1727 Panel::new()
1728 .background(knobs.surface_role("background"))
1729 .border_color(knobs.border_role("border_color"))
1730 .border_width(knobs.f32_("border_width").get())
1731 .corner_radius(knobs.f32_("corner_radius").get())
1732 .padding(knobs.f32_("padding").get())
1733 .child(sample_text(&knobs.text("content").get())),
1734 )
1735 }
1736 fn icon() -> Option<Box<dyn Widget>> {
1737 Some(icons::panel())
1738 }
1739 fn category() -> WidgetCategory {
1740 WidgetCategory::ContainerA
1741 }
1742 fn build_with_children(
1743 _variant: &str,
1744 knobs: &KnobValues,
1745 children: Vec<SlottedChild>,
1746 ) -> Box<dyn Widget> {
1747 let mut p = Panel::new()
1748 .background(knobs.surface_role("background"))
1749 .border_color(knobs.border_role("border_color"))
1750 .border_width(knobs.f32_("border_width").get())
1751 .corner_radius(knobs.f32_("corner_radius").get())
1752 .padding(knobs.f32_("padding").get());
1753 if let Some(c) = children.into_iter().next() {
1754 p = p.child_id(c.id);
1755 }
1756 Box::new(p)
1757 }
1758}
1759register_widget_catalog_at!("crates/teksilo-widgets/src/panel.rs", Panel);
1760
1761impl WidgetCatalog for GroupBox {
1766 fn id() -> &'static str {
1767 "group_box"
1768 }
1769 fn group() -> &'static str {
1770 "Containers"
1771 }
1772 fn display_name() -> &'static str {
1773 "GroupBox"
1774 }
1775 fn knobs() -> KnobSpec {
1776 KnobSpec::new().text("title", "Title", "Notifications")
1777 }
1778 fn variants() -> Vec<PreviewVariant> {
1779 vec![
1780 PreviewVariant::defaults("default"),
1781 PreviewVariant::knobs("alt-title", KnobOverrides::new().text("title", "Privacy")),
1782 ]
1783 }
1784 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1785 Box::new(
1786 GroupBox::new(lit!(knobs.text("title").get(),)).child(
1787 VStack::new()
1788 .spacing(8.0)
1789 .child(Checkbox::new(Signal::new(true)).label(lit!("Sounds")))
1790 .child(Checkbox::new(Signal::new(false)).label(lit!("Badges")))
1791 .child(Checkbox::new(Signal::new(true)).label(lit!("Banners"))),
1792 ),
1793 )
1794 }
1795}
1796register_widget_catalog_at!("crates/teksilo-widgets/src/group_box.rs", GroupBox);
1797
1798impl WidgetCatalog for GroupHeader {
1803 fn id() -> &'static str {
1804 "group_header"
1805 }
1806 fn group() -> &'static str {
1807 "Containers"
1808 }
1809 fn display_name() -> &'static str {
1810 "GroupHeader"
1811 }
1812 fn knobs() -> KnobSpec {
1813 KnobSpec::new().text("label", "Label", "Section title")
1814 }
1815 fn variants() -> Vec<PreviewVariant> {
1816 vec![
1817 PreviewVariant::defaults("default"),
1818 PreviewVariant::defaults("accent"),
1821 ]
1822 }
1823 fn build(variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1824 let header = GroupHeader::new(lit!(knobs.text("label").get(),));
1825 match variant {
1826 "accent" => Box::new(
1827 header
1828 .style(teksilo_tokens::TextStyleRole::BodyBold)
1829 .color(teksilo_tokens::TextRole::Accent),
1830 ),
1831 _ => Box::new(header),
1832 }
1833 }
1834}
1835register_widget_catalog_at!("crates/teksilo-widgets/src/group_header.rs", GroupHeader);
1836
1837impl WidgetCatalog for IconButton {
1842 fn id() -> &'static str {
1843 "icon_button"
1844 }
1845 fn group() -> &'static str {
1846 "Controls"
1847 }
1848 fn display_name() -> &'static str {
1849 "IconButton"
1850 }
1851 fn variants() -> Vec<PreviewVariant> {
1852 fn build_search_toolbar() -> Box<dyn Widget> {
1854 Box::new(IconButton::search().toolbar())
1855 }
1856 fn build_add_hero() -> Box<dyn Widget> {
1857 Box::new(IconButton::add().hero())
1858 }
1859 fn build_browse_embedded() -> Box<dyn Widget> {
1861 Box::new(IconButton::browse().embedded())
1862 }
1863 fn build_clear_embedded_compact() -> Box<dyn Widget> {
1864 Box::new(IconButton::clear().embedded().size(IconButtonSize::Compact))
1865 }
1866 vec![
1867 PreviewVariant::scenario("search-toolbar", build_search_toolbar),
1868 PreviewVariant::scenario("add-hero", build_add_hero),
1869 PreviewVariant::scenario("browse-embedded", build_browse_embedded),
1870 PreviewVariant::scenario("clear-embedded-compact", build_clear_embedded_compact),
1871 ]
1872 }
1873 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
1874 scenario_for::<Self>(variant)
1875 }
1876}
1877register_widget_catalog_at!("crates/teksilo-widgets/src/icon_button.rs", IconButton);
1878
1879impl WidgetCatalog for Snackbar {
1891 fn id() -> &'static str {
1892 "snackbar"
1893 }
1894 fn group() -> &'static str {
1895 "Feedback"
1896 }
1897 fn display_name() -> &'static str {
1898 "Snackbar"
1899 }
1900 fn knobs() -> KnobSpec {
1901 KnobSpec::new()
1902 .text("trigger_label", "Trigger label", "Show notification")
1903 .text("message", "Message", "File saved successfully.")
1904 }
1905 fn variants() -> Vec<PreviewVariant> {
1906 vec![
1907 PreviewVariant::defaults("default"),
1908 PreviewVariant::knobs(
1909 "long",
1910 KnobOverrides::new().text(
1911 "message",
1912 "The operation completed but with warnings — review the log for details.",
1913 ),
1914 ),
1915 ]
1916 }
1917 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
1918 let trigger_label = knobs.text("trigger_label").get();
1919 let message = knobs.text("message").get();
1920 let popup_content = Panel::new()
1921 .background(SurfaceRole::Raised)
1922 .border_color(BorderRole::Default)
1923 .border_width(1.0)
1924 .corner_radius(6.0)
1925 .padding(12.0)
1926 .child(
1927 TextWidget::new(lit!(message))
1928 .style(TextStyleRole::Body)
1929 .color(TextRole::Primary),
1930 );
1931 Box::new(Snackbar::new(lit!(trigger_label)).content(popup_content))
1932 }
1933}
1934register_widget_catalog_at!("crates/teksilo-widgets/src/snackbar.rs", Snackbar);
1935
1936impl WidgetCatalog for Breadcrumb {
1941 fn id() -> &'static str {
1942 "breadcrumb"
1943 }
1944 fn group() -> &'static str {
1945 "Containers"
1946 }
1947 fn display_name() -> &'static str {
1948 "Breadcrumb"
1949 }
1950 fn variants() -> Vec<PreviewVariant> {
1951 fn build_path() -> Box<dyn Widget> {
1952 Box::new(
1953 Breadcrumb::new()
1954 .item(BreadcrumbItem::new(lit!("Home",)))
1955 .item(BreadcrumbItem::new(lit!("Projects",)))
1956 .item(BreadcrumbItem::new(lit!("Teksilo",)))
1957 .item(BreadcrumbItem::new(lit!("crates",))),
1958 )
1959 }
1960 vec![PreviewVariant::scenario("path", build_path)]
1961 }
1962 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
1963 scenario_for::<Self>(variant)
1964 }
1965}
1966register_widget_catalog_at!("crates/teksilo-widgets/src/breadcrumb.rs", Breadcrumb);
1967
1968impl WidgetCatalog for Toolbar {
1973 fn id() -> &'static str {
1974 "toolbar"
1975 }
1976 fn group() -> &'static str {
1977 "Chrome"
1978 }
1979 fn display_name() -> &'static str {
1980 "Toolbar"
1981 }
1982 fn variants() -> Vec<PreviewVariant> {
1983 fn build_default() -> Box<dyn Widget> {
1984 Box::new(
1985 Toolbar::new()
1986 .child(Button::new(lit!("New")).variant(ButtonVariant::Ghost))
1987 .child(Button::new(lit!("Open…")).variant(ButtonVariant::Ghost))
1988 .child(Button::new(lit!("Save")).variant(ButtonVariant::Ghost)),
1989 )
1990 }
1991 vec![PreviewVariant::scenario("default", build_default)]
1992 }
1993 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
1994 scenario_for::<Self>(variant)
1995 }
1996}
1997register_widget_catalog_at!("crates/teksilo-widgets/src/toolbar.rs", Toolbar);
1998
1999impl WidgetCatalog for StatusBar {
2004 fn id() -> &'static str {
2005 "status_bar"
2006 }
2007 fn group() -> &'static str {
2008 "Chrome"
2009 }
2010 fn display_name() -> &'static str {
2011 "StatusBar"
2012 }
2013 fn variants() -> Vec<PreviewVariant> {
2014 fn build_default() -> Box<dyn Widget> {
2015 Box::new(
2016 StatusBar::new()
2017 .child(
2018 TextWidget::new(lit!("Ready"))
2019 .style(TextStyleRole::Tiny)
2020 .color(TextRole::Secondary),
2021 )
2022 .child(Spacer::new())
2023 .child(
2024 TextWidget::new(lit!("Ln 42, Col 17"))
2025 .style(TextStyleRole::Tiny)
2026 .color(TextRole::Secondary),
2027 ),
2028 )
2029 }
2030 vec![PreviewVariant::scenario("default", build_default)]
2031 }
2032 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2033 scenario_for::<Self>(variant)
2034 }
2035}
2036register_widget_catalog_at!("crates/teksilo-widgets/src/status_bar.rs", StatusBar);
2037
2038impl WidgetCatalog for Accordion {
2043 fn id() -> &'static str {
2044 "accordion"
2045 }
2046 fn group() -> &'static str {
2047 "Containers"
2048 }
2049 fn display_name() -> &'static str {
2050 "Accordion"
2051 }
2052 fn knobs() -> KnobSpec {
2053 KnobSpec::new()
2054 .text("title", "Title", "Advanced")
2055 .bool_("expanded", "Expanded", false)
2056 .text("content", "Content body", "Hidden until expanded.")
2057 }
2058 fn variants() -> Vec<PreviewVariant> {
2059 vec![
2060 PreviewVariant::defaults("collapsed"),
2061 PreviewVariant::knobs(
2062 "expanded",
2063 KnobOverrides::new()
2064 .bool_("expanded", true)
2065 .text("content", "Now visible because the section is expanded."),
2066 ),
2067 ]
2068 }
2069 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
2070 let title = knobs.text("title").get();
2071 let body = knobs.text("content").get();
2072 let expanded = knobs.bool_("expanded");
2076 Box::new(Accordion::new(lit!(title), expanded).content(sample_text(&body)))
2077 }
2078}
2079register_widget_catalog_at!("crates/teksilo-widgets/src/accordion.rs", Accordion);
2080
2081impl WidgetCatalog for RadioGroup {
2086 fn id() -> &'static str {
2087 "radio_group"
2088 }
2089 fn group() -> &'static str {
2090 "Controls"
2091 }
2092 fn display_name() -> &'static str {
2093 "RadioGroup"
2094 }
2095 fn variants() -> Vec<PreviewVariant> {
2096 fn build_default() -> Box<dyn Widget> {
2097 let selected = Signal::new(0_usize);
2098 Box::new(
2099 RadioGroup::new()
2100 .child(RadioButton::new(0, selected.clone()).label(lit!("First")))
2101 .child(RadioButton::new(1, selected.clone()).label(lit!("Second")))
2102 .child(RadioButton::new(2, selected).label(lit!("Third"))),
2103 )
2104 }
2105 vec![PreviewVariant::scenario("default", build_default)]
2106 }
2107 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2108 scenario_for::<Self>(variant)
2109 }
2110}
2111register_widget_catalog_at!("crates/teksilo-widgets/src/radio_group.rs", RadioGroup);
2112
2113impl WidgetCatalog for RadioTile {
2117 fn id() -> &'static str {
2118 "radio_tile"
2119 }
2120 fn group() -> &'static str {
2121 "Controls"
2122 }
2123 fn display_name() -> &'static str {
2124 "RadioTile"
2125 }
2126 fn knobs() -> KnobSpec {
2127 KnobSpec::new()
2128 .choice("selected", "Selected", &["Yes", "No"], 0)
2129 .opt_text("title", "Title", Some("Single file"))
2130 .opt_text(
2131 "description",
2132 "Description",
2133 Some("One .skrib archive (zip). Portable, easy to back up."),
2134 )
2135 .bool_("enabled", "Enabled", true)
2136 }
2137 fn variants() -> Vec<PreviewVariant> {
2138 vec![
2139 PreviewVariant::defaults("selected"),
2140 PreviewVariant::knobs("unselected", KnobOverrides::new().choice("selected", 1)),
2141 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
2142 PreviewVariant::knobs(
2143 "no-description",
2144 KnobOverrides::new().opt_text("description", None),
2145 ),
2146 ]
2147 }
2148 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
2149 let title = knobs.opt_text("title").get();
2150 let description = knobs.opt_text("description").get();
2151 let enabled = knobs.bool_("enabled").get();
2152 let mut tile = RadioTile::new()
2153 .selection(0, knobs.choice("selected"))
2154 .enabled(enabled);
2155 if let Some(title) = title {
2156 tile = tile.title(lit!(title));
2157 }
2158 if let Some(description) = description {
2159 tile = tile.description(lit!(description));
2160 }
2161 Box::new(FixedSize::new().width(300.0).child(tile))
2162 }
2163}
2164register_widget_catalog_at!("crates/teksilo-widgets/src/radio_tile.rs", RadioTile);
2165
2166impl WidgetCatalog for RadioTileGroup {
2170 fn id() -> &'static str {
2171 "radio_tile_group"
2172 }
2173 fn group() -> &'static str {
2174 "Controls"
2175 }
2176 fn display_name() -> &'static str {
2177 "RadioTileGroup"
2178 }
2179 fn variants() -> Vec<PreviewVariant> {
2180 fn build_row() -> Box<dyn Widget> {
2181 let selected = Signal::new(0_usize);
2182 Box::new(
2183 FixedSize::new().width(560.0).child(
2184 RadioTileGroup::new(selected)
2185 .tile(
2186 RadioTile::new()
2187 .title(lit!("Single file"))
2188 .description(lit!(
2189 "One .skrib archive (zip). Portable, easy to back up."
2190 )),
2191 )
2192 .tile(RadioTile::new().title(lit!("Bundle")).description(lit!(
2193 "A folder holding every text & asset. Friendlier to version control."
2194 )))
2195 .layout(TileLayout::Row),
2196 ),
2197 )
2198 }
2199 fn build_grid() -> Box<dyn Widget> {
2200 let selected = Signal::new(1_usize);
2201 Box::new(
2202 FixedSize::new().width(560.0).child(
2203 RadioTileGroup::new(selected)
2204 .tiles((0..4).map(|i| {
2205 RadioTile::new()
2206 .title(lit!(format!("Option {}", i + 1)))
2207 .description(lit!("A selectable option in the grid."))
2208 }))
2209 .layout(TileLayout::Grid {
2210 min_tile_width: 240.0,
2211 }),
2212 ),
2213 )
2214 }
2215 vec![
2216 PreviewVariant::scenario("row", build_row),
2217 PreviewVariant::scenario("grid", build_grid),
2218 ]
2219 }
2220 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2221 scenario_for::<Self>(variant)
2222 }
2223}
2224register_widget_catalog_at!(
2225 "crates/teksilo-widgets/src/radio_tile_group.rs",
2226 RadioTileGroup
2227);
2228
2229impl WidgetCatalog for SplitButton {
2234 fn id() -> &'static str {
2235 "split_button"
2236 }
2237 fn group() -> &'static str {
2238 "Controls"
2239 }
2240 fn display_name() -> &'static str {
2241 "SplitButton"
2242 }
2243 fn variants() -> Vec<PreviewVariant> {
2244 fn build_default() -> Box<dyn Widget> {
2245 Box::new(
2246 SplitButton::new_static()
2247 .item(MenuItem::new(lit!("Save")))
2248 .item(MenuItem::new(lit!("Save As…")))
2249 .item(MenuItem::new(lit!("Save All"))),
2250 )
2251 }
2252 vec![PreviewVariant::scenario("default", build_default)]
2253 }
2254 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2255 scenario_for::<Self>(variant)
2256 }
2257}
2258register_widget_catalog_at!("crates/teksilo-widgets/src/split_button.rs", SplitButton);
2259
2260impl WidgetCatalog for ListView<String> {
2269 fn id() -> &'static str {
2270 "list_view"
2271 }
2272 fn group() -> &'static str {
2273 "Data"
2274 }
2275 fn display_name() -> &'static str {
2276 "ListView"
2277 }
2278 fn variants() -> Vec<PreviewVariant> {
2279 fn build_short() -> Box<dyn Widget> {
2280 let model = teksilo_data::ListModel::from_vec(vec![
2281 "Apple".to_string(),
2282 "Banana".to_string(),
2283 "Cherry".to_string(),
2284 "Date".to_string(),
2285 "Elderberry".to_string(),
2286 "Fig".to_string(),
2287 ]);
2288 Box::new(
2289 FixedSize::new()
2290 .width(280.0_f32)
2291 .height(220.0_f32)
2292 .child(ListView::new(model, |_idx, item, selected| {
2293 Box::new(StandardListItem::new(lit!(item.clone())).selected(selected))
2294 })),
2295 )
2296 }
2297 vec![PreviewVariant::scenario("short", build_short)]
2298 }
2299 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2300 scenario_for::<Self>(variant)
2301 }
2302}
2303register_widget_catalog_at!("crates/teksilo-widgets/src/list_view.rs", ListView<String>);
2304
2305impl WidgetCatalog for GridView<String> {
2310 fn id() -> &'static str {
2311 "grid_view"
2312 }
2313 fn group() -> &'static str {
2314 "Data"
2315 }
2316 fn display_name() -> &'static str {
2317 "GridView"
2318 }
2319 fn variants() -> Vec<PreviewVariant> {
2320 fn items(n: usize) -> teksilo_data::ListModel<String> {
2321 teksilo_data::ListModel::from_vec((0..n).map(|i| format!("Tile {i}")).collect())
2322 }
2323 fn tile_z(caption: &str, selected: bool) -> Box<dyn Widget> {
2325 let bg = if selected {
2326 SurfaceRole::AccentSubtle
2327 } else {
2328 SurfaceRole::Raised
2329 };
2330 Box::new(
2331 crate::primitives::ZStack::new()
2332 .child(RectWidget::new().background(bg))
2333 .child(Center::new().child(
2334 TextWidget::new(lit!(caption.to_string())).color(TextRole::Primary),
2335 )),
2336 )
2337 }
2338 fn framed(grid: GridView<String>) -> Box<dyn Widget> {
2339 Box::new(
2340 FixedSize::new()
2341 .width(360.0_f32)
2342 .height(320.0_f32)
2343 .child(grid),
2344 )
2345 }
2346
2347 fn adaptive() -> Box<dyn Widget> {
2348 framed(
2349 GridView::new(items(40), |tc| tile_z(tc.item, tc.is_selected))
2350 .sizing(GridSizing::Adaptive {
2351 min_width: 90.0,
2352 max_width: None,
2353 height: 64.0,
2354 })
2355 .spacing(8.0),
2356 )
2357 }
2358 fn fixed_columns() -> Box<dyn Widget> {
2359 framed(
2360 GridView::new(items(40), |tc| tile_z(tc.item, tc.is_selected))
2361 .column_count(4, 64.0)
2362 .spacing(8.0),
2363 )
2364 }
2365 fn selectable() -> Box<dyn Widget> {
2366 use teksilo_data::{SelectionMode, SelectionModel};
2367 let sel = SelectionModel::new(SelectionMode::Multi);
2368 sel.select(2);
2369 framed(
2370 GridView::new(items(40), |tc| tile_z(tc.item, tc.is_selected))
2371 .sizing(GridSizing::Adaptive {
2372 min_width: 90.0,
2373 max_width: None,
2374 height: 64.0,
2375 })
2376 .spacing(8.0)
2377 .selection(sel),
2378 )
2379 }
2380 fn waterfall() -> Box<dyn Widget> {
2381 framed(
2384 GridView::new(items(40), |tc| tile_z(tc.item, tc.is_selected))
2385 .column_count(3, 64.0)
2386 .waterfall(64.0)
2387 .item_height(|i| 48.0 + (i % 5) as f32 * 18.0)
2388 .spacing(8.0),
2389 )
2390 }
2391
2392 vec![
2393 PreviewVariant::scenario("adaptive", adaptive),
2394 PreviewVariant::scenario("fixed_columns", fixed_columns),
2395 PreviewVariant::scenario("selection", selectable),
2396 PreviewVariant::scenario("waterfall", waterfall),
2397 ]
2398 }
2399 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2400 scenario_for::<Self>(variant)
2401 }
2402}
2403register_widget_catalog_at!("crates/teksilo-widgets/src/grid_view.rs", GridView<String>);
2404
2405impl WidgetCatalog for TreeView<String> {
2410 fn id() -> &'static str {
2411 "tree_view"
2412 }
2413 fn group() -> &'static str {
2414 "Data"
2415 }
2416 fn display_name() -> &'static str {
2417 "TreeView"
2418 }
2419 fn variants() -> Vec<PreviewVariant> {
2420 fn build_default() -> Box<dyn Widget> {
2421 let model = teksilo_data::TreeModel::<String>::new();
2422 let root = model.insert_root(0, "Project".to_string());
2423 let crates_node = model.insert_child(root, 0, "crates".to_string());
2424 model.insert_child(crates_node, 0, "teksilo-core".to_string());
2425 model.insert_child(crates_node, 1, "teksilo-widgets".to_string());
2426 model.insert_child(crates_node, 2, "teksilo-render".to_string());
2427 let docs = model.insert_child(root, 1, "docs".to_string());
2428 model.insert_child(docs, 0, "architecture.md".to_string());
2429 Box::new(FixedSize::new().width(280.0_f32).height(220.0_f32).child(
2430 TreeView::new_with_context(model, |item, entry, selected, ctx| {
2431 Box::new(
2432 StandardTreeItem::new(lit!(item.clone()))
2433 .from_entry(entry)
2434 .selected(selected)
2435 .on_toggle_rc(ctx.toggle_callback()),
2436 )
2437 }),
2438 ))
2439 }
2440 vec![PreviewVariant::scenario("default", build_default)]
2441 }
2442 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2443 scenario_for::<Self>(variant)
2444 }
2445}
2446register_widget_catalog_at!("crates/teksilo-widgets/src/tree_view.rs", TreeView<String>);
2447
2448impl WidgetCatalog for StandardListItem {
2453 fn id() -> &'static str {
2454 "standard_list_item"
2455 }
2456 fn group() -> &'static str {
2457 "Data"
2458 }
2459 fn display_name() -> &'static str {
2460 "StandardListItem"
2461 }
2462 fn variants() -> Vec<PreviewVariant> {
2463 fn build_single_line() -> Box<dyn Widget> {
2464 Box::new(StandardListItem::new(lit!("Single-line item")))
2465 }
2466 fn build_with_all_primary_slots() -> Box<dyn Widget> {
2467 Box::new(
2468 StandardListItem::new(lit!("With every primary slot"))
2469 .leading_slot(TextWidget::new(lit!("●")).color(TextRole::Accent))
2470 .center_slot(TextWidget::new(lit!("•")).color(TextRole::Secondary))
2471 .trailing_slot(TextWidget::new(lit!("12")).color(TextRole::Secondary)),
2472 )
2473 }
2474 fn build_two_line_with_subtitle_slots() -> Box<dyn Widget> {
2475 Box::new(
2476 StandardListItem::new(lit!("Title line"))
2477 .subtitle(lit!("Subtitle line"))
2478 .leading_slot(TextWidget::new(lit!("●")).color(TextRole::Accent))
2479 .subtitle_leading_slot(TextWidget::new(lit!("•")).color(TextRole::Secondary))
2480 .subtitle_trailing_slot(
2481 TextWidget::new(lit!("just now")).color(TextRole::Secondary),
2482 )
2483 .trailing_slot(TextWidget::new(lit!("∗")).color(TextRole::Accent)),
2484 )
2485 }
2486 fn build_with_checkbox() -> Box<dyn Widget> {
2487 let checked = Signal::new(true);
2488 Box::new(StandardListItem::new(lit!("With two-state checkbox")).checkbox(checked))
2489 }
2490 fn build_with_tristate_checkbox() -> Box<dyn Widget> {
2491 use teksilo_data::CheckState;
2492 let s = Signal::new(CheckState::Indeterminate);
2493 Box::new(StandardListItem::new(lit!("With tristate checkbox")).tristate_checkbox(s))
2494 }
2495 fn build_selected() -> Box<dyn Widget> {
2496 Box::new(StandardListItem::new(lit!("Selected")).selected(true))
2497 }
2498 fn build_disabled() -> Box<dyn Widget> {
2499 Box::new(StandardListItem::new(lit!("Disabled")).enabled(false))
2500 }
2501 vec![
2502 PreviewVariant::scenario("single_line", build_single_line),
2503 PreviewVariant::scenario("all_primary_slots", build_with_all_primary_slots),
2504 PreviewVariant::scenario("two_line", build_two_line_with_subtitle_slots),
2505 PreviewVariant::scenario("checkbox", build_with_checkbox),
2506 PreviewVariant::scenario("tristate_checkbox", build_with_tristate_checkbox),
2507 PreviewVariant::scenario("selected", build_selected),
2508 PreviewVariant::scenario("disabled", build_disabled),
2509 ]
2510 }
2511 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2512 scenario_for::<Self>(variant)
2513 }
2514}
2515register_widget_catalog_at!(
2516 "crates/teksilo-widgets/src/standard_item.rs",
2517 StandardListItem
2518);
2519
2520impl WidgetCatalog for StandardTreeItem {
2525 fn id() -> &'static str {
2526 "standard_tree_item"
2527 }
2528 fn group() -> &'static str {
2529 "Data"
2530 }
2531 fn display_name() -> &'static str {
2532 "StandardTreeItem"
2533 }
2534 fn variants() -> Vec<PreviewVariant> {
2535 fn build_collapsed_branch() -> Box<dyn Widget> {
2536 Box::new(
2537 StandardTreeItem::new(lit!("Folder (collapsed)"))
2538 .depth(0)
2539 .has_children(true)
2540 .is_expanded(false),
2541 )
2542 }
2543 fn build_expanded_branch() -> Box<dyn Widget> {
2544 Box::new(
2545 StandardTreeItem::new(lit!("Folder (expanded)"))
2546 .depth(0)
2547 .has_children(true)
2548 .is_expanded(true),
2549 )
2550 }
2551 fn build_leaf_indented() -> Box<dyn Widget> {
2552 Box::new(
2553 StandardTreeItem::new(lit!("Deep leaf"))
2554 .depth(2)
2555 .has_children(false),
2556 )
2557 }
2558 fn build_with_tristate_checkbox() -> Box<dyn Widget> {
2559 use teksilo_data::CheckState;
2560 let s = Signal::new(CheckState::Indeterminate);
2561 Box::new(
2562 StandardTreeItem::new(lit!("Folder with tristate"))
2563 .depth(1)
2564 .has_children(true)
2565 .is_expanded(true)
2566 .tristate_checkbox(s),
2567 )
2568 }
2569 fn build_two_line() -> Box<dyn Widget> {
2570 Box::new(
2571 StandardTreeItem::new(lit!("Folder"))
2572 .subtitle(lit!("3 items · last week"))
2573 .depth(0)
2574 .has_children(true)
2575 .is_expanded(false)
2576 .subtitle_trailing_slot(TextWidget::new(lit!("3")).color(TextRole::Secondary)),
2577 )
2578 }
2579 vec![
2580 PreviewVariant::scenario("collapsed_branch", build_collapsed_branch),
2581 PreviewVariant::scenario("expanded_branch", build_expanded_branch),
2582 PreviewVariant::scenario("leaf_indented", build_leaf_indented),
2583 PreviewVariant::scenario("tristate_checkbox", build_with_tristate_checkbox),
2584 PreviewVariant::scenario("two_line", build_two_line),
2585 ]
2586 }
2587 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2588 scenario_for::<Self>(variant)
2589 }
2590}
2591register_widget_catalog_at!(
2592 "crates/teksilo-widgets/src/standard_item.rs",
2593 StandardTreeItem
2594);
2595
2596impl WidgetCatalog for MenuList {
2601 fn id() -> &'static str {
2602 "menu_list"
2603 }
2604 fn group() -> &'static str {
2605 "Menus"
2606 }
2607 fn display_name() -> &'static str {
2608 "MenuList"
2609 }
2610 fn variants() -> Vec<PreviewVariant> {
2611 fn build_default() -> Box<dyn Widget> {
2612 Box::new(
2613 MenuList::new()
2614 .item(MenuItem::new(lit!("New")))
2615 .item(MenuItem::new(lit!("Open…")))
2616 .item(MenuItem::new(lit!("Save")))
2617 .item(MenuItem::new(lit!("Save As…")))
2618 .item(MenuItem::new(lit!("Close"))),
2619 )
2620 }
2621 vec![PreviewVariant::scenario("default", build_default)]
2622 }
2623 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2624 scenario_for::<Self>(variant)
2625 }
2626}
2627register_widget_catalog_at!("crates/teksilo-widgets/src/menu_list.rs", MenuList);
2628
2629impl WidgetCatalog for ScrollArea {
2634 fn id() -> &'static str {
2635 "scroll_area"
2636 }
2637 fn group() -> &'static str {
2638 "Data"
2639 }
2640 fn display_name() -> &'static str {
2641 "ScrollArea"
2642 }
2643 fn variants() -> Vec<PreviewVariant> {
2644 fn build_long_content() -> Box<dyn Widget> {
2645 let mut col = VStack::new().spacing(4.0);
2646 for i in 1..=40 {
2647 col = col.child(
2648 Padding::symmetric(4.0, 8.0).child(
2649 TextWidget::new(lit!(format!("Row {}", i)))
2650 .style(TextStyleRole::Body)
2651 .color(TextRole::Primary),
2652 ),
2653 );
2654 }
2655 Box::new(
2656 FixedSize::new()
2657 .width(280.0_f32)
2658 .height(180.0_f32)
2659 .child(ScrollArea::new().child(col)),
2660 )
2661 }
2662 vec![PreviewVariant::scenario("long-content", build_long_content)]
2663 }
2664 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2665 scenario_for::<Self>(variant)
2666 }
2667 fn icon() -> Option<Box<dyn Widget>> {
2668 Some(icons::scroll_area())
2669 }
2670 fn category() -> WidgetCategory {
2671 WidgetCategory::ContainerA
2672 }
2673 fn build_with_children(
2674 _variant: &str,
2675 _knobs: &KnobValues,
2676 children: Vec<SlottedChild>,
2677 ) -> Box<dyn Widget> {
2678 match children.into_iter().next() {
2679 Some(c) => Box::new(ScrollArea::from_id(c.id)),
2680 None => Box::new(ScrollArea::new()),
2681 }
2682 }
2683}
2684register_widget_catalog_at!("crates/teksilo-widgets/src/scroll_area.rs", ScrollArea);
2685
2686impl WidgetCatalog for Splitter {
2691 fn id() -> &'static str {
2692 "splitter"
2693 }
2694 fn group() -> &'static str {
2695 "Containers"
2696 }
2697 fn display_name() -> &'static str {
2698 "Splitter"
2699 }
2700 fn variants() -> Vec<PreviewVariant> {
2701 fn build_horizontal() -> Box<dyn Widget> {
2702 let left = Panel::new()
2703 .background(SurfaceRole::Sunken)
2704 .padding(12.0)
2705 .child(sample_text("Left pane"));
2706 let right = Panel::new()
2707 .background(SurfaceRole::Raised)
2708 .padding(12.0)
2709 .child(sample_text("Right pane"));
2710 Box::new(
2711 FixedSize::new().width(420.0_f32).height(220.0_f32).child(
2712 Splitter::new(SplitterModel::new(2, Orientation::Horizontal))
2713 .pane(left)
2714 .pane(right),
2715 ),
2716 )
2717 }
2718 fn build_three_pane() -> Box<dyn Widget> {
2719 let model = SplitterModel::from_panes(
2720 vec![
2721 PaneDescriptor::new()
2722 .size(120.0)
2723 .collapsible(true)
2724 .stretch(0.0),
2725 PaneDescriptor::new().stretch(1.0),
2726 PaneDescriptor::new()
2727 .size(120.0)
2728 .collapsible(true)
2729 .stretch(0.0),
2730 ],
2731 Orientation::Horizontal,
2732 );
2733 let pane = |label: &str, role| {
2734 Panel::new()
2735 .background(role)
2736 .padding(12.0)
2737 .child(sample_text(label))
2738 };
2739 Box::new(
2740 FixedSize::new().width(480.0_f32).height(220.0_f32).child(
2741 Splitter::new(model)
2742 .pane(pane("Sidebar", SurfaceRole::Sunken))
2743 .pane(pane("Editor", SurfaceRole::Raised))
2744 .pane(pane("Inspector", SurfaceRole::Sunken)),
2745 ),
2746 )
2747 }
2748 vec![
2749 PreviewVariant::scenario("horizontal", build_horizontal),
2750 PreviewVariant::scenario("three_pane", build_three_pane),
2751 ]
2752 }
2753 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2754 scenario_for::<Self>(variant)
2755 }
2756}
2757register_widget_catalog_at!("crates/teksilo-widgets/src/splitter.rs", Splitter);
2758
2759impl WidgetCatalog for TabWidget {
2764 fn id() -> &'static str {
2765 "tab_widget"
2766 }
2767 fn group() -> &'static str {
2768 "Containers"
2769 }
2770 fn display_name() -> &'static str {
2771 "TabWidget"
2772 }
2773 fn variants() -> Vec<PreviewVariant> {
2774 fn build_three_tabs() -> Box<dyn Widget> {
2775 use crate::tab_widget::{TabId, TabInfo};
2776 let selected: Signal<Option<TabId>> = Signal::new(None);
2777 Box::new(
2778 FixedSize::new().width(420.0_f32).height(220.0_f32).child(
2779 TabWidget::new(selected)
2780 .static_tab(
2781 TabInfo::new().title(lit!("Overview")),
2782 Center::new().child(sample_text("Overview tab content")),
2783 )
2784 .static_tab(
2785 TabInfo::new().title(lit!("Details")),
2786 Center::new().child(sample_text("Details tab content")),
2787 )
2788 .static_tab(
2789 TabInfo::new().title(lit!("Settings")),
2790 Center::new().child(sample_text("Settings tab content")),
2791 ),
2792 ),
2793 )
2794 }
2795 vec![PreviewVariant::scenario("three-tabs", build_three_tabs)]
2796 }
2797 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2798 scenario_for::<Self>(variant)
2799 }
2800}
2801register_widget_catalog_at!("crates/teksilo-widgets/src/tab_widget.rs", TabWidget);
2802
2803impl WidgetCatalog for ToolBox {
2808 fn id() -> &'static str {
2809 "tool_box"
2810 }
2811 fn group() -> &'static str {
2812 "Containers"
2813 }
2814 fn display_name() -> &'static str {
2815 "ToolBox"
2816 }
2817 fn variants() -> Vec<PreviewVariant> {
2818 fn build_three_items() -> Box<dyn Widget> {
2819 let selected = Signal::new(0_usize);
2820 Box::new(
2821 FixedSize::new().width(280.0_f32).height(280.0_f32).child(
2822 ToolBox::new(selected)
2823 .item(
2824 lit!("General"),
2825 Padding::uniform(12.0).child(sample_text("General settings")),
2826 )
2827 .item(
2828 lit!("Editor"),
2829 Padding::uniform(12.0).child(sample_text("Editor settings")),
2830 )
2831 .item(
2832 lit!("Keymap"),
2833 Padding::uniform(12.0).child(sample_text("Keymap settings")),
2834 ),
2835 ),
2836 )
2837 }
2838 vec![PreviewVariant::scenario("three-items", build_three_items)]
2839 }
2840 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2841 scenario_for::<Self>(variant)
2842 }
2843}
2844register_widget_catalog_at!("crates/teksilo-widgets/src/tool_box.rs", ToolBox);
2845
2846impl WidgetCatalog for crate::Repeater<String> {
2851 fn id() -> &'static str {
2852 "repeater"
2853 }
2854 fn group() -> &'static str {
2855 "Data"
2856 }
2857 fn display_name() -> &'static str {
2858 "Repeater"
2859 }
2860 fn variants() -> Vec<PreviewVariant> {
2861 fn build_default() -> Box<dyn Widget> {
2862 let model = teksilo_data::ListModel::from_vec(vec![
2863 "Alpha".to_string(),
2864 "Beta".to_string(),
2865 "Gamma".to_string(),
2866 "Delta".to_string(),
2867 ]);
2868 Box::new(
2869 crate::Repeater::new(model, |item| {
2870 Box::new(
2871 Padding::symmetric(4.0, 8.0).child(
2872 TextWidget::new(lit!(item.clone()))
2873 .style(TextStyleRole::Body)
2874 .color(TextRole::Primary),
2875 ),
2876 )
2877 })
2878 .spacing(4.0),
2879 )
2880 }
2881 vec![PreviewVariant::scenario("default", build_default)]
2882 }
2883 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2884 scenario_for::<Self>(variant)
2885 }
2886}
2887register_widget_catalog_at!(
2888 "crates/teksilo-widgets/src/repeater.rs",
2889 crate::Repeater<String>
2890);
2891
2892fn scenario_for<W: WidgetCatalog>(name: &str) -> Box<dyn Widget> {
2901 let variants = W::variants();
2902 let chosen = variants
2903 .iter()
2904 .find(|v| v.name() == name)
2905 .or_else(|| variants.first());
2906 match chosen {
2907 Some(PreviewVariant::Scenario { builder, .. }) => builder(),
2908 _ => Box::new(
2909 TextWidget::new(lit!(format!("(no scenario for variant '{}')", name)))
2910 .style(TextStyleRole::Small)
2911 .color(TextRole::Secondary),
2912 ),
2913 }
2914}
2915
2916mod color_family {
2921 use super::*;
2922 use crate::{ColorEdit, ColorPicker, ColorPickerLayout, HexColorInput};
2923 use teksilo_tokens::Color;
2924
2925 impl WidgetCatalog for HexColorInput {
2926 fn id() -> &'static str {
2927 "hex-color-input"
2928 }
2929 fn group() -> &'static str {
2930 "Color"
2931 }
2932 fn display_name() -> &'static str {
2933 "HexColorInput"
2934 }
2935 fn knobs() -> KnobSpec {
2936 KnobSpec::new()
2937 }
2938 fn variants() -> Vec<PreviewVariant> {
2939 fn default_var() -> Box<dyn Widget> {
2940 Box::new(HexColorInput::new(Signal::new(Color::from_hex("#3584E4"))))
2941 }
2942 fn alpha_var() -> Box<dyn Widget> {
2943 Box::new(
2944 HexColorInput::new(Signal::new(Color::from_rgba(1.0, 0.5, 0.0, 0.6)))
2945 .alpha_enabled(true),
2946 )
2947 }
2948 vec![
2949 PreviewVariant::scenario("default", default_var),
2950 PreviewVariant::scenario("with-alpha", alpha_var),
2951 ]
2952 }
2953 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
2954 scenario_for::<Self>(variant)
2955 }
2956 }
2957 register_widget_catalog_at!(
2958 "crates/teksilo-widgets/src/hex_color_input.rs",
2959 HexColorInput
2960 );
2961
2962 impl WidgetCatalog for ColorPicker {
2963 fn id() -> &'static str {
2964 "color-picker"
2965 }
2966 fn group() -> &'static str {
2967 "Color"
2968 }
2969 fn display_name() -> &'static str {
2970 "ColorPicker"
2971 }
2972 fn knobs() -> KnobSpec {
2973 KnobSpec::new()
2974 }
2975 fn variants() -> Vec<PreviewVariant> {
2976 fn default_var() -> Box<dyn Widget> {
2977 Box::new(ColorPicker::new(Signal::new(Color::from_hex("#3584E4"))))
2978 }
2979 fn with_alpha() -> Box<dyn Widget> {
2980 Box::new(
2981 ColorPicker::new(Signal::new(Color::from_rgba(0.21, 0.66, 0.40, 0.5)))
2982 .alpha_enabled(true),
2983 )
2984 }
2985 fn compact() -> Box<dyn Widget> {
2986 Box::new(
2987 ColorPicker::new(Signal::new(Color::from_hex("#E91E63")))
2988 .layout(ColorPickerLayout::Compact),
2989 )
2990 }
2991 fn wide() -> Box<dyn Widget> {
2992 Box::new(
2993 ColorPicker::new(Signal::new(Color::from_hex("#FF9800")))
2994 .alpha_enabled(true)
2995 .layout(ColorPickerLayout::Wide)
2996 .show_hsv_spinners(true),
2997 )
2998 }
2999 vec![
3000 PreviewVariant::scenario("default", default_var),
3001 PreviewVariant::scenario("with-alpha", with_alpha),
3002 PreviewVariant::scenario("compact", compact),
3003 PreviewVariant::scenario("wide", wide),
3004 ]
3005 }
3006 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
3007 scenario_for::<Self>(variant)
3008 }
3009 }
3010 register_widget_catalog_at!("crates/teksilo-widgets/src/color_picker.rs", ColorPicker);
3011
3012 impl WidgetCatalog for ColorEdit {
3013 fn id() -> &'static str {
3014 "color-edit"
3015 }
3016 fn group() -> &'static str {
3017 "Color"
3018 }
3019 fn display_name() -> &'static str {
3020 "ColorEdit"
3021 }
3022 fn knobs() -> KnobSpec {
3023 KnobSpec::new()
3024 }
3025 fn variants() -> Vec<PreviewVariant> {
3026 fn default_var() -> Box<dyn Widget> {
3027 Box::new(ColorEdit::new(Signal::new(Color::from_hex("#3584E4"))))
3028 }
3029 fn with_alpha() -> Box<dyn Widget> {
3030 Box::new(
3031 ColorEdit::new(Signal::new(Color::from_rgba(0.92, 0.27, 0.18, 0.6)))
3032 .alpha_enabled(true),
3033 )
3034 }
3035 fn no_hex_in_trigger() -> Box<dyn Widget> {
3036 Box::new(
3037 ColorEdit::new(Signal::new(Color::from_hex("#9C27B0")))
3038 .show_hex_in_trigger(false),
3039 )
3040 }
3041 fn nullable_var() -> Box<dyn Widget> {
3042 let v: Signal<Option<Color>> = Signal::new(None);
3043 Box::new(ColorEdit::nullable(v))
3044 }
3045 vec![
3046 PreviewVariant::scenario("default", default_var),
3047 PreviewVariant::scenario("with-alpha", with_alpha),
3048 PreviewVariant::scenario("no-hex-in-trigger", no_hex_in_trigger),
3049 PreviewVariant::scenario("nullable", nullable_var),
3050 ]
3051 }
3052 fn build(variant: &str, _knobs: &KnobValues) -> Box<dyn Widget> {
3053 scenario_for::<Self>(variant)
3054 }
3055 }
3056 register_widget_catalog_at!("crates/teksilo-widgets/src/color_edit.rs", ColorEdit);
3057}
3058
3059mod secure_input_family {
3064 use super::*;
3065 use crate::{EchoMode, PasswordField, RevealMode};
3066
3067 impl WidgetCatalog for PasswordField {
3068 fn id() -> &'static str {
3069 "password-field"
3070 }
3071 fn group() -> &'static str {
3072 "Inputs"
3073 }
3074 fn display_name() -> &'static str {
3075 "PasswordField"
3076 }
3077 fn knobs() -> KnobSpec {
3078 KnobSpec::new()
3079 .text("placeholder", "Placeholder", "Enter your password")
3080 .text("text", "Initial text", "hunter2")
3081 .choice(
3082 "echo_mode",
3083 "Echo mode",
3084 &["Masked", "NoEcho", "RevealWhileTyping"],
3085 0,
3086 )
3087 .choice(
3088 "reveal_mode",
3089 "Reveal button",
3090 &["Toggle", "Hold", "None"],
3091 0,
3092 )
3093 .bool_("enabled", "Enabled", true)
3094 .bool_("caps_warning", "Caps Lock warning", true)
3095 }
3096 fn variants() -> Vec<PreviewVariant> {
3097 vec![
3098 PreviewVariant::defaults("default"),
3099 PreviewVariant::knobs(
3100 "reveal-while-typing",
3101 KnobOverrides::new().choice("echo_mode", 2),
3102 ),
3103 PreviewVariant::knobs(
3104 "hold-to-reveal",
3105 KnobOverrides::new().choice("reveal_mode", 1),
3106 ),
3107 PreviewVariant::knobs("no-echo", KnobOverrides::new().choice("echo_mode", 1)),
3108 PreviewVariant::knobs(
3109 "no-reveal-button",
3110 KnobOverrides::new().choice("reveal_mode", 2),
3111 ),
3112 PreviewVariant::knobs("disabled", KnobOverrides::new().bool_("enabled", false)),
3113 ]
3114 }
3115 fn build(_variant: &str, knobs: &KnobValues) -> Box<dyn Widget> {
3116 let placeholder = knobs.text("placeholder").get();
3117 let initial = knobs.text("text").get();
3118 let echo = match knobs.choice("echo_mode").get() {
3119 1 => EchoMode::NoEcho,
3120 2 => EchoMode::RevealWhileTyping,
3121 _ => EchoMode::Masked,
3122 };
3123 let reveal = match knobs.choice("reveal_mode").get() {
3124 1 => RevealMode::Hold,
3125 2 => RevealMode::None,
3126 _ => RevealMode::Toggle,
3127 };
3128 let enabled = knobs.bool_("enabled").get();
3129 let caps = knobs.bool_("caps_warning").get();
3130 Box::new(
3131 PasswordField::new(Signal::new(initial))
3132 .label(lit!("Password"))
3133 .placeholder(lit!(placeholder))
3134 .echo_mode(echo)
3135 .reveal_mode(reveal)
3136 .enabled(enabled)
3137 .caps_lock_warning(caps),
3138 )
3139 }
3140 }
3141 register_widget_catalog_at!(
3142 "crates/teksilo-widgets/src/password_field.rs",
3143 PasswordField
3144 );
3145}
3146
3147#[cfg(all(test, feature = "preview"))]
3148mod build_with_children_tests {
3149 use super::*;
3150 use teksilo_canvas::SizeProposal;
3151 use teksilo_core::widget_tree::WidgetTree;
3152 use teksilo_preview::{CatalogEntry, KnobValues, SlottedChild, WidgetCategory, find_by_id};
3153
3154 fn knobs_for(entry: &dyn CatalogEntry) -> KnobValues {
3155 KnobValues::from_spec(&entry.knobs(), None)
3156 }
3157
3158 fn descendants(
3160 tree: &WidgetTree,
3161 root: teksilo_core::widget_id::WidgetId,
3162 ) -> Vec<teksilo_core::widget_id::WidgetId> {
3163 let mut out = Vec::new();
3164 let mut stack = vec![root];
3165 while let Some(n) = stack.pop() {
3166 for ch in tree.children(n) {
3167 out.push(ch);
3168 stack.push(ch);
3169 }
3170 }
3171 out
3172 }
3173
3174 #[test]
3175 fn leaf_button_default_ignores_children() {
3176 let entry = find_by_id("button").expect("button registered");
3177 assert_eq!(entry.category(), WidgetCategory::Leaf);
3178 assert!(entry.icon().is_some());
3179 let knobs = knobs_for(entry);
3180 let mut tree = WidgetTree::new();
3181 let stray = tree.add(TextWidget::new(lit!("x")));
3182 let w = entry.build_with_children(
3183 "default",
3184 &knobs,
3185 vec![SlottedChild {
3186 slot: None,
3187 id: stray,
3188 }],
3189 );
3190 let id = tree.add_boxed(w);
3191 tree.layout(SizeProposal::exact(400.0, 200.0));
3192 assert!(!descendants(&tree, id).contains(&stray));
3194 }
3195
3196 #[test]
3197 fn vstack_container_a_wires_ordered_children() {
3198 let entry = find_by_id("vstack").expect("vstack registered");
3199 assert_eq!(entry.category(), WidgetCategory::ContainerA);
3200 assert!(entry.icon().is_some());
3201 let knobs = knobs_for(entry);
3202 let mut tree = WidgetTree::new();
3203 let a = tree.add(TextWidget::new(lit!("A")));
3204 let b = tree.add(TextWidget::new(lit!("B")));
3205 let c = tree.add(TextWidget::new(lit!("C")));
3206 let w = entry.build_with_children(
3207 "default",
3208 &knobs,
3209 vec![
3210 SlottedChild { slot: None, id: a },
3211 SlottedChild { slot: None, id: b },
3212 SlottedChild { slot: None, id: c },
3213 ],
3214 );
3215 let id = tree.add_boxed(w);
3216 tree.layout(SizeProposal::exact(400.0, 300.0));
3217 assert_eq!(tree.children(id), vec![a, b, c]);
3218 }
3219
3220 #[test]
3221 fn card_container_b_routes_named_slots() {
3222 let entry = find_by_id("card").expect("card registered");
3223 assert_eq!(entry.category(), WidgetCategory::ContainerB);
3224 assert_eq!(entry.slots(), &["header", "content", "footer"][..]);
3225 let knobs = knobs_for(entry);
3226 let mut tree = WidgetTree::new();
3227 let header = tree.add(TextWidget::new(lit!("H")));
3228 let content = tree.add(TextWidget::new(lit!("C")));
3229 let footer = tree.add(TextWidget::new(lit!("F")));
3230 let w = entry.build_with_children(
3231 "default",
3232 &knobs,
3233 vec![
3234 SlottedChild {
3235 slot: Some("header".into()),
3236 id: header,
3237 },
3238 SlottedChild {
3239 slot: Some("content".into()),
3240 id: content,
3241 },
3242 SlottedChild {
3243 slot: Some("footer".into()),
3244 id: footer,
3245 },
3246 ],
3247 );
3248 let id = tree.add_boxed(w);
3249 tree.layout(SizeProposal::exact(400.0, 300.0));
3250 let all = descendants(&tree, id);
3251 assert!(all.contains(&header), "header slot wired");
3252 assert!(all.contains(&content), "content slot wired");
3253 assert!(all.contains(&footer), "footer slot wired");
3254 }
3255
3256 #[test]
3257 fn curated_widgets_have_icons() {
3258 for id in [
3259 "vstack",
3260 "hstack",
3261 "zstack",
3262 "grid",
3263 "padding",
3264 "expand",
3265 "center",
3266 "spacer",
3267 "button",
3268 "text_widget",
3269 "checkbox",
3270 "text_input",
3271 "toggle",
3272 "combo_box",
3273 "slider",
3274 "card",
3275 "panel",
3276 "scroll_area",
3277 ] {
3278 let entry = find_by_id(id).unwrap_or_else(|| panic!("{id} registered"));
3279 assert!(entry.icon().is_some(), "{id} should have an icon");
3280 }
3281 }
3282}