Add D-prefixed Blazor wrappers for every daisyUI 5 component
Components/Daisy/ wraps all 69 components in daisyUI's llms.txt, grouped by daisyUI's categories (Actions, DataDisplay, Navigation, Feedback, DataInput, Layout, Mockup), with part components where daisyUI has repeating parts (DTab, DMenuItem, DTimelineItem, ...). - Each enum maps to whole literal class names in a switch, so Tailwind's scanner generates them. Nothing is concatenated. - Value-holding fields derive from DInputBase<TValue>, Blazor's InputBase, so @bind-Value and EditForm validation work. Invalid fields get the component's *-error class. - Open and active state lives in C# (@bind-IsOpen, @bind-Active) and is rendered with daisyUI's *-open modifiers. DDropdown adds an outside-click layer and Escape. No component uses JS interop. - The one exception is DCalendar, which wraps the Cally web component, vendored as wwwroot/vendor/cally.js (0.9.2, MIT). /styleguide (not linked) renders every component in all its colours, sizes and states, with a theme and hue picker that previews without saving. neo.css follows what the styleguide showed: - select keeps daisyUI's arrow; - skeleton-text isn't sunk; - the phone mockup and dock are raised; - active menu items and OTP digits are inset; - a joined accordion is raised as one block. The primary, secondary and accent colours are deeper, so they still read as text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
8e03d12a3e
commit
90e4b9200c
127 files changed
+6961
-16
No files matched your search
@@ -0,0 +1,74 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Button: https://daisyui.com/components/button/ . Renders an <a> when Href is set. *@
|
||||
@if (Href is null)
|
||||
{
|
||||
<button @attributes="AdditionalAttributes" type="@Type" class="@CssClass" disabled="@Disabled" @onclick="OnClick">
|
||||
@ChildContent
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a @attributes="AdditionalAttributes" href="@(Disabled ? null : Href)" class="@CssClass" role="button"
|
||||
aria-disabled="@(Disabled ? "true" : null)" tabindex="@(Disabled ? "-1" : null)" @onclick="OnClick">
|
||||
@ChildContent
|
||||
</a>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DButtonVariant Variant { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public DButtonShape Shape { get; set; }
|
||||
/// <summary>Pressed look (btn-active); the neo theme renders it inset.</summary>
|
||||
[Parameter] public bool Active { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
[Parameter] public string Type { get; set; } = "button";
|
||||
[Parameter] public string Href { get; set; }
|
||||
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
||||
|
||||
string CssClass => Cx("btn", ColorClass, VariantClass, SizeClass, ShapeClass,
|
||||
Active ? "btn-active" : null, Disabled && Href is not null ? "btn-disabled" : null, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "btn-neutral",
|
||||
DColor.Primary => "btn-primary",
|
||||
DColor.Secondary => "btn-secondary",
|
||||
DColor.Accent => "btn-accent",
|
||||
DColor.Info => "btn-info",
|
||||
DColor.Success => "btn-success",
|
||||
DColor.Warning => "btn-warning",
|
||||
DColor.Error => "btn-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string VariantClass => Variant switch
|
||||
{
|
||||
DButtonVariant.Outline => "btn-outline",
|
||||
DButtonVariant.Dash => "btn-dash",
|
||||
DButtonVariant.Soft => "btn-soft",
|
||||
DButtonVariant.Ghost => "btn-ghost",
|
||||
DButtonVariant.Link => "btn-link",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "btn-xs",
|
||||
DSize.Sm => "btn-sm",
|
||||
DSize.Lg => "btn-lg",
|
||||
DSize.Xl => "btn-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string ShapeClass => Shape switch
|
||||
{
|
||||
DButtonShape.Wide => "btn-wide",
|
||||
DButtonShape.Block => "btn-block",
|
||||
DButtonShape.Square => "btn-square",
|
||||
DButtonShape.Circle => "btn-circle",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace decePubClient.Components.Daisy.Actions;
|
||||
|
||||
public enum DButtonVariant
|
||||
{
|
||||
Default,
|
||||
Outline,
|
||||
Dash,
|
||||
Soft,
|
||||
Ghost,
|
||||
Link
|
||||
}
|
||||
|
||||
public enum DButtonShape
|
||||
{
|
||||
Default,
|
||||
Wide,
|
||||
Block,
|
||||
Square,
|
||||
Circle
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Dropdown: https://daisyui.com/components/dropdown/ . The open state lives in C# (@bind-IsOpen):
|
||||
dropdown-open shows the content and dropdown-close keeps the focus-within rule from showing it while closed.
|
||||
While open, a transparent full-screen layer under the content closes it on an outside click; Escape closes it
|
||||
too. With Hover the CSS hover rule opens it instead. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass" @onkeydown="OnKeyDown">
|
||||
<div role="button" tabindex="0" class="@TriggerClass" aria-haspopup="true" aria-expanded="@(IsOpen ? "true" : "false")"
|
||||
@onclick="Toggle">
|
||||
@Trigger
|
||||
</div>
|
||||
@if (IsOpen)
|
||||
{
|
||||
<div class="fixed inset-0 z-[998] cursor-default" aria-hidden="true" @onclick="Close"></div>
|
||||
}
|
||||
<div tabindex="-1" class="@Cx("dropdown-content z-[999]", ContentClass)" @onclick="OnContentClick">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Trigger { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public bool IsOpen { get; set; }
|
||||
[Parameter] public EventCallback<bool> IsOpenChanged { get; set; }
|
||||
[Parameter] public DDropdownPlacement Placement { get; set; }
|
||||
[Parameter] public DDropdownAlign Align { get; set; }
|
||||
/// <summary>Open on hover (dropdown-hover) as well as on click.</summary>
|
||||
[Parameter] public bool Hover { get; set; }
|
||||
/// <summary>Close after any click inside the content, e.g. on a menu item.</summary>
|
||||
[Parameter] public bool CloseOnContentClick { get; set; }
|
||||
[Parameter] public string TriggerClass { get; set; }
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
|
||||
string CssClass => Cx("dropdown", PlacementClass, AlignClass,
|
||||
IsOpen ? "dropdown-open" : Hover ? "dropdown-hover" : "dropdown-close", Class);
|
||||
|
||||
string PlacementClass => Placement switch
|
||||
{
|
||||
DDropdownPlacement.Top => "dropdown-top",
|
||||
DDropdownPlacement.Left => "dropdown-left",
|
||||
DDropdownPlacement.Right => "dropdown-right",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string AlignClass => Align switch
|
||||
{
|
||||
DDropdownAlign.Center => "dropdown-center",
|
||||
DDropdownAlign.End => "dropdown-end",
|
||||
_ => null
|
||||
};
|
||||
|
||||
Task Toggle() => SetOpen(!IsOpen);
|
||||
|
||||
Task Close() => SetOpen(false);
|
||||
|
||||
Task OnContentClick() => CloseOnContentClick ? SetOpen(false) : Task.CompletedTask;
|
||||
|
||||
Task OnKeyDown(KeyboardEventArgs e) => e.Key == "Escape" && IsOpen ? SetOpen(false) : Task.CompletedTask;
|
||||
|
||||
async Task SetOpen(bool isOpen)
|
||||
{
|
||||
IsOpen = isOpen;
|
||||
await IsOpenChanged.InvokeAsync(isOpen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace decePubClient.Components.Daisy.Actions;
|
||||
|
||||
/// <summary>Which side of the trigger the content opens on; Bottom is daisyUI's default.</summary>
|
||||
public enum DDropdownPlacement
|
||||
{
|
||||
Bottom,
|
||||
Top,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
/// <summary>How the content lines up with the trigger along that side; Start is daisyUI's default.</summary>
|
||||
public enum DDropdownAlign
|
||||
{
|
||||
Start,
|
||||
Center,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI FAB: https://daisyui.com/components/fab/ . Opening is daisyUI's own :focus-within rule (there is no
|
||||
open-state class): the trigger is the focusable first child, and focus moving out of the FAB closes it. The order
|
||||
of the children matters to daisyUI: trigger, then Close or MainAction, then the speed-dial items (ChildContent,
|
||||
e.g. DButton Size="DSize.Lg" Shape="DButtonShape.Circle", optionally inside a <div> with a text label). *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<div tabindex="0" role="button" aria-label="@LabelText" class="@Cx("btn btn-lg btn-circle", ColorClass, TriggerClass)">
|
||||
@Trigger
|
||||
</div>
|
||||
@if (Close is not null)
|
||||
{
|
||||
<div class="fab-close">@Close</div>
|
||||
}
|
||||
@if (MainAction is not null)
|
||||
{
|
||||
<div class="fab-main-action">@MainAction</div>
|
||||
}
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
/// <summary>Content of the button shown while closed, usually an icon.</summary>
|
||||
[Parameter] public RenderFragment Trigger { get; set; }
|
||||
/// <summary>Accessible name of the trigger (aria-label), since it usually holds only an icon.</summary>
|
||||
[Parameter] public string LabelText { get; set; }
|
||||
[Parameter] public DColor Color { get; set; } = DColor.Primary;
|
||||
[Parameter] public string TriggerClass { get; set; }
|
||||
/// <summary>Replaces the trigger while open; clicking it closes the FAB. Not focusable, e.g. a text label and a
|
||||
/// <span class="btn btn-circle btn-lg">.</summary>
|
||||
[Parameter] public RenderFragment Close { get; set; }
|
||||
/// <summary>Replaces the trigger while open with a main action, e.g. a label and a button.</summary>
|
||||
[Parameter] public RenderFragment MainAction { get; set; }
|
||||
/// <summary>The speed-dial items.</summary>
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Arrange the items in a quarter circle (fab-flower) instead of a column.</summary>
|
||||
[Parameter] public bool Flower { get; set; }
|
||||
|
||||
string CssClass => Cx("fab", Flower ? "fab-flower" : null, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "btn-neutral",
|
||||
DColor.Primary => "btn-primary",
|
||||
DColor.Secondary => "btn-secondary",
|
||||
DColor.Accent => "btn-accent",
|
||||
DColor.Info => "btn-info",
|
||||
DColor.Success => "btn-success",
|
||||
DColor.Warning => "btn-warning",
|
||||
DColor.Error => "btn-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Modal: https://daisyui.com/components/modal/ . Always rendered; the open state lives in C# (@bind-IsOpen)
|
||||
and shows as modal-open. On opening, the box takes the focus (Blazor's FocusAsync) so that Escape, handled on the
|
||||
root, closes it; the backdrop button stretches over the area outside the box and closes it on click. *@
|
||||
<div @attributes="AdditionalAttributes" role="dialog" aria-modal="true" aria-labelledby="@(Heading is null ? null : uid)"
|
||||
class="@CssClass" @onkeydown="OnKeyDown">
|
||||
<div @ref="box" tabindex="-1" class="@Cx("modal-box focus:outline-none", BoxClass)">
|
||||
@if (Heading is not null)
|
||||
{
|
||||
<h3 id="@uid" class="text-lg font-bold">@Heading</h3>
|
||||
}
|
||||
@ChildContent
|
||||
@if (Actions is not null)
|
||||
{
|
||||
<div class="modal-action">
|
||||
@Actions
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (CloseOnBackdrop)
|
||||
{
|
||||
<div class="modal-backdrop">
|
||||
<button type="button" @onclick="Close">@CloseText</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Heading { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Actions { get; set; }
|
||||
[Parameter] public bool IsOpen { get; set; }
|
||||
[Parameter] public EventCallback<bool> IsOpenChanged { get; set; }
|
||||
[Parameter] public DModalPlacement Placement { get; set; }
|
||||
/// <summary>Close on a click outside the box (renders the modal-backdrop).</summary>
|
||||
[Parameter] public bool CloseOnBackdrop { get; set; } = true;
|
||||
/// <summary>Text of the invisible backdrop button, read by screen readers.</summary>
|
||||
[Parameter] public string CloseText { get; set; } = "close";
|
||||
[Parameter] public string BoxClass { get; set; }
|
||||
|
||||
readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8];
|
||||
ElementReference box;
|
||||
bool wasOpen;
|
||||
bool focusPending;
|
||||
|
||||
string CssClass => Cx("modal", PlacementClass, IsOpen ? "modal-open" : null, Class);
|
||||
|
||||
string PlacementClass => Placement switch
|
||||
{
|
||||
DModalPlacement.Top => "modal-top",
|
||||
DModalPlacement.Bottom => "modal-bottom",
|
||||
DModalPlacement.Start => "modal-start",
|
||||
DModalPlacement.End => "modal-end",
|
||||
_ => null
|
||||
};
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (IsOpen && !wasOpen)
|
||||
focusPending = true;
|
||||
wasOpen = IsOpen;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!focusPending)
|
||||
return;
|
||||
focusPending = false;
|
||||
await box.FocusAsync();
|
||||
}
|
||||
|
||||
Task Close() => SetOpen(false);
|
||||
|
||||
Task OnKeyDown(KeyboardEventArgs e) => e.Key == "Escape" && IsOpen ? SetOpen(false) : Task.CompletedTask;
|
||||
|
||||
async Task SetOpen(bool isOpen)
|
||||
{
|
||||
IsOpen = isOpen;
|
||||
wasOpen = isOpen;
|
||||
await IsOpenChanged.InvokeAsync(isOpen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace decePubClient.Components.Daisy.Actions;
|
||||
|
||||
/// <summary>Where the box sits on the screen; Middle is daisyUI's default.</summary>
|
||||
public enum DModalPlacement
|
||||
{
|
||||
Middle,
|
||||
Top,
|
||||
Bottom,
|
||||
Start,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Swap: https://daisyui.com/components/swap/ . A label around daisyUI's hidden checkbox: clicking anywhere on
|
||||
it toggles the checkbox, whose state is bound to Active (@bind-Active); On shows while checked, Off otherwise. *@
|
||||
<label @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<input type="checkbox" checked="@Active" disabled="@Disabled" aria-label="@LabelText" @onchange="OnChange" />
|
||||
<div class="swap-on">@On</div>
|
||||
<div class="swap-off">@Off</div>
|
||||
</label>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment On { get; set; }
|
||||
[Parameter] public RenderFragment Off { get; set; }
|
||||
[Parameter] public bool Active { get; set; }
|
||||
[Parameter] public EventCallback<bool> ActiveChanged { get; set; }
|
||||
[Parameter] public DSwapEffect Effect { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
/// <summary>Accessible name of the checkbox (aria-label), for swaps that show only icons.</summary>
|
||||
[Parameter] public string LabelText { get; set; }
|
||||
|
||||
string CssClass => Cx("swap", EffectClass, Disabled ? "cursor-not-allowed opacity-50" : null, Class);
|
||||
|
||||
string EffectClass => Effect switch
|
||||
{
|
||||
DSwapEffect.Rotate => "swap-rotate",
|
||||
DSwapEffect.Flip => "swap-flip",
|
||||
_ => null
|
||||
};
|
||||
|
||||
async Task OnChange(ChangeEventArgs e)
|
||||
{
|
||||
Active = e.Value is true;
|
||||
await ActiveChanged.InvokeAsync(Active);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace decePubClient.Components.Daisy.Actions;
|
||||
|
||||
public enum DSwapEffect
|
||||
{
|
||||
None,
|
||||
Rotate,
|
||||
Flip
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Theme controller: https://daisyui.com/components/theme-controller/ . While checked, the page uses the theme
|
||||
named in Value (daisyUI's :root:has(.theme-controller:checked) rule). The app itself sets data-theme from C#, so
|
||||
this exists for completeness. Style it with class="toggle", "checkbox", "radio", "btn" and so on. For a radio
|
||||
group, pass the same name attribute to each and set Checked from one shared value: a radio raises no change event
|
||||
when another one unchecks it. *@
|
||||
<input @attributes="AdditionalAttributes" type="@(Radio ? "radio" : "checkbox")" value="@Value" class="@CssClass"
|
||||
checked="@Checked" @onchange="OnChange" />
|
||||
|
||||
@code {
|
||||
/// <summary>A daisyUI theme name, e.g. "neo-dark".</summary>
|
||||
[Parameter] public string Value { get; set; }
|
||||
[Parameter] public bool Checked { get; set; }
|
||||
[Parameter] public EventCallback<bool> CheckedChanged { get; set; }
|
||||
/// <summary>Render a radio instead of a checkbox.</summary>
|
||||
[Parameter] public bool Radio { get; set; }
|
||||
|
||||
string CssClass => Cx("theme-controller", Class);
|
||||
|
||||
async Task OnChange(ChangeEventArgs e)
|
||||
{
|
||||
// A checkbox reports its checked state; a radio only reports its value when it becomes checked.
|
||||
Checked = Radio || e.Value is true;
|
||||
await CheckedChanged.InvokeAsync(Checked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace decePubClient.Components.Daisy;
|
||||
|
||||
/// <summary>
|
||||
/// Base of the daisyUI wrappers. A consumer's <c>class</c> lands in <see cref="Class"/> and is appended after the
|
||||
/// component's own classes; every other unmatched attribute is passed through to the root element.
|
||||
/// </summary>
|
||||
public abstract class DComponentBase : ComponentBase
|
||||
{
|
||||
[Parameter] public string Class { get; set; }
|
||||
|
||||
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }
|
||||
|
||||
protected static string Cx(params string[] classes) => DCx.Join(classes);
|
||||
}
|
||||
|
||||
public static class DCx
|
||||
{
|
||||
/// <summary>
|
||||
/// Joins the non-empty class names. Every daisyUI class handed to it must be written out whole somewhere in the
|
||||
/// source, e.g. a switch arm <c>DColor.Primary => "btn-primary"</c>, never assembled like <c>"btn-" + color</c>:
|
||||
/// Tailwind generates only the classes it finds in .razor and .cs files.
|
||||
/// </summary>
|
||||
public static string Join(params string[] classes) =>
|
||||
string.Join(' ', classes.Where(c => !string.IsNullOrWhiteSpace(c)));
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Globalization;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace decePubClient.Components.Daisy;
|
||||
|
||||
/// <summary>
|
||||
/// Base of the daisyUI form fields. It is Blazor's <see cref="InputBase{TValue}"/>, so <c>@bind-Value</c> and
|
||||
/// EditForm validation work as with InputText; it also works outside an EditForm, as long as the value is bound.
|
||||
/// A consumer's <c>class</c> stays in AdditionalAttributes, and <see cref="InputBase{TValue}.CssClass"/> adds the
|
||||
/// EditContext's valid/invalid/modified markers to it.
|
||||
/// </summary>
|
||||
public abstract class DInputBase<TValue> : InputBase<TValue>
|
||||
{
|
||||
/// <summary>Whether the bound field currently has validation messages.</summary>
|
||||
protected bool IsInvalid => EditContext != null && EditContext.GetValidationMessages(FieldIdentifier).Any();
|
||||
|
||||
/// <summary>The attributes for the inner input element, without class (that goes on the styled element).</summary>
|
||||
protected IReadOnlyDictionary<string, object> InputAttributes =>
|
||||
AdditionalAttributes?.Where(a => a.Key != "class").ToDictionary(a => a.Key, a => a.Value);
|
||||
|
||||
protected static string Cx(params string[] classes) => DCx.Join(classes);
|
||||
|
||||
protected override string FormatValueAsString(TValue value) =>
|
||||
BindConverter.FormatValue(value, CultureInfo.InvariantCulture)?.ToString();
|
||||
|
||||
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
|
||||
{
|
||||
if (BindConverter.TryConvertTo(value, CultureInfo.InvariantCulture, out result))
|
||||
{
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
validationErrorMessage = $"The {DisplayName ?? FieldIdentifier.FieldName} field is not valid.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace decePubClient.Components.Daisy;
|
||||
|
||||
// Modifiers shared by many components. Each component maps them to its own class names in a switch whose arms are
|
||||
// whole literals (DColor.Primary => "btn-primary"), so Tailwind finds them. The first member is the default and
|
||||
// maps to no class.
|
||||
|
||||
public enum DColor
|
||||
{
|
||||
None,
|
||||
Neutral,
|
||||
Primary,
|
||||
Secondary,
|
||||
Accent,
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>daisyUI's sizes; <see cref="Md"/> is the default and renders no class.</summary>
|
||||
public enum DSize
|
||||
{
|
||||
Md,
|
||||
Xs,
|
||||
Sm,
|
||||
Lg,
|
||||
Xl
|
||||
}
|
||||
|
||||
public enum DDirection
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Accordion: https://daisyui.com/components/accordion/ . Instead of daisyUI's radio group, the open item is an
|
||||
index in C# (@bind-Active, -1 = none). Each DAccordionItem registers itself through the cascading value and renders a
|
||||
DCollapse that is open only while its index is Active. The cascading value isn't fixed, so every item re-renders
|
||||
whenever the accordion does. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<CascadingValue Value="this">
|
||||
@ChildContent
|
||||
</CascadingValue>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Index of the open item, in registration order; -1 when all are closed.</summary>
|
||||
[Parameter] public int Active { get; set; } = -1;
|
||||
[Parameter] public EventCallback<int> ActiveChanged { get; set; }
|
||||
[Parameter] public DCollapseIcon Icon { get; set; }
|
||||
/// <summary>Join the items into one block (join join-vertical, join-item).</summary>
|
||||
[Parameter] public bool Joined { get; set; }
|
||||
|
||||
readonly List<DAccordionItem> items = new();
|
||||
|
||||
string CssClass => Cx(Joined ? "join join-vertical rounded-box neo-raised" : "flex flex-col gap-2", Class);
|
||||
|
||||
internal int IndexOf(DAccordionItem item) => items.IndexOf(item);
|
||||
|
||||
internal void Add(DAccordionItem item)
|
||||
{
|
||||
items.Add(item);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
internal void Remove(DAccordionItem item)
|
||||
{
|
||||
items.Remove(item);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
internal async Task Select(int index)
|
||||
{
|
||||
Active = index;
|
||||
await ActiveChanged.InvokeAsync(index);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
@inherits DComponentBase
|
||||
@implements IDisposable
|
||||
|
||||
@* One item of a DAccordion: a DCollapse (so collapse + neo-raised, keyboard and aria-expanded come from there) whose
|
||||
open state is the accordion's Active index. Opening it closes the others; clicking it while open closes it. *@
|
||||
<DCollapse @attributes="AdditionalAttributes" IsOpen="@IsOpen" IsOpenChanged="OnIsOpenChanged" Icon="@Parent.Icon"
|
||||
Disabled="@Disabled" HeaderClass="@HeaderClass" ContentClass="@ContentClass" Header="@Header"
|
||||
Class="@Cx(Parent.Joined ? "join-item shadow-none!" : null, Class)">
|
||||
@ChildContent
|
||||
</DCollapse>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] DAccordion Parent { get; set; }
|
||||
[Parameter] public RenderFragment Header { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
[Parameter] public string HeaderClass { get; set; }
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
|
||||
bool IsOpen => Parent.Active >= 0 && Parent.Active == Parent.IndexOf(this);
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (Parent is null)
|
||||
throw new InvalidOperationException($"{nameof(DAccordionItem)} must be inside a {nameof(DAccordion)}.");
|
||||
Parent.Add(this);
|
||||
}
|
||||
|
||||
Task OnIsOpenChanged(bool isOpen) => Parent.Select(isOpen ? Parent.IndexOf(this) : -1);
|
||||
|
||||
public void Dispose() => Parent?.Remove(this);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Aura: https://daisyui.com/components/aura/ . ChildContent must be exactly one element (a btn, card, …).
|
||||
The light takes the text colour (Color, or any text-* in Class); a bg-* sets its background and duration-* its
|
||||
speed. daisyUI advises at most one aura per page. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DAuraVariant Variant { get; set; }
|
||||
/// <summary>Colour of the light, as text-*; the child inherits it unless it sets its own text colour.</summary>
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
|
||||
string CssClass => Cx("aura", VariantClass, ColorClass, SizeClass, Class);
|
||||
|
||||
string VariantClass => Variant switch
|
||||
{
|
||||
DAuraVariant.Dual => "aura-dual",
|
||||
DAuraVariant.Rainbow => "aura-rainbow",
|
||||
DAuraVariant.Holo => "aura-holo",
|
||||
DAuraVariant.Gold => "aura-gold",
|
||||
DAuraVariant.Silver => "aura-silver",
|
||||
DAuraVariant.Glow => "aura-glow",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "text-neutral",
|
||||
DColor.Primary => "text-primary",
|
||||
DColor.Secondary => "text-secondary",
|
||||
DColor.Accent => "text-accent",
|
||||
DColor.Info => "text-info",
|
||||
DColor.Success => "text-success",
|
||||
DColor.Warning => "text-warning",
|
||||
DColor.Error => "text-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "aura-xs",
|
||||
DSize.Sm => "aura-sm",
|
||||
DSize.Lg => "aura-lg",
|
||||
DSize.Xl => "aura-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
public enum DAuraVariant
|
||||
{
|
||||
Default,
|
||||
Dual,
|
||||
Rainbow,
|
||||
Holo,
|
||||
Gold,
|
||||
Silver,
|
||||
Glow
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Avatar: https://daisyui.com/components/avatar/ . Class goes on the inner frame, where daisyUI puts the size
|
||||
and shape (w-16 rounded-full, mask mask-squircle) and, for a placeholder, the colours (bg-neutral
|
||||
text-neutral-content); the other attributes go on the outer .avatar. Without Src the frame shows ChildContent. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<div class="@Class">
|
||||
@if (Src is not null)
|
||||
{
|
||||
<img src="@Src" alt="@Alt" />
|
||||
}
|
||||
else
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string Src { get; set; }
|
||||
[Parameter] public string Alt { get; set; } = "";
|
||||
/// <summary>Shown in the frame when there is no Src, e.g. initials with Placeholder.</summary>
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DAvatarStatus Status { get; set; }
|
||||
/// <summary>Centre ChildContent in the frame (avatar-placeholder); give the frame a bg-* in Class.</summary>
|
||||
[Parameter] public bool Placeholder { get; set; }
|
||||
|
||||
string CssClass => Cx("avatar", StatusClass, Placeholder ? "avatar-placeholder" : null);
|
||||
|
||||
string StatusClass => Status switch
|
||||
{
|
||||
DAvatarStatus.Online => "avatar-online",
|
||||
DAvatarStatus.Offline => "avatar-offline",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
public enum DAvatarStatus
|
||||
{
|
||||
None,
|
||||
Online,
|
||||
Offline
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Avatar group: https://daisyui.com/components/avatar/ . Overlap the DAvatar children with a negative
|
||||
space-x in Class, e.g. -space-x-6. *@
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("avatar-group", Class)">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Badge: https://daisyui.com/components/badge/ . Without ChildContent it is an empty dot. *@
|
||||
<span @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</span>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DBadgeVariant Variant { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
|
||||
string CssClass => Cx("badge", ColorClass, VariantClass, SizeClass, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "badge-neutral",
|
||||
DColor.Primary => "badge-primary",
|
||||
DColor.Secondary => "badge-secondary",
|
||||
DColor.Accent => "badge-accent",
|
||||
DColor.Info => "badge-info",
|
||||
DColor.Success => "badge-success",
|
||||
DColor.Warning => "badge-warning",
|
||||
DColor.Error => "badge-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string VariantClass => Variant switch
|
||||
{
|
||||
DBadgeVariant.Outline => "badge-outline",
|
||||
DBadgeVariant.Dash => "badge-dash",
|
||||
DBadgeVariant.Soft => "badge-soft",
|
||||
DBadgeVariant.Ghost => "badge-ghost",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "badge-xs",
|
||||
DSize.Sm => "badge-sm",
|
||||
DSize.Lg => "badge-lg",
|
||||
DSize.Xl => "badge-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
public enum DBadgeVariant
|
||||
{
|
||||
Default,
|
||||
Outline,
|
||||
Dash,
|
||||
Soft,
|
||||
Ghost
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Card: https://daisyui.com/components/card/ . Figure is wrapped in <figure> before the body (after it with
|
||||
FigureLast); the body renders only when Heading, ChildContent or Actions is set. Give it a background in Class
|
||||
(bg-base-100). *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@if (Figure is not null && !FigureLast)
|
||||
{
|
||||
<figure class="@FigureClass">@Figure</figure>
|
||||
}
|
||||
@if (Heading is not null || ChildContent is not null || Actions is not null)
|
||||
{
|
||||
<div class="@Cx("card-body", BodyClass)">
|
||||
@if (Heading is not null)
|
||||
{
|
||||
<h2 class="card-title">@Heading</h2>
|
||||
}
|
||||
@ChildContent
|
||||
@if (Actions is not null)
|
||||
{
|
||||
<div class="@Cx("card-actions", ActionsClass)">@Actions</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (Figure is not null && FigureLast)
|
||||
{
|
||||
<figure class="@FigureClass">@Figure</figure>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Figure { get; set; }
|
||||
[Parameter] public RenderFragment Heading { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Actions { get; set; }
|
||||
[Parameter] public DCardVariant Variant { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
/// <summary>Figure beside the body instead of above it (card-side).</summary>
|
||||
[Parameter] public bool Side { get; set; }
|
||||
/// <summary>Figure as the background behind the body (image-full).</summary>
|
||||
[Parameter] public bool ImageFull { get; set; }
|
||||
/// <summary>Put the figure after the body: at the bottom, or on the right with Side.</summary>
|
||||
[Parameter] public bool FigureLast { get; set; }
|
||||
[Parameter] public string FigureClass { get; set; }
|
||||
[Parameter] public string BodyClass { get; set; }
|
||||
/// <summary>Extra classes on card-actions, e.g. justify-end.</summary>
|
||||
[Parameter] public string ActionsClass { get; set; }
|
||||
|
||||
string CssClass => Cx("card", VariantClass, SizeClass, Side ? "card-side" : null, ImageFull ? "image-full" : null, Class);
|
||||
|
||||
string VariantClass => Variant switch
|
||||
{
|
||||
DCardVariant.Border => "card-border",
|
||||
DCardVariant.Dash => "card-dash",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "card-xs",
|
||||
DSize.Sm => "card-sm",
|
||||
DSize.Lg => "card-lg",
|
||||
DSize.Xl => "card-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
public enum DCardVariant
|
||||
{
|
||||
Default,
|
||||
Border,
|
||||
Dash
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* 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. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DCarouselSnap Snap { get; set; }
|
||||
[Parameter] public DDirection Direction { get; set; }
|
||||
|
||||
string CssClass => Cx("carousel", SnapClass, Direction == DDirection.Vertical ? "carousel-vertical" : null, Class);
|
||||
|
||||
string SnapClass => Snap switch
|
||||
{
|
||||
DCarouselSnap.Center => "carousel-center",
|
||||
DCarouselSnap.End => "carousel-end",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
/// <summary>Where each item snaps; <see cref="Start"/> is daisyUI's default, so it renders no class.</summary>
|
||||
public enum DCarouselSnap
|
||||
{
|
||||
Start,
|
||||
Center,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One slide of a DCarousel. *@
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("carousel-item", Class)">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Chat: https://daisyui.com/components/chat/ . One line of a conversation: an optional avatar, header and
|
||||
footer around one DChatBubble (ChildContent). Avatar renders inside <div class="chat-image avatar">, so it takes
|
||||
the avatar's frame, e.g. <div class="w-10 rounded-full"><img … /></div>. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@if (Avatar is not null)
|
||||
{
|
||||
<div class="chat-image avatar">@Avatar</div>
|
||||
}
|
||||
@if (Header is not null)
|
||||
{
|
||||
<div class="@Cx("chat-header", HeaderClass)">@Header</div>
|
||||
}
|
||||
@ChildContent
|
||||
@if (Footer is not null)
|
||||
{
|
||||
<div class="@Cx("chat-footer", FooterClass)">@Footer</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Avatar { get; set; }
|
||||
[Parameter] public RenderFragment Header { get; set; }
|
||||
[Parameter] public RenderFragment Footer { get; set; }
|
||||
[Parameter] public DChatPlacement Placement { get; set; }
|
||||
[Parameter] public string HeaderClass { get; set; }
|
||||
[Parameter] public string FooterClass { get; set; } = "opacity-50";
|
||||
|
||||
string CssClass => Cx("chat", Placement == DChatPlacement.End ? "chat-end" : "chat-start", Class);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Chat bubble: https://daisyui.com/components/chat/ . The message of a DChat; one per DChat, since daisyUI
|
||||
places every bubble in the same grid cell. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
|
||||
string CssClass => Cx("chat-bubble", ColorClass, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "chat-bubble-neutral",
|
||||
DColor.Primary => "chat-bubble-primary",
|
||||
DColor.Secondary => "chat-bubble-secondary",
|
||||
DColor.Accent => "chat-bubble-accent",
|
||||
DColor.Info => "chat-bubble-info",
|
||||
DColor.Success => "chat-bubble-success",
|
||||
DColor.Warning => "chat-bubble-warning",
|
||||
DColor.Error => "chat-bubble-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
/// <summary>daisyUI requires a placement, so unlike other enums both members render a class.</summary>
|
||||
public enum DChatPlacement
|
||||
{
|
||||
Start,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Collapse: https://daisyui.com/components/collapse/ . The open state lives in C# (@bind-IsOpen) and is
|
||||
rendered with collapse-open / collapse-close; clicking the header (or Enter/Space on it) toggles it. neo-raised
|
||||
gives it the neumorphic panel look (see Styles/neo.css for why it isn't a @utility override). *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<div class="@Cx("collapse-title", Disabled ? "cursor-not-allowed" : "cursor-pointer", HeaderClass)" role="button"
|
||||
tabindex="@(Disabled ? "-1" : "0")" aria-expanded="@(IsOpen ? "true" : "false")" aria-disabled="@(Disabled ? "true" : null)"
|
||||
@onclick="Toggle" @onkeydown="OnKeyDown">
|
||||
@Header
|
||||
</div>
|
||||
<div class="@Cx("collapse-content", ContentClass)">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Header { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public bool IsOpen { get; set; }
|
||||
[Parameter] public EventCallback<bool> IsOpenChanged { get; set; }
|
||||
[Parameter] public DCollapseIcon Icon { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
[Parameter] public string HeaderClass { get; set; }
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
|
||||
string CssClass => Cx("collapse neo-raised", IconClass, IsOpen ? "collapse-open" : "collapse-close", Class);
|
||||
|
||||
string IconClass => Icon switch
|
||||
{
|
||||
DCollapseIcon.Arrow => "collapse-arrow",
|
||||
DCollapseIcon.Plus => "collapse-plus",
|
||||
_ => null
|
||||
};
|
||||
|
||||
async Task Toggle()
|
||||
{
|
||||
if (Disabled)
|
||||
return;
|
||||
IsOpen = !IsOpen;
|
||||
await IsOpenChanged.InvokeAsync(IsOpen);
|
||||
}
|
||||
|
||||
Task OnKeyDown(KeyboardEventArgs e) => e.Key is "Enter" or " " ? Toggle() : Task.CompletedTask;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace decePubClient.Components.Daisy.DataDisplay;
|
||||
|
||||
public enum DCollapseIcon
|
||||
{
|
||||
None,
|
||||
Arrow,
|
||||
Plus
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Countdown: https://daisyui.com/components/countdown/ . Shows one number (0-999) that rolls to each new
|
||||
Value; --value and --digits go in the inner span's style. For a clock, put several side by side with separators. *@
|
||||
<span @attributes="AdditionalAttributes" class="@Cx("countdown", Class)">
|
||||
<span style="@ValueStyle" aria-live="polite" aria-label="@Number">@Number</span>
|
||||
</span>
|
||||
|
||||
@code {
|
||||
/// <summary>The number to show, clamped to 0-999.</summary>
|
||||
[Parameter] public int Value { get; set; }
|
||||
/// <summary>Minimum digits shown (1-3); 2 pads 5 to 05.</summary>
|
||||
[Parameter] public int Digits { get; set; } = 1;
|
||||
|
||||
int Number => Math.Clamp(Value, 0, 999);
|
||||
|
||||
string ValueStyle => Digits > 1 ? $"--value:{Number}; --digits:{Math.Min(Digits, 3)};" : $"--value:{Number};";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Diff: https://daisyui.com/components/diff/ . Item1 and Item2 overlap, and the resizer handle reveals one over
|
||||
the other; each slot takes one element (an img, or a div that fills it). Keyboard focus on the figure or on item 1
|
||||
slides the handle to either end. It has no height of its own: AspectClass (or a height in Class) gives it one. *@
|
||||
<figure @attributes="AdditionalAttributes" class="@Cx("diff", AspectClass, Class)" tabindex="0">
|
||||
<div class="diff-item-1" role="img" aria-label="@Item1Alt" tabindex="0">@Item1</div>
|
||||
<div class="diff-item-2" role="img" aria-label="@Item2Alt">@Item2</div>
|
||||
<div class="diff-resizer"></div>
|
||||
</figure>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Item1 { get; set; }
|
||||
[Parameter] public RenderFragment Item2 { get; set; }
|
||||
/// <summary>Accessible name of item 1 (its role is img, which hides the alt of an img inside).</summary>
|
||||
[Parameter] public string Item1Alt { get; set; }
|
||||
[Parameter] public string Item2Alt { get; set; }
|
||||
/// <summary>An aspect-* class for the figure; null when Class gives it a height instead.</summary>
|
||||
[Parameter] public string AspectClass { get; set; } = "aspect-16/9";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Hover 3D: https://daisyui.com/components/hover-3d/ . ChildContent must be one non-interactive element (a
|
||||
<figure>, a card); the eight empty divs after it are the hover zones that tilt it. With Href the whole thing is a
|
||||
link, the only way to make it clickable. *@
|
||||
@if (Href is null)
|
||||
{
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("hover-3d", Class)">
|
||||
@ChildContent
|
||||
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a @attributes="AdditionalAttributes" href="@Href" class="@Cx("hover-3d", Class)">
|
||||
@ChildContent
|
||||
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
|
||||
</a>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string Href { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Hover gallery: https://daisyui.com/components/hover-gallery/ . Shows the first image; moving the pointer
|
||||
across it shows the others. Up to 10 images of the same size, from Images or as <img> children; cap its width in
|
||||
Class (max-w-60), or it fills the container. *@
|
||||
<figure @attributes="AdditionalAttributes" class="@Cx("hover-gallery", Class)">
|
||||
@if (Images is not null)
|
||||
{
|
||||
foreach (var src in Images.Take(10))
|
||||
{
|
||||
<img src="@src" alt="@Alt" />
|
||||
}
|
||||
}
|
||||
@ChildContent
|
||||
</figure>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<string> Images { get; set; }
|
||||
[Parameter] public string Alt { get; set; } = "";
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Kbd: https://daisyui.com/components/kbd/ *@
|
||||
<kbd @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</kbd>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
|
||||
string CssClass => Cx("kbd", SizeClass, Class);
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "kbd-xs",
|
||||
DSize.Sm => "kbd-sm",
|
||||
DSize.Lg => "kbd-lg",
|
||||
DSize.Xl => "kbd-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI List: https://daisyui.com/components/list/ . Rows are DListRow children; a plain <li> works as a caption. *@
|
||||
<ul @attributes="AdditionalAttributes" class="@Cx("list", Class)">
|
||||
@ChildContent
|
||||
</ul>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One row of a DList. Its second child fills the remaining width; put list-col-grow on another child to change that,
|
||||
and list-col-wrap on a child to move it to a second line. *@
|
||||
<li @attributes="AdditionalAttributes" class="@Cx("list-row", Class)">
|
||||
@ChildContent
|
||||
</li>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One stat of a DStats: ChildContent is the value (stat-value); each slot renders its part only when set. *@
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("stat", Class)">
|
||||
@if (Figure is not null)
|
||||
{
|
||||
<div class="@Cx("stat-figure", FigureClass)">@Figure</div>
|
||||
}
|
||||
@if (Heading is not null)
|
||||
{
|
||||
<div class="stat-title">@Heading</div>
|
||||
}
|
||||
@if (ChildContent is not null)
|
||||
{
|
||||
<div class="@Cx("stat-value", ValueClass)">@ChildContent</div>
|
||||
}
|
||||
@if (Desc is not null)
|
||||
{
|
||||
<div class="stat-desc">@Desc</div>
|
||||
}
|
||||
@if (Actions is not null)
|
||||
{
|
||||
<div class="stat-actions">@Actions</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Figure { get; set; }
|
||||
[Parameter] public RenderFragment Heading { get; set; }
|
||||
[Parameter] public RenderFragment Desc { get; set; }
|
||||
[Parameter] public RenderFragment Actions { get; set; }
|
||||
/// <summary>Extra classes on stat-figure, e.g. text-primary.</summary>
|
||||
[Parameter] public string FigureClass { get; set; }
|
||||
/// <summary>Extra classes on stat-value, e.g. text-primary.</summary>
|
||||
[Parameter] public string ValueClass { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Stat: https://daisyui.com/components/stat/ . The container of DStat items; horizontal unless Direction is
|
||||
Vertical (for a responsive switch add lg:stats-horizontal in Class). *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DDirection Direction { get; set; }
|
||||
|
||||
string CssClass => Cx("stats", Direction == DDirection.Vertical ? "stats-vertical" : null, Class);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Status: https://daisyui.com/components/status/ . Give it an aria-label (online, error, …). Ping stacks an
|
||||
animate-ping copy under the dot, as in daisyUI's ping example; the attributes stay on the visible dot. *@
|
||||
@if (Ping)
|
||||
{
|
||||
<span class="inline-grid *:[grid-area:1/1]">
|
||||
<span class="@Cx(CssClass, "animate-ping")" aria-hidden="true"></span>
|
||||
<span @attributes="AdditionalAttributes" class="@CssClass"></span>
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span @attributes="AdditionalAttributes" class="@CssClass"></span>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Ping { get; set; }
|
||||
|
||||
string CssClass => Cx("status", ColorClass, SizeClass, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "status-neutral",
|
||||
DColor.Primary => "status-primary",
|
||||
DColor.Secondary => "status-secondary",
|
||||
DColor.Accent => "status-accent",
|
||||
DColor.Info => "status-info",
|
||||
DColor.Success => "status-success",
|
||||
DColor.Warning => "status-warning",
|
||||
DColor.Error => "status-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "status-xs",
|
||||
DSize.Sm => "status-sm",
|
||||
DSize.Lg => "status-lg",
|
||||
DSize.Xl => "status-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Table: https://daisyui.com/components/table/ . ChildContent is the <thead>/<tbody>/<tfoot> markup. Class and
|
||||
the other attributes go on the <table>; with Scroll (the default) it sits in an overflow-x-auto div, whose classes
|
||||
are WrapperClass (pinned rows need it to have a height, e.g. h-96). *@
|
||||
@if (Scroll)
|
||||
{
|
||||
<div class="@Cx("overflow-x-auto", WrapperClass)">
|
||||
<table @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</table>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Zebra { get; set; }
|
||||
/// <summary>Sticky thead/tfoot rows while the wrapper scrolls vertically.</summary>
|
||||
[Parameter] public bool PinRows { get; set; }
|
||||
/// <summary>Sticky <th> columns while the wrapper scrolls horizontally.</summary>
|
||||
[Parameter] public bool PinCols { get; set; }
|
||||
/// <summary>Wrap the table in an overflow-x-auto div so it scrolls on narrow screens.</summary>
|
||||
[Parameter] public bool Scroll { get; set; } = true;
|
||||
[Parameter] public string WrapperClass { get; set; }
|
||||
|
||||
string CssClass => Cx("table", Zebra ? "table-zebra" : null, PinRows ? "table-pin-rows" : null,
|
||||
PinCols ? "table-pin-cols" : null, SizeClass, Class);
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "table-xs",
|
||||
DSize.Sm => "table-sm",
|
||||
DSize.Lg => "table-lg",
|
||||
DSize.Xl => "table-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Text rotate: https://daisyui.com/components/text-rotate/ . Loops through 2-6 lines, one at a time, pausing on
|
||||
hover. The lines are Items, or <span> children in ChildContent (for per-line classes). Set the loop time with a
|
||||
duration-* in Class (default 10s). *@
|
||||
<span @attributes="AdditionalAttributes" class="@Cx("text-rotate", Class)">
|
||||
<span class="@(Centered ? "justify-items-center" : null)">
|
||||
@if (Items is not null)
|
||||
{
|
||||
foreach (var item in Items.Take(6))
|
||||
{
|
||||
<span>@item</span>
|
||||
}
|
||||
}
|
||||
@ChildContent
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<string> Items { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Centre the lines horizontally.</summary>
|
||||
[Parameter] public bool Centered { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Timeline: https://daisyui.com/components/timeline/ . Horizontal unless Direction is Vertical (for a
|
||||
responsive switch add lg:timeline-horizontal in Class). Every DTimelineItem draws its connecting <hr> on both
|
||||
sides; the two arbitrary variants here hide the outer ends, so the line runs only between items. *@
|
||||
<ul @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</ul>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DDirection Direction { get; set; }
|
||||
/// <summary>Put every item on one side of the line (timeline-compact).</summary>
|
||||
[Parameter] public bool Compact { get; set; }
|
||||
/// <summary>Align the icon with the start of the content instead of its middle (timeline-snap-icon).</summary>
|
||||
[Parameter] public bool SnapIcon { get; set; }
|
||||
|
||||
string CssClass => Cx("timeline", Direction == DDirection.Vertical ? "timeline-vertical" : null,
|
||||
Compact ? "timeline-compact" : null, SnapIcon ? "timeline-snap-icon" : null,
|
||||
"[&>li:first-child>hr:first-child]:hidden [&>li:last-child>hr:last-child]:hidden", Class);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One event of a DTimeline: Start and End on either side of the line, Middle (an icon) on it. Each part renders only
|
||||
when set. The <hr> before and after are the line segments to the neighbouring items; colour them with
|
||||
LineBeforeClass / LineAfterClass (bg-primary). *@
|
||||
<li @attributes="AdditionalAttributes" class="@Class">
|
||||
<hr class="@LineBeforeClass" />
|
||||
@if (Start is not null)
|
||||
{
|
||||
<div class="@Cx("timeline-start", StartBox ? "timeline-box" : null, StartClass)">@Start</div>
|
||||
}
|
||||
@if (Middle is not null)
|
||||
{
|
||||
<div class="@Cx("timeline-middle", MiddleClass)">@Middle</div>
|
||||
}
|
||||
@if (End is not null)
|
||||
{
|
||||
<div class="@Cx("timeline-end", EndBox ? "timeline-box" : null, EndClass)">@End</div>
|
||||
}
|
||||
<hr class="@LineAfterClass" />
|
||||
</li>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Start { get; set; }
|
||||
[Parameter] public RenderFragment Middle { get; set; }
|
||||
[Parameter] public RenderFragment End { get; set; }
|
||||
/// <summary>Draw Start as a box (timeline-box).</summary>
|
||||
[Parameter] public bool StartBox { get; set; }
|
||||
/// <summary>Draw End as a box (timeline-box).</summary>
|
||||
[Parameter] public bool EndBox { get; set; }
|
||||
[Parameter] public string StartClass { get; set; }
|
||||
[Parameter] public string MiddleClass { get; set; }
|
||||
[Parameter] public string EndClass { get; set; }
|
||||
[Parameter] public string LineBeforeClass { get; set; }
|
||||
[Parameter] public string LineAfterClass { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@inherits DInputBase<DateOnly?>
|
||||
@using System.Globalization
|
||||
|
||||
@* daisyUI Calendar (Cally): https://daisyui.com/components/calendar/ . Wraps the <calendar-date> web component from
|
||||
wwwroot/vendor/cally.js; value, min and max are yyyy-MM-dd, and Blazor reads the element's value on its change
|
||||
event. Other Cally attributes (locale, first-day-of-week, months) pass through. *@
|
||||
<calendar-date @attributes="InputAttributes" class="@Cx("cally", CssClass)" value="@CurrentValueAsString" min="@Format(Min)"
|
||||
max="@Format(Max)" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)">
|
||||
<svg aria-label="@PreviousText" class="fill-current size-4" slot="previous" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.75 19.5 8.25 12l7.5-7.5"></path></svg>
|
||||
<svg aria-label="@NextText" class="fill-current size-4" slot="next" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m8.25 4.5 7.5 7.5-7.5 7.5"></path></svg>
|
||||
<calendar-month></calendar-month>
|
||||
</calendar-date>
|
||||
|
||||
@code {
|
||||
[Parameter] public DateOnly? Min { get; set; }
|
||||
[Parameter] public DateOnly? Max { get; set; }
|
||||
[Parameter] public string PreviousText { get; set; } = "Previous";
|
||||
[Parameter] public string NextText { get; set; } = "Next";
|
||||
|
||||
static string Format(DateOnly? date) => date?.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
|
||||
|
||||
protected override string FormatValueAsString(DateOnly? value) => Format(value);
|
||||
|
||||
protected override bool TryParseValueFromString(string value, out DateOnly? result, out string validationErrorMessage)
|
||||
{
|
||||
validationErrorMessage = null;
|
||||
result = null;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return true;
|
||||
if (DateOnly.TryParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
|
||||
{
|
||||
result = date;
|
||||
return true;
|
||||
}
|
||||
validationErrorMessage = $"The {DisplayName ?? FieldIdentifier.FieldName} field is not a valid date.";
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
@inherits DInputBase<bool>
|
||||
|
||||
@* daisyUI Checkbox: https://daisyui.com/components/checkbox/ . With LabelText the checkbox sits in a <label class="label">
|
||||
followed by the text, as in daisyUI's fieldset examples. *@
|
||||
@if (LabelText is null)
|
||||
{
|
||||
@Checkbox
|
||||
}
|
||||
else
|
||||
{
|
||||
<label class="label">
|
||||
@Checkbox
|
||||
@LabelText
|
||||
</label>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public string LabelText { get; set; }
|
||||
|
||||
string CssClassFull => Cx("checkbox", IsInvalid ? "checkbox-error" : ColorClass, SizeClass, CssClass);
|
||||
|
||||
RenderFragment Checkbox => @<input @attributes="InputAttributes" type="checkbox" class="@CssClassFull" name="@NameAttributeValue"
|
||||
checked="@CurrentValue" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)" />;
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "checkbox-neutral",
|
||||
DColor.Primary => "checkbox-primary",
|
||||
DColor.Secondary => "checkbox-secondary",
|
||||
DColor.Accent => "checkbox-accent",
|
||||
DColor.Info => "checkbox-info",
|
||||
DColor.Success => "checkbox-success",
|
||||
DColor.Warning => "checkbox-warning",
|
||||
DColor.Error => "checkbox-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "checkbox-xs",
|
||||
DSize.Sm => "checkbox-sm",
|
||||
DSize.Lg => "checkbox-lg",
|
||||
DSize.Xl => "checkbox-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValue = e.Value is true;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Fieldset: https://daisyui.com/components/fieldset/ . Legend becomes the fieldset-legend, Description the
|
||||
trailing p.label; disabled on the fieldset disables every field in it. *@
|
||||
<fieldset @attributes="AdditionalAttributes" class="@Cx("fieldset", Class)">
|
||||
@if (Legend is not null)
|
||||
{
|
||||
<legend class="fieldset-legend">@Legend</legend>
|
||||
}
|
||||
@ChildContent
|
||||
@if (Description is not null)
|
||||
{
|
||||
<p class="label">@Description</p>
|
||||
}
|
||||
</fieldset>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Legend { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Description { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI File input: https://daisyui.com/components/file-input/ . Blazor's InputFile with the file-input classes;
|
||||
read the files in OnChange (e.File / e.GetMultipleFiles()). *@
|
||||
<InputFile @attributes="AdditionalAttributes" class="@CssClass" OnChange="OnChange" multiple="@Multiple" accept="@Accept" />
|
||||
|
||||
@code {
|
||||
[Parameter] public EventCallback<InputFileChangeEventArgs> OnChange { get; set; }
|
||||
[Parameter] public bool Multiple { get; set; }
|
||||
/// <summary>The accept attribute: file types, e.g. "image/*,.pdf".</summary>
|
||||
[Parameter] public string Accept { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Ghost { get; set; }
|
||||
|
||||
string CssClass => Cx("file-input", ColorClass, SizeClass, Ghost ? "file-input-ghost" : null, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "file-input-neutral",
|
||||
DColor.Primary => "file-input-primary",
|
||||
DColor.Secondary => "file-input-secondary",
|
||||
DColor.Accent => "file-input-accent",
|
||||
DColor.Info => "file-input-info",
|
||||
DColor.Success => "file-input-success",
|
||||
DColor.Warning => "file-input-warning",
|
||||
DColor.Error => "file-input-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "file-input-xs",
|
||||
DSize.Sm => "file-input-sm",
|
||||
DSize.Lg => "file-input-lg",
|
||||
DSize.Xl => "file-input-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
@typeparam TValue
|
||||
@inherits DInputBase<TValue>
|
||||
|
||||
@* daisyUI Filter: https://daisyui.com/components/filter/ . The <div> form of the filter: a native form reset would
|
||||
not tell Blazor, and a <form> can't nest in an EditForm. Each option is a radio button checked while Value equals
|
||||
it; the filter-reset radio sets Value back to default, so with no default among the options nothing is selected. *@
|
||||
<div @attributes="InputAttributes" class="@Cx("filter", CssClass)">
|
||||
<input type="radio" name="@GroupName" class="@Cx("btn", "filter-reset", SizeClass)" aria-label="@ResetText"
|
||||
disabled="@Disabled" @onchange="() => CurrentValue = default" />
|
||||
@if (Options is not null)
|
||||
{
|
||||
foreach (var option in Options)
|
||||
{
|
||||
<input type="radio" name="@GroupName" class="@Cx("btn", SizeClass)" aria-label="@(TextOf?.Invoke(option) ?? option?.ToString())"
|
||||
checked="@IsSelected(option)" disabled="@Disabled"
|
||||
@onchange="() => CurrentValue = option" />
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<TValue> Options { get; set; }
|
||||
/// <summary>An option's button text; ToString() when null.</summary>
|
||||
[Parameter] public Func<TValue, string> TextOf { get; set; }
|
||||
[Parameter] public string ResetText { get; set; } = "×";
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
|
||||
readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8];
|
||||
|
||||
string GroupName => string.IsNullOrEmpty(NameAttributeValue) ? uid : NameAttributeValue;
|
||||
|
||||
bool IsSelected(TValue option) => EqualityComparer<TValue>.Default.Equals(CurrentValue, option);
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "btn-xs",
|
||||
DSize.Sm => "btn-sm",
|
||||
DSize.Lg => "btn-lg",
|
||||
DSize.Xl => "btn-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
@typeparam TValue
|
||||
@inherits DInputBase<TValue>
|
||||
|
||||
@* daisyUI Input: https://daisyui.com/components/input/ . With Prefix or Suffix (icons, labels, kbd) the input class
|
||||
moves to a wrapping <label>, as daisyUI requires when the field holds more than one element. *@
|
||||
@if (Prefix is null && Suffix is null)
|
||||
{
|
||||
<input @attributes="InputAttributes" type="@Type" class="@CssClassFull" name="@NameAttributeValue"
|
||||
value="@CurrentValueAsString" @oninput="OnInput" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<label class="@CssClassFull">
|
||||
@Prefix
|
||||
<input @attributes="InputAttributes" type="@Type" class="grow" name="@NameAttributeValue"
|
||||
value="@CurrentValueAsString" @oninput="OnInput" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)" />
|
||||
@Suffix
|
||||
</label>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public string Type { get; set; } = "text";
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Ghost { get; set; }
|
||||
/// <summary>Update the bound value on every keystroke instead of on change.</summary>
|
||||
[Parameter] public bool Immediate { get; set; }
|
||||
[Parameter] public RenderFragment Prefix { get; set; }
|
||||
[Parameter] public RenderFragment Suffix { get; set; }
|
||||
/// <summary>Adds daisyUI's validator class: error/success colors and a following DValidatorHint.</summary>
|
||||
[Parameter] public bool Validator { get; set; }
|
||||
|
||||
string CssClassFull => Cx("input", IsInvalid ? "input-error" : ColorClass, SizeClass, Ghost ? "input-ghost" : null,
|
||||
Validator ? "validator" : null, CssClass);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "input-neutral",
|
||||
DColor.Primary => "input-primary",
|
||||
DColor.Secondary => "input-secondary",
|
||||
DColor.Accent => "input-accent",
|
||||
DColor.Info => "input-info",
|
||||
DColor.Success => "input-success",
|
||||
DColor.Warning => "input-warning",
|
||||
DColor.Error => "input-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "input-xs",
|
||||
DSize.Sm => "input-sm",
|
||||
DSize.Lg => "input-lg",
|
||||
DSize.Xl => "input-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
void OnInput(ChangeEventArgs e)
|
||||
{
|
||||
if (Immediate)
|
||||
CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Label: https://daisyui.com/components/label/ . By default a span.label holding ChildContent, for a DInput or
|
||||
DSelect Prefix/Suffix or a fieldset. Floating renders label.floating-label around the field in ChildContent, with
|
||||
Text as the label that floats above it once the field has focus or a value. *@
|
||||
@if (Floating)
|
||||
{
|
||||
<label @attributes="AdditionalAttributes" class="@Cx("floating-label", Class)">
|
||||
@ChildContent
|
||||
<span>@Text</span>
|
||||
</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span @attributes="AdditionalAttributes" class="@Cx("label", Class)">@(Text)@ChildContent</span>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string Text { get; set; }
|
||||
[Parameter] public bool Floating { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
@inherits DInputBase<string>
|
||||
|
||||
@* daisyUI OTP: https://daisyui.com/components/otp/ . The otp class is on the label; Length (4 to 6) sets the number of
|
||||
digit boxes, maxlength and pattern together. required is on by default because daisyUI styles the complete code
|
||||
through :valid; required="false" removes it. *@
|
||||
<label class="@CssClassFull">
|
||||
@for (var i = 0; i < Digits; i++)
|
||||
{
|
||||
<span></span>
|
||||
}
|
||||
<input type="text" autocomplete="one-time-code" inputmode="numeric" required @attributes="InputAttributes"
|
||||
maxlength="@Digits" pattern="@($"[0-9]{{{Digits}}}")" name="@NameAttributeValue" value="@CurrentValueAsString"
|
||||
@oninput="OnInput" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)" />
|
||||
</label>
|
||||
|
||||
@code {
|
||||
[Parameter] public int Length { get; set; } = 6;
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
/// <summary>Joins the digit boxes into one field (otp-joined).</summary>
|
||||
[Parameter] public bool Joined { get; set; }
|
||||
/// <summary>Update the bound value on every keystroke instead of on change.</summary>
|
||||
[Parameter] public bool Immediate { get; set; }
|
||||
|
||||
int Digits => Math.Clamp(Length, 4, 6);
|
||||
|
||||
string CssClassFull => Cx("otp", IsInvalid ? "otp-error" : ColorClass, SizeClass, Joined ? "otp-joined" : null, CssClass);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "otp-neutral",
|
||||
DColor.Primary => "otp-primary",
|
||||
DColor.Secondary => "otp-secondary",
|
||||
DColor.Accent => "otp-accent",
|
||||
DColor.Info => "otp-info",
|
||||
DColor.Success => "otp-success",
|
||||
DColor.Warning => "otp-warning",
|
||||
DColor.Error => "otp-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "otp-xs",
|
||||
DSize.Sm => "otp-sm",
|
||||
DSize.Lg => "otp-lg",
|
||||
DSize.Xl => "otp-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
void OnInput(ChangeEventArgs e)
|
||||
{
|
||||
if (Immediate)
|
||||
CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
@typeparam TValue
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One radio of a DRadioGroup: checked while the group's value equals Value; choosing it sets the group's value. With
|
||||
LabelText it sits in a label.label followed by the text. *@
|
||||
@if (LabelText is null)
|
||||
{
|
||||
@Radio
|
||||
}
|
||||
else
|
||||
{
|
||||
<label class="label">
|
||||
@Radio
|
||||
@LabelText
|
||||
</label>
|
||||
}
|
||||
|
||||
@code {
|
||||
[CascadingParameter] DRadioGroup<TValue> Group { get; set; }
|
||||
[Parameter] public TValue Value { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
[Parameter] public string LabelText { get; set; }
|
||||
|
||||
RenderFragment Radio =>
|
||||
@<input @attributes="AdditionalAttributes" type="radio" class="@CssClass" name="@Group.GroupName"
|
||||
checked="@Group.IsSelected(Value)" disabled="@Disabled" @onchange="() => Group.Select(Value)"
|
||||
aria-invalid="@(Group.GroupInvalid ? "true" : null)" />;
|
||||
|
||||
string CssClass => Cx("radio", Group.GroupInvalid ? "radio-error" : ColorClass, SizeClass, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "radio-neutral",
|
||||
DColor.Primary => "radio-primary",
|
||||
DColor.Secondary => "radio-secondary",
|
||||
DColor.Accent => "radio-accent",
|
||||
DColor.Info => "radio-info",
|
||||
DColor.Success => "radio-success",
|
||||
DColor.Warning => "radio-warning",
|
||||
DColor.Error => "radio-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "radio-xs",
|
||||
DSize.Sm => "radio-sm",
|
||||
DSize.Lg => "radio-lg",
|
||||
DSize.Xl => "radio-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (Group is null)
|
||||
throw new InvalidOperationException("DRadio must be inside a DRadioGroup of the same TValue.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
@typeparam TValue
|
||||
@inherits DInputBase<TValue>
|
||||
@attribute [CascadingTypeParameter(nameof(TValue))]
|
||||
|
||||
@* daisyUI Radio: https://daisyui.com/components/radio/ . The group holds the bound value and cascades itself (not
|
||||
fixed, so every DRadio re-renders with it); its radios share one name, the name attribute or a per-instance id.
|
||||
It adds a div role="radiogroup" only when given attributes (a class, an aria-label). *@
|
||||
<CascadingValue Value="this">
|
||||
@if (AdditionalAttributes is null || AdditionalAttributes.Count == 0)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<div @attributes="InputAttributes" role="radiogroup" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
}
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
|
||||
readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8];
|
||||
|
||||
internal string GroupName => string.IsNullOrEmpty(NameAttributeValue) ? uid : NameAttributeValue;
|
||||
|
||||
internal bool GroupInvalid => IsInvalid;
|
||||
|
||||
internal bool IsSelected(TValue value) => EqualityComparer<TValue>.Default.Equals(CurrentValue, value);
|
||||
|
||||
internal void Select(TValue value) => CurrentValue = value;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
@typeparam TValue
|
||||
@inherits DInputBase<TValue>
|
||||
|
||||
@* daisyUI Range: https://daisyui.com/components/range/ . TValue is numeric; Min and Max are always rendered, as daisyUI
|
||||
requires. *@
|
||||
<input @attributes="InputAttributes" type="range" class="@CssClassFull" name="@NameAttributeValue" min="@Format(Min)"
|
||||
max="@Format(Max)" step="@(Step is null ? null : Format(Step.Value))" value="@CurrentValueAsString"
|
||||
@oninput="OnInput" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)" />
|
||||
|
||||
@code {
|
||||
[Parameter] public double Min { get; set; }
|
||||
[Parameter] public double Max { get; set; } = 100;
|
||||
[Parameter] public double? Step { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public DDirection Direction { get; set; }
|
||||
/// <summary>Update the bound value while dragging instead of on release.</summary>
|
||||
[Parameter] public bool Immediate { get; set; }
|
||||
|
||||
string CssClassFull => Cx("range", IsInvalid ? "range-error" : ColorClass, SizeClass,
|
||||
Direction == DDirection.Vertical ? "range-vertical" : null, CssClass);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "range-neutral",
|
||||
DColor.Primary => "range-primary",
|
||||
DColor.Secondary => "range-secondary",
|
||||
DColor.Accent => "range-accent",
|
||||
DColor.Info => "range-info",
|
||||
DColor.Success => "range-success",
|
||||
DColor.Warning => "range-warning",
|
||||
DColor.Error => "range-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "range-xs",
|
||||
DSize.Sm => "range-sm",
|
||||
DSize.Lg => "range-lg",
|
||||
DSize.Xl => "range-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
static string Format(double value) => value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
void OnInput(ChangeEventArgs e)
|
||||
{
|
||||
if (Immediate)
|
||||
CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
@inherits DInputBase<int>
|
||||
|
||||
@* daisyUI Rating: https://daisyui.com/components/rating/ . A radio per item, sharing a per-instance name; Value is the
|
||||
number of the checked item, 0 for none. With Half every item is two half radios and Value counts halves (3 = 1.5
|
||||
stars). Static renders the read-only <div> version, marking the current item with aria-current. *@
|
||||
<div @attributes="InputAttributes" class="@CssClassFull">
|
||||
@if (Static)
|
||||
{
|
||||
for (var i = 1; i <= Items; i++)
|
||||
{
|
||||
<div class="@ItemCss(i)" aria-label="@ItemText(i)" aria-current="@(i == CurrentValue ? "true" : null)"></div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Clearable)
|
||||
{
|
||||
<input type="radio" name="@GroupName" class="rating-hidden" aria-label="@ClearText" checked="@(CurrentValue == 0)"
|
||||
disabled="@Disabled" @onchange="() => CurrentValue = 0" />
|
||||
}
|
||||
for (var i = 1; i <= Items; i++)
|
||||
{
|
||||
var item = i;
|
||||
<input type="radio" name="@GroupName" class="@ItemCss(item)" aria-label="@ItemText(item)" checked="@(item == CurrentValue)"
|
||||
disabled="@Disabled" @onchange="() => CurrentValue = item" aria-invalid="@(IsInvalid ? "true" : null)" />
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public int Count { get; set; } = 5;
|
||||
[Parameter] public DRatingMask Mask { get; set; }
|
||||
[Parameter] public bool Half { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
/// <summary>Adds the hidden first radio (rating-hidden) that clears the rating to 0.</summary>
|
||||
[Parameter] public bool Clearable { get; set; }
|
||||
/// <summary>Read-only rating: <div> items instead of radios.</summary>
|
||||
[Parameter] public bool Static { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
/// <summary>Classes for every item, e.g. a color: bg-orange-400.</summary>
|
||||
[Parameter] public string ItemClass { get; set; }
|
||||
/// <summary>The unit in each item's aria-label ("2 star").</summary>
|
||||
[Parameter] public string UnitText { get; set; } = "star";
|
||||
[Parameter] public string ClearText { get; set; } = "clear";
|
||||
|
||||
readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8];
|
||||
|
||||
string GroupName => string.IsNullOrEmpty(NameAttributeValue) ? uid : NameAttributeValue;
|
||||
|
||||
int Items => Half ? Count * 2 : Count;
|
||||
|
||||
string CssClassFull => Cx("rating", Half ? "rating-half" : null, SizeClass, CssClass);
|
||||
|
||||
string ItemCss(int item) => Cx("mask", MaskClass, Half ? (item % 2 == 1 ? "mask-half-1" : "mask-half-2") : null, ItemClass);
|
||||
|
||||
string ItemText(int item) =>
|
||||
$"{(Half ? item / 2.0 : item).ToString(System.Globalization.CultureInfo.InvariantCulture)} {UnitText}";
|
||||
|
||||
string MaskClass => Mask switch
|
||||
{
|
||||
DRatingMask.Star2 => "mask-star-2",
|
||||
DRatingMask.Heart => "mask-heart",
|
||||
DRatingMask.Squircle => "mask-squircle",
|
||||
DRatingMask.Hexagon => "mask-hexagon",
|
||||
DRatingMask.Hexagon2 => "mask-hexagon-2",
|
||||
DRatingMask.Decagon => "mask-decagon",
|
||||
DRatingMask.Pentagon => "mask-pentagon",
|
||||
DRatingMask.Diamond => "mask-diamond",
|
||||
DRatingMask.Circle => "mask-circle",
|
||||
DRatingMask.Triangle => "mask-triangle",
|
||||
DRatingMask.Triangle2 => "mask-triangle-2",
|
||||
DRatingMask.Triangle3 => "mask-triangle-3",
|
||||
DRatingMask.Triangle4 => "mask-triangle-4",
|
||||
_ => "mask-star"
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "rating-xs",
|
||||
DSize.Sm => "rating-sm",
|
||||
DSize.Lg => "rating-lg",
|
||||
DSize.Xl => "rating-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace decePubClient.Components.Daisy.DataInput;
|
||||
|
||||
/// <summary>The mask shape of a DRating's items. A rating always needs one, so the default Star is mask-star.</summary>
|
||||
public enum DRatingMask
|
||||
{
|
||||
Star,
|
||||
Star2,
|
||||
Heart,
|
||||
Squircle,
|
||||
Hexagon,
|
||||
Hexagon2,
|
||||
Decagon,
|
||||
Pentagon,
|
||||
Diamond,
|
||||
Circle,
|
||||
Triangle,
|
||||
Triangle2,
|
||||
Triangle3,
|
||||
Triangle4
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@typeparam TValue
|
||||
@inherits DInputBase<TValue>
|
||||
|
||||
@* daisyUI Select: https://daisyui.com/components/select/ . The options are ChildContent (<option value="...">);
|
||||
enums bind by name. With Prefix (an icon or a span.label) the select class moves to a wrapping <label>. *@
|
||||
@if (Prefix is null)
|
||||
{
|
||||
<select @attributes="InputAttributes" class="@CssClassFull" name="@NameAttributeValue" value="@CurrentValueAsString"
|
||||
@onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)">
|
||||
@ChildContent
|
||||
</select>
|
||||
}
|
||||
else
|
||||
{
|
||||
<label class="@CssClassFull">
|
||||
@Prefix
|
||||
<select @attributes="InputAttributes" name="@NameAttributeValue" value="@CurrentValueAsString" @onchange="OnChange"
|
||||
aria-invalid="@(IsInvalid ? "true" : null)">
|
||||
@ChildContent
|
||||
</select>
|
||||
</label>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Prefix { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Ghost { get; set; }
|
||||
/// <summary>Adds daisyUI's validator class: error/success colors and a following DValidatorHint.</summary>
|
||||
[Parameter] public bool Validator { get; set; }
|
||||
|
||||
string CssClassFull => Cx("select", IsInvalid ? "select-error" : ColorClass, SizeClass, Ghost ? "select-ghost" : null,
|
||||
Validator ? "validator" : null, CssClass);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "select-neutral",
|
||||
DColor.Primary => "select-primary",
|
||||
DColor.Secondary => "select-secondary",
|
||||
DColor.Accent => "select-accent",
|
||||
DColor.Info => "select-info",
|
||||
DColor.Success => "select-success",
|
||||
DColor.Warning => "select-warning",
|
||||
DColor.Error => "select-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "select-xs",
|
||||
DSize.Sm => "select-sm",
|
||||
DSize.Lg => "select-lg",
|
||||
DSize.Xl => "select-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
@typeparam TValue
|
||||
@inherits DInputBase<TValue>
|
||||
|
||||
@* daisyUI Textarea: https://daisyui.com/components/textarea/ . Blazor sets the textarea's value property, so the bound
|
||||
value is the content; rows, placeholder and the like pass through. *@
|
||||
<textarea @attributes="InputAttributes" class="@CssClassFull" name="@NameAttributeValue" value="@CurrentValueAsString"
|
||||
@oninput="OnInput" @onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)"></textarea>
|
||||
|
||||
@code {
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public bool Ghost { get; set; }
|
||||
/// <summary>Update the bound value on every keystroke instead of on change.</summary>
|
||||
[Parameter] public bool Immediate { get; set; }
|
||||
/// <summary>Adds daisyUI's validator class: error/success colors and a following DValidatorHint.</summary>
|
||||
[Parameter] public bool Validator { get; set; }
|
||||
|
||||
string CssClassFull => Cx("textarea", IsInvalid ? "textarea-error" : ColorClass, SizeClass, Ghost ? "textarea-ghost" : null,
|
||||
Validator ? "validator" : null, CssClass);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "textarea-neutral",
|
||||
DColor.Primary => "textarea-primary",
|
||||
DColor.Secondary => "textarea-secondary",
|
||||
DColor.Accent => "textarea-accent",
|
||||
DColor.Info => "textarea-info",
|
||||
DColor.Success => "textarea-success",
|
||||
DColor.Warning => "textarea-warning",
|
||||
DColor.Error => "textarea-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "textarea-xs",
|
||||
DSize.Sm => "textarea-sm",
|
||||
DSize.Lg => "textarea-lg",
|
||||
DSize.Xl => "textarea-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
void OnInput(ChangeEventArgs e)
|
||||
{
|
||||
if (Immediate)
|
||||
CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValueAsString = e.Value?.ToString();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
@inherits DInputBase<bool>
|
||||
|
||||
@* daisyUI Toggle: https://daisyui.com/components/toggle/ . With CheckedIcon/UncheckedIcon the toggle class moves to a
|
||||
wrapper around the checkbox and the two icons (each must be a single element: daisyUI shows the wrapper's 2nd child
|
||||
while unchecked and its 3rd while checked). daisyUI colors such a wrapper only through aria-checked, which a label
|
||||
may not carry, so its color is a has-checked: variable instead. With LabelText all of it sits in a label.label. *@
|
||||
@if (!HasIcons && LabelText is null)
|
||||
{
|
||||
@Checkbox(Cx("toggle", IsInvalid ? "toggle-error" : ColorClass, SizeClass, CssClass))
|
||||
}
|
||||
else if (!HasIcons)
|
||||
{
|
||||
<label class="label">
|
||||
@Checkbox(Cx("toggle", IsInvalid ? "toggle-error" : ColorClass, SizeClass, CssClass))
|
||||
@LabelText
|
||||
</label>
|
||||
}
|
||||
else if (LabelText is null)
|
||||
{
|
||||
<label class="@WrapperClass">@IconParts</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<label class="label">
|
||||
<span class="@WrapperClass">@IconParts</span>
|
||||
@LabelText
|
||||
</label>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public string LabelText { get; set; }
|
||||
[Parameter] public RenderFragment CheckedIcon { get; set; }
|
||||
[Parameter] public RenderFragment UncheckedIcon { get; set; }
|
||||
|
||||
bool HasIcons => CheckedIcon is not null || UncheckedIcon is not null;
|
||||
|
||||
string WrapperClass => Cx("toggle", IsInvalid ? "has-checked:[--input-color:var(--color-error)]" : WrapperColorClass, SizeClass, CssClass);
|
||||
|
||||
RenderFragment Checkbox(string cssClass) =>
|
||||
@<input @attributes="InputAttributes" type="checkbox" class="@cssClass" name="@NameAttributeValue" checked="@CurrentValue"
|
||||
@onchange="OnChange" aria-invalid="@(IsInvalid ? "true" : null)" />;
|
||||
|
||||
RenderFragment IconParts => @<text>
|
||||
@Checkbox(null)
|
||||
@if (UncheckedIcon is null)
|
||||
{
|
||||
<span></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@UncheckedIcon
|
||||
}
|
||||
@if (CheckedIcon is null)
|
||||
{
|
||||
<span></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@CheckedIcon
|
||||
}
|
||||
</text>;
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "toggle-neutral",
|
||||
DColor.Primary => "toggle-primary",
|
||||
DColor.Secondary => "toggle-secondary",
|
||||
DColor.Accent => "toggle-accent",
|
||||
DColor.Info => "toggle-info",
|
||||
DColor.Success => "toggle-success",
|
||||
DColor.Warning => "toggle-warning",
|
||||
DColor.Error => "toggle-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string WrapperColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "has-checked:[--input-color:var(--color-neutral)]",
|
||||
DColor.Primary => "has-checked:[--input-color:var(--color-primary)]",
|
||||
DColor.Secondary => "has-checked:[--input-color:var(--color-secondary)]",
|
||||
DColor.Accent => "has-checked:[--input-color:var(--color-accent)]",
|
||||
DColor.Info => "has-checked:[--input-color:var(--color-info)]",
|
||||
DColor.Success => "has-checked:[--input-color:var(--color-success)]",
|
||||
DColor.Warning => "has-checked:[--input-color:var(--color-warning)]",
|
||||
DColor.Error => "has-checked:[--input-color:var(--color-error)]",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "toggle-xs",
|
||||
DSize.Sm => "toggle-sm",
|
||||
DSize.Lg => "toggle-lg",
|
||||
DSize.Xl => "toggle-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
void OnChange(ChangeEventArgs e) => CurrentValue = e.Value is true;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Validator: https://daisyui.com/components/validator/ . Place it right after a field with Validator set (a
|
||||
following sibling): it shows, in the error color, while that field is :user-invalid or aria-invalid (failed
|
||||
EditForm validation). *@
|
||||
<p @attributes="AdditionalAttributes" class="@Cx("validator-hint", Compact ? "hidden" : null, Class)">@ChildContent</p>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Takes no space while the field is valid (daisyUI's validator-hint hidden) instead of keeping its line.</summary>
|
||||
[Parameter] public bool Compact { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Alert: https://daisyui.com/components/alert/ . The alert is a grid of up to three cells: Icon, ChildContent
|
||||
(wrapped in one <div>, so a heading plus text stays one cell) and Actions. role="alert" comes before the splatted
|
||||
attributes, so a consumer can pass role="status" for a non-urgent message. *@
|
||||
<div role="alert" @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@Icon
|
||||
<div>@ChildContent</div>
|
||||
@if (Actions is not null)
|
||||
{
|
||||
<div class="flex gap-2">@Actions</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Icon { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public RenderFragment Actions { get; set; }
|
||||
[Parameter] public DAlertColor Color { get; set; }
|
||||
[Parameter] public DAlertVariant Variant { get; set; }
|
||||
[Parameter] public DDirection Direction { get; set; }
|
||||
/// <summary>Vertical on small screens, horizontal from sm up (alert-vertical sm:alert-horizontal).</summary>
|
||||
[Parameter] public bool Responsive { get; set; }
|
||||
|
||||
string CssClass => Cx("alert", ColorClass, VariantClass,
|
||||
Direction == DDirection.Vertical || Responsive ? "alert-vertical" : null, Responsive ? "sm:alert-horizontal" : null, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DAlertColor.Info => "alert-info",
|
||||
DAlertColor.Success => "alert-success",
|
||||
DAlertColor.Warning => "alert-warning",
|
||||
DAlertColor.Error => "alert-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string VariantClass => Variant switch
|
||||
{
|
||||
DAlertVariant.Outline => "alert-outline",
|
||||
DAlertVariant.Dash => "alert-dash",
|
||||
DAlertVariant.Soft => "alert-soft",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace decePubClient.Components.Daisy.Feedback;
|
||||
|
||||
/// <summary>daisyUI's alert colours; there is no neutral/primary/secondary/accent alert, hence not DColor.</summary>
|
||||
public enum DAlertColor
|
||||
{
|
||||
Default,
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public enum DAlertVariant
|
||||
{
|
||||
Default,
|
||||
Outline,
|
||||
Dash,
|
||||
Soft
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Loading: https://daisyui.com/components/loading/ . It is drawn in currentColor, so Color maps to text-*
|
||||
classes. role="status" and aria-label come before the splatted attributes, so a consumer can override them. *@
|
||||
<span role="status" aria-label="@LabelText" @attributes="AdditionalAttributes" class="@CssClass"></span>
|
||||
|
||||
@code {
|
||||
[Parameter] public DLoadingVariant Variant { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
/// <summary>What screen readers announce (aria-label).</summary>
|
||||
[Parameter] public string LabelText { get; set; } = "Loading";
|
||||
|
||||
string CssClass => Cx("loading", VariantClass, SizeClass, ColorClass, Class);
|
||||
|
||||
string VariantClass => Variant switch
|
||||
{
|
||||
DLoadingVariant.Dots => "loading-dots",
|
||||
DLoadingVariant.Ring => "loading-ring",
|
||||
DLoadingVariant.Ball => "loading-ball",
|
||||
DLoadingVariant.Bars => "loading-bars",
|
||||
DLoadingVariant.Infinity => "loading-infinity",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "loading-xs",
|
||||
DSize.Sm => "loading-sm",
|
||||
DSize.Lg => "loading-lg",
|
||||
DSize.Xl => "loading-xl",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "text-neutral",
|
||||
DColor.Primary => "text-primary",
|
||||
DColor.Secondary => "text-secondary",
|
||||
DColor.Accent => "text-accent",
|
||||
DColor.Info => "text-info",
|
||||
DColor.Success => "text-success",
|
||||
DColor.Warning => "text-warning",
|
||||
DColor.Error => "text-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace decePubClient.Components.Daisy.Feedback;
|
||||
|
||||
/// <summary>The animation; Spinner is what a bare loading class draws.</summary>
|
||||
public enum DLoadingVariant
|
||||
{
|
||||
Spinner,
|
||||
Dots,
|
||||
Ring,
|
||||
Ball,
|
||||
Bars,
|
||||
Infinity
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
@using System.Globalization
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Progress: https://daisyui.com/components/progress/ . A null Value leaves out the value attribute, which makes
|
||||
the bar indeterminate (animated). Numbers are written with the invariant culture. The width comes from class
|
||||
(it is 100% by default). *@
|
||||
<progress @attributes="AdditionalAttributes" class="@CssClass" value="@(Value?.ToString(CultureInfo.InvariantCulture))"
|
||||
max="@(Max.ToString(CultureInfo.InvariantCulture))"></progress>
|
||||
|
||||
@code {
|
||||
/// <summary>The progress, from 0 to Max; null shows an indeterminate bar.</summary>
|
||||
[Parameter] public double? Value { get; set; }
|
||||
[Parameter] public double Max { get; set; } = 100;
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
|
||||
string CssClass => Cx("progress", ColorClass, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "progress-neutral",
|
||||
DColor.Primary => "progress-primary",
|
||||
DColor.Secondary => "progress-secondary",
|
||||
DColor.Accent => "progress-accent",
|
||||
DColor.Info => "progress-info",
|
||||
DColor.Success => "progress-success",
|
||||
DColor.Warning => "progress-warning",
|
||||
DColor.Error => "progress-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
@using System.Globalization
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Radial progress: https://daisyui.com/components/radial-progress/ . Value/Max becomes a 0-100 percentage in
|
||||
the --value style variable and aria-valuenow, and the default text; Diameter and Thickness set --size and
|
||||
--thickness. A consumer's style is appended to those. It is drawn in currentColor, so Color maps to text-*. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass" style="@StyleValue" role="progressbar"
|
||||
aria-valuenow="@PercentText" aria-valuemin="0" aria-valuemax="100">
|
||||
@if (ChildContent is null)
|
||||
{
|
||||
@($"{Math.Round(Percent)}%")
|
||||
}
|
||||
else
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public double Value { get; set; }
|
||||
[Parameter] public double Max { get; set; } = 100;
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
/// <summary>A CSS length for --size (daisyUI's default is 5rem).</summary>
|
||||
[Parameter] public string Diameter { get; set; }
|
||||
/// <summary>A CSS length for --thickness (daisyUI's default is a tenth of the size).</summary>
|
||||
[Parameter] public string Thickness { get; set; }
|
||||
/// <summary>Replaces the default "NN%" text.</summary>
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
|
||||
double Percent => Max > 0 ? Math.Clamp(Value / Max * 100, 0, 100) : 0;
|
||||
|
||||
string PercentText => Math.Round(Percent, 2).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
string StyleValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var style = $"--value:{PercentText};";
|
||||
if (!string.IsNullOrEmpty(Diameter))
|
||||
style += $" --size:{Diameter};";
|
||||
if (!string.IsNullOrEmpty(Thickness))
|
||||
style += $" --thickness:{Thickness};";
|
||||
if (AdditionalAttributes != null && AdditionalAttributes.TryGetValue("style", out var extra) && extra is not null)
|
||||
style += " " + extra;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
string CssClass => Cx("radial-progress", ColorClass, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "text-neutral",
|
||||
DColor.Primary => "text-primary",
|
||||
DColor.Secondary => "text-secondary",
|
||||
DColor.Accent => "text-accent",
|
||||
DColor.Info => "text-info",
|
||||
DColor.Success => "text-success",
|
||||
DColor.Warning => "text-warning",
|
||||
DColor.Error => "text-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Skeleton: https://daisyui.com/components/skeleton/ . Size it with h-* / w-* classes; IsText (skeleton-text)
|
||||
animates ChildContent's text instead of drawing a block. *@
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("skeleton", IsText ? "skeleton-text" : null, Class)">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Shimmer the text in ChildContent (skeleton-text) rather than a filled block.</summary>
|
||||
[Parameter] public bool IsText { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Toast: https://daisyui.com/components/toast/ . A fixed-position stack (ChildContent, usually DAlerts) in a
|
||||
corner or edge of the viewport; End/Bottom is daisyUI's default and adds no class. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DToastHorizontal Horizontal { get; set; }
|
||||
[Parameter] public DToastVertical Vertical { get; set; }
|
||||
|
||||
string CssClass => Cx("toast", HorizontalClass, VerticalClass, Class);
|
||||
|
||||
string HorizontalClass => Horizontal switch
|
||||
{
|
||||
DToastHorizontal.Start => "toast-start",
|
||||
DToastHorizontal.Center => "toast-center",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string VerticalClass => Vertical switch
|
||||
{
|
||||
DToastVertical.Top => "toast-top",
|
||||
DToastVertical.Middle => "toast-middle",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace decePubClient.Components.Daisy.Feedback;
|
||||
|
||||
/// <summary>Where the toast sits horizontally; End is daisyUI's default.</summary>
|
||||
public enum DToastHorizontal
|
||||
{
|
||||
End,
|
||||
Start,
|
||||
Center
|
||||
}
|
||||
|
||||
/// <summary>Where the toast sits vertically; Bottom is daisyUI's default.</summary>
|
||||
public enum DToastVertical
|
||||
{
|
||||
Bottom,
|
||||
Top,
|
||||
Middle
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Tooltip: https://daisyui.com/components/tooltip/ . Shows on hover or keyboard focus of ChildContent: Tip
|
||||
sets data-tip, or TipContent renders a tooltip-content part for markup. IsOpen keeps it shown (tooltip-open). *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass" data-tip="@Tip">
|
||||
@if (TipContent is not null)
|
||||
{
|
||||
<div class="tooltip-content">@TipContent</div>
|
||||
}
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string Tip { get; set; }
|
||||
[Parameter] public RenderFragment TipContent { get; set; }
|
||||
[Parameter] public DTooltipPlacement Placement { get; set; }
|
||||
[Parameter] public DTooltipAlign Align { get; set; }
|
||||
/// <summary>Neutral is the default look; daisyUI has no tooltip-neutral.</summary>
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public bool IsOpen { get; set; }
|
||||
|
||||
string CssClass => Cx("tooltip", PlacementClass, AlignClass, ColorClass, IsOpen ? "tooltip-open" : null, Class);
|
||||
|
||||
string PlacementClass => Placement switch
|
||||
{
|
||||
DTooltipPlacement.Bottom => "tooltip-bottom",
|
||||
DTooltipPlacement.Left => "tooltip-left",
|
||||
DTooltipPlacement.Right => "tooltip-right",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string AlignClass => Align switch
|
||||
{
|
||||
DTooltipAlign.Start => "tooltip-start",
|
||||
DTooltipAlign.End => "tooltip-end",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Primary => "tooltip-primary",
|
||||
DColor.Secondary => "tooltip-secondary",
|
||||
DColor.Accent => "tooltip-accent",
|
||||
DColor.Info => "tooltip-info",
|
||||
DColor.Success => "tooltip-success",
|
||||
DColor.Warning => "tooltip-warning",
|
||||
DColor.Error => "tooltip-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace decePubClient.Components.Daisy.Feedback;
|
||||
|
||||
/// <summary>Which side of the element the tooltip shows on; Top is daisyUI's default.</summary>
|
||||
public enum DTooltipPlacement
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
/// <summary>How the tooltip lines up with the element along that side; Center is daisyUI's default.</summary>
|
||||
public enum DTooltipAlign
|
||||
{
|
||||
Center,
|
||||
Start,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Divider: https://daisyui.com/components/divider/ . Leave ChildContent empty for a blank line. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
[Parameter] public DDividerDirection Direction { get; set; }
|
||||
[Parameter] public DDividerPlacement Placement { get; set; }
|
||||
|
||||
string CssClass => Cx("divider", DirectionClass, ColorClass, PlacementClass, Class);
|
||||
|
||||
string DirectionClass => Direction switch
|
||||
{
|
||||
DDividerDirection.Horizontal => "divider-horizontal",
|
||||
DDividerDirection.Vertical => "divider-vertical",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "divider-neutral",
|
||||
DColor.Primary => "divider-primary",
|
||||
DColor.Secondary => "divider-secondary",
|
||||
DColor.Accent => "divider-accent",
|
||||
DColor.Info => "divider-info",
|
||||
DColor.Success => "divider-success",
|
||||
DColor.Warning => "divider-warning",
|
||||
DColor.Error => "divider-error",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string PlacementClass => Placement switch
|
||||
{
|
||||
DDividerPlacement.Start => "divider-start",
|
||||
DDividerPlacement.End => "divider-end",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace decePubClient.Components.Daisy.Layout;
|
||||
|
||||
/// <summary>
|
||||
/// daisyUI's names, which describe the content around the divider: <see cref="Horizontal"/> (divider-horizontal) is a
|
||||
/// vertical line between items side by side; <see cref="Vertical"/> (divider-vertical) is a horizontal line between
|
||||
/// stacked items, as is the default.
|
||||
/// </summary>
|
||||
public enum DDividerDirection
|
||||
{
|
||||
Default,
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
|
||||
/// <summary>Where the text sits along the line; Center is daisyUI's default.</summary>
|
||||
public enum DDividerPlacement
|
||||
{
|
||||
Center,
|
||||
Start,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Drawer: https://daisyui.com/components/drawer/ . daisyUI's hidden drawer-toggle checkbox is bound to IsOpen
|
||||
(@bind-IsOpen); the overlay label toggles it natively, Escape closes it, and any
|
||||
<label for="{ToggleId}" class="btn drawer-button"> in the content toggles it too. Put the navbar and footer in
|
||||
ChildContent. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass" @onkeydown="OnKeyDown">
|
||||
<input id="@toggleId" type="checkbox" class="drawer-toggle" checked="@IsOpen" @onchange="OnChange" />
|
||||
<div class="@Cx("drawer-content", ContentClass)">
|
||||
@ChildContent
|
||||
</div>
|
||||
<div class="@Cx("drawer-side", SideClass)">
|
||||
<label for="@toggleId" aria-label="@CloseText" class="drawer-overlay"></label>
|
||||
@Side
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
/// <summary>The page content, beside the side.</summary>
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>The sidebar, e.g. <ul class="menu bg-base-200 min-h-full w-80 p-4">.</summary>
|
||||
[Parameter] public RenderFragment Side { get; set; }
|
||||
[Parameter] public bool IsOpen { get; set; }
|
||||
[Parameter] public EventCallback<bool> IsOpenChanged { get; set; }
|
||||
/// <summary>Put the side on the right (drawer-end).</summary>
|
||||
[Parameter] public bool End { get; set; }
|
||||
[Parameter] public DDrawerOpenFrom OpenFrom { get; set; }
|
||||
/// <summary>The checkbox id, for drawer-button labels; defaults to a per-instance id.</summary>
|
||||
[Parameter] public string ToggleId { get; set; }
|
||||
/// <summary>Accessible name of the overlay, which closes the side.</summary>
|
||||
[Parameter] public string CloseText { get; set; } = "close sidebar";
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
[Parameter] public string SideClass { get; set; }
|
||||
|
||||
readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8];
|
||||
|
||||
string toggleId => ToggleId ?? uid;
|
||||
|
||||
string CssClass => Cx("drawer", End ? "drawer-end" : null, OpenFromClass, Class);
|
||||
|
||||
string OpenFromClass => OpenFrom switch
|
||||
{
|
||||
DDrawerOpenFrom.Sm => "sm:drawer-open",
|
||||
DDrawerOpenFrom.Md => "md:drawer-open",
|
||||
DDrawerOpenFrom.Lg => "lg:drawer-open",
|
||||
DDrawerOpenFrom.Xl => "xl:drawer-open",
|
||||
DDrawerOpenFrom.Always => "drawer-open",
|
||||
_ => null
|
||||
};
|
||||
|
||||
Task OnChange(ChangeEventArgs e) => SetOpen(e.Value is true);
|
||||
|
||||
Task OnKeyDown(KeyboardEventArgs e) => e.Key == "Escape" && IsOpen ? SetOpen(false) : Task.CompletedTask;
|
||||
|
||||
async Task SetOpen(bool isOpen)
|
||||
{
|
||||
IsOpen = isOpen;
|
||||
await IsOpenChanged.InvokeAsync(isOpen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace decePubClient.Components.Daisy.Layout;
|
||||
|
||||
/// <summary>From which breakpoint the side stays open beside the content (drawer-open), whatever IsOpen says.</summary>
|
||||
public enum DDrawerOpenFrom
|
||||
{
|
||||
Never,
|
||||
Sm,
|
||||
Md,
|
||||
Lg,
|
||||
Xl,
|
||||
Always
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Footer: https://daisyui.com/components/footer/ . Its children are laid out on a grid; use DFooterNav (or
|
||||
<nav> / <aside>) for each column. *@
|
||||
<footer @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</footer>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DFooterDirection Direction { get; set; }
|
||||
/// <summary>Center the children (footer-center).</summary>
|
||||
[Parameter] public bool Center { get; set; }
|
||||
|
||||
string CssClass => Cx("footer", DirectionClass, Center ? "footer-center" : null, Class);
|
||||
|
||||
string DirectionClass => Direction switch
|
||||
{
|
||||
DFooterDirection.Horizontal => "footer-horizontal",
|
||||
DFooterDirection.HorizontalFromSm => "sm:footer-horizontal",
|
||||
DFooterDirection.HorizontalFromMd => "md:footer-horizontal",
|
||||
DFooterDirection.HorizontalFromLg => "lg:footer-horizontal",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace decePubClient.Components.Daisy.Layout;
|
||||
|
||||
/// <summary>How the footer lays out its children: in a column (daisyUI's default), in a row, or in a row from a
|
||||
/// breakpoint up (the responsive sm:footer-horizontal that daisyUI recommends).</summary>
|
||||
public enum DFooterDirection
|
||||
{
|
||||
Vertical,
|
||||
Horizontal,
|
||||
HorizontalFromSm,
|
||||
HorizontalFromMd,
|
||||
HorizontalFromLg
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One column of a DFooter: a <nav> with an optional footer-title heading above its links. *@
|
||||
<nav @attributes="AdditionalAttributes" class="@Class">
|
||||
@if (Heading is not null)
|
||||
{
|
||||
<h6 class="footer-title">@Heading</h6>
|
||||
}
|
||||
@ChildContent
|
||||
</nav>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Heading { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Hero: https://daisyui.com/components/hero/ . ChildContent goes in hero-content (text, and optionally a
|
||||
figure); with Overlay a hero-overlay tints the background image. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass" style="@StyleValue">
|
||||
@if (Overlay)
|
||||
{
|
||||
<div class="@Cx("hero-overlay", OverlayClass)"></div>
|
||||
}
|
||||
<div class="@Cx("hero-content", ContentClass)">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>URL of a background image, set as an inline background-image.</summary>
|
||||
[Parameter] public string BackgroundImage { get; set; }
|
||||
/// <summary>Add a hero-overlay color layer above the background image.</summary>
|
||||
[Parameter] public bool Overlay { get; set; }
|
||||
[Parameter] public string OverlayClass { get; set; }
|
||||
/// <summary>Classes for hero-content, e.g. "text-center" or "flex-col lg:flex-row".</summary>
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
|
||||
string CssClass => Cx("hero", Class);
|
||||
|
||||
// The consumer's own style attribute is kept, after the background image.
|
||||
string StyleValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var style = AdditionalAttributes?.GetValueOrDefault("style")?.ToString();
|
||||
return BackgroundImage is null ? style : Cx($"background-image: url('{BackgroundImage}');", style);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Indicator: https://daisyui.com/components/indicator/ . Put the DIndicatorItem children first and the main
|
||||
element after them, as daisyUI requires. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
|
||||
string CssClass => Cx("indicator", Class);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace decePubClient.Components.Daisy.Layout;
|
||||
|
||||
/// <summary>Horizontal position of an indicator item; End is daisyUI's default.</summary>
|
||||
public enum DIndicatorHorizontal
|
||||
{
|
||||
End,
|
||||
Start,
|
||||
Center
|
||||
}
|
||||
|
||||
/// <summary>Vertical position of an indicator item; Top is daisyUI's default.</summary>
|
||||
public enum DIndicatorVertical
|
||||
{
|
||||
Top,
|
||||
Middle,
|
||||
Bottom
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* An indicator-item of a DIndicator, placed on its corner or edge, e.g. with class="badge badge-primary". *@
|
||||
<span @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</span>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DIndicatorHorizontal Horizontal { get; set; }
|
||||
[Parameter] public DIndicatorVertical Vertical { get; set; }
|
||||
|
||||
string CssClass => Cx("indicator-item", HorizontalClass, VerticalClass, Class);
|
||||
|
||||
string HorizontalClass => Horizontal switch
|
||||
{
|
||||
DIndicatorHorizontal.Start => "indicator-start",
|
||||
DIndicatorHorizontal.Center => "indicator-center",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string VerticalClass => Vertical switch
|
||||
{
|
||||
DIndicatorVertical.Middle => "indicator-middle",
|
||||
DIndicatorVertical.Bottom => "indicator-bottom",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Join: https://daisyui.com/components/join/ . Groups its direct children, rounding only the outer corners;
|
||||
give each of them the join-item class (e.g. <DButton class="join-item">). For a responsive group, add
|
||||
class="lg:join-horizontal" to a vertical one. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DDirection Direction { get; set; }
|
||||
|
||||
string CssClass => Cx("join", Direction == DDirection.Vertical ? "join-vertical" : null, Class);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Mask: https://daisyui.com/components/mask/ . Renders an <img> when Src is set, otherwise a <div> around
|
||||
ChildContent. daisyUI requires a Shape or a Half; size it with w-* and h-*. *@
|
||||
@if (Src is not null)
|
||||
{
|
||||
<img @attributes="AdditionalAttributes" src="@Src" class="@CssClass" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">@ChildContent</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public string Src { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DMaskShape Shape { get; set; }
|
||||
[Parameter] public DMaskHalf Half { get; set; }
|
||||
|
||||
string CssClass => Cx("mask", ShapeClass, HalfClass, Class);
|
||||
|
||||
string ShapeClass => Shape switch
|
||||
{
|
||||
DMaskShape.Squircle => "mask-squircle",
|
||||
DMaskShape.Heart => "mask-heart",
|
||||
DMaskShape.Hexagon => "mask-hexagon",
|
||||
DMaskShape.Hexagon2 => "mask-hexagon-2",
|
||||
DMaskShape.Decagon => "mask-decagon",
|
||||
DMaskShape.Pentagon => "mask-pentagon",
|
||||
DMaskShape.Diamond => "mask-diamond",
|
||||
DMaskShape.Circle => "mask-circle",
|
||||
DMaskShape.Star => "mask-star",
|
||||
DMaskShape.Star2 => "mask-star-2",
|
||||
DMaskShape.Triangle => "mask-triangle",
|
||||
DMaskShape.Triangle2 => "mask-triangle-2",
|
||||
DMaskShape.Triangle3 => "mask-triangle-3",
|
||||
DMaskShape.Triangle4 => "mask-triangle-4",
|
||||
_ => null
|
||||
};
|
||||
|
||||
string HalfClass => Half switch
|
||||
{
|
||||
DMaskHalf.First => "mask-half-1",
|
||||
DMaskHalf.Second => "mask-half-2",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace decePubClient.Components.Daisy.Layout;
|
||||
|
||||
public enum DMaskShape
|
||||
{
|
||||
None,
|
||||
Squircle,
|
||||
Heart,
|
||||
Hexagon,
|
||||
Hexagon2,
|
||||
Decagon,
|
||||
Pentagon,
|
||||
Diamond,
|
||||
Circle,
|
||||
Star,
|
||||
Star2,
|
||||
Triangle,
|
||||
Triangle2,
|
||||
Triangle3,
|
||||
Triangle4
|
||||
}
|
||||
|
||||
/// <summary>Keep only the first or second half of the shape (mask-half-1 / mask-half-2), as rating does.</summary>
|
||||
public enum DMaskHalf
|
||||
{
|
||||
None,
|
||||
First,
|
||||
Second
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Stack: https://daisyui.com/components/stack/ . The first child is on top; w-* and h-* on the stack make
|
||||
all its children the same size. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DStackPlacement Placement { get; set; }
|
||||
|
||||
string CssClass => Cx("stack", PlacementClass, Class);
|
||||
|
||||
string PlacementClass => Placement switch
|
||||
{
|
||||
DStackPlacement.Top => "stack-top",
|
||||
DStackPlacement.Start => "stack-start",
|
||||
DStackPlacement.End => "stack-end",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace decePubClient.Components.Daisy.Layout;
|
||||
|
||||
/// <summary>Which side the items behind the first one peek out from; Bottom is daisyUI's default.</summary>
|
||||
public enum DStackPlacement
|
||||
{
|
||||
Bottom,
|
||||
Top,
|
||||
Start,
|
||||
End
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Browser mockup: https://daisyui.com/components/mockup-browser/ . The toolbar shows Toolbar if given,
|
||||
otherwise Url in an address bar (a div with the input class). *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<div class="mockup-browser-toolbar">
|
||||
@if (Toolbar is not null)
|
||||
{
|
||||
@Toolbar
|
||||
}
|
||||
else if (Url is not null)
|
||||
{
|
||||
<div class="input">@Url</div>
|
||||
}
|
||||
</div>
|
||||
<div class="@ContentClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Text of the address bar.</summary>
|
||||
[Parameter] public string Url { get; set; }
|
||||
/// <summary>Custom toolbar content, in place of the address bar.</summary>
|
||||
[Parameter] public RenderFragment Toolbar { get; set; }
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
|
||||
string CssClass => Cx("mockup-browser", Class);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Code mockup: https://daisyui.com/components/mockup-code/ . Each line is a DMockupCodeLine. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
|
||||
string CssClass => Cx("mockup-code", Class);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One line of a DMockupCode: <pre data-prefix><code>. Highlight it with a class, e.g. "bg-warning text-warning-content". *@
|
||||
<pre @attributes="AdditionalAttributes" data-prefix="@Prefix" class="@Class"><code>@ChildContent</code></pre>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Shown before the line, e.g. "$", ">" or a line number.</summary>
|
||||
[Parameter] public string Prefix { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Phone mockup: https://daisyui.com/components/mockup-phone/ . ChildContent fills the display. *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<div class="mockup-phone-camera"></div>
|
||||
<div class="@Cx("mockup-phone-display", DisplayClass)">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string DisplayClass { get; set; }
|
||||
|
||||
string CssClass => Cx("mockup-phone", Class);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Window mockup: https://daisyui.com/components/mockup-window/ . *@
|
||||
<div @attributes="AdditionalAttributes" class="@CssClass">
|
||||
<div class="@ContentClass">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
|
||||
string CssClass => Cx("mockup-window", Class);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One crumb of a DBreadcrumbs: <li><a> with Href, otherwise <li><span> (the current page). class and other
|
||||
attributes go on the <a>/<span>. *@
|
||||
<li>
|
||||
@if (Href is null)
|
||||
{
|
||||
<span @attributes="AdditionalAttributes" class="@Cx("inline-flex items-center gap-2", Class)">
|
||||
@ChildContent
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a @attributes="AdditionalAttributes" href="@Href" class="@Class">
|
||||
@ChildContent
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string Href { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Breadcrumbs: https://daisyui.com/components/breadcrumbs/ . Holds DBreadcrumbItem children; a max-width
|
||||
(e.g. class="max-w-xs") makes the list scroll. *@
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("breadcrumbs", Class)">
|
||||
<ul>
|
||||
@ChildContent
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Dock: https://daisyui.com/components/dock/ . A bar fixed to the bottom of the viewport holding DDockItem
|
||||
children; add class="relative" to place it in the flow instead. *@
|
||||
<div @attributes="AdditionalAttributes" class="@Cx("dock", SizeClass, Class)">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DSize Size { get; set; }
|
||||
|
||||
string SizeClass => Size switch
|
||||
{
|
||||
DSize.Xs => "dock-xs",
|
||||
DSize.Sm => "dock-sm",
|
||||
DSize.Lg => "dock-lg",
|
||||
DSize.Xl => "dock-xl",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* One button of a DDock: ChildContent is the icon and Text the dock-label under it. Renders an <a> when Href is set,
|
||||
with aria-current="page" while Active. *@
|
||||
@if (Href is null)
|
||||
{
|
||||
<button @attributes="AdditionalAttributes" type="button" class="@CssClass" disabled="@Disabled" @onclick="OnClick">
|
||||
@ChildContent
|
||||
@if (Text is not null)
|
||||
{
|
||||
<span class="dock-label">@Text</span>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a @attributes="AdditionalAttributes" href="@(Disabled ? null : Href)" class="@CssClass"
|
||||
aria-current="@(Active ? "page" : null)" aria-disabled="@(Disabled ? "true" : null)" tabindex="@(Disabled ? "-1" : null)"
|
||||
@onclick="OnClick">
|
||||
@ChildContent
|
||||
@if (Text is not null)
|
||||
{
|
||||
<span class="dock-label">@Text</span>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
|
||||
@code {
|
||||
/// <summary>The icon.</summary>
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string Text { get; set; }
|
||||
[Parameter] public bool Active { get; set; }
|
||||
[Parameter] public bool Disabled { get; set; }
|
||||
[Parameter] public string Href { get; set; }
|
||||
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
||||
|
||||
string CssClass => Cx(Active ? "dock-active" : null, Class);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@inherits DComponentBase
|
||||
|
||||
@* daisyUI Link: https://daisyui.com/components/link/ . Without Href it renders a <button> styled as a link, so an
|
||||
OnClick-only link stays keyboard-reachable. *@
|
||||
@if (Href is null)
|
||||
{
|
||||
<button @attributes="AdditionalAttributes" type="button" class="@CssClass" @onclick="OnClick">
|
||||
@ChildContent
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a @attributes="AdditionalAttributes" href="@Href" class="@CssClass" @onclick="OnClick">
|
||||
@ChildContent
|
||||
</a>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public string Href { get; set; }
|
||||
[Parameter] public DColor Color { get; set; }
|
||||
/// <summary>Underline only on hover (link-hover).</summary>
|
||||
[Parameter] public bool Hover { get; set; }
|
||||
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
||||
|
||||
string CssClass => Cx("link", ColorClass, Hover ? "link-hover" : null, Class);
|
||||
|
||||
string ColorClass => Color switch
|
||||
{
|
||||
DColor.Neutral => "link-neutral",
|
||||
DColor.Primary => "link-primary",
|
||||
DColor.Secondary => "link-secondary",
|
||||
DColor.Accent => "link-accent",
|
||||
DColor.Info => "link-info",
|
||||
DColor.Success => "link-success",
|
||||
DColor.Warning => "link-warning",
|
||||
DColor.Error => "link-error",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user