{
  "name": "DateTimePicker",
  "category": "Forms",
  "subcategory": "Specialized",
  "description": "Combined date + time picker with timezone awareness.",
  "thumbnail": "/preview-cards/date-time-picker.png",
  "hasDocsPage": true,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/DateTimePicker/DateTimePicker.razor"
  ],
  "dependencies": [
    "calendar",
    "popover"
  ],
  "packageDependencies": [
    "Blazicons.Lucide"
  ],
  "cssVars": [
    "--color-accent",
    "--color-accent-foreground",
    "--color-border",
    "--color-destructive",
    "--color-foreground",
    "--color-input",
    "--color-muted",
    "--color-muted-foreground",
    "--color-primary",
    "--color-primary-foreground",
    "--radius"
  ],
  "api": {
    "name": "DateTimePicker",
    "category": "Forms",
    "subcategory": "Specialized",
    "description": "Combined date + time picker with timezone awareness.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/DateTimePicker/DateTimePicker.razor"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [],
    "parameters": [
      {
        "name": "FormField",
        "type": "FormField.FormFieldContext?",
        "default": null,
        "description": null,
        "isCascading": true,
        "captureUnmatched": false
      },
      {
        "name": "Required",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Invalid",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ErrorText",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "HelperText",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Label",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Name",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Value",
        "type": "DateTime?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ValueChanged",
        "type": "EventCallback<DateTime?>",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Use24Hour",
        "type": "bool",
        "default": "true",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ShowSeconds",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "DateFormat",
        "type": "string?",
        "default": null,
        "description": "Date portion format. When null (default) falls back to the effective culture's ShortDatePattern.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Placeholder",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Culture",
        "type": "System.Globalization.CultureInfo?",
        "default": null,
        "description": "Overrides CurrentCulture for date/time formatting. Leave null to honour ASP.NET request localization.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Disabled",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Clearable",
        "type": "bool",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "MinDate",
        "type": "DateTime?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "MaxDate",
        "type": "DateTime?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "IsDateDisabled",
        "type": "Func<DateTime, bool>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "FirstDayOfWeek",
        "type": "DayOfWeek?",
        "default": null,
        "description": "Override the locale-derived first day of week shown by the embedded calendar. Forwarded to the inner Calendar; leave null to fall back to CurrentCulture.",
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Class",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AdditionalAttributes",
        "type": "Dictionary<string, object>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": true
      }
    ],
    "events": [
      {
        "name": "ValueChanged",
        "type": "EventCallback<DateTime?>",
        "description": null
      }
    ],
    "enums": [],
    "records": [],
    "cssVars": [
      "--color-accent",
      "--color-accent-foreground",
      "--color-border",
      "--color-destructive",
      "--color-foreground",
      "--color-input",
      "--color-muted",
      "--color-muted-foreground",
      "--color-primary",
      "--color-primary-foreground",
      "--radius"
    ],
    "examples": [
      {
        "title": "Basic DateTimePicker",
        "code": "<DateTimePicker @bind-Value=\"_dateTime\" />"
      },
      {
        "title": "Clearable",
        "code": "<DateTimePicker @bind-Value=\"_dateTime\" Clearable=\"true\" />"
      },
      {
        "title": "12-Hour with Seconds",
        "code": "<DateTimePicker @bind-Value=\"_dateTime\" Use24Hour=\"false\" ShowSeconds=\"true\" />"
      },
      {
        "title": "Min & Max Date",
        "code": "<DateTimePicker @bind-Value=\"_dateTime\" MinDate=\"@_minDate\" MaxDate=\"@_maxDate\" Placeholder=\"Select within range\" />\n\n@code {\n    private DateTime? _dateTime;\n    private DateTime _minDate = DateTime.Today;\n    private DateTime _maxDate = DateTime.Today.AddDays(30);\n}"
      }
    ],
    "subComponents": {},
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "date-time-picker",
  "docsUrl": "https://lumeo.nativ.sh/components/date-time-picker",
  "sourceUrl": [
    {
      "file": "UI/DateTimePicker/DateTimePicker.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/DateTimePicker/DateTimePicker.razor"
    }
  ],
  "source": [
    {
      "path": "UI/DateTimePicker/DateTimePicker.razor",
      "content": "@namespace Lumeo\n\n@if (Label is not null && !InsideFormField)\n{\n    <label class=\"text-sm font-medium leading-none\">\n        @Label\n        @if (Required)\n        {\n            <span class=\"text-destructive ml-0.5\">*</span>\n        }\n    </label>\n}\n\n<Popover @bind-Open=\"_isOpen\">\n    <PopoverTrigger>\n        <button type=\"button\"\n                name=\"@Name\"\n                aria-required=\"@Required.ToString().ToLower()\"\n                aria-invalid=\"@EffectiveInvalid.ToString().ToLower()\"\n                class=\"@CssClass\"\n                disabled=\"@Disabled\"\n                @attributes=\"AdditionalAttributes\">\n            <Blazicon Svg=\"Lucide.Calendar\" class=\"mr-2 h-4 w-4 shrink-0\" />\n            @if (Value.HasValue)\n            {\n                <span>@FormatDateTime(Value.Value)</span>\n            }\n            else\n            {\n                <span class=\"text-muted-foreground\">@EffectivePlaceholder</span>\n            }\n            @if (Clearable && Value.HasValue)\n            {\n                <button type=\"button\" class=\"ml-auto pl-1 hover:text-foreground\" aria-label=\"@L[\"DateTimePicker.ClearDate\"]\" @onclick=\"HandleClear\" @onclick:stopPropagation=\"true\">\n                    <Blazicon Svg=\"Lucide.X\" class=\"h-3.5 w-3.5\" />\n                </button>\n            }\n        </button>\n    </PopoverTrigger>\n    <PopoverContent Align=\"PopoverContent.PopoverAlign.Start\" Class=\"w-auto p-0\">\n        <div class=\"flex\">\n            <div>\n                <Calendar Value=\"@_dateValue\" ValueChanged=\"HandleDateChanged\"\n                  MinDate=\"@(MinDate.HasValue ? (DateOnly?)DateOnly.FromDateTime(MinDate.Value) : null)\"\n                  MaxDate=\"@(MaxDate.HasValue ? (DateOnly?)DateOnly.FromDateTime(MaxDate.Value) : null)\"\n                  IsDateDisabled=\"@IsDateDisabled\"\n                  FirstDayOfWeek=\"@FirstDayOfWeek\" />\n            </div>\n            <div class=\"border-l border-border/40\">\n                <div class=\"p-3 pb-1\">\n                    <span class=\"text-xs font-medium text-muted-foreground\">@L[\"DateTimePicker.TimeLabel\"]</span>\n                </div>\n                <div class=\"flex divide-x divide-border/40\">\n                    @* Hours column *@\n                    <div class=\"flex flex-col h-[240px] overflow-y-auto py-1 scrollbar-thin\">\n                        @foreach (var hour in GetHours())\n                        {\n                            var h = hour;\n                            var isSelected = _timeValue.HasValue && GetDisplayHour(_timeValue.Value) == h;\n                            <button type=\"button\"\n                                    class=\"@(isSelected ? \"px-3 py-1.5 text-sm cursor-pointer bg-primary text-primary-foreground\" : \"px-3 py-1.5 text-sm cursor-pointer hover:bg-muted/50\")\"\n                                    @onclick=\"() => SelectHour(h)\">\n                                @h.ToString(\"D2\")\n                            </button>\n                        }\n                    </div>\n\n                    @* Minutes column *@\n                    <div class=\"flex flex-col h-[240px] overflow-y-auto py-1 scrollbar-thin\">\n                        @for (var i = 0; i < 60; i++)\n                        {\n                            var m = i;\n                            var isSelected = _timeValue.HasValue && _timeValue.Value.Minutes == m;\n                            <button type=\"button\"\n                                    class=\"@(isSelected ? \"px-3 py-1.5 text-sm cursor-pointer bg-primary text-primary-foreground\" : \"px-3 py-1.5 text-sm cursor-pointer hover:bg-muted/50\")\"\n                                    @onclick=\"() => SelectMinute(m)\">\n                                @m.ToString(\"D2\")\n                            </button>\n                        }\n                    </div>\n\n                    @* Seconds column *@\n                    @if (ShowSeconds)\n                    {\n                        <div class=\"flex flex-col h-[240px] overflow-y-auto py-1 scrollbar-thin\">\n                            @for (var i = 0; i < 60; i++)\n                            {\n                                var s = i;\n                                var isSelected = _timeValue.HasValue && _timeValue.Value.Seconds == s;\n                                <button type=\"button\"\n                                        class=\"@(isSelected ? \"px-3 py-1.5 text-sm cursor-pointer bg-primary text-primary-foreground\" : \"px-3 py-1.5 text-sm cursor-pointer hover:bg-muted/50\")\"\n                                        @onclick=\"() => SelectSecond(s)\">\n                                    @s.ToString(\"D2\")\n                                </button>\n                            }\n                        </div>\n                    }\n\n                    @* AM/PM column *@\n                    @if (!Use24Hour)\n                    {\n                        <div class=\"flex flex-col py-1\">\n                            @{\n                                var isAm = !_timeValue.HasValue || _timeValue.Value.Hours < 12;\n                                var amLabel = string.IsNullOrEmpty(EffectiveCulture.DateTimeFormat.AMDesignator) ? \"AM\" : EffectiveCulture.DateTimeFormat.AMDesignator;\n                                var pmLabel = string.IsNullOrEmpty(EffectiveCulture.DateTimeFormat.PMDesignator) ? \"PM\" : EffectiveCulture.DateTimeFormat.PMDesignator;\n                            }\n                            <button type=\"button\"\n                                    class=\"@(isAm ? \"px-3 py-1.5 text-sm cursor-pointer bg-primary text-primary-foreground\" : \"px-3 py-1.5 text-sm cursor-pointer hover:bg-muted/50\")\"\n                                    @onclick=\"() => SelectAmPm(true)\">\n                                @amLabel\n                            </button>\n                            <button type=\"button\"\n                                    class=\"@(!isAm ? \"px-3 py-1.5 text-sm cursor-pointer bg-primary text-primary-foreground\" : \"px-3 py-1.5 text-sm cursor-pointer hover:bg-muted/50\")\"\n                                    @onclick=\"() => SelectAmPm(false)\">\n                                @pmLabel\n                            </button>\n                        </div>\n                    }\n                </div>\n            </div>\n        </div>\n    </PopoverContent>\n</Popover>\n\n@if (!InsideFormField)\n{\n    @if (EffectiveInvalid && ErrorText is not null)\n    {\n        <p class=\"text-xs text-destructive\">@ErrorText</p>\n    }\n    else if (HelperText is not null)\n    {\n        <p class=\"text-xs text-muted-foreground\">@HelperText</p>\n    }\n}\n\n@inject Lumeo.Services.Localization.ILumeoLocalizer L\n\n@code {\n    [CascadingParameter] private FormField.FormFieldContext? FormField { get; set; }\n\n    [Parameter] public bool Required { get; set; }\n    [Parameter] public bool Invalid { get; set; }\n    [Parameter] public string? ErrorText { get; set; }\n    [Parameter] public string? HelperText { get; set; }\n    [Parameter] public string? Label { get; set; }\n    [Parameter] public string? Name { get; set; }\n\n    private bool EffectiveInvalid => Invalid || FormField?.HasError == true;\n    private bool InsideFormField => FormField is not null;\n\n    [Parameter] public DateTime? Value { get; set; }\n    [Parameter] public EventCallback<DateTime?> ValueChanged { get; set; }\n    [Parameter] public bool Use24Hour { get; set; } = true;\n    [Parameter] public bool ShowSeconds { get; set; }\n    /// <summary>\n    /// Date portion format. When null (default) falls back to the effective culture's\n    /// <c>ShortDatePattern</c>.\n    /// </summary>\n    [Parameter] public string? DateFormat { get; set; }\n    [Parameter] public string? Placeholder { get; set; }\n\n    /// <summary>\n    /// Overrides <see cref=\"System.Globalization.CultureInfo.CurrentCulture\"/> for\n    /// date/time formatting. Leave null to honour ASP.NET request localization.\n    /// </summary>\n    [Parameter] public System.Globalization.CultureInfo? Culture { get; set; }\n\n    private System.Globalization.CultureInfo EffectiveCulture => Culture ?? System.Globalization.CultureInfo.CurrentCulture;\n    private string EffectiveDateFormat => DateFormat ?? EffectiveCulture.DateTimeFormat.ShortDatePattern;\n\n    private string EffectivePlaceholder => Placeholder ?? L[\"DateTimePicker.Placeholder\"];\n    [Parameter] public bool Disabled { get; set; }\n    [Parameter] public bool Clearable { get; set; }\n    [Parameter] public DateTime? MinDate { get; set; }\n    [Parameter] public DateTime? MaxDate { get; set; }\n    [Parameter] public Func<DateTime, bool>? IsDateDisabled { get; set; }\n    /// <summary>\n    /// Override the locale-derived first day of week shown by the embedded calendar.\n    /// Forwarded to the inner <see cref=\"Calendar\"/>; leave null to fall back to\n    /// <see cref=\"System.Globalization.CultureInfo.CurrentCulture\"/>.\n    /// </summary>\n    [Parameter] public DayOfWeek? FirstDayOfWeek { get; set; }\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private bool _isOpen;\n    private DateOnly? _dateValue;\n    private TimeSpan? _timeValue;\n\n    private string CssClass => $\"flex h-9 w-full items-center rounded-md border {(EffectiveInvalid ? \"border-destructive ring-1 ring-destructive/20\" : \"border-input\")} bg-transparent px-3 py-2 text-sm shadow-sm hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-50 {Class}\".Trim();\n\n    protected override void OnParametersSet()\n    {\n        if (Value.HasValue)\n        {\n            _dateValue = DateOnly.FromDateTime(Value.Value);\n            _timeValue = Value.Value.TimeOfDay;\n        }\n        else\n        {\n            _dateValue = null;\n            _timeValue = null;\n        }\n    }\n\n    private string FormatDateTime(DateTime dt)\n    {\n        var timeFormat = Use24Hour\n            ? (ShowSeconds ? \"HH:mm:ss\" : \"HH:mm\")\n            : (ShowSeconds ? \"hh:mm:ss tt\" : \"hh:mm tt\");\n        return $\"{dt.ToString(EffectiveDateFormat, EffectiveCulture)} {dt.ToString(timeFormat, EffectiveCulture)}\";\n    }\n\n    private List<int> GetHours()\n    {\n        var hours = new List<int>();\n        if (Use24Hour)\n        {\n            for (var i = 0; i < 24; i++) hours.Add(i);\n        }\n        else\n        {\n            for (var i = 1; i <= 12; i++) hours.Add(i);\n        }\n        return hours;\n    }\n\n    private int GetDisplayHour(TimeSpan time)\n    {\n        if (Use24Hour) return time.Hours;\n        var h = time.Hours % 12;\n        return h == 0 ? 12 : h;\n    }\n\n    private async Task HandleDateChanged(DateOnly? date)\n    {\n        _dateValue = date;\n        await UpdateValue();\n    }\n\n    private async Task SelectHour(int hour)\n    {\n        var current = _timeValue ?? TimeSpan.Zero;\n        int actualHour;\n        if (Use24Hour)\n        {\n            actualHour = hour;\n        }\n        else\n        {\n            var isPm = current.Hours >= 12;\n            actualHour = hour % 12;\n            if (isPm) actualHour += 12;\n        }\n        _timeValue = new TimeSpan(actualHour, current.Minutes, current.Seconds);\n        await UpdateValue();\n    }\n\n    private async Task SelectMinute(int minute)\n    {\n        var current = _timeValue ?? TimeSpan.Zero;\n        _timeValue = new TimeSpan(current.Hours, minute, current.Seconds);\n        await UpdateValue();\n    }\n\n    private async Task SelectSecond(int second)\n    {\n        var current = _timeValue ?? TimeSpan.Zero;\n        _timeValue = new TimeSpan(current.Hours, current.Minutes, second);\n        await UpdateValue();\n    }\n\n    private async Task SelectAmPm(bool isAm)\n    {\n        var current = _timeValue ?? TimeSpan.Zero;\n        var hour = current.Hours % 12;\n        if (!isAm) hour += 12;\n        _timeValue = new TimeSpan(hour, current.Minutes, current.Seconds);\n        await UpdateValue();\n    }\n\n    private async Task UpdateValue()\n    {\n        if (_dateValue.HasValue)\n        {\n            var time = _timeValue ?? TimeSpan.Zero;\n            var dt = _dateValue.Value.ToDateTime(new TimeOnly(time.Hours, time.Minutes, time.Seconds));\n            Value = dt;\n            await ValueChanged.InvokeAsync(dt);\n        }\n    }\n\n    private async Task HandleClear()\n    {\n        Value = null;\n        _dateValue = null;\n        _timeValue = null;\n        await ValueChanged.InvokeAsync(null);\n    }\n}\n"
    }
  ],
  "slots": [],
  "serviceDependencies": [
    {
      "service": "ILumeoLocalizer",
      "namespace": "Lumeo.Services.Localization"
    }
  ],
  "cascadingDependencies": [
    {
      "name": "FormField",
      "type": "FormField.FormFieldContext?",
      "required": false
    }
  ],
  "relatedComponents": [
    {
      "name": "Popover",
      "reason": "used internally"
    },
    {
      "name": "Calendar",
      "reason": "used internally"
    }
  ],
  "keyboardInteractions": [],
  "tests": [
    "tests/Lumeo.Tests/Components/DateTimePicker/DateTimePickerTests.cs"
  ],
  "mdSummary": "# DateTimePicker\n\nCombined date + time picker with timezone awareness.\n\n**Package:** `Lumeo` · **Category:** Forms/Specialized\n**Docs:** https://lumeo.nativ.sh/components/date-time-picker\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| DateTimePicker | FormField | `FormField.FormFieldContext?` | `` |  |\n| DateTimePicker | Required | `bool` | `` |  |\n| DateTimePicker | Invalid | `bool` | `` |  |\n| DateTimePicker | ErrorText | `string?` | `` |  |\n| DateTimePicker | HelperText | `string?` | `` |  |\n| DateTimePicker | Label | `string?` | `` |  |\n| DateTimePicker | Name | `string?` | `` |  |\n| DateTimePicker | Value | `DateTime?` | `` |  |\n| DateTimePicker | ValueChanged | `EventCallback<DateTime?>` | `` |  |\n| DateTimePicker | Use24Hour | `bool` | `true` |  |\n| DateTimePicker | ShowSeconds | `bool` | `` |  |\n| DateTimePicker | DateFormat | `string?` | `` | Date portion format. When null (default) falls back to the effective culture's ShortDatePattern. |\n| DateTimePicker | Placeholder | `string?` | `` |  |\n| DateTimePicker | Culture | `System.Globalization.CultureInfo?` | `` | Overrides CurrentCulture for date/time formatting. Leave null to honour ASP.NET request localization. |\n| DateTimePicker | Disabled | `bool` | `` |  |\n| DateTimePicker | Clearable | `bool` | `` |  |\n| DateTimePicker | MinDate | `DateTime?` | `` |  |\n| DateTimePicker | MaxDate | `DateTime?` | `` |  |\n| DateTimePicker | IsDateDisabled | `Func<DateTime, bool>?` | `` |  |\n| DateTimePicker | FirstDayOfWeek | `DayOfWeek?` | `` | Override the locale-derived first day of week shown by the embedded calendar. Forwarded to the inner Calendar; leave null to fall back to CurrentCulture. |\n| DateTimePicker | Class | `string?` | `` |  |\n| DateTimePicker | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- (none)\n\n## Events\n- `ValueChanged` (`EventCallback<DateTime?>`)\n\n## Sub-components\n- (none)\n\n## Cascading values it expects\n- `FormField` (`FormField.FormFieldContext?`)\n\n## Services it injects\n- `ILumeoLocalizer` (Lumeo.Services.Localization)\n\n## Keyboard\n—\n\n## Example\n```razor\n<DateTimePicker />\n```\n\n## CSS variables\n`--color-accent`, `--color-accent-foreground`, `--color-border`, `--color-destructive`, `--color-foreground`, `--color-input`, `--color-muted`, `--color-muted-foreground`, `--color-primary`, `--color-primary-foreground`, `--radius`\n\n## Files\n- `UI/DateTimePicker/DateTimePicker.razor`\n"
}