Added components, authentication and authorization
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<button type="$$Type$$" class="$$Classes$$" $$HxAttrs$$>$$Label$$</button>
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace Htmx.ApiDemo.Templates.Components;
|
||||
|
||||
/// <summary>
|
||||
/// shadcn-style Button component.
|
||||
/// Variant: default | destructive | outline | secondary | ghost | link
|
||||
/// Size: default | sm | lg | icon
|
||||
/// </summary>
|
||||
public sealed class Button : ButtonBase
|
||||
{
|
||||
private static readonly Dictionary<string, string> VariantClasses = new()
|
||||
{
|
||||
["default"] = "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
["destructive"] = "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
["outline"] = "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
||||
["secondary"] = "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
["ghost"] = "hover:bg-accent hover:text-accent-foreground",
|
||||
["link"] = "text-primary underline-offset-4 hover:underline",
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> SizeClasses = new()
|
||||
{
|
||||
["default"] = "h-10 px-4 py-2 text-sm",
|
||||
["sm"] = "h-9 rounded-md px-3 text-xs",
|
||||
["lg"] = "h-11 rounded-md px-8 text-base",
|
||||
["icon"] = "h-10 w-10",
|
||||
};
|
||||
|
||||
private const string BaseClasses =
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium " +
|
||||
"ring-offset-background transition-colors " +
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 " +
|
||||
"disabled:pointer-events-none disabled:opacity-50";
|
||||
|
||||
private readonly byte[] _labelData;
|
||||
private readonly byte[] _classesData;
|
||||
private readonly byte[] _typeData;
|
||||
private readonly byte[] _hxAttrsData;
|
||||
|
||||
public Button(
|
||||
string label,
|
||||
string variant = "default",
|
||||
string size = "default",
|
||||
string type = "button",
|
||||
string hxAttrs = "")
|
||||
{
|
||||
_labelData = label.ToUtf8Bytes();
|
||||
_typeData = type.ToUtf8Bytes();
|
||||
_hxAttrsData = hxAttrs.ToUtf8Bytes();
|
||||
|
||||
var v = VariantClasses.GetValueOrDefault(variant, VariantClasses["default"]);
|
||||
var s = SizeClasses.GetValueOrDefault(size, SizeClasses["default"]);
|
||||
_classesData = $"{BaseClasses} {s} {v}".ToUtf8Bytes();
|
||||
}
|
||||
|
||||
protected override void RenderLabel(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_labelData);
|
||||
protected override void RenderClasses(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_classesData);
|
||||
protected override void RenderType(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_typeData);
|
||||
protected override void RenderHxAttrs(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_hxAttrsData);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<div id="cal-$$Id$$"
|
||||
class="calendar-root inline-block min-w-72 rounded-md border border-border bg-card p-4 shadow-sm"
|
||||
data-year="$$Year$$"
|
||||
data-month="$$Month$$"
|
||||
data-sel-day="$$SelectedDay$$"
|
||||
data-sel-month="$$SelectedMonth$$"
|
||||
data-sel-year="$$SelectedYear$$"
|
||||
data-view="days">
|
||||
|
||||
<!-- Header row -->
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<button type="button" class="cal-prev cal-nav inline-flex h-8 w-8 items-center justify-center rounded-md border border-input
|
||||
bg-transparent hover:bg-accent hover:text-accent-foreground transition-colors text-base"
|
||||
aria-label="Previous month">‹</button>
|
||||
<button type="button" class="cal-month-label text-sm font-semibold px-2 py-0.5 rounded-md
|
||||
hover:bg-accent hover:text-accent-foreground transition-colors cursor-pointer"></button>
|
||||
<button type="button" class="cal-next cal-nav inline-flex h-8 w-8 items-center justify-center rounded-md border border-input
|
||||
bg-transparent hover:bg-accent hover:text-accent-foreground transition-colors text-base"
|
||||
aria-label="Next month">›</button>
|
||||
</div>
|
||||
|
||||
<!-- Day-of-week headers -->
|
||||
<div class="cal-dow-row mb-1 grid grid-cols-7 text-center">
|
||||
<span class="cal-dow">Su</span>
|
||||
<span class="cal-dow">Mo</span>
|
||||
<span class="cal-dow">Tu</span>
|
||||
<span class="cal-dow">We</span>
|
||||
<span class="cal-dow">Th</span>
|
||||
<span class="cal-dow">Fr</span>
|
||||
<span class="cal-dow">Sa</span>
|
||||
</div>
|
||||
|
||||
<!-- Day grid (populated by JS below) -->
|
||||
<div class="cal-grid grid grid-cols-7 gap-0.5 text-center"></div>
|
||||
|
||||
<!-- Hidden input -->
|
||||
<input type="hidden" name="$$Name$$" class="cal-hidden-input" value="$$DefaultValue$$" />
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace Htmx.ApiDemo.Templates.Components;
|
||||
|
||||
/// <summary>
|
||||
/// shadcn-style Calendar (date-picker) component driven entirely by HyperScript.
|
||||
/// Pass a selected date to pre-highlight a day; defaults to today.
|
||||
/// </summary>
|
||||
public sealed class Calendar : CalendarBase
|
||||
{
|
||||
private readonly byte[] _idData;
|
||||
private readonly byte[] _nameData;
|
||||
private readonly byte[] _yearData;
|
||||
private readonly byte[] _monthData; // 0-based JS month
|
||||
private readonly byte[] _selectedDayData;
|
||||
private readonly byte[] _selectedMonthData; // 0-based
|
||||
private readonly byte[] _selectedYearData;
|
||||
private readonly byte[] _defaultValueData;
|
||||
|
||||
public Calendar(
|
||||
string id,
|
||||
string name = "date",
|
||||
DateOnly? selected = null)
|
||||
{
|
||||
var date = selected ?? DateOnly.FromDateTime(DateTime.Today);
|
||||
|
||||
_idData = id.ToUtf8Bytes();
|
||||
_nameData = name.ToUtf8Bytes();
|
||||
_yearData = date.Year.ToString().ToUtf8Bytes();
|
||||
_monthData = (date.Month - 1).ToString().ToUtf8Bytes(); // JS months are 0-based
|
||||
_selectedDayData = date.Day.ToString().ToUtf8Bytes();
|
||||
_selectedMonthData= (date.Month - 1).ToString().ToUtf8Bytes();
|
||||
_selectedYearData = date.Year.ToString().ToUtf8Bytes();
|
||||
_defaultValueData = date.ToString("yyyy-MM-dd").ToUtf8Bytes();
|
||||
}
|
||||
|
||||
protected override void RenderId(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_idData);
|
||||
protected override void RenderName(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_nameData);
|
||||
protected override void RenderYear(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_yearData);
|
||||
protected override void RenderMonth(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_monthData);
|
||||
protected override void RenderSelectedDay(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_selectedDayData);
|
||||
protected override void RenderSelectedMonth(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_selectedMonthData);
|
||||
protected override void RenderSelectedYear(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_selectedYearData);
|
||||
protected override void RenderDefaultValue(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_defaultValueData);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<div id="calr-$$Id$$"
|
||||
class="calr-root inline-block min-w-72 rounded-md border border-border bg-card p-4 shadow-sm"
|
||||
data-year="$$Year$$"
|
||||
data-month="$$Month$$"
|
||||
data-start="$$DefaultStart$$"
|
||||
data-end="$$DefaultEnd$$"
|
||||
data-view="days">
|
||||
|
||||
<!-- Header row -->
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<button type="button" class="calr-prev cal-nav inline-flex h-8 w-8 items-center justify-center rounded-md border border-input
|
||||
bg-transparent hover:bg-accent hover:text-accent-foreground transition-colors text-base"
|
||||
aria-label="Previous month">‹</button>
|
||||
<button type="button" class="calr-month-label text-sm font-semibold px-2 py-0.5 rounded-md
|
||||
hover:bg-accent hover:text-accent-foreground transition-colors cursor-pointer"></button>
|
||||
<button type="button" class="calr-next cal-nav inline-flex h-8 w-8 items-center justify-center rounded-md border border-input
|
||||
bg-transparent hover:bg-accent hover:text-accent-foreground transition-colors text-base"
|
||||
aria-label="Next month">›</button>
|
||||
</div>
|
||||
|
||||
<!-- Day-of-week headers -->
|
||||
<div class="cal-dow-row mb-1 grid grid-cols-7 text-center">
|
||||
<span class="cal-dow">Su</span>
|
||||
<span class="cal-dow">Mo</span>
|
||||
<span class="cal-dow">Tu</span>
|
||||
<span class="cal-dow">We</span>
|
||||
<span class="cal-dow">Th</span>
|
||||
<span class="cal-dow">Fr</span>
|
||||
<span class="cal-dow">Sa</span>
|
||||
</div>
|
||||
|
||||
<!-- Day grid (populated by JS) -->
|
||||
<div class="calr-grid grid grid-cols-7 text-center"></div>
|
||||
|
||||
<!-- Range label -->
|
||||
<div class="calr-label mt-3 text-xs text-muted-foreground min-h-4"></div>
|
||||
|
||||
<!-- Hidden inputs -->
|
||||
<input type="hidden" name="$$NameStart$$" class="calr-hidden-start" value="$$DefaultStart$$" />
|
||||
<input type="hidden" name="$$NameEnd$$" class="calr-hidden-end" value="$$DefaultEnd$$" />
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace Htmx.ApiDemo.Templates.Components;
|
||||
|
||||
/// <summary>
|
||||
/// shadcn-style range Calendar. Lets the user pick a start and end date.
|
||||
/// State and rendering are handled by components.js (initCalendarRange).
|
||||
/// Fires a <c>rangeChange</c> CustomEvent with <c>{ start, end }</c> detail.
|
||||
/// </summary>
|
||||
public sealed class CalendarRange : CalendarRangeBase
|
||||
{
|
||||
private readonly byte[] _idData;
|
||||
private readonly byte[] _nameStartData;
|
||||
private readonly byte[] _nameEndData;
|
||||
private readonly byte[] _yearData;
|
||||
private readonly byte[] _monthData;
|
||||
private readonly byte[] _defaultStartData;
|
||||
private readonly byte[] _defaultEndData;
|
||||
|
||||
public CalendarRange(
|
||||
string id,
|
||||
string name = "date",
|
||||
DateOnly? selectedStart = null,
|
||||
DateOnly? selectedEnd = null)
|
||||
{
|
||||
// Show the start month if provided, otherwise today
|
||||
var viewDate = selectedStart ?? DateOnly.FromDateTime(DateTime.Today);
|
||||
|
||||
_idData = id.ToUtf8Bytes();
|
||||
_nameStartData = (name + "-start").ToUtf8Bytes();
|
||||
_nameEndData = (name + "-end").ToUtf8Bytes();
|
||||
_yearData = viewDate.Year.ToString().ToUtf8Bytes();
|
||||
_monthData = (viewDate.Month - 1).ToString().ToUtf8Bytes(); // 0-based
|
||||
|
||||
_defaultStartData = selectedStart.HasValue
|
||||
? selectedStart.Value.ToString("yyyy-MM-dd").ToUtf8Bytes()
|
||||
: [] ;
|
||||
|
||||
_defaultEndData = selectedEnd.HasValue
|
||||
? selectedEnd.Value.ToString("yyyy-MM-dd").ToUtf8Bytes()
|
||||
: [];
|
||||
}
|
||||
|
||||
protected override void RenderId(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_idData);
|
||||
protected override void RenderNameStart(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_nameStartData);
|
||||
protected override void RenderNameEnd(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_nameEndData);
|
||||
protected override void RenderYear(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_yearData);
|
||||
protected override void RenderMonth(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_monthData);
|
||||
protected override void RenderDefaultStart(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_defaultStartData);
|
||||
protected override void RenderDefaultEnd(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_defaultEndData);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="flex flex-col gap-1.5">
|
||||
$$Label$$
|
||||
<input
|
||||
id="$$Id$$"
|
||||
name="$$Name$$"
|
||||
type="$$InputType$$"
|
||||
placeholder="$$Placeholder$$"
|
||||
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
|
||||
ring-offset-background placeholder:text-muted-foreground
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
|
||||
disabled:cursor-not-allowed disabled:opacity-50 $$ExtraClasses$$"
|
||||
$$HxAttrs$$
|
||||
/>
|
||||
$$Description$$
|
||||
</div>
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace Htmx.ApiDemo.Templates.Components;
|
||||
|
||||
/// <summary>
|
||||
/// shadcn-style Input component with optional label and description.
|
||||
/// InputType: text | email | password | number | search | tel | url | date | time
|
||||
/// </summary>
|
||||
public sealed class Input : InputBase
|
||||
{
|
||||
private readonly byte[] _idData;
|
||||
private readonly byte[] _nameData;
|
||||
private readonly byte[] _inputTypeData;
|
||||
private readonly byte[] _placeholderData;
|
||||
private readonly byte[] _labelData;
|
||||
private readonly byte[] _descriptionData;
|
||||
private readonly byte[] _extraClassesData;
|
||||
private readonly byte[] _hxAttrsData;
|
||||
|
||||
public Input(
|
||||
string id,
|
||||
string name = "",
|
||||
string inputType = "text",
|
||||
string placeholder = "",
|
||||
string label = "",
|
||||
string description = "",
|
||||
string extraClasses = "",
|
||||
string hxAttrs = "")
|
||||
{
|
||||
_idData = id.ToUtf8Bytes();
|
||||
_nameData = (string.IsNullOrEmpty(name) ? id : name).ToUtf8Bytes();
|
||||
_inputTypeData = inputType.ToUtf8Bytes();
|
||||
_placeholderData = placeholder.ToUtf8Bytes();
|
||||
_extraClassesData = extraClasses.ToUtf8Bytes();
|
||||
_hxAttrsData = hxAttrs.ToUtf8Bytes();
|
||||
|
||||
_labelData = string.IsNullOrEmpty(label)
|
||||
? []
|
||||
: $"""<label for="{id}" class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">{label}</label>""".ToUtf8Bytes();
|
||||
|
||||
_descriptionData = string.IsNullOrEmpty(description)
|
||||
? []
|
||||
: $"""<p class="text-xs text-muted-foreground">{description}</p>""".ToUtf8Bytes();
|
||||
}
|
||||
|
||||
protected override void RenderId(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_idData);
|
||||
protected override void RenderName(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_nameData);
|
||||
protected override void RenderInputType(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_inputTypeData);
|
||||
protected override void RenderPlaceholder(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_placeholderData);
|
||||
protected override void RenderLabel(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_labelData);
|
||||
protected override void RenderDescription(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_descriptionData);
|
||||
protected override void RenderExtraClasses(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_extraClassesData);
|
||||
protected override void RenderHxAttrs(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_hxAttrsData);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="flex flex-col gap-1.5">
|
||||
$$Label$$
|
||||
<select
|
||||
id="$$Id$$"
|
||||
name="$$Name$$"
|
||||
class="flex h-10 w-full items-center justify-between rounded-md border border-input
|
||||
bg-background px-3 py-2 text-sm ring-offset-background
|
||||
focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2
|
||||
disabled:cursor-not-allowed disabled:opacity-50 appearance-none $$ExtraClasses$$"
|
||||
$$HxAttrs$$>
|
||||
$$Options$$
|
||||
</select>
|
||||
$$Description$$
|
||||
</div>
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace Htmx.ApiDemo.Templates.Components;
|
||||
|
||||
/// <summary>
|
||||
/// shadcn-style Select (native HTML select) component.
|
||||
/// </summary>
|
||||
public sealed class Select : SelectBase
|
||||
{
|
||||
private readonly byte[] _idData;
|
||||
private readonly byte[] _nameData;
|
||||
private readonly byte[] _labelData;
|
||||
private readonly byte[] _descriptionData;
|
||||
private readonly byte[] _optionsData;
|
||||
private readonly byte[] _extraClassesData;
|
||||
private readonly byte[] _hxAttrsData;
|
||||
|
||||
/// <param name="options">Collection of (value, display) tuples. Mark selected with selectedValue.</param>
|
||||
public Select(
|
||||
string id,
|
||||
IEnumerable<(string Value, string Display)> options,
|
||||
string selectedValue = "",
|
||||
string name = "",
|
||||
string label = "",
|
||||
string description = "",
|
||||
string extraClasses = "",
|
||||
string hxAttrs = "")
|
||||
{
|
||||
_idData = id.ToUtf8Bytes();
|
||||
_nameData = (string.IsNullOrEmpty(name) ? id : name).ToUtf8Bytes();
|
||||
_extraClassesData = extraClasses.ToUtf8Bytes();
|
||||
_hxAttrsData = hxAttrs.ToUtf8Bytes();
|
||||
|
||||
_labelData = string.IsNullOrEmpty(label)
|
||||
? []
|
||||
: $"""<label for="{id}" class="text-sm font-medium leading-none">{label}</label>""".ToUtf8Bytes();
|
||||
|
||||
_descriptionData = string.IsNullOrEmpty(description)
|
||||
? []
|
||||
: $"""<p class="text-xs text-muted-foreground">{description}</p>""".ToUtf8Bytes();
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
foreach (var (value, display) in options)
|
||||
{
|
||||
var selected = value == selectedValue ? " selected" : "";
|
||||
sb.Append($"""<option value="{value}"{selected}>{display}</option>""");
|
||||
}
|
||||
_optionsData = sb.ToString().ToUtf8Bytes();
|
||||
}
|
||||
|
||||
protected override void RenderId(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_idData);
|
||||
protected override void RenderName(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_nameData);
|
||||
protected override void RenderLabel(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_labelData);
|
||||
protected override void RenderDescription(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_descriptionData);
|
||||
protected override void RenderOptions(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_optionsData);
|
||||
protected override void RenderExtraClasses(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_extraClassesData);
|
||||
protected override void RenderHxAttrs(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_hxAttrsData);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<div class="timepicker-root flex flex-col gap-1.5" data-use12h="$$Use12h$$" id="tp-$$UniqueId$$">
|
||||
$$Label$$
|
||||
<div class="flex items-center gap-1">
|
||||
|
||||
<!-- Hour -->
|
||||
<input type="number" min="$$HourMin$$" max="$$HourMax$$" step="1"
|
||||
class="timepicker-hour h-10 w-16 rounded-md border border-input bg-background px-2 text-center text-sm
|
||||
ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
|
||||
focus-visible:ring-offset-2"
|
||||
value="$$DefaultHour$$" />
|
||||
|
||||
<span class="text-sm font-bold text-foreground">:</span>
|
||||
|
||||
<!-- Minute -->
|
||||
<input type="number" min="0" max="59" step="1"
|
||||
class="timepicker-minute h-10 w-16 rounded-md border border-input bg-background px-2 text-center text-sm
|
||||
ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
|
||||
focus-visible:ring-offset-2"
|
||||
value="$$DefaultMinute$$" />
|
||||
|
||||
<!-- AM/PM toggle (only rendered when use12h=true) -->
|
||||
$$AmPmToggle$$
|
||||
|
||||
<!-- Hidden input that stores HH:MM value -->
|
||||
<input type="hidden" name="$$Name$$" class="timepicker-hidden" value="$$DefaultValue$$" />
|
||||
</div>
|
||||
$$Description$$
|
||||
</div>
|
||||
@@ -0,0 +1,89 @@
|
||||
namespace Htmx.ApiDemo.Templates.Components;
|
||||
|
||||
/// <summary>
|
||||
/// shadcn-style TimePicker. Syncs hour+minute inputs to a hidden HH:MM field via inline JS.
|
||||
/// </summary>
|
||||
public sealed class TimePicker : TimePickerBase
|
||||
{
|
||||
private readonly byte[] _uniqueIdData;
|
||||
private readonly byte[] _nameData;
|
||||
private readonly byte[] _use12hData;
|
||||
private readonly byte[] _labelData;
|
||||
private readonly byte[] _descriptionData;
|
||||
private readonly byte[] _defaultHourData;
|
||||
private readonly byte[] _defaultMinuteData;
|
||||
private readonly byte[] _defaultValueData;
|
||||
private readonly byte[] _hourMinData;
|
||||
private readonly byte[] _hourMaxData;
|
||||
private readonly byte[] _amPmToggleData;
|
||||
|
||||
public TimePicker(
|
||||
string name = "time",
|
||||
TimeOnly? selected = null,
|
||||
string label = "",
|
||||
string description = "",
|
||||
bool use12h = false)
|
||||
{
|
||||
var time = selected ?? TimeOnly.FromDateTime(DateTime.Now);
|
||||
var uid = Guid.NewGuid().ToString("N")[..8]; // short unique suffix
|
||||
|
||||
_uniqueIdData = uid.ToUtf8Bytes();
|
||||
_nameData = name.ToUtf8Bytes();
|
||||
_use12hData = (use12h ? "true" : "false").ToUtf8Bytes();
|
||||
_defaultValueData = time.ToString("HH:mm").ToUtf8Bytes();
|
||||
|
||||
_labelData = string.IsNullOrEmpty(label)
|
||||
? []
|
||||
: $"""<span class="text-sm font-medium leading-none">{label}</span>""".ToUtf8Bytes();
|
||||
|
||||
_descriptionData = string.IsNullOrEmpty(description)
|
||||
? []
|
||||
: $"""<p class="text-xs text-muted-foreground">{description}</p>""".ToUtf8Bytes();
|
||||
|
||||
if (use12h)
|
||||
{
|
||||
int hour12 = time.Hour % 12;
|
||||
if (hour12 == 0) hour12 = 12;
|
||||
bool isPm = time.Hour >= 12;
|
||||
|
||||
_defaultHourData = hour12.ToString().ToUtf8Bytes();
|
||||
_defaultMinuteData = time.Minute.ToString("D2").ToUtf8Bytes();
|
||||
_hourMinData = "1".ToUtf8Bytes();
|
||||
_hourMaxData = "12".ToUtf8Bytes();
|
||||
_amPmToggleData = BuildAmPmToggle(isPm);
|
||||
}
|
||||
else
|
||||
{
|
||||
_defaultHourData = time.Hour.ToString("D2").ToUtf8Bytes();
|
||||
_defaultMinuteData = time.Minute.ToString("D2").ToUtf8Bytes();
|
||||
_hourMinData = "0".ToUtf8Bytes();
|
||||
_hourMaxData = "23".ToUtf8Bytes();
|
||||
_amPmToggleData = [];
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] BuildAmPmToggle(bool isPm)
|
||||
{
|
||||
var amSel = isPm ? "" : " selected";
|
||||
var pmSel = isPm ? " selected" : "";
|
||||
return $"""
|
||||
<select class="timepicker-ampm h-10 rounded-md border border-input bg-background px-2 text-sm
|
||||
focus:outline-none focus:ring-2 focus:ring-ring">
|
||||
<option value="AM"{amSel}>AM</option>
|
||||
<option value="PM"{pmSel}>PM</option>
|
||||
</select>
|
||||
""".ToUtf8Bytes();
|
||||
}
|
||||
|
||||
protected override void RenderUniqueId(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_uniqueIdData);
|
||||
protected override void RenderName(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_nameData);
|
||||
protected override void RenderUse12h(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_use12hData);
|
||||
protected override void RenderLabel(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_labelData);
|
||||
protected override void RenderDescription(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_descriptionData);
|
||||
protected override void RenderDefaultHour(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_defaultHourData);
|
||||
protected override void RenderDefaultMinute(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_defaultMinuteData);
|
||||
protected override void RenderDefaultValue(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_defaultValueData);
|
||||
protected override void RenderHourMin(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_hourMinData);
|
||||
protected override void RenderHourMax(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_hourMaxData);
|
||||
protected override void RenderAmPmToggle(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_amPmToggleData);
|
||||
}
|
||||
Reference in New Issue
Block a user