Skip to content

Commit

Permalink
feat: TimeField component
Browse files Browse the repository at this point in the history
  • Loading branch information
HBS999 committed Nov 16, 2024
1 parent 9abd5d0 commit 309e5e4
Show file tree
Hide file tree
Showing 19 changed files with 2,763 additions and 24 deletions.
1 change: 1 addition & 0 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"dependencies": {
"@docsearch/css": "3.5.2",
"@docsearch/js": "3.5.2",
"@internationalized/date": "3.4.0",
"@kobalte/core": "workspace:*",
"@solidjs/meta": "0.29.3",
"@solidjs/router": "0.12.4",
Expand Down
91 changes: 91 additions & 0 deletions apps/docs/src/examples/time-field.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
.time-field {
display: flex;
flex-direction: column;
gap: 4px;
}

.time-field__label {
color: hsl(240 6% 10%);
font-size: 14px;
font-weight: 500;
user-select: none;
}

.time-field__field {
display: flex;
padding: 4px 8px;
border-radius: 6px;
width: 150px;
white-space: nowrap;
forced-color-adjust: none;
background-color: #fff;
border: 1px solid hsl(240 6% 90%);
color: hsl(240 4% 16%);
font-size: 14px;
}

.time-field__field:focus-within {
outline: 2px solid hsl(200 98% 39%);
outline-offset: -1px;
}

.time-field__segment {
padding: 0 2px;
font-variant-numeric: tabular-nums;
text-align: end;
}

.time-field__segment[data-type="literal"] {
padding: 0;
}

.time-field__segment[data-placeholder] {
color: hsl(240 4% 46%);
font-style: italic;
}

.time-field__segment:focus {
color: #fff !important;
background: hsl(200 98% 39%);
outline: none;
border-radius: 4px;
}

.time-field__segment::selection {
background-color: transparent;
}

.time-field__description {
color: hsl(240 5% 26%);
font-size: 12px;
user-select: none;
}

.time-field__error-message {
color: hsl(0 72% 51%);
font-size: 12px;
user-select: none;
}

[data-kb-theme="dark"] .time-field__label {
color: hsl(240 5% 84%);
}

[data-kb-theme="dark"] .time-field__field {
background-color: hsl(240 4% 16%);
border: 1px solid hsl(240 5% 34%);
color: hsl(0 100% 100% / 0.9);
}

.time-field__field[data-invalid] {
border-color: hsl(0 72% 51%);
outline-color: hsl(0 72% 51%);
}

[data-kb-theme="dark"] .time-field__segment[data-placeholder] {
color: hsl(0 100% 100% / 0.5);
}

[data-kb-theme="dark"] .time-field__description {
color: hsl(240 5% 65%);
}
273 changes: 273 additions & 0 deletions apps/docs/src/examples/time-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
import {
Time,
getLocalTimeZone,
parseZonedDateTime,
toCalendarDateTime,
today,
} from "@internationalized/date";
import { createSignal } from "solid-js";
import { createDateFormatter } from "../../../../packages/core/src/i18n";
import { TimeField } from "../../../../packages/core/src/time-field";
import style from "./time-field.module.css";

export function BasicExample() {
return (
<TimeField class={style["time-field"]}>
<TimeField.Label class={style["time-field__label"]}>
Event time
</TimeField.Label>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function DefaultValueExample() {
return (
<TimeField class={style["time-field"]} defaultValue={new Time(11, 45)}>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function ControlledValueExample() {
const [value, setValue] = createSignal(new Time(9, 45));

const dateFormatter = createDateFormatter({
hour12: true,
timeStyle: "short",
});

return (
<>
<TimeField
class={style["time-field"]}
value={value()}
onChange={setValue}
>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
<p class="not-prose text-sm mt-4">
Selected time:{" "}
{value()
? dateFormatter().format(
toCalendarDateTime(today(getLocalTimeZone()), value()).toDate(
getLocalTimeZone(),
),
)
: "––"}
</p>
</>
);
}

export function TimeZoneExample() {
return (
<TimeField
class={style["time-field"]}
defaultValue={parseZonedDateTime("2022-11-07T00:45[America/Los_Angeles]")}
>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function GranularityExample() {
return (
<TimeField class={style["time-field"]} granularity="second">
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function MinMaxExample() {
return (
<TimeField
class={style["time-field"]}
defaultValue={new Time(9, 45)}
minValue={new Time(9)}
maxValue={new Time(17)}
>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
<TimeField.ErrorMessage class={style["time-field__error-message"]}>
Select time between 9 AM and 5 PM.
</TimeField.ErrorMessage>
</TimeField>
);
}

export function PlaceholderValueExample() {
return (
<TimeField class={style["time-field"]} placeholderValue={new Time(9)}>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function HideTimeZoneExample() {
return (
<TimeField
class={style["time-field"]}
defaultValue={parseZonedDateTime("2022-11-07T00:45[America/Los_Angeles]")}
hideTimeZone
>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function HourCycleExample() {
return (
<TimeField class={style["time-field"]} hourCycle={24}>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
</TimeField>
);
}

export function DescriptionExample() {
return (
<TimeField class={style["time-field"]}>
<TimeField.Label class={style["time-field__label"]}>Time</TimeField.Label>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
<TimeField.Description class={style["time-field__description"]}>
Select a meeting time.
</TimeField.Description>
</TimeField>
);
}

export function ErrorMessageExample() {
const [value, setValue] = createSignal(undefined);

return (
<TimeField
class={style["time-field"]}
value={value()}
onChange={setValue}
validationState={value() === undefined ? "invalid" : "valid"}
>
<TimeField.Label class={style["time-field__label"]}>Time</TimeField.Label>
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
<TimeField.ErrorMessage class={style["time-field__error-message"]}>
Please select a time.
</TimeField.ErrorMessage>
</TimeField>
);
}

export function HTMLFormExample() {
let formRef: HTMLFormElement | undefined;

const onSubmit = (e: SubmitEvent) => {
e.preventDefault();
e.stopPropagation();

const formData = new FormData(formRef);

alert(JSON.stringify(Object.fromEntries(formData), null, 2));
};

return (
<form
ref={formRef}
onSubmit={onSubmit}
class="flex flex-col items-center space-y-6"
>
<TimeField class={style["time-field"]} name="time">
<TimeField.Field class={style["time-field__field"]}>
{(segment) => (
<TimeField.Segment
class={style["time-field__segment"]}
segment={segment()}
/>
)}
</TimeField.Field>
<TimeField.HiddenInput />
</TimeField>
<div class="flex space-x-2">
<button type="reset" class="kb-button">
Reset
</button>
<button type="submit" class="kb-button-primary">
Submit
</button>
</div>
</form>
);
}
5 changes: 5 additions & 0 deletions apps/docs/src/routes/docs/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ const CORE_NAV_SECTIONS: NavSection[] = [
title: "Text Field",
href: "/docs/core/components/text-field",
},
{
title: "Time Field",
href: "/docs/core/components/time-field",
status: "new",
},
{
title: "Toast",
href: "/docs/core/components/toast",
Expand Down
Loading

0 comments on commit 309e5e4

Please sign in to comment.