Klarhimmel UI

App, time-slot picker

App, time-slot picker preview

Install

npx shadcn add @klarhimmel/app-scheduling-slots

Usage

import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppSchedulingSlots from '@/components/klarhimmel/blocks/app-scheduling-slots';

export default function Section() {
  return (
    <Theme theme="brand">
      <BlockSurface>
        <AppSchedulingSlots props={{
          "eyebrow": "Spa at Exempelhotellet",
          "title": "Choose a time for your treatment",
          "description": "Sixty minutes. You can change it up to a day before.",
          "legend": "Available times",
          "help": "Arrow keys move between times. Space or Enter picks one.",
          "days": [
            {
              "label": "Mon 16 Nov",
              "unavailable": [
                "09:00",
                "10:00"
              ]
            },
            {
              "label": "Tue 17 Nov",
              "unavailable": []
            },
            {
              "label": "Wed 18 Nov",
              "unavailable": [
                "13:00",
                "14:00",
                "15:00"
              ]
            },
            {
              "label": "Thu 19 Nov",
              "unavailable": [
                "11:00"
              ]
            },
            {
              "label": "Fri 20 Nov",
              "unavailable": []
            }
          ],
          "times": [
            "09:00",
            "10:00",
            "11:00",
            "13:00",
            "14:00",
            "15:00"
          ],
          "selected": {
            "day": "Tue 17 Nov",
            "time": "10:00"
          },
          "scrollHint": "Scroll sideways for more days.",
          "fullLabel": "Full",
          "summary": "Selected",
          "none": "Nothing selected yet",
          "at": "at",
          "confirm": "Confirm booking",
          "change": "Change booking",
          "state": "idle",
          "messages": {
            "error": "Choose a time first.",
            "confirmed": "Booked. A confirmation is on its way to your email.",
            "empty": "No times are free this week. Try next week.",
            "taken": "That time is full. Choose another."
          }
        }} />
      </BlockSurface>
    </Theme>
  );
}

Install

Import

import Block from '@/components/klarhimmel/blocks/app-scheduling-slots';
Also called
  • app/scheduling-slots
About
Pick a time: days across, times down, as a keyboard grid (arrow keys, Home and End, Space picks). A taken slot says so in words, the choice is repeated in a status line, and idle, error, confirmed and empty states. Never submits.
Category
Interface sections for signed-in products.

Notes

When to use it

A grid of days and times where the person picks one slot and confirms it. For a read-only overview of a month, use `app-scheduling-calendar` instead; this block exists for choosing, and it never submits anything itself.

Writing for it

- List the times once in times (2 to 8, 10 characters each) and mark the full ones per day in that day's unavailable list. A time you mention there must also exist in times. - selected starts the grid on a slot using the exact day label and time. Day labels hold 20 characters, so 'Tue 17 Nov' fits but a full date sentence does not. - The help line (160) is shown under the grid. Say what the keys do, and keep fullLabel to one word because a taken slot gets it in its accessible name.

Watch out for

With more than five days the grid gets wider and scrolls sideways, and scrollHint is only shown then. A selected slot that is marked unavailable is ignored, so the grid starts with nothing chosen.

Usage

The block in a page, with its default content (long lists shortened). Every prop is in the table below.

section.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppSchedulingSlots from '@/components/klarhimmel/blocks/app-scheduling-slots';

export default function Section() {
  return (
    <Theme theme="brand">
      <BlockSurface>
        <AppSchedulingSlots props={{
          "eyebrow": "Spa at Exempelhotellet",
          "title": "Choose a time for your treatment",
          "description": "Sixty minutes. You can change it up to a day before.",
          "legend": "Available times",
          "help": "Arrow keys move between times. Space or Enter picks one.",
          "days": [
            {
              "label": "Mon 16 Nov",
              "unavailable": [
                "09:00",
                "10:00"
              ]
            },
            {
              "label": "Tue 17 Nov",
              "unavailable": []
            }
          ],
          "times": [
            "09:00",
            "10:00"
          ],
          "selected": {
            "day": "Tue 17 Nov",
            "time": "10:00"
          },
          "scrollHint": "Scroll sideways for more days.",
          "fullLabel": "Full",
          "summary": "Selected",
          "none": "Nothing selected yet",
          "at": "at",
          "confirm": "Confirm booking",
          "change": "Change booking",
          "state": "idle",
          "messages": {
            "error": "Choose a time first.",
            "confirmed": "Booked. A confirmation is on its way to your email.",
            "empty": "No times are free this week. Try next week.",
            "taken": "That time is full. Choose another."
          }
        }} />
      </BlockSurface>
    </Theme>
  );
}

Props

The type, default and text limit of every prop. On a real page, over-limit text is kept and checkSections flags it (Installation, Check a page before you ship); this live preview shows the default until it fits. The panel above edits all props.

PropTypeDefaultDescription
eyebrowstring'Spa at Exempelhotellet'Text, up to 60 characters. Optional; empty is not rendered.
titlestring'Choose a time for your treatment'Text, up to 120 characters.
descriptionstring'Sixty minutes. You can change it up…'Text, up to 300 characters. Optional; empty is not rendered.
legendstring'Available times'Text, up to 60 characters.
helpstring'Arrow keys move between times. Spac…'Text, up to 160 characters.
days{ label, unavailable }[]5 itemsList of 2 to 7. Each item: label, unavailable.
timesstring[]6 itemsList of 2 to 8.
selected{ day, time }{…}Fields: day, time. Optional; empty is not rendered.
scrollHintstring'Scroll sideways for more days.'Text, up to 60 characters.
fullLabelstring'Full'Text, up to 20 characters.
summarystring'Selected'Text, up to 40 characters.
nonestring'Nothing selected yet'Text, up to 60 characters.
atstring'at'Text, up to 10 characters.
confirmstring'Confirm booking'Text, up to 30 characters.
changestring'Change booking'Text, up to 30 characters.
state'idle' | 'error' | 'confirmed' | 'empty''idle'One of 'idle', 'error', 'confirmed', 'empty'.
messages{ error, confirmed, empty, taken }{…}Fields: error, confirmed, empty, taken.
headingLevel'1' | '2'-One of '1', '2'. Optional; empty is not rendered.