Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Time picker #255

Closed
its-monotype opened this issue Apr 29, 2023 · 46 comments
Closed

Feature request: Time picker #255

its-monotype opened this issue Apr 29, 2023 · 46 comments
Labels

Comments

@its-monotype
Copy link
Contributor

No description provided.

@uncvrd
Copy link

uncvrd commented Jun 1, 2023

Hi everyone, as we wait for an official DateTime picker for the library, I decided to re-create this using React Aria's useDatePicker and have styled it using shadcn (using the Button and Popover components so it looks visually similar but also includes Time functionality.

See the demo below:

Screen.Recording.2023-06-01.at.2.47.23.PM.mov

Would this be of interest to any of you? I can move this to a repo/codesandbox.

I ran in to a "focus" problem with React Aria that requires two clicks to close the popover though (since they manually control event propagation and I guess Radix needs it to trigger the popover to close). As a workaround, I'm using another Aria hook called useInteractOutside to listen for clicks outside of the Popover and made the popover a controlled component. So maybe we call all work together to build this in to a better temporary solution?

@jonnguyen12
Copy link

That looks very nice, does the time picker has to go with date picker? can it be separate like mantine.dev time picker?

@uncvrd
Copy link

uncvrd commented Jun 2, 2023

Thanks! I ended up creating a separate component for a TimePicker too since having a Popover for just the time felt excessive. This is what it looks like. With the DateTime picker, you can select the granularity to day and at that point you have just a Date picker (hides the time picker input on the Popover) so that covers all three basis for me at least.

Screen.Recording.2023-06-01.at.7.30.09.PM.mov

@uncvrd
Copy link

uncvrd commented Jun 3, 2023

Lets see what they're planning for a release next week and if it isn't something related to a time picker, I'll spend some time extracting this out in to a separate repo for everyone to use/contribute to until then.

@gtandes
Copy link

gtandes commented Jun 15, 2023

Do you already have this on repo/codesandbox ser?

@uncvrd
Copy link

uncvrd commented Jun 15, 2023

I got caught up on a separate project - I will share a repo/sandbox by end of the weekend!

@gtandes
Copy link

gtandes commented Jun 15, 2023 via email

@uncvrd
Copy link

uncvrd commented Jun 19, 2023

Hi everyone - as promised here is a repository that demo's react-aria's useDatePicker using shadcn theming & components. I would like to highly encourage PR's on this repository as I'm sure this can be implemented better.

EDIT: I also don't use NextJS App Router in my projects so I just threw "use client" everywhere and hoped for the best lol

Hope it helps as a starting point :)

https://github.com/uncvrd/shadcn-ui-date-time-picker

@jonnguyen12
Copy link

Hi everyone - as promised here is a repository that demo's react-aria's useDatePicker using shadcn theming & components. I would like to highly encourage PR's on this repository as I'm sure this can be implemented better.

Hope it helps as a starting point :)

https://github.com/uncvrd/shadcn-ui-date-time-picker

This is a nice surprise for the Father's Day. :) Well done.

@dBianchii
Copy link

+1 To this...
I need it for my project now

@dBianchii
Copy link

dBianchii commented Jun 21, 2023

Hi everyone - as promised here is a repository that demo's react-aria's useDatePicker using shadcn theming & components. I would like to highly encourage PR's on this repository as I'm sure this can be implemented better.

EDIT: I also don't use NextJS App Router in my projects so I just threw "use client" everywhere and hoped for the best lol

Hope it helps as a starting point :)

https://github.com/uncvrd/shadcn-ui-date-time-picker

@uncvrd
Thats really cool. I am trying to use it. How do I pass in the date state into the date-time-picker ?

@uncvrd
Copy link

uncvrd commented Jun 21, 2023

@dBianchii

so useDatePicker works with the @internationalized/date library, to handle a controlled state, I've been doing the following:

<DateTimePicker
	value={!!field.value ? parseAbsolute(field.value.toISOString(), getLocalTimeZone()) : null}
	onChange={(date) => {
		field.onChange(!!date ? date.toDate(getLocalTimeZone()) : null);
	}}
	shouldCloseOnSelect={false}
/>

Not sure if this is the best approach, but since the date can be null if a user has only typed in a few of the MM/dd/YYYY values, I needed to do those ternary checks 🤷

See instructions on useDatePicker here, you'll see the @internationalized/date library being used in implementation further down the page, there's a couple options besides parseAbsolute: https://react-spectrum.adobe.com/react-aria/useDatePicker.html

Let me know if y'all have better implementations!

@tika
Copy link

tika commented Jul 7, 2023

This is really nice @uncvrd, thank you! I can't seem to figure out how to just disregard the timezone, so instead I've done this:

<DateTimePicker
  onBlur={field.onBlur}
  value={
    !!field.value
      ? parseAbsolute(field.value.toISOString(), "GMT")
      : null
  }
  onChange={(date) => {
    field.onChange(!!date ? date.toDate("GMT") : new Date());
  }}
  granularity="minute"
/>

Do you have any idea how I could instead just use parseDateTime from Adobe's localisation lib? I had a problem parsing the Date ISO string like this: parseDateTime(field.value.toISOString()).

Super helpful component!!

@wasauce
Copy link

wasauce commented Jul 14, 2023

Lets see what they're planning for a release next week and if it isn't something related to a time picker, I'll spend some time extracting this out in to a separate repo for everyone to use/contribute to until then.

What was released? I'm having trouble finding something related to a time picker.

@uncvrd
Copy link

uncvrd commented Jul 14, 2023

@wasauce nothing related to a date picker - it was the new CLI which is why I shared my own implementation for now

@wasauce
Copy link

wasauce commented Jul 14, 2023

@uncvrd it looks wonderful. going to dive in. Thank you!

@codingwithashu
Copy link

How to use in form ?

@danvoyce
Copy link

For anyone like myself wanting a simple (interim) solution, you can use <Input type="time" /> and leverage the native browser UI. I actually much prefer this on mobile devices.

@zacwellmer
Copy link

Here's a simple one that re-uses shadcn's calendar component. I stole the time field from @uncvrd.
https://gist.github.com/zacwellmer/316e38705b6534907c142af8ca21fa9d

Screen.Recording.2023-10-25.at.10.22.05.mov

@statusunknown418
Copy link

statusunknown418 commented Nov 10, 2023

this is breaking when used with forms :( @zacwellmer

@stevegiorgi
Copy link

stevegiorgi commented Dec 10, 2023

This works great until you set an onChange event -- then it begins to close before you've had a chance to fill in all values.

Example: I select a date, type in my hour, and begin to type in minutes, but it closes (loses focus) as soon as I've typed in a single number. If I wanted to type in "45", it only allows me to type in the first number before closing. I have to reopen it to type "5" and yet again to select "AM/PM".

@tiagobnobrega
Copy link

tiagobnobrega commented Feb 8, 2024

Reached this request while searching for this. Here's the implementation I settled. It uses timespace lib in case anyone is interested.
Usage:

const form = useForm(...)

 <FormField
    control={form.control}
    {/*statTime field type is: Date */}
    name="startTime"
    render={({ field }) => (
      <FormItem className="w-1/3">
        <FormLabel>Label</FormLabel>
        <FormControl>
        {/*This is the component*/}
          <TimePicker onChange={field.onChange} value={field.value}>
            <TimePickerSegment segment={"hours"} />
            <TimePickerSeparator>:</TimePickerSeparator>
            <TimePickerSegment segment={"minutes"} />
          </TimePicker>
        </FormControl>
      </FormItem>
    )}
  />

Component Source:

//time-picker.tsx
import { type DateType, useTimescape } from "timescape/react";
import * as React from "react";
import {
  createContext,
  forwardRef,
  type HTMLAttributes,
  type ReactNode,
  useContext,
} from "react";
import { cn } from "@/lib/utils/style";
import type { Options } from "timescape";

export type TimePickerProps = HTMLAttributes<HTMLDivElement> & {
  value?: Date;
  onChange: (date?: Date) => void;
  children: ReactNode;
  options?: Omit<Options, "date" | "onChangeDate">;
};
type TimePickerContextValue = ReturnType<typeof useTimescape>;
const TimePickerContext = createContext<TimePickerContextValue | null>(null);

const useTimepickerContext = (): TimePickerContextValue => {
  const context = useContext(TimePickerContext);
  if (!context) {
    throw new Error(
      "Unable to access TimePickerContext. This component should be wrapped by a TimePicker component",
    );
  }
  return context;
};

const TimePicker = forwardRef<React.ElementRef<"div">, TimePickerProps>(
  ({ value, onChange, options, className, ...props }, ref) => {
    const timePickerContext = useTimescape({
      date: value,
      onChangeDate: onChange,
      ...options,
    });
    return (
      <TimePickerContext.Provider value={timePickerContext}>
        <div
          ref={ref}
          {...props}
          className={cn(
            "flex w-auto h-10 rounded-md border border-input bg-background px-3 py-1 text-sm",
            "ring-offset-background focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
            "disabled:cursor-not-allowed disabled:opacity-50",
            className,
          )}
        ></div>
      </TimePickerContext.Provider>
    );
  },
);
TimePicker.displayName = "TimePicker";

type TimePickerSegmentProps = Omit<
  HTMLAttributes<HTMLInputElement>,
  "value" | "onChange"
> & {
  segment: DateType;
  inputClassName?: string;
};

const TimePickerSegment = forwardRef<
  React.ElementRef<"input">,
  TimePickerSegmentProps
>(({ segment, inputClassName, className, ...props }, ref) => {
  const { getInputProps } = useTimepickerContext();
  const { ref: timePickerInputRef } = getInputProps(segment);
  return (
    <div
      className={cn(
        "rounded-md focus-within:bg-accent text-accent-foreground px-2 py-1",
      )}
    >
      <input
        ref={(node) => {
          if (typeof ref === "function") {
            ref(node);
          } else {
            if (ref) ref.current = node;
          }
          timePickerInputRef(node);
        }}
        {...props}
        className={cn(
          "tabular-nums caret-transparent",
          "bg-transparent ring-0 ring-offset-0 border-transparent focus-visible:border-transparent focus-visible:ring-0 outline-none",
          inputClassName,
        )}
      />
    </div>
  );
});
TimePickerSegment.displayName = "TimePickerSegment";

type TimePickerSeparatorProps = HTMLAttributes<HTMLSpanElement>;
const TimePickerSeparator = forwardRef<
  React.ElementRef<"span">,
  TimePickerSeparatorProps
>(({ className, ...props }, ref) => {
  return (
    <span ref={ref} {...props} className={cn("text-sm py-1", className)}></span>
  );
});
TimePickerSeparator.displayName = "TimePickerSeparator";

export { TimePicker, TimePickerSegment, TimePickerSeparator };

PS: This could also be used for date and time. Check out timescape docs: https://github.com/dan-lee/timescape

@bpena707
Copy link

This is great. I have tried some sort of implementation like this but how would you store this into a Prisma? would it be a simple DateTime? and would MySQL accept this too?

@Snailedlt
Copy link

Snailedlt commented Feb 25, 2024

A time-picker would be great!
In one of our projects we need a time-picker where we can pick a time-range (from-to) for customers to receive their order.

For now we're just using a select, with time options in the 3hr range like so:
image

This way of doing it has some issues though. For example we need custom logic to return the correct type instead of a string, and it also doesn't look that good, so a separate TimePicker component would be really nice to have. Would also be cool to see how it could be integrated into the existing DatePicker.

@liamlows
Copy link

liamlows commented Feb 26, 2024

For those looking for a super easy drop in solution that surprisingly themes well to shadcn automatically, here is an example using the native time selector as @danvoyce mentioned using react hook form and modifying the original date object so form submission is easy:

<FormField
  control={form.control}
  name="date"
  render={({ field }) => (
    <FormItem>
      <FormLabel className="pr-4">Date</FormLabel>
      <Popover
        open={calendarOpen}
        onOpenChange={(open) => setCalendarOpen(open)}
      >
        <PopoverTrigger asChild>
          <FormControl>
            <Button
              variant="outline"
              className={cn(
                'w-full pl-3 text-left font-normal',
                !field.value && 'text-muted-foreground',
              )}
            >
              {field.value ? (
                `${field.value.toLocaleString([], {
                  year: 'numeric',
                  month: 'numeric',
                  day: 'numeric',
                  hour: '2-digit',
                  minute: '2-digit',
                })}`
              ) : (
                <span>Select Date</span>
              )}
              <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
            </Button>
          </FormControl>
        </PopoverTrigger>
        <PopoverContent>
          <Calendar
            className="p-0"
            mode="single"
            selected={field.value}
            onSelect={field.onChange}
            disabled={(date) => date > new Date() || date < new Date('1900-01-01')}
            initialFocus
          />
          <Input
            type="time"
            className="mt-2"
            // take locale date time string in format that the input expects (24hr time)
            value={field.value.toLocaleTimeString([], {
              hourCycle: 'h23',
              hour: '2-digit',
              minute: '2-digit',
            })}
            // take hours and minutes and update our Date object then change date object to our new value
            onChange={(selectedTime) => {
              const currentTime = field.value;
              currentTime.setHours(
                parseInt(selectedTime.target.value.split(':')[0]),
                parseInt(selectedTime.target.value.split(':')[1]),
                0,
              );
              field.onChange(currentTime);
            }}
          />
        </PopoverContent>
      </Popover>
      <FormMessage />
      <FormDescription>
        This is the date & time the assessment finished.
      </FormDescription>
    </FormItem>
  )}
/>

now that i think of it this could also be a variant element of the native Shadcn calendar too 😅

@hsuanyi-chou
Copy link

thanks to @uncvrd
I've created a datetime picker by his repo and add usages.

datetime-picker

@anasmohammed361
Copy link

Found this component . Was simple and solved the purpose , It uses Shadcn.

@uncvrd
Copy link

uncvrd commented Mar 10, 2024

@hsuanyi-chou thanks for the credit ❤️

@heynish
Copy link

heynish commented Mar 13, 2024

@liamlows is it possible to provide full implementation? with the imports, calendar and input files?

@psiddharthdesign
Copy link

Is there a way to convert this DateTimePicker value to ISO 8601 like : 2024-04-10 19:22:36.2213+00 ? I work with Supabase and Nextjs

@anasmohammed361
Copy link

Is there a way to convert this DateTimePicker value to ISO 8601 like : 2024-04-10 19:22:36.2213+00 ? I work with Supabase and Nextjs

toISOString()

i think you can convert the Date object to your wish when assigning the value either on onChange or store the date as it is and converting it once before writing to your supabase db makes much more sense

@bennobuilder
Copy link

Screen.Recording.2024-05-19.at.6.40.12.PM.mov

Here's my spin on a basic date/time picker using shadcn/ui, react-stately, and react-aria (first iteration):

https://github.com/dyndotart/monorepo/tree/79-add-basic-input-events/packages/ui/src/components/composed/DateTimePicker

@Maliksidk19
Copy link

Maliksidk19 commented May 31, 2024

image

html default input time type is also pretty much good when used with shadcn Input

<Input type="time" className="w-max py-6" />

@ilorez
Copy link

ilorez commented Jun 2, 2024

thanks to @uncvrd I've created a datetime picker by his repo and add usages.

datetime-picker
Thanks that was so fast and helpful <3

@dubbajubba
Copy link

image

html default input time type is also pretty much good when used with shadcn Input

<Input type="time" className="w-max py-6" />

I agree. This seems to be the easiest solution by far and still looks great!

@bpena707
Copy link

i am using the component and it works fine when i input the time but I want it to display to the columns.tsx shadcn ui component using the data table in the format of HH:MMAM but instead it displays as HH:MM:SS in 24 hour format. how would you make it display? would it be a regular expression?

@shadcn shadcn added the Stale label Jul 4, 2024
@shadcn
Copy link
Collaborator

shadcn commented Jul 11, 2024

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.

@shadcn shadcn closed this as completed Jul 11, 2024
@Snailedlt
Copy link

Please keep this issue open. It would be a very useful feature

@anthonysgro
Copy link

agree, i think it would be a great feature and we should keep it open

@anthonysgro
Copy link

anthonysgro commented Jul 13, 2024

image

html default input time type is also pretty much good when used with shadcn Input

<Input type="time" className="w-max py-6" />

Just an FYI, that nice dropdown UI looks only supported on chrome. It looks worse on other browsers

@huybuidac
Copy link

You can try this simple date picker or a powerful datetime picker with options for timezone, month, year, and time selection.

https://shadcn-datetime-picker-xi.vercel.app/

https://github.com/huybuidac/shadcn-datetime-picker

Screen.Recording.2024-10-13.at.23.28.30.mov

@njacob1001
Copy link

@huybuidac did you create a pull request with this component? it looks awesome

@Andrew-Chen-Wang
Copy link

Andrew-Chen-Wang commented Nov 10, 2024

If anyone is using the default shadcn Input component with Tailwind, the time input might be missing its input icon. To fix this, use these Tailwind classes:

[&::-webkit-calendar-picker-indicator]:bg-black [&::-webkit-calendar-picker-indicator]:cursor-pointer

Screenshot 2024-11-10 at 6 32 51 AM
<Input 
className='w-max [&::-webkit-calendar-picker-indicator]:bg-black [&::-webkit-calendar-picker-indicator]:cursor-pointer'
type="time"
/>

@portoaj
Copy link

portoaj commented Dec 4, 2024

Please open this back up this would be very useful

@Maliksidk19
Copy link

You can checkout this datetime picker here

Source Code: https://github.com/Maliksidk19/shadcn-datetime-picker

URL: https://shadcn-datetime-picker.vercel.app/datetime-picker

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests