@vvedantb/ui 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -5
- package/app/README.md +1 -1
- package/package.json +6 -1
- package/shell/src/app-shell-classes.ts +2 -2
- package/src/index.ts +2 -0
- package/src/ui/_picker-classes.ts +18 -0
- package/src/ui/calendar.tsx +161 -0
- package/src/ui/clearInputDissolve.ts +10 -5
- package/src/ui/date-picker.tsx +545 -0
- package/src/ui/dialog.tsx +5 -1
- package/src/ui/time-picker.tsx +12 -5
- package/tokens/README.md +30 -8
- package/tokens/src/css/clear-input.css +71 -0
- package/tokens/src/css/compat-shadcn-rgb.css +8 -7
- package/tokens/src/css/index.css +8 -5
- package/tokens/src/css/modal.css +3 -2
- package/tokens/src/css/motion.css +156 -0
- package/tokens/src/css/scrollbar-thin.css +54 -26
- package/tokens/src/css/tabs.css +16 -5
- package/tokens/src/tokens.css +52 -0
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@ Shared React 19 web UI. Source-exported: no build step.
|
|
|
4
4
|
|
|
5
5
|
| Import | What it is |
|
|
6
6
|
| --- | --- |
|
|
7
|
-
| `@vvedantb/ui` | The only JavaScript entry. Primitives (Radix-based), AI elements, audio (`playChime`, `useReplyChime`, `playTone`), `toast` and `SonnerToaster`, the composer (`InputArea`, deprecated 0.3 `Composer`), shell (`SidebarHeader`, `PillTabs`, `SessionsHoverCard`, and the app shell: `AppShell`, `AppSidebar`, `SidebarRail`, `RailTile`, `SidebarPanel`, `SidebarAccountMenu`, `SignOutDialog` and related helpers), app helpers (confirm skip, update watcher, settings chrome, `useStableAuth`, and from 0.8.0 `createThemeProvider`, `createPageTitleContext`, `PageMotionProvider` and `formatCompactRelativeTime`), marketing pieces from 0.9.0 (`createLandingKit` and the `Landing*` parts, `SiteFooter`, `PricingModal`), animated nav icons from 0.11.0 (`MemoryIcon`, `BoardIcon`, ... and `NavIconBase`; `SettingsIcon` `variant` from 0.12.0), artifact viewers from 0.13.0 (`ArtifactFrame`, `ArtifactList`, `ArtifactViewer`, ...), `cn`, and the native tokens `tokens` and `Tokens`. See `app/README.md` in the repository for the app helpers. |
|
|
7
|
+
| `@vvedantb/ui` | The only JavaScript entry. Primitives (Radix-based), AI elements, audio (`playChime`, `useReplyChime`, `playTone`), `toast` and `SonnerToaster`, the composer (`InputArea`, deprecated 0.3 `Composer`), shell (`SidebarHeader`, `PillTabs`, `SessionsHoverCard`, and the app shell: `AppShell`, `AppSidebar`, `SidebarRail`, `RailTile`, `SidebarPanel`, `SidebarAccountMenu`, `SignOutDialog` and related helpers), app helpers (confirm skip, update watcher, settings chrome, `useStableAuth`, and from 0.8.0 `createThemeProvider`, `createPageTitleContext`, `PageMotionProvider` and `formatCompactRelativeTime`), marketing pieces from 0.9.0 (`createLandingKit` and the `Landing*` parts, `SiteFooter`, `PricingModal`), animated nav icons from 0.11.0 (`MemoryIcon`, `BoardIcon`, ... and `NavIconBase`; `SettingsIcon` `variant` from 0.12.0), artifact viewers from 0.13.0 (`ArtifactFrame`, `ArtifactList`, `ArtifactViewer`, ...), date pickers from 0.14.0 (`Calendar`, `DatePicker`, `DateTimePicker`, `MonthPicker`), `cn`, and the native tokens `tokens` and `Tokens`. See `app/README.md` in the repository for the app helpers. |
|
|
8
8
|
| `@vvedantb/ui/tokens` | `tokens.css`: colour, radius, shadow, ease and font tokens for Tailwind v4. CSS. |
|
|
9
9
|
| `@vvedantb/ui/tokens/tokens.css` | Same file as `@vvedantb/ui/tokens`. CSS. |
|
|
10
|
-
| `@vvedantb/ui/tokens/css/*.css` | `index`, `base`, `dialog`, `modal`, `tabs`, `tabs-pills`, `utilities`, `scrollbar-thin`, `animations`, `nav-icons`, `compat-shadcn-rgb`. CSS. |
|
|
10
|
+
| `@vvedantb/ui/tokens/css/*.css` | `index`, `base`, `dialog`, `modal`, `tabs`, `tabs-pills`, `utilities`, `scrollbar-thin`, `clear-input`, `motion`, `animations`, `nav-icons`, `compat-shadcn-rgb`. CSS. `tokens.css` imports `scrollbar-thin`, `clear-input` and `motion`. |
|
|
11
11
|
| `@vvedantb/ui/tokens/compat/shadcn-rgb.css` | Same file as `tokens/css/compat-shadcn-rgb.css`. CSS. |
|
|
12
12
|
| `@vvedantb/ui/tokens/tailwind-preset` | Tailwind preset (`.mjs`) for `@config "@vvedantb/ui/tokens/tailwind-preset"`. Kept as a build-tool entry: Tailwind needs a resolvable package path, and it is not a React API. |
|
|
13
13
|
| `@vvedantb/ui/landing.css` | Landing page classes (`landing-atmosphere`, `landing-grain`, `landing-pulse-dot`, ambient graph and preview animations, smooth `#top` scrolling). CSS. |
|
|
@@ -661,7 +661,7 @@ Rows in the panel beside the rail. Rail tiles are unchanged.
|
|
|
661
661
|
| Focus ring | none | `focus-visible:ring-2 focus-visible:ring-foreground/35`, no outline |
|
|
662
662
|
| Icon-only row | `justify-center px-0 py-1.5` | same, plus the radius, text, fill and focus classes above |
|
|
663
663
|
| Icon (`sidebarNavIconClass`) | none (apps used 18px in a 20px box) | `size-[19px] shrink-0`; `text-muted-foreground` idle, `text-foreground` active |
|
|
664
|
-
| Section label | `px-3 py-1 text-[11px]` | `px-4 py-1 text-[11px] leading-4` (16px line). Colour
|
|
664
|
+
| Section label | `px-3 py-1 text-[11px]` | `px-4 py-1 text-[11px] leading-4` (16px line). Colour comes from `.sidebar-section-label` in `tokens.css` (0.15.0; `--muted` at `--sidebar-section-label-mix` 55%, 80% on hover). |
|
|
665
665
|
|
|
666
666
|
The hover fill is static: it does not slide between rows. While another row is hovered, the active row keeps its fill.
|
|
667
667
|
|
|
@@ -985,6 +985,23 @@ const store: ArtifactStateStore = {
|
|
|
985
985
|
- Rename and delete use `ConfirmDialog` (`ArtifactRenameDialog`, `ArtifactDeleteDialog`, and `useArtifactEditDialogs` for custom surfaces). Rename sends the trimmed title, and does nothing when it is unchanged. Alt-click on Delete skips the dialog, as with `useRequestConfirm`. The delete dialog says "This cannot be undone."; pass `deleteDescription` to `ArtifactViewer`, `ArtifactCard` or `ArtifactList` to change it (0.13.1), for example when your delete can be undone. A callback that rejects keeps its dialog open and shows an error toast; copy and load failures toast too, so mount `SonnerToaster`.
|
|
986
986
|
- `ArtifactTypeIcon` (`type`, `size`, `stroke`) and `ARTIFACT_TYPE_LABELS` (`mindmap` is "Mind map"). Helpers `downloadArtifactBody(title, format, body)`, `copyArtifactBody(body)`, `openArtifactInNewTab(href)`. Row types: `ArtifactSummary` (any `ArtifactListRow` fits), `ArtifactDocument`, `ArtifactVersionSummary`.
|
|
987
987
|
|
|
988
|
+
## Date pickers (0.14.0)
|
|
989
|
+
|
|
990
|
+
Controlled pickers that read and write the native input strings, as local calendar dates with no timezone logic. They need `react-day-picker` 9, a dependency of this package.
|
|
991
|
+
|
|
992
|
+
```tsx
|
|
993
|
+
import { DatePicker, DateTimePicker, MonthPicker } from "@vvedantb/ui";
|
|
994
|
+
|
|
995
|
+
<DatePicker value={due} onChange={setDue} min="2026-01-01" clearable name="due" ariaLabel="Due date" />
|
|
996
|
+
<DateTimePicker value={startsAt} onChange={setStartsAt} defaultTime="09:00" name="startsAt" />
|
|
997
|
+
<MonthPicker value={month} onChange={setMonth} max="2026-12" />
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
- `DatePicker`: `value` "YYYY-MM-DD" or "". The trigger matches `TimePicker` and shows the date with `dateStyle: "medium"` (`locale`) or `placeholder` ("Pick a date"). `min` and `max` disable days; `clearable` adds a clear control (`onChange("")`); `name` adds a hidden input. `className` goes on the wrapper, `triggerClassName` merges onto the button (default `w-40`). A width on either sizes the field, and `w-full` on either fills the row; pill inputs can pass `triggerClassName="h-10 w-full rounded-full px-4 text-sm"`. `ariaLabel` names the trigger, followed by the shown text.
|
|
1001
|
+
- `DateTimePicker`: `value` "YYYY-MM-DDTHH:mm" or "". A date with no time uses `defaultTime` ("00:00"); a time with no date uses today. Only the date part of `min` and `max` applies. `triggerClassName` goes to both triggers, a width only to the date.
|
|
1002
|
+
- `MonthPicker`: `value` "YYYY-MM" or "", with year navigation and a month grid. `min` and `max` are "YYYY-MM".
|
|
1003
|
+
- `Calendar`: the styled `DayPicker` for custom layouts (every `DayPicker` prop). `parseDateValue` and `formatDateValue` convert between "YYYY-MM-DD" and a local `Date`.
|
|
1004
|
+
|
|
988
1005
|
## Toaster
|
|
989
1006
|
|
|
990
1007
|
```tsx
|
|
@@ -1042,9 +1059,9 @@ Use this when the app defines shadcn-style variables as space-separated channels
|
|
|
1042
1059
|
@source "../node_modules/@vvedantb/ui/src/**/*.{ts,tsx}";
|
|
1043
1060
|
```
|
|
1044
1061
|
|
|
1045
|
-
Shared CSS that is safe without `tokens.css`: `compat-shadcn-rgb.css`, `dialog.css`, `utilities.css`, `scrollbar-thin.css` (reads `--scrollbar`, which the compat file defines), `modal.css` and `tabs.css` (both need the
|
|
1062
|
+
Shared CSS that is safe without `tokens.css`: `compat-shadcn-rgb.css`, `dialog.css`, `utilities.css`, `scrollbar-thin.css` (reads `--scrollbar`, which the compat file defines), `clear-input.css` and `motion.css` (import them yourself; `tokens.css` is what imports them otherwise), `modal.css` and `tabs.css` (both need the motion variables listed in `tokens/README.md` on the app `:root`, because `tokens.css` sets them), and `animations.css` (not with the Tailwind preset). `base.css` and `tabs-pills.css` read raw `--accent`, `--foreground`, `--muted`, `--segment` and `--default`; skip them unless the app defines those as full colour values.
|
|
1046
1063
|
|
|
1047
|
-
The compat file
|
|
1064
|
+
The compat file defines `.glass-panel-strong` for toasts, dialogs and hover cards as the solid popover colour (`rgb(var(--popover))`, as `bg-overlay`), the same as `tokens.css`. Before 0.15.0 it was `rgb(var(--card) / 0.95)` with a 12px blur. The rule is in `@layer components`, so an app `.glass-panel-strong` rule after the import wins.
|
|
1048
1065
|
|
|
1049
1066
|
A fixture that renders the main components against sample light and dark RGB variables is in `tokens/examples/compat-shadcn-rgb/`.
|
|
1050
1067
|
|
package/app/README.md
CHANGED
|
@@ -160,7 +160,7 @@ import { PageMotionProvider, useDisablePageMotion } from "@vvedantb/ui";
|
|
|
160
160
|
</PageMotionProvider>;
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
- Sets `html[data-page-motion]` to `"off"` or `"on"` in a layout effect
|
|
163
|
+
- Sets `html[data-page-motion]` to `"off"` or `"on"` in a layout effect. The attribute stays after unmount. From 0.15.0 `tokens.css` (through `tokens/css/motion.css`) ships the `"off"` rules: `.ui-page-enter`, `.ui-enter`, `.ui-enter-up` and `.ui-stagger` fade only, `animate-in` / `animate-out`, `.t-modal` and `.t-tabs-pill` stop transitioning, and view transitions stop.
|
|
164
164
|
- Renders `motionConfig` with `reducedMotion` `"always"` when disabled, else `"never"`.
|
|
165
165
|
- `useDisablePageMotion()`: the `disabled` value, `false` outside the provider.
|
|
166
166
|
- This module does not import `motion`. Pass `MotionConfig`, or an app component that wraps it, defined at module level, for example with `LazyMotion`:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vvedantb/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Shared React 19 UI for web apps: one JavaScript entry (primitives, shell pieces, app helpers including the Convex and Clerk provider, VITE env parsing and ensure-user, artifact viewers over the @vvedantb/server artifacts core, native tokens), design-token CSS, shared Tailwind v4 CSS and a Tailwind preset.",
|
|
6
6
|
"type": "module",
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"./tokens/css/tabs-pills.css": "./tokens/src/css/tabs-pills.css",
|
|
28
28
|
"./tokens/css/utilities.css": "./tokens/src/css/utilities.css",
|
|
29
29
|
"./tokens/css/scrollbar-thin.css": "./tokens/src/css/scrollbar-thin.css",
|
|
30
|
+
"./tokens/css/clear-input.css": "./tokens/src/css/clear-input.css",
|
|
31
|
+
"./tokens/css/motion.css": "./tokens/src/css/motion.css",
|
|
30
32
|
"./tokens/css/animations.css": "./tokens/src/css/animations.css",
|
|
31
33
|
"./tokens/css/nav-icons.css": "./tokens/src/css/nav-icons.css",
|
|
32
34
|
"./tokens/css/compat-shadcn-rgb.css": "./tokens/src/css/compat-shadcn-rgb.css",
|
|
@@ -64,6 +66,9 @@
|
|
|
64
66
|
"analyze": "node tokens/verify/analyze.mjs",
|
|
65
67
|
"prepack": "node tokens/scripts/check-exports.mjs"
|
|
66
68
|
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"react-day-picker": "^9.14.0"
|
|
71
|
+
},
|
|
67
72
|
"peerDependencies": {
|
|
68
73
|
"@clerk/clerk-react": ">=5",
|
|
69
74
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
@@ -21,8 +21,8 @@ export const railBadgeClass = "absolute -bottom-0.5 -right-0.5 flex h-4 min-w-4
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Static section label above a group of panel nav rows. `px-4` lines the text
|
|
24
|
-
* up with the nav row icons; the colour comes from
|
|
25
|
-
*
|
|
24
|
+
* up with the nav row icons; the colour comes from `.sidebar-section-label` in
|
|
25
|
+
* tokens.css.
|
|
26
26
|
*/
|
|
27
27
|
export const sidebarSectionLabelClass =
|
|
28
28
|
"sidebar-section-label px-4 py-1 text-[11px] leading-4 font-medium tracking-[-0.01em]";
|
package/src/index.ts
CHANGED
|
@@ -55,6 +55,8 @@ export * from "./ui/tabs";
|
|
|
55
55
|
export * from "./ui/textarea";
|
|
56
56
|
export * from "./ui/toolbar-search-input";
|
|
57
57
|
export * from "./ui/time-picker";
|
|
58
|
+
export * from "./ui/calendar";
|
|
59
|
+
export * from "./ui/date-picker";
|
|
58
60
|
export * from "./ui/tooltip";
|
|
59
61
|
export * from "./ui/kbd";
|
|
60
62
|
export * from "./ui/sheet";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// input-styled trigger shared by TimePicker and the date pickers
|
|
2
|
+
export const pickerTriggerClass =
|
|
3
|
+
"inline-flex h-8 items-center justify-between gap-2 rounded-field border border-border bg-field-background px-2.5 text-xs font-medium tabular-nums transition-[border-color,box-shadow,background-color] duration-200 ease-smooth hover:bg-field-background/90 focus-visible:border-focus-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring disabled:cursor-not-allowed disabled:opacity-50";
|
|
4
|
+
|
|
5
|
+
// selected item in a picker popover (TimePicker columns, calendar days, months)
|
|
6
|
+
export const pickerSelectedClass = "bg-primary text-primary-foreground";
|
|
7
|
+
|
|
8
|
+
export const pickerItemHoverClass = "hover:bg-surface-tertiary/50";
|
|
9
|
+
|
|
10
|
+
export const pickerFocusClass =
|
|
11
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring";
|
|
12
|
+
|
|
13
|
+
// wrapper around a trigger and its siblings (clear control, second picker). it
|
|
14
|
+
// shrinks to the trigger, grows with a `w-*` className, and goes full width
|
|
15
|
+
// when a trigger inside is `w-full` (a percent width cannot resolve against a
|
|
16
|
+
// shrink-to-fit wrapper)
|
|
17
|
+
export const pickerFieldClass =
|
|
18
|
+
"relative inline-flex max-w-full align-middle has-[.w-full]:flex has-[.w-full]:w-full";
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
IconChevronDown,
|
|
4
|
+
IconChevronLeft,
|
|
5
|
+
IconChevronRight,
|
|
6
|
+
} from "@tabler/icons-react";
|
|
7
|
+
import {
|
|
8
|
+
type Chevron,
|
|
9
|
+
DayPicker,
|
|
10
|
+
type Root,
|
|
11
|
+
getDefaultClassNames,
|
|
12
|
+
} from "react-day-picker";
|
|
13
|
+
import { cn } from "../utils/cn";
|
|
14
|
+
import {
|
|
15
|
+
pickerFocusClass,
|
|
16
|
+
} from "./_picker-classes";
|
|
17
|
+
|
|
18
|
+
// shadcn's v9 calendar on package tokens: no background of its own (it takes
|
|
19
|
+
// the popover or card surface it sits on), selected days use the picker
|
|
20
|
+
// active treatment, today gets a dot, outside days are muted and disabled
|
|
21
|
+
// days dimmed. day state lives on the cell (`data-selected`, `data-today`,
|
|
22
|
+
// ...), so the day button reads it through `group/day`
|
|
23
|
+
const navButtonClass = cn(
|
|
24
|
+
"inline-flex size-(--cell-size) select-none items-center justify-center rounded-lg p-0 text-muted-foreground transition-colors hover:bg-surface-tertiary hover:text-foreground aria-disabled:pointer-events-none aria-disabled:opacity-50",
|
|
25
|
+
pickerFocusClass,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
function CalendarRoot({
|
|
29
|
+
className,
|
|
30
|
+
rootRef,
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentProps<typeof Root>) {
|
|
33
|
+
return (
|
|
34
|
+
<div data-slot="calendar" ref={rootRef} className={className} {...props} />
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function CalendarChevron({
|
|
39
|
+
className,
|
|
40
|
+
orientation,
|
|
41
|
+
}: React.ComponentProps<typeof Chevron>) {
|
|
42
|
+
const iconClass = cn("size-4", className);
|
|
43
|
+
if (orientation === "left") return <IconChevronLeft className={iconClass} />;
|
|
44
|
+
if (orientation === "right") {
|
|
45
|
+
return <IconChevronRight className={iconClass} />;
|
|
46
|
+
}
|
|
47
|
+
return <IconChevronDown className={iconClass} />;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function Calendar({
|
|
51
|
+
className,
|
|
52
|
+
classNames,
|
|
53
|
+
showOutsideDays = true,
|
|
54
|
+
captionLayout = "label",
|
|
55
|
+
formatters,
|
|
56
|
+
components,
|
|
57
|
+
...props
|
|
58
|
+
}: React.ComponentProps<typeof DayPicker>) {
|
|
59
|
+
const defaultClassNames = getDefaultClassNames();
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<DayPicker
|
|
63
|
+
showOutsideDays={showOutsideDays}
|
|
64
|
+
className={cn(
|
|
65
|
+
"group/calendar p-1 text-foreground [--cell-size:--spacing(8)]",
|
|
66
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
67
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
captionLayout={captionLayout}
|
|
71
|
+
formatters={{
|
|
72
|
+
formatMonthDropdown: (date) =>
|
|
73
|
+
date.toLocaleString("default", { month: "short" }),
|
|
74
|
+
...formatters,
|
|
75
|
+
}}
|
|
76
|
+
classNames={{
|
|
77
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
78
|
+
months: cn(
|
|
79
|
+
"relative flex flex-col gap-4 md:flex-row",
|
|
80
|
+
defaultClassNames.months,
|
|
81
|
+
),
|
|
82
|
+
month: cn("flex w-full flex-col gap-2", defaultClassNames.month),
|
|
83
|
+
nav: cn(
|
|
84
|
+
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
85
|
+
defaultClassNames.nav,
|
|
86
|
+
),
|
|
87
|
+
button_previous: cn(navButtonClass, defaultClassNames.button_previous),
|
|
88
|
+
button_next: cn(navButtonClass, defaultClassNames.button_next),
|
|
89
|
+
month_caption: cn(
|
|
90
|
+
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
|
91
|
+
defaultClassNames.month_caption,
|
|
92
|
+
),
|
|
93
|
+
dropdowns: cn(
|
|
94
|
+
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-xs font-medium",
|
|
95
|
+
defaultClassNames.dropdowns,
|
|
96
|
+
),
|
|
97
|
+
dropdown_root: cn(
|
|
98
|
+
"relative rounded-field border border-border has-focus:border-focus-border has-focus:ring-2 has-focus:ring-focus-ring",
|
|
99
|
+
defaultClassNames.dropdown_root,
|
|
100
|
+
),
|
|
101
|
+
dropdown: cn(
|
|
102
|
+
"absolute inset-0 bg-overlay opacity-0",
|
|
103
|
+
defaultClassNames.dropdown,
|
|
104
|
+
),
|
|
105
|
+
caption_label: cn(
|
|
106
|
+
"select-none text-xs font-medium",
|
|
107
|
+
captionLayout !== "label" &&
|
|
108
|
+
"flex h-7 items-center gap-1 rounded-field pl-2 pr-1 [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
|
109
|
+
defaultClassNames.caption_label,
|
|
110
|
+
),
|
|
111
|
+
month_grid: "w-full border-collapse",
|
|
112
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
113
|
+
weekday: cn(
|
|
114
|
+
"flex-1 select-none text-[10px] font-normal uppercase tracking-wider text-muted-foreground",
|
|
115
|
+
defaultClassNames.weekday,
|
|
116
|
+
),
|
|
117
|
+
week: cn("mt-1 flex w-full", defaultClassNames.week),
|
|
118
|
+
week_number_header: cn(
|
|
119
|
+
"w-(--cell-size) select-none",
|
|
120
|
+
defaultClassNames.week_number_header,
|
|
121
|
+
),
|
|
122
|
+
week_number: cn(
|
|
123
|
+
"select-none text-[10px] text-muted-foreground",
|
|
124
|
+
defaultClassNames.week_number,
|
|
125
|
+
),
|
|
126
|
+
day: cn(
|
|
127
|
+
"group/day relative aspect-square h-full w-full select-none p-0 text-center",
|
|
128
|
+
defaultClassNames.day,
|
|
129
|
+
),
|
|
130
|
+
day_button: cn(
|
|
131
|
+
"relative inline-flex size-(--cell-size) items-center justify-center rounded-lg text-xs tabular-nums text-foreground/80 transition-colors disabled:cursor-not-allowed disabled:opacity-40",
|
|
132
|
+
pickerFocusClass,
|
|
133
|
+
// today, outside and hover rules exclude selected days, so the
|
|
134
|
+
// selected tone never depends on the order Tailwind emits rules in.
|
|
135
|
+
// the today dot is `bg-current`: foreground, or primary-foreground
|
|
136
|
+
// when selected
|
|
137
|
+
"not-group-data-selected/day:enabled:hover:bg-surface-tertiary/50",
|
|
138
|
+
"group-data-today/day:font-semibold group-data-today/day:not-group-data-selected/day:text-foreground group-data-today/day:after:absolute group-data-today/day:after:bottom-1 group-data-today/day:after:left-1/2 group-data-today/day:after:size-1 group-data-today/day:after:-translate-x-1/2 group-data-today/day:after:rounded-full group-data-today/day:after:bg-current",
|
|
139
|
+
"group-data-outside/day:not-group-data-selected/day:text-muted-foreground",
|
|
140
|
+
// spelled out (not built from pickerSelectedClass) so Tailwind's
|
|
141
|
+
// scanner sees it
|
|
142
|
+
"group-data-selected/day:bg-primary group-data-selected/day:text-primary-foreground",
|
|
143
|
+
defaultClassNames.day_button,
|
|
144
|
+
),
|
|
145
|
+
range_start: cn("rounded-l-lg", defaultClassNames.range_start),
|
|
146
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
147
|
+
range_end: cn("rounded-r-lg", defaultClassNames.range_end),
|
|
148
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
149
|
+
...classNames,
|
|
150
|
+
}}
|
|
151
|
+
components={{
|
|
152
|
+
Root: CalendarRoot,
|
|
153
|
+
Chevron: CalendarChevron,
|
|
154
|
+
...components,
|
|
155
|
+
}}
|
|
156
|
+
{...props}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { Calendar };
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
/** transitions.dev input clear with dissolve — see 13-input-clear-dissolve.md */
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Times come back in ms. CSS minifiers rewrite `1000ms` as `1s`, so a value
|
|
4
|
+
// in seconds is converted rather than read as 1.
|
|
5
|
+
export function readCssNumber(
|
|
4
6
|
root: HTMLElement,
|
|
5
7
|
name: string,
|
|
6
8
|
fallback: number,
|
|
7
9
|
): number {
|
|
8
|
-
const raw = getComputedStyle(root).getPropertyValue(name);
|
|
10
|
+
const raw = getComputedStyle(root).getPropertyValue(name).trim();
|
|
9
11
|
const parsed = parseFloat(raw);
|
|
10
|
-
|
|
12
|
+
if (!Number.isFinite(parsed)) {
|
|
13
|
+
return fallback;
|
|
14
|
+
}
|
|
15
|
+
return /\ds$/.test(raw) ? parsed * 1000 : parsed;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
function readCssEase(root: HTMLElement, name: string): (t: number) => number {
|
|
18
|
+
export function readCssEase(root: HTMLElement, name: string): (t: number) => number {
|
|
14
19
|
const raw = getComputedStyle(root).getPropertyValue(name);
|
|
15
20
|
const match = raw.match(
|
|
16
|
-
/cubic-bezier\(([-\d.]+)
|
|
21
|
+
/cubic-bezier\(\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*\)/,
|
|
17
22
|
);
|
|
18
23
|
if (!match) {
|
|
19
24
|
return (t) => t;
|