diff --git a/Components/Daisy/DataDisplay/DCarousel.razor b/Components/Daisy/DataDisplay/DCarousel.razor index fa38295..e6ba869 100644 --- a/Components/Daisy/DataDisplay/DCarousel.razor +++ b/Components/Daisy/DataDisplay/DCarousel.razor @@ -1,15 +1,45 @@ @inherits DComponentBase +@inject NavigationManager NavigationManager @* daisyUI Carousel: https://daisyui.com/components/carousel/ . A scroll-snap strip of DCarouselItem children; the - user scrolls or swipes it. Give each item w-full for one slide at a time. *@ -
- @ChildContent -
+ user scrolls or swipes it. Give each item w-full for one slide at a time. ShowNavigation (previous/next on every + slide) and ShowIndicators (numbered buttons under the strip) are links to the slides' ids, as in daisyUI's examples: + they keep the current path, so Blazor treats them as same-page fragments and scrolls the slide into view. *@ + + @if (ShowIndicators) + { +
+ @Strip +
+ @for (var i = 0; i < items.Count; i++) + { + @(i + 1) + } +
+
+ } + else + { + @Strip + } +
@code { [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] public DCarouselSnap Snap { get; set; } [Parameter] public DDirection Direction { get; set; } + /// Previous/next buttons on every slide. + [Parameter] public bool ShowNavigation { get; set; } + /// A row of numbered buttons under the strip, one per slide. + [Parameter] public bool ShowIndicators { get; set; } + [Parameter] public string PreviousText { get; set; } = "Previous slide"; + [Parameter] public string NextText { get; set; } = "Next slide"; + [Parameter] public string SlideText { get; set; } = "Slide"; + + readonly List items = new(); + readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8]; + + RenderFragment Strip => @
@ChildContent
; string CssClass => Cx("carousel", SnapClass, Direction == DDirection.Vertical ? "carousel-vertical" : null, Class); @@ -19,4 +49,32 @@ DCarouselSnap.End => "carousel-end", _ => null }; + + internal int Count => items.Count; + + internal int IndexOf(DCarouselItem item) => items.IndexOf(item); + + internal string SlideId(int index) => $"{uid}-{index + 1}"; + + /// The current page plus the slide's fragment; the index wraps around. + internal string Link(int index) + { + var path = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + var hash = path.IndexOf('#'); + if (hash >= 0) + path = path[..hash]; + return $"{path}#{SlideId((index % items.Count + items.Count) % items.Count)}"; + } + + internal void Add(DCarouselItem item) + { + items.Add(item); + StateHasChanged(); + } + + internal void Remove(DCarouselItem item) + { + items.Remove(item); + StateHasChanged(); + } } diff --git a/Components/Daisy/DataDisplay/DCarouselItem.razor b/Components/Daisy/DataDisplay/DCarouselItem.razor index 7a8c9bc..159d9f7 100644 --- a/Components/Daisy/DataDisplay/DCarouselItem.razor +++ b/Components/Daisy/DataDisplay/DCarouselItem.razor @@ -1,10 +1,28 @@ @inherits DComponentBase +@implements IDisposable -@* One slide of a DCarousel. *@ -
+@* One slide of a DCarousel. It registers with the carousel for its id and, with ShowNavigation, draws the + previous/next links over itself. *@ +
@ChildContent + @if (HasNavigation) + { +
+ ❮ + ❯ +
+ }
@code { + [CascadingParameter] DCarousel Parent { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } + + int Index => Parent?.IndexOf(this) ?? -1; + string Id => Parent is null ? null : Parent.SlideId(Index); + bool HasNavigation => Parent is { ShowNavigation: true, Count: > 1 }; + + protected override void OnInitialized() => Parent?.Add(this); + + public void Dispose() => Parent?.Remove(this); } diff --git a/Components/Daisy/DataInput/DCheckbox.razor b/Components/Daisy/DataInput/DCheckbox.razor index 01f8799..43fcbfa 100644 --- a/Components/Daisy/DataInput/DCheckbox.razor +++ b/Components/Daisy/DataInput/DCheckbox.razor @@ -1,7 +1,8 @@ @inherits DInputBase @* daisyUI Checkbox: https://daisyui.com/components/checkbox/ . With LabelText the checkbox sits in a
+ @@ -333,6 +334,8 @@ @code { + bool mixedCheck; + bool mixed = true; class DemoModel { [Required] public string Name { get; set; } diff --git a/Styles/neo.css b/Styles/neo.css index 56d1f3c..7e06195 100644 --- a/Styles/neo.css +++ b/Styles/neo.css @@ -191,7 +191,7 @@ /* Selectors: an inset well while unchecked; once checked they keep daisyUI's filled look. */ @utility checkbox { - &:not(:where(:checked, :indeterminate, [aria-checked="true"], :disabled)) { + &:not(:where(:checked, :indeterminate, [aria-checked="true"], [aria-checked="mixed"], :disabled)) { border-color: transparent; background-image: linear-gradient(145deg, var(--neo-grad-dark), var(--neo-grad-light)); box-shadow: inset 2px 2px 4px var(--neo-dark), inset -2px -2px 4px var(--neo-light);