{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"date-range-picker","title":"Date Range Picker","description":"Segmented, locale-aware date-range picker with one Calendar action and two native form values.","dependencies":["@internationalized/date@3.12.3","lucide-react@1.18.0","react-aria-components@1.20.0"],"registryDependencies":["@siteplane/base","@siteplane/button","@siteplane/calendar","@siteplane/date-field","@siteplane/input","@siteplane/input-group","@siteplane/locale"],"files":[{"path":"packages/ui/src/components/date-range-picker/date-range-picker.tsx","content":"\"use client\";\n\nimport { type CalendarDate, type DateValue } from \"@internationalized/date\";\nimport { CalendarDaysIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport {\n  Button as DateRangeButtonPrimitive,\n  DateFieldContext,\n  DateFieldStateContext,\n  DateInput as DateRangeInputPrimitive,\n  DateRangePicker as DateRangePickerPrimitive,\n  type DateRangePickerState,\n  DateSegment as DateRangeSegmentPrimitive,\n  Dialog as DateRangeDialogPrimitive,\n  Group as DateRangeGroupPrimitive,\n  Popover as DateRangePopoverPrimitive,\n  type RangeValue,\n  useLocale,\n  useSlottedContext,\n} from \"react-aria-components\";\n\nimport { isInvalidAriaValue } from \"../../system/form/aria-invalid\";\nimport { useFormReset } from \"../../system/form/use-form-reset\";\nimport { normalizeFormattedText } from \"../../system/locale/formatted-text\";\nimport { SiteplaneLocaleProvider } from \"../../system/locale/siteplane-locale-provider\";\nimport {\n  type SiteplaneControlSize,\n  SiteplaneUIPortalScope,\n  useResolvedControlSize,\n} from \"../../system/provider/siteplane-ui-provider\";\nimport {\n  surfaceClassNames,\n  type FormControlTextSize,\n  type FormControlVariant,\n} from \"../../system/styles/surface\";\nimport { cn } from \"../../system/utils/cn\";\nimport { Button } from \"../button\";\nimport { Calendar } from \"../calendar\";\nimport {\n  calendarMonthBounds,\n  type CalendarOptions,\n  type DateRange,\n  fromNativeDate,\n  isCalendarDayDisabled,\n  toNativeDate,\n} from \"../calendar/calendar\";\nimport {\n  type FieldControlBridge,\n  hasClearedSegment,\n  type SegmentedValue,\n  useFieldControlBridge,\n  useFieldControlProxy,\n  useFieldErrorSegments,\n  useSegmentedValue,\n} from \"../field/field-control-bridge\";\nimport { Input } from \"../input\";\nimport { ExternalInputOwner } from \"../input/input\";\nimport { InputGroup, InputGroupAddon, InputGroupText } from \"../input-group\";\n\n// The shared date segment styles are owned by DateField.\nimport \"../date-field/date-field.css\";\nimport \"./date-range-picker.css\";\n\n// The range shape is the Calendar's, so a range moves between the picker and\n// a range Calendar as it is.\nexport type { DateRange } from \"../calendar/calendar\";\n\nexport interface DateRangePickerProps {\n  \"aria-describedby\"?: string | undefined;\n  \"aria-invalid\"?: React.AriaAttributes[\"aria-invalid\"];\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string | undefined;\n  calendarProps?: CalendarOptions;\n  className?: string;\n  defaultValue?: DateRange;\n  disabled?: boolean;\n  endLabel?: string;\n  endName?: string;\n  form?: string;\n  /**\n   * `action-end` ends the field with the button that opens the calendar;\n   * `none` leaves the range to typing, as DateField's `iconMode=\"none\"` does,\n   * for a range beside a calendar of its own.\n   */\n  iconMode?: \"action-end\" | \"none\" | undefined;\n  /**\n   * The error a required picker shows while its range is incomplete, such as\n   * after a segment is cleared. Default: \"Enter a complete date range.\"\n   */\n  incompleteLabel?: string;\n  locale?: string;\n  onValueChange?: (value: DateRange | undefined) => void;\n  openPickerLabel?: string;\n  readOnly?: boolean;\n  required?: boolean;\n  size?: SiteplaneControlSize;\n  startLabel?: string;\n  startName?: string;\n  textSize?: FormControlTextSize;\n  value?: DateRange | null;\n  variant?: FormControlVariant;\n}\n\nconst actionSizes = {\n  md: \"icon-xs\",\n  lg: \"icon-xs\",\n  sm: \"icon-2xs\",\n  xl: \"icon-sm\",\n} as const satisfies Record<\n  SiteplaneControlSize,\n  \"icon-2xs\" | \"icon-xs\" | \"icon-sm\"\n>;\n\nfunction toCalendarDate(value: Date | null | undefined): CalendarDate | null {\n  return value ? fromNativeDate(value) : null;\n}\n\nfunction toOptionalNativeDate(\n  value: DateValue | null | undefined,\n): Date | undefined {\n  return value ? toNativeDate(value) : undefined;\n}\n\nfunction toInternalRange(\n  value: DateRange | null | undefined,\n): RangeValue<CalendarDate> | null {\n  const start = toCalendarDate(value?.from);\n  const end = toCalendarDate(value?.to);\n  return start && end ? { end, start } : null;\n}\n\nfunction toNativeRange(\n  value: RangeValue<DateValue | null> | null | undefined,\n): DateRange | undefined {\n  if (!value?.start && !value?.end) return undefined;\n  return {\n    from: toOptionalNativeDate(value.start),\n    to: toOptionalNativeDate(value.end),\n  };\n}\n\nfunction formatDate(value: DateValue | null | undefined): string {\n  if (!value) return \"\";\n  return `${String(value.year).padStart(4, \"0\")}-${String(value.month).padStart(2, \"0\")}-${String(value.day).padStart(2, \"0\")}`;\n}\n\nfunction formatRange(value: RangeValue<DateValue | null> | null): string {\n  return value?.start && value.end\n    ? `${formatDate(value.start)}/${formatDate(value.end)}`\n    : \"\";\n}\n\ntype ClearedEndpoints = { end: boolean; start: boolean };\n\nfunction DateRangeHiddenInputs({\n  cleared,\n  controlled,\n  controlledInvalid,\n  controlRef,\n  defaultValue,\n  endName,\n  field,\n  focusTarget,\n  form,\n  required,\n  segmented,\n  startName,\n  state,\n}: {\n  cleared: ClearedEndpoints;\n  controlled: boolean;\n  controlledInvalid: boolean | undefined;\n  controlRef: React.RefObject<HTMLDivElement | null>;\n  defaultValue: DateRange | undefined;\n  endName: string | undefined;\n  field: FieldControlBridge;\n  focusTarget: () => HTMLElement | null;\n  form: string | undefined;\n  required: boolean | undefined;\n  segmented: SegmentedValue<RangeValue<DateValue>>;\n  startName: string | undefined;\n  state: DateRangePickerState;\n}): React.ReactElement {\n  const startInputRef = useFormReset<HTMLInputElement>(form, () => {\n    if (!controlled) state.setValue(toInternalRange(defaultValue));\n  });\n  // A date with a cleared segment has no value, although React Aria still\n  // holds the last complete one.\n  const startValue = cleared.start ? null : state.value.start;\n  const endValue = cleared.end ? null : state.value.end;\n  const start = formatDate(startValue);\n  const end = formatDate(endValue);\n  React.useLayoutEffect(() => {\n    const ownerValue =\n      state.value.start && state.value.end\n        ? { end: state.value.end, start: state.value.start }\n        : null;\n    segmented.sync({\n      cleared: cleared.start || cleared.end,\n      effective:\n        startValue && endValue ? { end: endValue, start: startValue } : null,\n      value: ownerValue,\n    });\n  });\n  const proxyProps = useFieldControlProxy({\n    controlled: controlledInvalid !== undefined,\n    disabled: field.disabled,\n    displayValidation: state.displayValidation,\n    focusTarget,\n    form,\n    name: undefined,\n    onRevalidate: state.commitValidation,\n    realtimeValidation: state.realtimeValidation,\n    required,\n    value: start && end ? `${start}/${end}` : \"\",\n  });\n  useFieldErrorSegments(controlRef, {\n    controlled: controlledInvalid !== undefined,\n    ownerInvalid: state.isInvalid,\n  });\n\n  return (\n    <>\n      <Input\n        disabled={field.disabled}\n        form={form}\n        name={startName}\n        readOnly\n        ref={startInputRef}\n        required={required}\n        type=\"hidden\"\n        value={start}\n      />\n      <Input\n        disabled={field.disabled}\n        form={form}\n        name={endName}\n        readOnly\n        required={required}\n        type=\"hidden\"\n        value={end}\n      />\n      {proxyProps ? (\n        <ExternalInputOwner>\n          <Input nativeInput unstyled {...proxyProps} />\n        </ExternalInputOwner>\n      ) : null}\n    </>\n  );\n}\n\n// React Aria names each date segment with its date field's name and the range\n// label, such as \"month, Start Date, Trip dates\". A label replaces the\n// localized Start Date or End Date field name.\nfunction DateRangeEndpoint({\n  label,\n  onClearedChange,\n  slot,\n}: {\n  label: string | undefined;\n  onClearedChange: (slot: \"end\" | \"start\", cleared: boolean) => void;\n  slot: \"end\" | \"start\";\n}): React.ReactElement {\n  const fieldProps = useSlottedContext(DateFieldContext, slot);\n\n  return (\n    <DateFieldContext.Provider\n      value={\n        label && fieldProps\n          ? { ...fieldProps, \"aria-label\": label }\n          : fieldProps\n      }\n    >\n      <DateRangeInputPrimitive\n        className={cn(\n          \"sp-base-date-field\",\n          `sp-date-range-picker-${slot}`,\n          \"contents\",\n        )}\n        data-foundation=\"date-field\"\n        data-profile=\"input-group\"\n        data-slot={`date-range-picker-${slot}`}\n        slot={slot}\n      >\n        {(segment) => (\n          <DateRangeSegment\n            onClearedChange={(cleared) => onClearedChange(slot, cleared)}\n            segment={segment}\n          />\n        )}\n      </DateRangeInputPrimitive>\n    </DateFieldContext.Provider>\n  );\n}\n\n// The start date, the dash and the end date read in the locale's direction,\n// which React Aria's arrow keys follow, whatever the page's: an en-US range\n// stays 01/01/2024 – 10/14/2026 in a right-to-left page. The dir attribute\n// also isolates the row from the text around it; date-range-picker.css keeps\n// the field's insets on the field's sides.\nfunction DateRangeSegments({\n  children,\n}: {\n  children: React.ReactNode;\n}): React.ReactElement {\n  const { direction } = useLocale();\n\n  return (\n    <div\n      className=\"sp-date-range-picker-segments\"\n      data-slot=\"date-range-picker-segments\"\n      dir={direction}\n    >\n      {children}\n    </div>\n  );\n}\n\n// Each date of the range keeps its own field state inside its DateInput; the\n// segments tell the picker whether that date shows a cleared segment.\nfunction DateRangeSegment({\n  onClearedChange,\n  segment,\n}: {\n  onClearedChange: (cleared: boolean) => void;\n  segment: React.ComponentProps<typeof DateRangeSegmentPrimitive>[\"segment\"];\n}): React.ReactElement {\n  const cleared = hasClearedSegment(React.useContext(DateFieldStateContext));\n  React.useLayoutEffect(() => onClearedChange(cleared));\n\n  // The segment shows the text that the server's ICU and the browser's agree\n  // on, as the Date Field's do (date-field.tsx), so the prerendered range\n  // hydrates.\n  return (\n    <DateRangeSegmentPrimitive\n      className=\"sp-date-field-segment sp-date-segment\"\n      data-literal-kind={\n        segment.type === \"literal\" && /[./-]/.test(segment.text)\n          ? \"separator\"\n          : undefined\n      }\n      data-slot=\"date-field-segment\"\n      segment={segment}\n    >\n      {({ text }) => normalizeFormattedText(text)}\n    </DateRangeSegmentPrimitive>\n  );\n}\n\nexport function DateRangePicker({\n  \"aria-describedby\": ariaDescribedBy,\n  \"aria-invalid\": ariaInvalid,\n  \"aria-label\": ariaLabel,\n  \"aria-labelledby\": ariaLabelledBy,\n  calendarProps,\n  className,\n  defaultValue,\n  disabled,\n  endLabel,\n  endName,\n  form,\n  iconMode = \"action-end\",\n  incompleteLabel = \"Enter a complete date range.\",\n  locale,\n  onValueChange,\n  openPickerLabel = \"Open date range calendar\",\n  readOnly,\n  required,\n  size,\n  startLabel,\n  startName,\n  textSize,\n  value,\n  variant = \"default\",\n}: DateRangePickerProps): React.ReactElement {\n  const controlled = value !== undefined;\n  const resolvedSize = useResolvedControlSize(size);\n  const controlRef = React.useRef<HTMLDivElement>(null);\n  const field = useFieldControlBridge({\n    \"aria-describedby\": ariaDescribedBy,\n    \"aria-labelledby\": ariaLabelledBy,\n    disabled,\n  });\n  const fieldAriaLabel = field.labelledBy ? undefined : ariaLabel;\n  // React Aria validates typed dates unless the application controls the\n  // invalid state through aria-invalid or an invalid Field.\n  const controlledInvalid =\n    ariaInvalid !== undefined\n      ? isInvalidAriaValue(ariaInvalid)\n      : field.invalid || undefined;\n  // A cleared segment empties the range's value.\n  const [cleared, setCleared] = React.useState<ClearedEndpoints>({\n    end: false,\n    start: false,\n  });\n  const onClearedChange = React.useCallback(\n    (slot: \"end\" | \"start\", next: boolean) =>\n      setCleared((current) =>\n        current[slot] === next ? current : { ...current, [slot]: next },\n      ),\n    [],\n  );\n  const segmented = useSegmentedValue<RangeValue<DateValue>>({\n    controlled,\n    defaultValue: toInternalRange(defaultValue),\n    incompleteMessage: incompleteLabel,\n    onValueChange: (nextValue) =>\n      onValueChange?.(\n        nextValue?.start && nextValue.end\n          ? toNativeRange(nextValue)\n          : undefined,\n      ),\n    required,\n    serialize: formatRange,\n    value: toInternalRange(value),\n  });\n  // React Aria's optional props reject `undefined` under\n  // exactOptionalPropertyTypes, so unset values are omitted.\n  const picker = (\n    <DateRangePickerPrimitive\n      {...(field.describedBy === undefined\n        ? {}\n        : { \"aria-describedby\": field.describedBy })}\n      {...(fieldAriaLabel === undefined\n        ? {}\n        : { \"aria-label\": fieldAriaLabel })}\n      {...(field.labelledBy === undefined\n        ? {}\n        : { \"aria-labelledby\": field.labelledBy })}\n      className=\"sp-date-range-picker sp-base-date-range-picker contents\"\n      data-foundation=\"date-range-picker\"\n      data-slot=\"date-range-picker\"\n      {...(controlled\n        ? { value: segmented.ownerValue }\n        : { defaultValue: toInternalRange(defaultValue) })}\n      isDateUnavailable={(date) =>\n        isCalendarDayDisabled(toNativeDate(date), calendarProps?.disabled)\n      }\n      isDisabled={field.disabled}\n      {...(controlledInvalid === undefined\n        ? {}\n        : { isInvalid: controlledInvalid })}\n      {...(readOnly === undefined ? {} : { isReadOnly: readOnly })}\n      {...(required === undefined ? {} : { isRequired: required })}\n      {...calendarMonthBounds(calendarProps ?? {})}\n      // Typed segments start from the calendar's today, `todayDate` when set.\n      {...(calendarProps?.todayDate === undefined\n        ? {}\n        : { placeholderValue: fromNativeDate(calendarProps.todayDate) })}\n      onChange={segmented.onOwnerChange}\n      shouldCloseOnSelect\n      shouldForceLeadingZeros\n      validate={segmented.validate}\n    >\n      {({ isInvalid, state }) => (\n        <>\n          <DateRangeGroupPrimitive\n            className={cn(\"inline-flex max-w-full\", className)}\n          >\n            <InputGroup\n              aria-label={undefined}\n              className={cn(\"sp-date-range-control w-full\", className)}\n              data-icon-mode={iconMode}\n              data-invalid={\n                (controlledInvalid ?? (isInvalid || field.valid === false)) ||\n                undefined\n              }\n              data-readonly={(readOnly && !field.disabled) || undefined}\n              data-size={resolvedSize}\n              data-slot=\"date-range-picker-control\"\n              ref={controlRef}\n              // The field text scale: 14 px, or 16 px at xl.\n              textSize={\n                textSize ?? (resolvedSize === \"xl\" ? \"base\" : undefined)\n              }\n              variant={variant}\n            >\n              <DateRangeSegments>\n                <DateRangeEndpoint\n                  label={startLabel}\n                  onClearedChange={onClearedChange}\n                  slot=\"start\"\n                />\n                <InputGroupText\n                  aria-hidden=\"true\"\n                  className=\"mx-1 text-foreground\"\n                  data-slot=\"date-range-picker-separator\"\n                >\n                  –\n                </InputGroupText>\n                <DateRangeEndpoint\n                  label={endLabel}\n                  onClearedChange={onClearedChange}\n                  slot=\"end\"\n                />\n              </DateRangeSegments>\n              {iconMode === \"action-end\" ? (\n                <InputGroupAddon align=\"inline-end\" className=\"ms-auto\">\n                  <Button\n                    aria-label={openPickerLabel}\n                    data-slot=\"date-range-picker-trigger\"\n                    disabled={field.disabled || readOnly}\n                    render={<DateRangeButtonPrimitive />}\n                    profile=\"field-action\"\n                    size={actionSizes[resolvedSize]}\n                  >\n                    <CalendarDaysIcon aria-hidden=\"true\" />\n                  </Button>\n                </InputGroupAddon>\n              ) : null}\n            </InputGroup>\n          </DateRangeGroupPrimitive>\n          {/* Without its button the range opens no calendar: React Aria's\n              picker works with its date fields alone. */}\n          {iconMode === \"action-end\" ? (\n            <DateRangePopoverPrimitive\n              className={cn(\n                \"sp-date-range-picker-popover\",\n                surfaceClassNames.floatingOverlay,\n                \"sp-date-range-popover\",\n              )}\n              data-slot=\"date-range-picker-popover\"\n              offset={4}\n              placement=\"bottom end\"\n            >\n              <SiteplaneUIPortalScope>\n                <DateRangeDialogPrimitive\n                  className=\"sp-date-range-picker-dialog outline-none\"\n                  data-slot=\"date-range-picker-dialog\"\n                >\n                  <Calendar\n                    {...calendarProps}\n                    {...(fieldAriaLabel === undefined\n                      ? {}\n                      : { \"aria-label\": fieldAriaLabel })}\n                    {...(fieldAriaLabel || field.labelledBy === undefined\n                      ? {}\n                      : { \"aria-labelledby\": field.labelledBy })}\n                    className=\"sp-date-range-calendar\"\n                    data-slot=\"date-range-picker-calendar\"\n                    selectionMode=\"range\"\n                  />\n                </DateRangeDialogPrimitive>\n              </SiteplaneUIPortalScope>\n            </DateRangePopoverPrimitive>\n          ) : null}\n          <DateRangeHiddenInputs\n            cleared={cleared}\n            controlled={controlled}\n            controlledInvalid={controlledInvalid}\n            controlRef={controlRef}\n            defaultValue={defaultValue}\n            endName={endName}\n            field={field}\n            focusTarget={() =>\n              controlRef.current?.querySelector<HTMLElement>(\n                '[role=\"spinbutton\"], [role=\"textbox\"]',\n              ) ?? null\n            }\n            form={form}\n            required={required}\n            segmented={segmented}\n            startName={startName}\n            state={state}\n          />\n        </>\n      )}\n    </DateRangePickerPrimitive>\n  );\n\n  return locale ? (\n    <SiteplaneLocaleProvider locale={locale}>{picker}</SiteplaneLocaleProvider>\n  ) : (\n    picker\n  );\n}\n","type":"registry:ui","target":"~/src/siteplane/components/date-range-picker/date-range-picker.tsx"},{"path":"packages/ui/src/components/date-range-picker/date-range-picker.css","content":"@layer theme, base, components, siteplane, utilities;\n\n@layer siteplane {\n  /* The heights are the Input scale (28, 32, 36 and 48 px), so the picker is as\n   * tall as the Input, Select, Button, DateField and TimeField of the same size. */\n  .sp-date-range-control {\n    --date-range-picker-control-height: var(--sp-rw-control-md);\n    --date-range-picker-segment-start: calc(\n      var(--sp-rw-space-3) - var(--input-segment-padding-inline)\n    );\n    height: var(--date-range-picker-control-height);\n  }\n\n  .sp-date-range-control[data-size=\"sm\"] {\n    --date-range-picker-control-height: var(--sp-rw-control-sm);\n    --date-range-picker-segment-start: calc(\n      var(--sp-rw-space-2-5) - var(--input-segment-padding-inline)\n    );\n  }\n\n  .sp-date-range-control[data-size=\"lg\"] {\n    --date-range-picker-control-height: var(--sp-rw-control-lg);\n  }\n\n  .sp-date-range-control[data-size=\"xl\"] {\n    --date-range-picker-control-height: var(--sp-rw-control-xl);\n    --date-range-picker-segment-start: calc(\n      var(--sp-rw-space-4) - var(--input-segment-padding-inline)\n    );\n  }\n\n  /* The segment row holds the start date, the dash and the end date, in the\n   * locale's direction (its dir). The insets belong to the field, which takes\n   * the page's direction: it pads its start, and without the calendar button\n   * (iconMode=\"none\") its end too, as a DateField without its button does, so\n   * a range as wide as its segments is balanced. */\n  .sp-date-range-control {\n    padding-inline-start: var(--date-range-picker-segment-start);\n  }\n\n  .sp-date-range-control[data-icon-mode=\"none\"] {\n    padding-inline-end: var(--date-range-picker-segment-start);\n  }\n\n  .sp-date-range-picker-segments {\n    display: flex;\n    flex: none;\n    align-items: center;\n    align-self: stretch;\n  }\n\n  /* The surface (border, radius, shadow) is the floating-md recipe that\n   * design-rework.css draws for the popover's data-slot. */\n  .sp-date-range-popover {\n    z-index: 50;\n    width: max-content;\n    padding: var(--sp-rw-space-4);\n    outline: none;\n    transform-origin: var(--trigger-anchor-point);\n  }\n\n  .sp-date-range-popover[data-entering] {\n    animation: sp-date-range-popover-enter var(--motion-duration-overlay)\n      var(--motion-ease-standard) both;\n  }\n\n  .sp-date-range-popover[data-exiting] {\n    animation: sp-date-range-popover-exit var(--motion-duration-fast)\n      var(--motion-ease-soft-in-out) both;\n  }\n\n  @keyframes sp-date-range-popover-enter {\n    from {\n      opacity: 0;\n      transform: scale(0.98);\n    }\n  }\n\n  @keyframes sp-date-range-popover-exit {\n    to {\n      opacity: 0;\n      transform: scale(0.98);\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .sp-date-range-popover[data-entering],\n    .sp-date-range-popover[data-exiting] {\n      animation-duration: 1ms;\n    }\n  }\n\n  html[data-motion-reduce=\"true\"] .sp-date-range-popover[data-entering],\n  html[data-motion-reduce=\"true\"] .sp-date-range-popover[data-exiting],\n  [data-sp-motion=\"reduced\"] .sp-date-range-popover[data-entering],\n  [data-sp-motion=\"reduced\"] .sp-date-range-popover[data-exiting] {\n    animation-duration: 1ms;\n  }\n}\n","type":"registry:ui","target":"~/src/siteplane/components/date-range-picker/date-range-picker.css"},{"path":"packages/ui/src/components/date-range-picker/index.ts","content":"export * from \"./date-range-picker\";\n","type":"registry:ui","target":"~/src/siteplane/components/date-range-picker/index.ts"}],"type":"registry:ui","docs":"https://ui.siteplane.io/docs-markdown/components/date-range-picker.md","categories":["Date & color"],"meta":{"avoidWhen":["Avoid for a single date or a date and time; use DateField, DatePicker or DateTimePicker instead."],"description":"Segmented, locale-aware date-range picker with one Calendar action and two native form values.","knownLimitations":["Values use native Date calendar fields in the local time zone; the locale changes formatting, not the zone.","The control commits a complete range, not a start date alone."],"providerNotes":["Use the locale prop for this picker, or SiteplaneLocaleProvider for a shared locale. SiteplaneUIProvider configures visual defaults, not locale."],"releaseStatus":"stable","searchTerms":["date","range","picker","calendar","form"],"useWhen":["Use when users must choose and submit a contiguous start and end date."]}}