#![allow(clippy::missing_docs_in_private_items)]
use std::marker::PhantomData;
use leptos::html::Div;
use leptos::prelude::*;
use ui_components::widgets::details::Details;
use utils::prelude::ThreadSafe;
use utils_leptos::signal::URwSignal;
use super::canvas::Canvas;
use super::component_panel::ComponentPanel;
use super::control_pane::ControlPane;
use super::description::Description;
use super::tab_panel::TabName;
use crate::IntoStory;
use crate::Story;
use crate::views::canvas::EmbeddedCanvas;
use crate::views::control_pane::EmbeddedControlPane;
use crate::views::tab_panel::Tab;
use crate::views::tab_panel::TabPanel;
use crate::views::widgets::test_viewer::TestView;
#[component]
pub fn Story<S: 'static + IntoStory + Default + Copy + ThreadSafe>(
#[prop(optional)]
_story: PhantomData<S>,
) -> impl IntoView {
let story = S::default().into_story();
let canvas = NodeRef::new();
let tabs: Vec<Box<dyn Tab<SidePanelTabs> + 'static>> = vec![
Box::new(DescriptionTab {
text: story.description(),
}),
Box::new(TestsTabs { story, canvas }),
];
let selector = URwSignal::new(SidePanelTabs::Description);
view! {
<>
<ComponentPanel>
<Canvas story=story node_ref=canvas />
<TabPanel
id="side-panel"
tabs
selector
/>
</ComponentPanel>
<div class="flex flex-col basis-1/3 first:basis-1/1 px-4 py-4 overflow-auto print:hidden print:basis-0 min-w-xs w-xs shrink-0 @md:shrink-1">
<ControlPane story=story />
</div>
</>
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SidePanelTabs {
Description,
Tests,
}
impl TabName for SidePanelTabs {
fn name(&self) -> &'static str {
use SidePanelTabs::*;
match self {
Description => "Description",
Tests => "Tests",
}
}
fn html_id(&self) -> &'static str {
use SidePanelTabs::*;
match self {
Description => "description-tab",
Tests => "tests-tab",
}
}
fn try_from_name(name: &str) -> Option<Self> {
use SidePanelTabs::*;
match name {
"Description" => Some(Description),
"Tests" => Some(Tests),
_ => None,
}
}
}
struct DescriptionTab {
text: &'static str,
}
impl Tab<SidePanelTabs> for DescriptionTab {
fn id(&self) -> SidePanelTabs {
SidePanelTabs::Description
}
fn view(&self) -> AnyView {
(view! {
<Description text=self.text />
})
.into_any()
}
}
struct TestsTabs<StoryImpl: Story> {
story: StoryImpl,
canvas: NodeRef<Div>,
}
impl<StoryImpl> Tab<SidePanelTabs> for TestsTabs<StoryImpl>
where
StoryImpl: Story + ThreadSafe,
{
fn id(&self) -> SidePanelTabs {
SidePanelTabs::Tests
}
fn view(&self) -> AnyView {
let plays = self.story.plays();
let canvas = self.canvas;
let story = self.story;
if !plays.is_empty() {
let tests = plays
.iter()
.enumerate()
.map(|(idx, _play)| TestView::new(story, idx, canvas))
.collect::<Vec<_>>();
let views = tests
.iter()
.map(|test| {
view! {
<Details details={test} />
}
})
.collect_view();
(view! {
<div class="flex-row basis-1/3 first:basis-1/1 px-4 py-4 overflow-auto">
{views}
</div>
})
.into_any()
} else {
(view! {
<div class="flex-row basis-1/3 first:basis-1/1 px-4 py-4 overflow-auto">
<div class="leptos-forge-message-box">No tests were defined!</div>
</div>
})
.into_any()
}
}
}
#[component]
pub fn EmbeddedStory<S: 'static + IntoStory + Default + Copy + ThreadSafe>(
view: bool,
controls: bool,
#[prop(default = false)]
#[allow(unused_variables)]
description: bool,
#[prop(default = false)]
_tests: bool,
#[prop(optional)]
_story: PhantomData<S>,
) -> impl IntoView {
let canvas_ref = NodeRef::new();
let story = S::default().into_story();
let canvas = if view {
Some(view! {
<EmbeddedCanvas story node_ref=canvas_ref />
})
} else {
None
};
let control_pane = if controls {
Some(view! {
<EmbeddedControlPane story />
})
} else {
None
};
view! {
<div class="flex flex-col">
{canvas}
{control_pane}
</div>
}
}