@stll/ui 0.44.1 → 0.46.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 +2 -1
- package/dist/components/accordion.d.ts +6 -2
- package/dist/components/accordion.js +4 -4
- package/dist/components/ai-label.d.ts +33 -0
- package/dist/components/ai-label.js +42 -0
- package/dist/components/alert-dialog.js +3 -2
- package/dist/components/animated-count.d.ts +13 -0
- package/dist/components/animated-count.js +63 -0
- package/dist/components/application-rail.js +1 -1
- package/dist/components/brand-icons.d.ts +1 -0
- package/dist/components/brand-icons.js +8 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/button.js +12 -2
- package/dist/components/command.js +1 -1
- package/dist/components/court-badge.d.ts +7 -1
- package/dist/components/court-badge.js +13 -3
- package/dist/components/date-picker-popover.d.ts +12 -2
- package/dist/components/date-picker-popover.js +105 -9
- package/dist/components/date-range-filter.d.ts +14 -0
- package/dist/components/date-range-filter.js +74 -0
- package/dist/components/dialog.js +3 -2
- package/dist/components/document-identity-badge.d.ts +12 -0
- package/dist/components/document-identity-badge.js +94 -0
- package/dist/components/document-identity-badge.logic.d.ts +25 -0
- package/dist/components/document-identity-badge.logic.js +25 -0
- package/dist/components/input-group.d.ts +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/landing.d.ts +1 -1
- package/dist/components/landing.js +2 -2
- package/dist/components/list.d.ts +8 -2
- package/dist/components/list.js +10 -4
- package/dist/components/matter-icon.d.ts +16 -0
- package/dist/components/matter-icon.js +56 -0
- package/dist/components/menu.d.ts +9 -1
- package/dist/components/menu.js +6 -6
- package/dist/components/outline-rail.d.ts +8 -1
- package/dist/components/outline-rail.js +95 -48
- package/dist/components/popover.js +1 -1
- package/dist/components/search-field.js +30 -34
- package/dist/components/select.js +1 -1
- package/dist/components/sheet.js +22 -4
- package/dist/components/sidebar.d.ts +1 -1
- package/dist/components/sidebar.js +8 -8
- package/dist/components/status-mark.d.ts +17 -0
- package/dist/components/status-mark.js +74 -0
- package/dist/components/swap-text.d.ts +16 -0
- package/dist/components/swap-text.js +52 -0
- package/dist/components/switch.d.ts +5 -0
- package/dist/components/switch.js +16 -0
- package/dist/components/table-chart.d.ts +20 -0
- package/dist/components/table-chart.js +274 -0
- package/dist/components/tabs.js +1 -1
- package/dist/components/toast.js +42 -25
- package/dist/components/workspace-shell.js +5 -4
- package/dist/icons.js +1 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/inspector/dock.js +1 -1
- package/dist/inspector/entity-tab.js +1 -2
- package/dist/kanban/virtual-cell.js +1 -1
- package/dist/lib/control-size.d.ts +1 -1
- package/dist/lib/matter-colors.d.ts +38 -0
- package/dist/lib/matter-colors.js +65 -0
- package/dist/lib/panel-inset.d.ts +11 -0
- package/dist/lib/panel-inset.js +33 -0
- package/dist/lib/safe-area.d.ts +11 -0
- package/dist/lib/safe-area.js +12 -0
- package/dist/review/review-word-diff.d.ts +13 -0
- package/dist/review/review-word-diff.js +78 -0
- package/dist/styles/theme.css +79 -0
- package/package.json +64 -8
package/README.md
CHANGED
|
@@ -213,7 +213,8 @@ column and keep their trailing column, so they never scroll sideways.
|
|
|
213
213
|
<ListGroupHeader>
|
|
214
214
|
<ListGroupHeading>
|
|
215
215
|
<ListGroupTitle id="apps">
|
|
216
|
-
Apps with access
|
|
216
|
+
Apps with access
|
|
217
|
+
<ListGroupCount count="3" />
|
|
217
218
|
</ListGroupTitle>
|
|
218
219
|
<ListGroupDescription>Apps you authorized.</ListGroupDescription>
|
|
219
220
|
</ListGroupHeading>
|
|
@@ -2,7 +2,11 @@ import { Accordion as Accordion$1 } from "@base-ui/react/accordion";
|
|
|
2
2
|
//#region src/components/accordion.d.ts
|
|
3
3
|
declare const Accordion: <Value = unknown>(props: Accordion$1.Root.Props<Value>) => import("react").JSX.Element;
|
|
4
4
|
declare const AccordionItem: ({ className, ...props }: Accordion$1.Item.Props) => import("react").JSX.Element;
|
|
5
|
-
declare const AccordionTrigger: ({ className, children, ...props }: Accordion$1.Trigger.Props
|
|
6
|
-
|
|
5
|
+
declare const AccordionTrigger: ({ className, children, size, ...props }: Accordion$1.Trigger.Props & {
|
|
6
|
+
size?: "default" | "compact";
|
|
7
|
+
}) => import("react").JSX.Element;
|
|
8
|
+
declare const AccordionPanel: ({ className, children, size, ...props }: Accordion$1.Panel.Props & {
|
|
9
|
+
size?: "default" | "compact";
|
|
10
|
+
}) => import("react").JSX.Element;
|
|
7
11
|
//#endregion
|
|
8
12
|
export { Accordion, AccordionPanel as AccordionContent, AccordionPanel, AccordionItem, AccordionTrigger };
|
|
@@ -13,10 +13,10 @@ const AccordionItem = ({ className, ...props }) => /* @__PURE__ */ jsx(Accordion
|
|
|
13
13
|
"data-slot": "accordion-item",
|
|
14
14
|
...props
|
|
15
15
|
});
|
|
16
|
-
const AccordionTrigger = ({ className, children, ...props }) => /* @__PURE__ */ jsx(Accordion$1.Header, {
|
|
16
|
+
const AccordionTrigger = ({ className, children, size = "default", ...props }) => /* @__PURE__ */ jsx(Accordion$1.Header, {
|
|
17
17
|
className: "flex",
|
|
18
18
|
children: /* @__PURE__ */ jsxs(Accordion$1.Trigger, {
|
|
19
|
-
className: cn("focus-visible:ring-ring flex flex-1 cursor-pointer items-start justify-between gap-4 rounded-md
|
|
19
|
+
className: cn("focus-visible:ring-ring flex flex-1 cursor-pointer items-start justify-between gap-4 rounded-md text-start font-medium outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-64 data-panel-open:*:data-[slot=accordion-indicator]:rotate-180", size === "compact" ? "py-2 text-xs" : "py-4 text-sm", className),
|
|
20
20
|
"data-slot": "accordion-trigger",
|
|
21
21
|
...props,
|
|
22
22
|
children: [children, /* @__PURE__ */ jsx(ChevronDownIcon, {
|
|
@@ -25,12 +25,12 @@ const AccordionTrigger = ({ className, children, ...props }) => /* @__PURE__ */
|
|
|
25
25
|
})]
|
|
26
26
|
})
|
|
27
27
|
});
|
|
28
|
-
const AccordionPanel = ({ className, children, ...props }) => /* @__PURE__ */ jsx(Accordion$1.Panel, {
|
|
28
|
+
const AccordionPanel = ({ className, children, size = "default", ...props }) => /* @__PURE__ */ jsx(Accordion$1.Panel, {
|
|
29
29
|
className: "text-muted-foreground h-(--accordion-panel-height) overflow-hidden text-sm transition-[height] duration-200 ease-in-out data-ending-style:h-0 data-starting-style:h-0",
|
|
30
30
|
"data-slot": "accordion-panel",
|
|
31
31
|
...props,
|
|
32
32
|
children: /* @__PURE__ */ jsx("div", {
|
|
33
|
-
className: cn("pt-0 pb-4", className),
|
|
33
|
+
className: cn("pt-0", size === "compact" ? "pb-3" : "pb-4", className),
|
|
34
34
|
children
|
|
35
35
|
})
|
|
36
36
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/components/ai-label.d.ts
|
|
2
|
+
type AiLabelProps = {
|
|
3
|
+
/**
|
|
4
|
+
* The words the mark shows ("AI", "AI topics"), already translated. They
|
|
5
|
+
* are also the control's accessible name, so what a sighted reader sees is
|
|
6
|
+
* what a screen-reader user hears.
|
|
7
|
+
*/
|
|
8
|
+
label: string;
|
|
9
|
+
/**
|
|
10
|
+
* What the reader is told about the content the mark stands beside, already
|
|
11
|
+
* translated. Hosts pass the sentence for one closed disclosure context
|
|
12
|
+
* (`@stll/api-contract/ai-disclosure`), never free text.
|
|
13
|
+
*/
|
|
14
|
+
disclosure: string;
|
|
15
|
+
className?: string | undefined;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The mark on content a model wrote, and the one place a reader learns what
|
|
19
|
+
* that means.
|
|
20
|
+
*
|
|
21
|
+
* It keeps the weight of the plain label it replaces: the surrounding text's
|
|
22
|
+
* size, colour, case and tracking, a small sparkle, no chip and no tint. The
|
|
23
|
+
* only addition is that it is a button: a click, a tap, Enter or Space opens
|
|
24
|
+
* a small popover with the disclosure, and Escape or a click outside closes
|
|
25
|
+
* it. Nothing depends on hover.
|
|
26
|
+
*
|
|
27
|
+
* The trigger stays inline, so it can sit inside a heading or a line of
|
|
28
|
+
* text. Never inside another control (a `<summary>`, a link, a button): it is
|
|
29
|
+
* one itself, and nested interactive content is unreachable to assistive
|
|
30
|
+
* technology.
|
|
31
|
+
*/
|
|
32
|
+
export declare const AiLabel: ({ label, disclosure, className }: AiLabelProps) => import("react").JSX.Element;
|
|
33
|
+
//#endregion
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { SparklesIcon } from "../icons.js";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { Popover, PopoverContent as PopoverPopup, PopoverDescription, PopoverTrigger } from "./popover.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/ai-label.tsx
|
|
7
|
+
/**
|
|
8
|
+
* The mark on content a model wrote, and the one place a reader learns what
|
|
9
|
+
* that means.
|
|
10
|
+
*
|
|
11
|
+
* It keeps the weight of the plain label it replaces: the surrounding text's
|
|
12
|
+
* size, colour, case and tracking, a small sparkle, no chip and no tint. The
|
|
13
|
+
* only addition is that it is a button: a click, a tap, Enter or Space opens
|
|
14
|
+
* a small popover with the disclosure, and Escape or a click outside closes
|
|
15
|
+
* it. Nothing depends on hover.
|
|
16
|
+
*
|
|
17
|
+
* The trigger stays inline, so it can sit inside a heading or a line of
|
|
18
|
+
* text. Never inside another control (a `<summary>`, a link, a button): it is
|
|
19
|
+
* one itself, and nested interactive content is unreachable to assistive
|
|
20
|
+
* technology.
|
|
21
|
+
*/
|
|
22
|
+
const AiLabel = ({ label, disclosure, className }) => /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
23
|
+
className: cn("focus-visible:ring-ring relative inline-flex cursor-pointer items-center justify-center gap-1 rounded-sm align-middle tracking-[inherit] text-inherit [text-transform:inherit] decoration-dotted underline-offset-2 outline-none [font:inherit] hover:underline focus-visible:ring-2 data-popup-open:underline pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11", className),
|
|
24
|
+
"data-slot": "ai-label",
|
|
25
|
+
type: "button",
|
|
26
|
+
children: [/* @__PURE__ */ jsx(SparklesIcon, {
|
|
27
|
+
"aria-hidden": true,
|
|
28
|
+
className: "size-3 shrink-0"
|
|
29
|
+
}), label]
|
|
30
|
+
}), /* @__PURE__ */ jsx(PopoverPopup, {
|
|
31
|
+
"aria-label": label,
|
|
32
|
+
className: "max-w-72",
|
|
33
|
+
"data-slot": "ai-label-popup",
|
|
34
|
+
padding: "sm",
|
|
35
|
+
side: "bottom",
|
|
36
|
+
children: /* @__PURE__ */ jsx(PopoverDescription, {
|
|
37
|
+
className: "text-xs leading-relaxed text-pretty",
|
|
38
|
+
children: disclosure
|
|
39
|
+
})
|
|
40
|
+
})] });
|
|
41
|
+
//#endregion
|
|
42
|
+
export { AiLabel };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
|
|
4
|
+
import { SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME } from "../lib/safe-area.js";
|
|
4
5
|
import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
|
|
@@ -24,7 +25,7 @@ const AlertDialogBackdrop = ({ className, layer = "default", ...props }) => /* @
|
|
|
24
25
|
...props
|
|
25
26
|
});
|
|
26
27
|
const AlertDialogViewport = ({ className, layer = "default", ...props }) => /* @__PURE__ */ jsx(AlertDialog$1.Viewport, {
|
|
27
|
-
className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center
|
|
28
|
+
className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center", SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME, OVERLAY_LAYER_CLASS_NAMES[layer], className),
|
|
28
29
|
"data-slot": "alert-dialog-viewport",
|
|
29
30
|
...props
|
|
30
31
|
});
|
|
@@ -32,7 +33,7 @@ const AlertDialogPopup = ({ className, bottomStickOnMobile = true, layer = "defa
|
|
|
32
33
|
className: cn(bottomStickOnMobile && "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12"),
|
|
33
34
|
layer,
|
|
34
35
|
children: /* @__PURE__ */ jsx(AlertDialog$1.Popup, {
|
|
35
|
-
className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-
|
|
36
|
+
className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-ending-style:duration-150 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:pb-(--safe-area-bottom) max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
|
|
36
37
|
"data-slot": "alert-dialog-popup",
|
|
37
38
|
...props
|
|
38
39
|
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/components/animated-count.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A formatted number whose changed characters fade in. The caller formats
|
|
4
|
+
* (locale digits, units); the glyphs stay plain inline text so bidirectional
|
|
5
|
+
* ordering is the browser's, and only opacity moves. The first render is
|
|
6
|
+
* still: a count that loads does not announce itself.
|
|
7
|
+
*/
|
|
8
|
+
declare const AnimatedCount: ({ className, value }: {
|
|
9
|
+
className?: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}) => import("react").JSX.Element;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { AnimatedCount };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
//#region src/components/animated-count.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Aligns characters from the end, where digits keep their place value, so
|
|
8
|
+
* 99 → 100 changes every glyph but 120 → 121 only the last one. An unchanged
|
|
9
|
+
* glyph keeps its key and therefore its DOM node: it never replays its fade.
|
|
10
|
+
*/
|
|
11
|
+
const toGlyphs = (value, previous, generation) => {
|
|
12
|
+
const chars = Array.from(value);
|
|
13
|
+
return chars.map((char, index) => {
|
|
14
|
+
const fromEnd = chars.length - index;
|
|
15
|
+
const priorIndex = previous === null ? -1 : previous.length - fromEnd;
|
|
16
|
+
const prior = priorIndex >= 0 ? previous?.[priorIndex] : void 0;
|
|
17
|
+
if (prior?.char === char) return prior;
|
|
18
|
+
return {
|
|
19
|
+
char,
|
|
20
|
+
enters: previous !== null,
|
|
21
|
+
key: `${generation}:${fromEnd}`
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A formatted number whose changed characters fade in. The caller formats
|
|
27
|
+
* (locale digits, units); the glyphs stay plain inline text so bidirectional
|
|
28
|
+
* ordering is the browser's, and only opacity moves. The first render is
|
|
29
|
+
* still: a count that loads does not announce itself.
|
|
30
|
+
*/
|
|
31
|
+
const AnimatedCount = ({ className, value }) => {
|
|
32
|
+
const [state, setState] = useState(() => ({
|
|
33
|
+
generation: 0,
|
|
34
|
+
glyphs: toGlyphs(value, null, 0),
|
|
35
|
+
value
|
|
36
|
+
}));
|
|
37
|
+
let { glyphs } = state;
|
|
38
|
+
if (state.value !== value) {
|
|
39
|
+
const generation = state.generation + 1;
|
|
40
|
+
glyphs = toGlyphs(value, state.glyphs, generation);
|
|
41
|
+
setState({
|
|
42
|
+
generation,
|
|
43
|
+
glyphs,
|
|
44
|
+
value
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
48
|
+
className: cn("tabular-nums", className),
|
|
49
|
+
"data-slot": "animated-count",
|
|
50
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
51
|
+
className: "sr-only",
|
|
52
|
+
children: value
|
|
53
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
54
|
+
"aria-hidden": "true",
|
|
55
|
+
children: glyphs.map((glyph) => /* @__PURE__ */ jsx("span", {
|
|
56
|
+
className: cn(glyph.enters && "motion-safe:animate-text-fade"),
|
|
57
|
+
children: glyph.char
|
|
58
|
+
}, glyph.key))
|
|
59
|
+
})]
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { AnimatedCount };
|
|
@@ -22,7 +22,7 @@ const ApplicationRailHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("
|
|
|
22
22
|
...props
|
|
23
23
|
});
|
|
24
24
|
const ApplicationRailContent = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
25
|
-
className: cn("flex min-h-0 flex-1 [scrollbar-width:none] flex-col gap-2 overflow-x-hidden overflow-y-auto [&::-webkit-scrollbar]:hidden", className),
|
|
25
|
+
className: cn("flex min-h-0 flex-1 [scrollbar-width:none] flex-col gap-2 overflow-x-hidden overflow-y-auto overscroll-contain [&::-webkit-scrollbar]:hidden", className),
|
|
26
26
|
"data-slot": "application-rail-content",
|
|
27
27
|
...props
|
|
28
28
|
});
|
|
@@ -3,4 +3,5 @@ import * as React$1 from "react";
|
|
|
3
3
|
type BrandIconProps = React$1.ComponentProps<"svg">;
|
|
4
4
|
export declare const DiscordLogoIcon: (props: BrandIconProps) => React$1.JSX.Element;
|
|
5
5
|
export declare const GitHubLogoIcon: (props: BrandIconProps) => React$1.JSX.Element;
|
|
6
|
+
export declare const XLogoIcon: (props: BrandIconProps) => React$1.JSX.Element;
|
|
6
7
|
//#endregion
|
|
@@ -14,5 +14,12 @@ const GitHubLogoIcon = (props) => /* @__PURE__ */ jsx("svg", {
|
|
|
14
14
|
...props,
|
|
15
15
|
children: /* @__PURE__ */ jsx("path", { d: "M6.766 11.328c-2.063-.25-3.516-1.734-3.516-3.656 0-.781.281-1.625.75-2.188-.203-.515-.172-1.609.063-2.062.625-.078 1.468.25 1.968.703.594-.187 1.219-.281 1.985-.281.765 0 1.39.094 1.953.265.484-.437 1.344-.765 1.969-.687.218.422.25 1.515.046 2.047.5.593.766 1.39.766 2.203 0 1.922-1.453 3.375-3.547 3.64.531.344.89 1.094.89 1.954v1.625c0 .468.391.734.86.547C13.781 14.359 16 11.53 16 8.03 16 3.61 12.406 0 7.984 0 3.563 0 0 3.61 0 8.031a7.88 7.88 0 0 0 5.172 7.422c.422.156.828-.125.828-.547v-1.25c-.219.094-.5.156-.75.156-1.031 0-1.64-.562-2.078-1.609-.172-.422-.36-.672-.719-.719-.187-.015-.25-.093-.25-.187 0-.188.313-.328.625-.328.453 0 .844.281 1.25.86.313.452.64.655 1.031.655s.641-.14 1-.5c.266-.265.47-.5.657-.656" })
|
|
16
16
|
});
|
|
17
|
+
const XLogoIcon = (props) => /* @__PURE__ */ jsx("svg", {
|
|
18
|
+
"aria-hidden": "true",
|
|
19
|
+
fill: "currentColor",
|
|
20
|
+
viewBox: "0 0 24 24",
|
|
21
|
+
...props,
|
|
22
|
+
children: /* @__PURE__ */ jsx("path", { d: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.64 7.584H.47l8.6-9.835L0 1.153h7.594l5.243 6.932 6.064-6.932Zm-1.29 19.49h2.039L6.486 3.24H4.298l13.313 17.403Z" })
|
|
23
|
+
});
|
|
17
24
|
//#endregion
|
|
18
|
-
export { DiscordLogoIcon, GitHubLogoIcon };
|
|
25
|
+
export { DiscordLogoIcon, GitHubLogoIcon, XLogoIcon };
|
|
@@ -13,8 +13,8 @@ import { VariantProps } from "class-variance-authority";
|
|
|
13
13
|
*/
|
|
14
14
|
declare const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
|
|
15
15
|
declare const buttonVariants: (props?: ({
|
|
16
|
-
size?: "
|
|
17
|
-
variant?: "link" | "
|
|
16
|
+
size?: "default" | "row" | "chip" | "icon" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "sm" | "xl" | "xs" | null | undefined;
|
|
17
|
+
variant?: "link" | "default" | "destructive" | "muted" | "destructive-outline" | "destructive-ghost" | "ghost" | "outline" | "secondary" | null | undefined;
|
|
18
18
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
19
19
|
type ButtonProps = {
|
|
20
20
|
variant?: VariantProps<typeof buttonVariants>["variant"];
|
|
@@ -19,7 +19,17 @@ import { cva } from "class-variance-authority";
|
|
|
19
19
|
* swaps the cursor so the control still reads as unavailable on hover.
|
|
20
20
|
*/
|
|
21
21
|
const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Press feedback: the button gives slightly under the pointer.
|
|
24
|
+
*
|
|
25
|
+
* Applied by `Button` only while it can act, so a disabled or loading button
|
|
26
|
+
* never appears to respond. `scale` is its own property in Tailwind v4, so it
|
|
27
|
+
* composes with a caller's `translate` (a vertically centred icon button) and
|
|
28
|
+
* the transition names only the transform family. Menu rows and link-styled
|
|
29
|
+
* buttons keep the treatment of their surface instead.
|
|
30
|
+
*/
|
|
31
|
+
const buttonPressClass = "motion-safe:transition-transform motion-safe:duration-150 ease-out motion-safe:active:scale-[0.98]";
|
|
32
|
+
const buttonVariants = cva("focus-visible:ring-ring focus-visible:ring-offset-background relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-lg border text-base font-medium whitespace-nowrap outline-none select-none before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] focus-visible:ring-2 focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-64 sm:text-sm pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", {
|
|
23
33
|
defaultVariants: {
|
|
24
34
|
size: "default",
|
|
25
35
|
variant: "default"
|
|
@@ -68,7 +78,7 @@ function Button({ className, variant, size, render, loading, children, disabled,
|
|
|
68
78
|
size: "sm",
|
|
69
79
|
variant: "decorative"
|
|
70
80
|
}), children] }) : children,
|
|
71
|
-
className: cn(buttonVariants({
|
|
81
|
+
className: cn(disposition === BUTTON_DISPOSITION.enabled && size !== "row" && variant !== "link" && buttonPressClass, buttonVariants({
|
|
72
82
|
className,
|
|
73
83
|
size,
|
|
74
84
|
variant
|
|
@@ -66,7 +66,7 @@ const CommandEmpty = ({ className, ...props }) => /* @__PURE__ */ jsx(Autocomple
|
|
|
66
66
|
...props
|
|
67
67
|
});
|
|
68
68
|
const CommandList = ({ className, ...props }) => /* @__PURE__ */ jsx(Autocomplete.List, {
|
|
69
|
-
className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
|
|
69
|
+
className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto overscroll-contain", className),
|
|
70
70
|
"data-slot": "command-list",
|
|
71
71
|
...props
|
|
72
72
|
});
|
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
* carries meaning on its own anyway: the court's name stands beside it.
|
|
10
10
|
*/
|
|
11
11
|
export type CourtBadgeWeight = "solid" | "tinted" | "outline" | "dashed";
|
|
12
|
+
/**
|
|
13
|
+
* `default` stands on its own beside a court's name; `inline` sits inside a
|
|
14
|
+
* line of small text (a list row's meta line) without making it taller.
|
|
15
|
+
*/
|
|
16
|
+
export type CourtBadgeSize = "default" | "inline";
|
|
12
17
|
type CourtBadgeProps = {
|
|
13
18
|
/** The court's own abbreviation, in the language its name is in. */
|
|
14
19
|
abbreviation: string;
|
|
15
20
|
weight: CourtBadgeWeight;
|
|
21
|
+
size?: CourtBadgeSize | undefined;
|
|
16
22
|
className?: string;
|
|
17
23
|
};
|
|
18
24
|
/**
|
|
@@ -23,5 +29,5 @@ type CourtBadgeProps = {
|
|
|
23
29
|
* abbreviation is actually known. There is no placeholder: a court nobody
|
|
24
30
|
* abbreviates gets no chip.
|
|
25
31
|
*/
|
|
26
|
-
export declare const CourtBadge: ({ abbreviation, className, weight }: CourtBadgeProps) => import("react").JSX.Element;
|
|
32
|
+
export declare const CourtBadge: ({ abbreviation, className, size, weight }: CourtBadgeProps) => import("react").JSX.Element;
|
|
27
33
|
//#endregion
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
//#region src/components/court-badge.tsx
|
|
4
|
-
const COURT_BADGE_BASE_CLASS = "inline-flex shrink-0 items-center rounded-sm border
|
|
4
|
+
const COURT_BADGE_BASE_CLASS = "inline-flex shrink-0 items-center rounded-sm border font-mono font-semibold tracking-wide whitespace-nowrap";
|
|
5
|
+
/**
|
|
6
|
+
* The line box runs a third taller than the type, because these capitals
|
|
7
|
+
* carry diacritics (Ú, Ř, Š) whose ink rises above cap height; a tighter box
|
|
8
|
+
* sets the acute against the pill's border. `inline` keeps that ratio at a
|
|
9
|
+
* smaller size and drops the padding, so the chip fits a 16px text line.
|
|
10
|
+
*/
|
|
11
|
+
const SIZE_CLASS = {
|
|
12
|
+
default: "px-1 py-0.5 text-xs leading-4",
|
|
13
|
+
inline: "px-0.5 text-3xs leading-3.5"
|
|
14
|
+
};
|
|
5
15
|
/**
|
|
6
16
|
* Every weight keeps a border, because `--muted` is a 4% tint: a borderless
|
|
7
17
|
* tinted chip on a sidebar or a muted row is a pill whose edge nobody can
|
|
@@ -31,8 +41,8 @@ const WEIGHT_CLASS = {
|
|
|
31
41
|
* abbreviation is actually known. There is no placeholder: a court nobody
|
|
32
42
|
* abbreviates gets no chip.
|
|
33
43
|
*/
|
|
34
|
-
const CourtBadge = ({ abbreviation, className, weight }) => /* @__PURE__ */ jsx("span", {
|
|
35
|
-
className: cn(COURT_BADGE_BASE_CLASS, WEIGHT_CLASS[weight], className),
|
|
44
|
+
const CourtBadge = ({ abbreviation, className, size = "default", weight }) => /* @__PURE__ */ jsx("span", {
|
|
45
|
+
className: cn(COURT_BADGE_BASE_CLASS, SIZE_CLASS[size], WEIGHT_CLASS[weight], className),
|
|
36
46
|
"data-slot": "court-badge",
|
|
37
47
|
dir: "ltr",
|
|
38
48
|
children: abbreviation
|
|
@@ -41,6 +41,16 @@ type DatePickerPopoverProps = {
|
|
|
41
41
|
placeholderLabel?: string;
|
|
42
42
|
clearLabel?: string;
|
|
43
43
|
defaultOpen?: boolean;
|
|
44
|
+
open?: boolean;
|
|
45
|
+
triggerRef?: React.Ref<HTMLButtonElement> | undefined;
|
|
46
|
+
/** Keep the trigger focused when opening a following range bound. */
|
|
47
|
+
focusTriggerOnOpen?: boolean;
|
|
48
|
+
/** Enables direct date entry, with host-localized validation. */
|
|
49
|
+
dateInputLabel?: string;
|
|
50
|
+
outOfRangeLabel?: string;
|
|
51
|
+
noFuture?: boolean;
|
|
52
|
+
/** Host-owned calendar date; defaults to the browser timezone. */
|
|
53
|
+
getToday?: () => string;
|
|
44
54
|
onOpenChange?: (open: boolean) => void;
|
|
45
55
|
/** Label for the "go to today" button. Auto-localized from the locale when omitted. */
|
|
46
56
|
todayLabel?: string;
|
|
@@ -57,8 +67,8 @@ type DatePickerPopoverProps = {
|
|
|
57
67
|
/** Label of the time input in `date-time` mode. */
|
|
58
68
|
timeLabel?: string;
|
|
59
69
|
/** Inclusive `YYYY-MM-DD` bounds; in `date-time` mode they bound the day. */
|
|
60
|
-
minDate?: string;
|
|
61
|
-
maxDate?: string;
|
|
70
|
+
minDate?: string | undefined;
|
|
71
|
+
maxDate?: string | undefined;
|
|
62
72
|
isDateDisabled?: (date: string) => boolean;
|
|
63
73
|
layer?: OverlayLayer;
|
|
64
74
|
};
|
|
@@ -3,6 +3,7 @@ import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "../icons.js";
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
5
|
import { DirectionalIcon } from "./directional-icon.js";
|
|
6
|
+
import { Input } from "./input.js";
|
|
6
7
|
import { FIELD_TRIGGER_CLASS_NAME } from "../lib/field-trigger.js";
|
|
7
8
|
import { getLocaleWeekInfo, getWeekendDays } from "../lib/week.js";
|
|
8
9
|
import { DATE_PICKER_MODE, formatDateTimeValue, getHourOptions, getMinuteOptions, getTimeFieldNames, joinPickerTime, localDateFromTimestamp, millisecondsUntilNextLocalDate, parseDateTimeValue, resolveCalendarViewMonth, shiftCalendarDate, splitPickerTime } from "./date-picker-popover.logic.js";
|
|
@@ -10,7 +11,7 @@ import { Popover, PopoverContent as PopoverPopup, PopoverTrigger } from "./popov
|
|
|
10
11
|
import { Select, SelectContent as SelectPopup, SelectItem, SelectTrigger, SelectValue } from "./select.js";
|
|
11
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
13
|
import { useCallback, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
13
|
-
import { panic } from "better-result";
|
|
14
|
+
import { Result, panic } from "better-result";
|
|
14
15
|
import { Temporal } from "temporal-polyfill/full";
|
|
15
16
|
//#region src/components/date-picker-popover.tsx
|
|
16
17
|
const toISODate = (date) => date.toString();
|
|
@@ -57,7 +58,7 @@ const subscribeToLocalDate = (onStoreChange) => {
|
|
|
57
58
|
if (localDateListeners.size === 0) stopLocalDateSubscription();
|
|
58
59
|
};
|
|
59
60
|
};
|
|
60
|
-
const useHydrationSafeToday = () => useSyncExternalStore(subscribeToLocalDate,
|
|
61
|
+
const useHydrationSafeToday = (getToday) => useSyncExternalStore(subscribeToLocalDate, getToday, () => HYDRATION_DATE);
|
|
61
62
|
const useHydrationSafeBrowserLocale = () => useSyncExternalStore(noopSubscribe, () => navigator.language || HYDRATION_LOCALE, () => HYDRATION_LOCALE);
|
|
62
63
|
const getFirstDayOfWeek = (locale) => {
|
|
63
64
|
const info = getLocaleWeekInfo(locale);
|
|
@@ -252,7 +253,7 @@ const PICKER_SIZE_CLASS_NAMES = {
|
|
|
252
253
|
}
|
|
253
254
|
};
|
|
254
255
|
const DatePickerPopoverContent = (props) => {
|
|
255
|
-
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, clearLabel = "Clear date", defaultOpen = false, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
256
|
+
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, clearLabel = "Clear date", defaultOpen = false, open: controlledOpen, triggerRef, focusTriggerOnOpen = false, dateInputLabel, outOfRangeLabel, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
256
257
|
const selection = resolveSelection(mode, rawValue);
|
|
257
258
|
const value = selection.date;
|
|
258
259
|
const labels = resolvePopupLabels(props);
|
|
@@ -294,14 +295,19 @@ const DatePickerPopoverContent = (props) => {
|
|
|
294
295
|
maxDate,
|
|
295
296
|
isDateDisabled
|
|
296
297
|
]);
|
|
298
|
+
const dateDraft = useDatePickerDraft({
|
|
299
|
+
value,
|
|
300
|
+
isDayDisabled,
|
|
301
|
+
onChange
|
|
302
|
+
});
|
|
297
303
|
const displayLabel = formatSelection(locale, selection) ?? placeholderLabel ?? "—";
|
|
298
304
|
const selectDay = (date) => {
|
|
299
305
|
switch (selection.mode) {
|
|
300
306
|
case DATE_PICKER_MODE.date:
|
|
301
|
-
|
|
307
|
+
dateDraft.apply(date);
|
|
302
308
|
return;
|
|
303
309
|
case DATE_PICKER_MODE.dateTime:
|
|
304
|
-
|
|
310
|
+
dateDraft.apply(formatDateTimeValue({
|
|
305
311
|
date,
|
|
306
312
|
time: selection.time ?? "00:00"
|
|
307
313
|
}));
|
|
@@ -311,7 +317,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
311
317
|
};
|
|
312
318
|
const handleTimeChange = (clock) => {
|
|
313
319
|
if (value === "") return;
|
|
314
|
-
|
|
320
|
+
dateDraft.apply(formatDateTimeValue({
|
|
315
321
|
date: value,
|
|
316
322
|
time: joinPickerTime(clock)
|
|
317
323
|
}));
|
|
@@ -443,6 +449,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
443
449
|
})();
|
|
444
450
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
445
451
|
defaultOpen,
|
|
452
|
+
open: controlledOpen,
|
|
446
453
|
onOpenChange: handleOpenChange,
|
|
447
454
|
children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
448
455
|
disabled,
|
|
@@ -451,6 +458,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
451
458
|
"aria-labelledby": labelledBy ? `${labelledBy} ${displayValueId}` : void 0,
|
|
452
459
|
className: cn(TRIGGER_VARIANT_CLASS_NAMES[variant], triggerToneClassName, className),
|
|
453
460
|
id,
|
|
461
|
+
ref: triggerRef,
|
|
454
462
|
type: "button"
|
|
455
463
|
}),
|
|
456
464
|
children: [
|
|
@@ -466,7 +474,8 @@ const DatePickerPopoverContent = (props) => {
|
|
|
466
474
|
})
|
|
467
475
|
]
|
|
468
476
|
}), /* @__PURE__ */ jsx(PopoverPopup, {
|
|
469
|
-
|
|
477
|
+
onBlur: dateDraft.handleBlur,
|
|
478
|
+
initialFocus: focusTriggerOnOpen ? false : () => gridRef.current?.querySelector(`[data-date="${value || today}"]`) ?? gridRef.current,
|
|
470
479
|
className: "data-starting-style:scale-100",
|
|
471
480
|
layer,
|
|
472
481
|
padding: "sm",
|
|
@@ -516,6 +525,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
516
525
|
onClick: () => {
|
|
517
526
|
if (!isUnavailable) selectDay(day.date);
|
|
518
527
|
},
|
|
528
|
+
onFocus: () => setFocusedDate(day.date),
|
|
519
529
|
role: "gridcell",
|
|
520
530
|
tabIndex: isFocused ? 0 : -1,
|
|
521
531
|
type: "button",
|
|
@@ -546,11 +556,21 @@ const DatePickerPopoverContent = (props) => {
|
|
|
546
556
|
onChange: handleTimeChange,
|
|
547
557
|
time: selection.time
|
|
548
558
|
}),
|
|
559
|
+
/* @__PURE__ */ jsx(TypedDateField, {
|
|
560
|
+
mode: selection.mode,
|
|
561
|
+
label: dateInputLabel,
|
|
562
|
+
value: dateDraft.value,
|
|
563
|
+
invalid: dateDraft.invalid,
|
|
564
|
+
errorLabel: outOfRangeLabel,
|
|
565
|
+
onDraftChange: dateDraft.edit,
|
|
566
|
+
onCommit: dateDraft.submit
|
|
567
|
+
}),
|
|
549
568
|
/* @__PURE__ */ jsxs("div", {
|
|
550
569
|
className: "mt-1 flex items-center gap-1 border-t pt-1",
|
|
551
570
|
children: [/* @__PURE__ */ jsx(Button, {
|
|
552
571
|
className: "flex-1",
|
|
553
572
|
onClick: () => {
|
|
573
|
+
dateDraft.discard();
|
|
554
574
|
const todayDate = Temporal.PlainDate.from(today);
|
|
555
575
|
setViewMonthOverride({
|
|
556
576
|
month: todayDate.month - 1,
|
|
@@ -563,7 +583,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
563
583
|
children: todayLabel
|
|
564
584
|
}), value && !hideClear && /* @__PURE__ */ jsx(Button, {
|
|
565
585
|
className: "flex-1",
|
|
566
|
-
onClick: () =>
|
|
586
|
+
onClick: () => dateDraft.apply(null),
|
|
567
587
|
size: "xs",
|
|
568
588
|
variant: "ghost",
|
|
569
589
|
children: clearLabel
|
|
@@ -576,10 +596,12 @@ const DatePickerPopoverContent = (props) => {
|
|
|
576
596
|
};
|
|
577
597
|
const DatePickerPopover = (props) => {
|
|
578
598
|
const browserLocale = useHydrationSafeBrowserLocale();
|
|
579
|
-
const today = useHydrationSafeToday();
|
|
599
|
+
const today = useHydrationSafeToday(props.getToday ?? getLocalToday);
|
|
580
600
|
const locale = props.locale ?? browserLocale;
|
|
601
|
+
const maxDate = props.noFuture && (!props.maxDate || props.maxDate > today) ? today : props.maxDate;
|
|
581
602
|
return /* @__PURE__ */ jsx(DatePickerPopoverContent, {
|
|
582
603
|
...props,
|
|
604
|
+
maxDate,
|
|
583
605
|
locale,
|
|
584
606
|
today
|
|
585
607
|
});
|
|
@@ -738,5 +760,79 @@ const YearGrid = ({ decadeBase, currentYear, cellClassName, onSelect, today }) =
|
|
|
738
760
|
}, row[0]))
|
|
739
761
|
});
|
|
740
762
|
};
|
|
763
|
+
const useDatePickerDraft = ({ value, isDayDisabled, onChange }) => {
|
|
764
|
+
const [draft, setDraft] = useState(null);
|
|
765
|
+
const commitStatus = useRef("consumed");
|
|
766
|
+
const hasDraft = draft?.baseValue === value;
|
|
767
|
+
const inputValue = hasDraft ? draft.value : value;
|
|
768
|
+
const parsed = Result.try(() => Temporal.PlainDate.from(inputValue));
|
|
769
|
+
const invalid = hasDraft && inputValue !== "" && (parsed.isErr() || parsed.value.toString() !== inputValue || isDayDisabled(inputValue));
|
|
770
|
+
const discard = () => {
|
|
771
|
+
commitStatus.current = "consumed";
|
|
772
|
+
setDraft(null);
|
|
773
|
+
};
|
|
774
|
+
const apply = (next) => {
|
|
775
|
+
discard();
|
|
776
|
+
onChange(next);
|
|
777
|
+
};
|
|
778
|
+
const commit = () => {
|
|
779
|
+
if (commitStatus.current !== "pending" || invalid || inputValue === value) return;
|
|
780
|
+
apply(inputValue || null);
|
|
781
|
+
};
|
|
782
|
+
return {
|
|
783
|
+
value: inputValue,
|
|
784
|
+
invalid,
|
|
785
|
+
edit: (next) => {
|
|
786
|
+
commitStatus.current = "pending";
|
|
787
|
+
setDraft({
|
|
788
|
+
baseValue: value,
|
|
789
|
+
value: next
|
|
790
|
+
});
|
|
791
|
+
},
|
|
792
|
+
discard,
|
|
793
|
+
apply,
|
|
794
|
+
submit: () => {
|
|
795
|
+
if (!invalid) apply(inputValue || null);
|
|
796
|
+
},
|
|
797
|
+
handleBlur: (event) => {
|
|
798
|
+
if (event.currentTarget.contains(event.relatedTarget)) return;
|
|
799
|
+
commit();
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
};
|
|
803
|
+
const TypedDateField = ({ mode, label, value, invalid, errorLabel = "Choose a date within the allowed range.", onDraftChange, onCommit }) => {
|
|
804
|
+
const id = useId();
|
|
805
|
+
if (!label || mode !== DATE_PICKER_MODE.date) return null;
|
|
806
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
807
|
+
className: "mt-2 flex flex-col gap-1",
|
|
808
|
+
children: [
|
|
809
|
+
/* @__PURE__ */ jsx("label", {
|
|
810
|
+
htmlFor: id,
|
|
811
|
+
className: "text-xs",
|
|
812
|
+
children: label
|
|
813
|
+
}),
|
|
814
|
+
/* @__PURE__ */ jsx(Input, {
|
|
815
|
+
id,
|
|
816
|
+
value,
|
|
817
|
+
placeholder: "YYYY-MM-DD",
|
|
818
|
+
dir: "ltr",
|
|
819
|
+
"aria-invalid": invalid,
|
|
820
|
+
"aria-describedby": invalid ? `${id}-error` : void 0,
|
|
821
|
+
onChange: (event) => onDraftChange(event.currentTarget.value),
|
|
822
|
+
onKeyDown: (event) => {
|
|
823
|
+
if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
|
|
824
|
+
event.preventDefault();
|
|
825
|
+
onCommit();
|
|
826
|
+
}
|
|
827
|
+
}),
|
|
828
|
+
invalid && /* @__PURE__ */ jsx("p", {
|
|
829
|
+
id: `${id}-error`,
|
|
830
|
+
role: "alert",
|
|
831
|
+
className: "text-destructive text-xs",
|
|
832
|
+
children: errorLabel
|
|
833
|
+
})
|
|
834
|
+
]
|
|
835
|
+
});
|
|
836
|
+
};
|
|
741
837
|
//#endregion
|
|
742
838
|
export { DatePickerPopover };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DatePickerPopoverProps } from "./date-picker-popover.js";
|
|
2
|
+
//#region src/components/date-range-filter.d.ts
|
|
3
|
+
type DateRangeFilterProps = Omit<DatePickerPopoverProps, "value" | "onChange" | "minDate" | "open" | "defaultOpen" | "onOpenChange" | "triggerRef" | "focusTriggerOnOpen" | "id" | "labelledBy" | "mode"> & {
|
|
4
|
+
from: string | null;
|
|
5
|
+
to: string | null;
|
|
6
|
+
onFromChange: (value: string | null) => void;
|
|
7
|
+
onToChange: (value: string | null) => void;
|
|
8
|
+
fromLabel: string;
|
|
9
|
+
toLabel: string;
|
|
10
|
+
};
|
|
11
|
+
/** Both calendar bounds and the focus handoff belong to the range. */
|
|
12
|
+
declare const DateRangeFilter: ({ from, to, onFromChange, onToChange, fromLabel, toLabel, className, maxDate, ...pickerProps }: DateRangeFilterProps) => import("react").JSX.Element;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { DateRangeFilter, type DateRangeFilterProps };
|