{
  "tag": "ed-calendar",
  "kind": "component",
  "summary": "An always-visible day grid for picking a date, a range, or a whole week, month, quarter or year.",
  "status": "experimental",
  "level": "atom",
  "since": "1.0",
  "docs": "https://test-apps.eurodata.de/a/eurodata-ui/docs/components/calendar/",
  "usage": "https://test-apps.eurodata.de/a/eurodata-ui/docs/components/calendar/usage/",
  "playwright": "getByTestId('…')",
  "dependencies": [
    "ed-divider",
    "ed-icon"
  ],
  "use": [
    "Choosing a date as a primary, frequent task on the surface, where the grid should stay visible at all times — dashboards, schedule surfaces, and date filters",
    "Dates close to the present (roughly within a year), where scanning a month grid is faster than typing",
    "Picking where surrounding context matters — seeing weekends, adjacent days, today's position, and which days are unavailable",
    "Picking a whole period (a month, quarter, half-year, or year) by switching its granularity",
    "Selecting a whole calendar week as one unit when users report or filter by week"
  ],
  "notFor": [
    {
      "when": "Date entry as a secondary field in a larger form, or limited screen space — the calendar is the inline grid, not a popup, always visible and permanently occupying layout space",
      "instead": "[Date Picker](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/date-picker), the compact field that opens a calendar on demand"
    },
    {
      "when": "Users usually know the exact date and typing it would be faster — including dates far in the past or future that need heavy month-and-year navigation",
      "instead": "[Date Picker](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/date-picker), whose field is typed into directly"
    },
    {
      "when": "The field for choosing a start-and-end span inside a form",
      "instead": "[Date Picker](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/date-picker) with `range`, which is a form control and validates"
    }
  ],
  "rules": [
    {
      "id": "calendar/not-a-form-control",
      "rule": "Never treat it as a form control — it has no name, no validity and no form value of its own.",
      "check": "error"
    },
    {
      "id": "calendar/disable-invalid-dates",
      "rule": "Declare unavailable or illogical dates up front so users cannot pick them and hit a dead end — never flag a bad pick with an error after the fact when disabling the invalid dates prevents it. Don't add your own greyed-out rendering — the calendar renders them greyed out and non-selectable.",
      "check": "warning"
    },
    {
      "id": "calendar/bounds",
      "rule": "Restrict selection to a sensible minimum and maximum when only a bounded window is valid."
    },
    {
      "id": "calendar/keep-today-and-selection",
      "rule": "Keep the highlighting of today and of the current selection visible — the calendar draws both, so don't add your own.",
      "why": "Users need that reference point.",
      "check": "error"
    },
    {
      "id": "calendar/today-shortcut",
      "rule": "Offer a \"Today\" shortcut on surfaces where jumping back to the current date is common — the `footer` slot is designed for exactly that."
    },
    {
      "id": "calendar/locale-first-day",
      "rule": "Leave the first day of the week on its locale-driven default unless a domain convention genuinely requires otherwise — the calendar derives it from the locale.",
      "check": "warning"
    },
    {
      "id": "calendar/page-change-for-fetching",
      "rule": "Listen for `ed-calendar-page-change` when the entries of the month on screen have to be fetched.",
      "why": "It reports where the calendar is looking, which is a different question from what is selected."
    },
    {
      "id": "calendar/room-for-the-grid",
      "rule": "Never place it in a cramped container where the grid wraps or scrolls awkwardly.",
      "check": "error"
    },
    {
      "id": "calendar/range-only-for-dates",
      "rule": "Use `range` only with `type=\"date\"`, never together with month, quarter, half-year, week or year granularity — only `type=\"date\"` spans, and the attribute is ignored elsewhere.",
      "check": "error",
      "covered": true
    }
  ],
  "api": {
    "attributes": [
      {
        "name": "value",
        "property": "value",
        "type": "string",
        "description": "The selected value, in the wire form this `type` writes — `2026-07-01` for `date`, `2026-W27`\nfor `week`, then `2026-07`, `2026-Q3`, `2026-H2`, `2026`. Empty when nothing is selected.\n\nA string of another kind is ignored rather than reinterpreted, the way a native\n`<input type=\"month\">` ignores `2026-08-15` (measured in Chromium) — the value stays as it was.\n\nWith `range` set, a range has two ends and one string cannot carry both: `value` reads the\nstart, and writing it is ignored — except for `''`, which clears, since emptying means the same\nfor one value and for two. start and end are the way in and out.\n\nThe attribute is a **starting** value and is not reflected, the way `<input value>` is: what the\nuser picks afterwards does not rewrite it, so the markup keeps saying what the calendar opened\nwith."
      },
      {
        "name": "start",
        "property": "start",
        "type": "string",
        "description": "The start of the selected range, in the same wire form as value. Empty when no range is\nselected, and inert unless `range` is set.\n\nSetting `start` alone places the anchor without committing a range — which is both what the\nfirst click of the gesture does and what \"from X, indefinitely\" means. The calendar cannot tell\nthose apart and does not try to; whether a one-ended range counts as a value is the consumer's\nquestion, and a picker answers it by keeping its panel open."
      },
      {
        "name": "end",
        "property": "end",
        "type": "string",
        "description": "The end of the selected range, in the same wire form as value."
      },
      {
        "name": "min",
        "property": "min",
        "type": "string",
        "description": "The earliest selectable value: the wire form this `type` writes, or a `YYYY-MM-DD` day to bound\nthe range more finely than the type's own unit. Empty for no bound.\n\nAnything else means no bound rather than an approximation of one — a native field ignores a\n`min` it cannot read, and a bound guessed from a value of the wrong kind would disable days the\nconsumer never excluded."
      },
      {
        "name": "max",
        "property": "max",
        "type": "string",
        "description": "The latest selectable value, in the same forms as min. Empty for no bound."
      },
      {
        "name": "default-month",
        "property": "defaultMonth",
        "type": "string",
        "description": "Which page to open on when nothing is selected: `2026-11` or `2026-11-01`, both opening\nNovember 2026. Only the part the page needs is read — a day grid pages by month, a period list\nby year.\n\nUnlike min and max this is **not** in the type's wire form, because a page is\nnot a value: `type=\"month\"` selects a month and pages by year, so neither `2026-11` nor `2026`\nwould mean the same thing for both jobs. A month names a page for every type."
      },
      {
        "name": "show-week-numbers",
        "property": "showWeekNumbers",
        "type": "boolean",
        "default": "false",
        "description": "When true, a week-number column is shown in the day grid."
      },
      {
        "name": "first-day-of-week",
        "property": "firstDayOfWeek",
        "type": "1 | 2 | 3 | 4 | 5 | 6 | 7 | 'auto'",
        "default": "'auto'",
        "description": "The first day of the week as an **ISO 8601 weekday**: `1` for Monday through `7` for Sunday, or\n`'auto'` to read it from the locale via `Intl.Locale`, falling back to Monday.\n\n`0` is not a weekday here — see EdCalendarFirstDay for why the numbering is ISO rather\nthan `getDay()`'s.\n\nIgnored for `type=\"week\"`: an ISO week starts on Monday by definition, so that grid renders\nMonday-first whatever this says."
      },
      {
        "name": "range",
        "property": "range",
        "type": "boolean",
        "description": "Pick a span of two values instead of one: the first click sets the anchor, the second closes\nthe range, and dragging from the anchor marks the span as the pointer moves. start and\nend carry the two ends.\n\nA range is orthogonal to `type` in this API, but only `type=\"date\"` spans one today; the other\ntypes pick a single value regardless."
      },
      {
        "name": "panels",
        "property": "panels",
        "type": "number",
        "description": "Number of month panels rendered while a range is live: `1` or `2` (default `2`). Every other\ncombination renders a single panel. `panels=\"1\"` shows one month with the month/year\ndrill-down (matching the single date pickers); `panels=\"2\"` shows two linked months.\nInvalid values fall back to `2`."
      },
      {
        "name": "type",
        "property": "type",
        "type": "'year' | 'month' | 'quarter' | 'week' | 'date' | 'halfyear'",
        "description": "What the calendar lets the user pick, which is also the wire form value takes:\n\n- `date` (default) — a day grid; `2026-07-01`.\n- `week` — the same grid, with a clickable week-number column that takes the whole week;\n  `2026-W27`.\n- `month` — a list of months; `2026-07`.\n- `quarter` — four quarters; `2026-Q3`.\n- `halfyear` — two halves; `2026-H2`.\n- `year` — a list of years; `2026`.\n\nInvalid values fall back to `'date'` with a `logger.warn`."
      },
      {
        "name": "dir",
        "property": "dir",
        "type": "string"
      },
      {
        "name": "lang",
        "property": "lang",
        "type": "string"
      }
    ],
    "properties": [
      {
        "name": "isDisabled",
        "type": "(value: string) => boolean",
        "description": "Answers whether one value is selectable, on top of `min`, `max` and the type's own rules.\nReceives the value in the same wire form as value, so a consumer's rule is written\nagainst the string it already holds rather than against a `Date` it has to build."
      },
      {
        "name": "selectionStrategy",
        "type": "SelectionStrategy",
        "description": "The selection strategy controlling how day cells are highlighted and what value is emitted.\nReassigning this property triggers a re-render so cell states reflect the new strategy.\nSetting this externally opts out of the internal `type`-driven strategy recreation —\nthe consumer becomes responsible for keeping its strategy consistent with `type`."
      }
    ],
    "methods": [
      {
        "name": "focus",
        "parameters": [
          {
            "name": "options",
            "type": "FocusOptions",
            "optional": true
          }
        ],
        "returns": "void",
        "description": "Move focus into the calendar. Delegates to whichever element currently holds the roving\ntabindex so popup wrappers (`<ed-date-picker>`) can hand focus over through the standard\nHTMLElement.focus() contract.\n\nBoth shapes have to be covered: the `date` type renders a grid of day cells, every other type\nrenders a listbox of period options. Looking only for the grid made this a no-op for four of\nthe five types."
      }
    ],
    "events": [
      {
        "name": "ed-calendar-page-change",
        "react": "onEdCalendarPageChange",
        "type": "EdCalendarPageChangeEvent",
        "description": "Emitted when the page on screen moves, whichever way it was moved. The detail carries `page`: `YYYY-MM` for the types that page by month, `YYYY` for those that page by year. `type=\"year\"` has one page and never fires. Settling on the first page is not a move and says nothing."
      },
      {
        "name": "ed-calendar-change",
        "react": "onEdCalendarChange",
        "type": "EdCalendarChangeEvent",
        "description": "Emitted when the user picks a value. The detail carries `value`, `start` and `end` as wire strings, in the form the current `type` writes. With `range` set it fires twice per gesture — once when the anchor is placed, once when the span closes — and `end` is what tells the two apart."
      }
    ],
    "slots": [
      {
        "name": "footer",
        "description": "Custom footer content (e.g. a \"Today\" jump button). Rendered as the first row of the footer."
      },
      {
        "name": "footer-controls",
        "description": "Action controls for the footer's second row, e.g. an OK/Cancel pair. Nothing is rendered by default, and nothing is wired: what a control does is the consumer's."
      }
    ],
    "cssProperties": [],
    "parts": [
      {
        "name": "base",
        "description": "The outer wrapper element."
      },
      {
        "name": "header",
        "description": "The row of page controls above the body. Not rendered for `type=\"year\"`, whose list is a single page."
      },
      {
        "name": "header-spacer",
        "description": "The flexible gap between the month and year cyclers."
      },
      {
        "name": "cycler",
        "description": "Each cycler block: the month cycler for `type=\"date\"` and `type=\"week\"`, the year cycler for every type that has pages."
      },
      {
        "name": "cycler-month",
        "description": "The month cycler block."
      },
      {
        "name": "cycler-year",
        "description": "The year cycler block."
      },
      {
        "name": "cycler-prev",
        "description": "A cycler's previous-page button."
      },
      {
        "name": "cycler-next",
        "description": "A cycler's next-page button."
      },
      {
        "name": "cycler-trigger",
        "description": "A cycler's menu trigger (shows the current month or year, opens the in-surface menu)."
      },
      {
        "name": "range-month-label",
        "description": "A read-only \"Month Year\" label shown above each calendar in two-panel range mode (one per panel)."
      },
      {
        "name": "body",
        "description": "The body area below the header. Contains the day grid or the active picker menu."
      },
      {
        "name": "picker-menu",
        "description": "Any in-surface list of periods: the body's own list for `type=\"month\"` and up, and the month or year menu a cycler trigger opens."
      },
      {
        "name": "picker-menu-primary",
        "description": "Only the list a click selects from, as opposed to a cycler's navigation menu."
      },
      {
        "name": "picker-item",
        "description": "Each menu item in the picker menu."
      },
      {
        "name": "picker-item-selected",
        "description": "The currently-selected menu item."
      },
      {
        "name": "footer-divider",
        "description": "The divider between the panel content and the footer. Only visible when the footer is."
      },
      {
        "name": "footer",
        "description": "The footer wrapper. Only visible when at least one footer slot has content."
      },
      {
        "name": "footer-row",
        "description": "Each row inside the footer (first row = `footer` slot, second row = `footer-controls` slot)."
      },
      {
        "name": "footer-row-custom",
        "description": "The first footer row (the `footer` slot)."
      },
      {
        "name": "footer-row-controls",
        "description": "The second footer row (the `footer-controls` slot)."
      },
      {
        "name": "grid",
        "description": "The day grid container."
      },
      {
        "name": "weekday-header-row",
        "description": "The row of weekday column headers."
      },
      {
        "name": "weekday-header-cell",
        "description": "Each weekday column header cell."
      },
      {
        "name": "day-row",
        "description": "Each body row of day cells."
      },
      {
        "name": "day-cell",
        "description": "Each day cell button."
      },
      {
        "name": "day-cell-label",
        "description": "The inner state layer of a day cell (holds the digit; carries the selected/endpoint circle and the today ring)."
      },
      {
        "name": "day-cell-today",
        "description": "The day cell for today."
      },
      {
        "name": "day-cell-selected",
        "description": "A selected day cell (single-selection strategy)."
      },
      {
        "name": "day-cell-start",
        "description": "The first cell of a span: the range start, or the Monday of the picked week."
      },
      {
        "name": "day-cell-in-range",
        "description": "A cell inside a span."
      },
      {
        "name": "day-cell-end",
        "description": "The last cell of a span: the range end, or the Sunday of the picked week."
      },
      {
        "name": "day-cell-preview-start",
        "description": "The preview start cell while a range is being built."
      },
      {
        "name": "day-cell-preview-in-range",
        "description": "A cell inside the in-progress preview range."
      },
      {
        "name": "day-cell-preview-end",
        "description": "The preview end cell while a range is being built."
      },
      {
        "name": "panel",
        "description": "Wrapper around each panel (header + body). Two are rendered while a range is live, one otherwise."
      },
      {
        "name": "week-number",
        "description": "The week-number cell (rendered when `show-week-numbers` is set, and always for `type=\"week\"`). For `type=\"week\"` the cell is a clickable `<div role=\"rowheader\">` that selects the whole week."
      }
    ],
    "states": []
  },
  "import": "import '@eurodata/eurodata-ui/dist/components/calendar/calendar.js';",
  "react": {
    "component": "EdCalendar",
    "import": "import EdCalendar from '@eurodata/eurodata-ui/dist/react/calendar/index.js';"
  }
}
