chore(ui,icons,ui-preset,toolbox): Move design system packages to monorepo (#5470)
This commit is contained in:
committed by
GitHub
parent
71853eafdd
commit
e4ce2f4e07
@@ -0,0 +1 @@
|
||||
export * from "./time-input"
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react"
|
||||
import * as React from "react"
|
||||
|
||||
import { isBrowserLocaleClockType24h } from "../../utils/is-browser-locale-hour-cycle-24h"
|
||||
import { TimeInput } from "./time-input"
|
||||
|
||||
const meta: Meta<typeof TimeInput> = {
|
||||
title: "Components/TimeInput",
|
||||
component: TimeInput,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
render: (args) => (
|
||||
<div className="w-[300px]">
|
||||
<TimeInput aria-labelledby="time" {...args} />
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof TimeInput>
|
||||
|
||||
// Hour Cycle defaults to your browser's locale.
|
||||
export const Default: Story = {
|
||||
render: (args) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<TimeInput aria-labelledby="time" {...args} />
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<p className="text-xs">
|
||||
Will use 24h or 12h cycle depending on your locale
|
||||
</p>
|
||||
<div className="text-xs">
|
||||
<pre>
|
||||
Locale: {window.navigator.language}, Hour Cycle:{" "}
|
||||
{isBrowserLocaleClockType24h() ? "24h" : "12h"}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export const Hour24: Story = {
|
||||
args: {
|
||||
hourCycle: 24,
|
||||
},
|
||||
}
|
||||
|
||||
export const Hour12: Story = {
|
||||
args: {
|
||||
hourCycle: 12,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
AriaTimeFieldProps,
|
||||
TimeValue,
|
||||
useDateSegment,
|
||||
useTimeField,
|
||||
} from "@react-aria/datepicker"
|
||||
import {
|
||||
useTimeFieldState,
|
||||
type DateFieldState,
|
||||
type DateSegment,
|
||||
} from "@react-stately/datepicker"
|
||||
import * as React from "react"
|
||||
|
||||
import { inputBaseStyles } from "@/components/input"
|
||||
import { clx } from "@/utils/clx"
|
||||
|
||||
type TimeSegmentProps = {
|
||||
segment: DateSegment
|
||||
state: DateFieldState
|
||||
}
|
||||
|
||||
const TimeSegment = ({ segment, state }: TimeSegmentProps) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
const { segmentProps } = useDateSegment(segment, state, ref)
|
||||
|
||||
const isColon = segment.type === "literal" && segment.text === ":"
|
||||
const isSpace = segment.type === "literal" && segment.text === " "
|
||||
|
||||
const isDecorator = isColon || isSpace
|
||||
|
||||
return (
|
||||
<div
|
||||
{...segmentProps}
|
||||
ref={ref}
|
||||
className={clx(
|
||||
"txt-compact-medium w-full rounded-md px-2 py-[5px] text-left uppercase tabular-nums",
|
||||
inputBaseStyles,
|
||||
"group-aria-[invalid=true]/time-input:!shadow-borders-error group-invalid/time-input:!shadow-borders-error",
|
||||
{
|
||||
"text-ui-fg-muted !w-fit border-none bg-transparent px-0 shadow-none":
|
||||
isDecorator,
|
||||
hidden: isSpace,
|
||||
"text-ui-fg-disabled bg-ui-bg-disabled border-ui-border-base shadow-none":
|
||||
state.isDisabled,
|
||||
"!text-ui-fg-muted !bg-transparent": !segment.isEditable,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={clx(
|
||||
"txt-compact-medium text-ui-fg-muted pointer-events-none block w-full text-left",
|
||||
{
|
||||
hidden: !segment.isPlaceholder,
|
||||
"h-0": !segment.isPlaceholder,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{segment.placeholder}
|
||||
</span>
|
||||
{segment.isPlaceholder ? "" : segment.text}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type TimeInputProps = Omit<
|
||||
AriaTimeFieldProps<TimeValue>,
|
||||
"label" | "shouldForceLeadingZeros" | "description" | "errorMessage"
|
||||
>
|
||||
|
||||
const TimeInput = React.forwardRef<HTMLDivElement, TimeInputProps>(
|
||||
({ hourCycle, ...props }: TimeInputProps, ref) => {
|
||||
const innerRef = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
React.useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
|
||||
ref,
|
||||
() => innerRef?.current
|
||||
)
|
||||
|
||||
const locale = window !== undefined ? window.navigator.language : "en-US"
|
||||
|
||||
const state = useTimeFieldState({
|
||||
hourCycle: hourCycle,
|
||||
locale: locale,
|
||||
shouldForceLeadingZeros: true,
|
||||
autoFocus: true,
|
||||
...props,
|
||||
})
|
||||
|
||||
const { fieldProps } = useTimeField(
|
||||
{
|
||||
...props,
|
||||
hourCycle: hourCycle,
|
||||
shouldForceLeadingZeros: true,
|
||||
},
|
||||
state,
|
||||
innerRef
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
{...fieldProps}
|
||||
ref={innerRef}
|
||||
className="group/time-input inline-flex w-full gap-x-2"
|
||||
>
|
||||
{state.segments.map((segment, i) => (
|
||||
<TimeSegment key={i} segment={segment} state={state} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
TimeInput.displayName = "TimeInput"
|
||||
|
||||
export { TimeInput }
|
||||
Reference in New Issue
Block a user