@godxjp/ui 13.11.1 → 13.12.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/dist/components/data-display/card.d.ts +16 -0
- package/dist/components/data-display/card.js +8 -0
- package/dist/components/data-display/index.d.ts +2 -2
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-entry/date-picker.js +2 -1
- package/dist/components/data-entry/date-range-picker.js +2 -1
- package/dist/components/data-entry/month-picker.js +2 -1
- package/dist/components/data-entry/month-range-picker.js +2 -1
- package/dist/lib/hooks.d.ts +15 -0
- package/dist/lib/hooks.js +6 -0
- package/dist/props/registry.d.ts +5 -0
- package/dist/props/registry.js +5 -0
- package/dist/styles/card-layout.css +37 -0
- package/dist/styles/form-layout.css +8 -0
- package/package.json +1 -1
|
@@ -65,6 +65,22 @@ export declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttri
|
|
|
65
65
|
/** Full-bleed footer (Ant Design `actions` bar). */
|
|
66
66
|
flush?: boolean;
|
|
67
67
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
68
|
+
export type CardBarProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
69
|
+
/** Right-aligned actions slot (settings/save), Ant `tabBarExtraContent`-style. */
|
|
70
|
+
extra?: React.ReactNode;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* CardBar — a horizontal bar (view tabs, toolbar, filter chips) that can sit at
|
|
74
|
+
* ANY position inside a Card. It draws its own separators FROM its position: a
|
|
75
|
+
* top bar gets a bottom border, a bottom bar gets a top border, a middle bar
|
|
76
|
+
* gets both, and a sole child gets none (the card border is enough). The main
|
|
77
|
+
* area scrolls horizontally; the `extra` slot is pinned to the inline-end edge
|
|
78
|
+
* for actions (column settings, save view, …).
|
|
79
|
+
*/
|
|
80
|
+
export declare const CardBar: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
81
|
+
/** Right-aligned actions slot (settings/save), Ant `tabBarExtraContent`-style. */
|
|
82
|
+
extra?: React.ReactNode;
|
|
83
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
68
84
|
export type StatCardProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof cardVariants> & {
|
|
69
85
|
label: React.ReactNode;
|
|
70
86
|
value: React.ReactNode;
|
|
@@ -77,6 +77,13 @@ const CardFooter = React.forwardRef(
|
|
|
77
77
|
)
|
|
78
78
|
);
|
|
79
79
|
CardFooter.displayName = "CardFooter";
|
|
80
|
+
const CardBar = React.forwardRef(
|
|
81
|
+
({ className, children, extra, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, "data-slot": "card-bar", className: cn("ui-card-bar", className), ...props, children: [
|
|
82
|
+
/* @__PURE__ */ jsx("div", { "data-slot": "card-bar-main", className: "ui-card-bar-main", children }),
|
|
83
|
+
extra != null ? /* @__PURE__ */ jsx("div", { "data-slot": "card-bar-extra", className: "ui-card-bar-extra", children: extra }) : null
|
|
84
|
+
] })
|
|
85
|
+
);
|
|
86
|
+
CardBar.displayName = "CardBar";
|
|
80
87
|
function getDeltaTone(delta, inverse) {
|
|
81
88
|
const text = typeof delta === "string" || typeof delta === "number" ? String(delta).trim() : "";
|
|
82
89
|
const sign = text.match(/^[+\-−]/)?.[0];
|
|
@@ -143,6 +150,7 @@ CardAction.displayName = "CardAction";
|
|
|
143
150
|
export {
|
|
144
151
|
Card,
|
|
145
152
|
CardAction,
|
|
153
|
+
CardBar,
|
|
146
154
|
CardContent,
|
|
147
155
|
CardCover,
|
|
148
156
|
CardDescription,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { Badge } from "./badge";
|
|
2
2
|
export type { BadgeProps } from "./badge";
|
|
3
3
|
export { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
4
|
-
export { Card, CardContent, CardCover, CardDescription, CardFooter, CardHeader, CardTitle, CardAction, StatCard, } from "./card";
|
|
5
|
-
export type { StatCardProps } from "./card";
|
|
4
|
+
export { Card, CardBar, CardContent, CardCover, CardDescription, CardFooter, CardHeader, CardTitle, CardAction, StatCard, } from "./card";
|
|
5
|
+
export type { StatCardProps, CardBarProps } from "./card";
|
|
6
6
|
export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
|
|
7
7
|
export { Descriptions } from "./descriptions";
|
|
8
8
|
export { DataTable } from "./data-table";
|
|
@@ -2,6 +2,7 @@ import { Badge } from "./badge";
|
|
|
2
2
|
import { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
3
3
|
import {
|
|
4
4
|
Card,
|
|
5
|
+
CardBar,
|
|
5
6
|
CardContent,
|
|
6
7
|
CardCover,
|
|
7
8
|
CardDescription,
|
|
@@ -51,6 +52,7 @@ export {
|
|
|
51
52
|
Badge,
|
|
52
53
|
Card,
|
|
53
54
|
CardAction,
|
|
55
|
+
CardBar,
|
|
54
56
|
CardContent,
|
|
55
57
|
CardCover,
|
|
56
58
|
CardDescription,
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { CalendarIcon, X } from "lucide-react";
|
|
4
4
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
|
|
5
5
|
import { parseDateInput, toIsoDate } from "../../lib/datetime/parse";
|
|
6
|
+
import { useControlledLatch } from "../../lib/hooks";
|
|
6
7
|
import { cn } from "../../lib/utils";
|
|
7
8
|
import { Button } from "../general/button";
|
|
8
9
|
import { Input } from "./input";
|
|
@@ -26,7 +27,7 @@ function DatePicker({
|
|
|
26
27
|
const { t } = useTranslation();
|
|
27
28
|
const { dayPickerLocale } = usePickerLocales(localeProp);
|
|
28
29
|
const [open, setOpen] = React.useState(false);
|
|
29
|
-
const isControlled =
|
|
30
|
+
const isControlled = useControlledLatch(valueProp !== void 0);
|
|
30
31
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
31
32
|
const value = isControlled ? valueProp : internalValue;
|
|
32
33
|
const emit = (next) => {
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { ArrowRight, CalendarIcon, X } from "lucide-react";
|
|
4
4
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
|
|
5
5
|
import { parseDateInput, toIsoDate } from "../../lib/datetime";
|
|
6
|
+
import { useControlledLatch } from "../../lib/hooks";
|
|
6
7
|
import { cn } from "../../lib/utils";
|
|
7
8
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover";
|
|
8
9
|
import { Calendar } from "./calendar";
|
|
@@ -27,7 +28,7 @@ function DateRangePicker({
|
|
|
27
28
|
const autoId = React.useId();
|
|
28
29
|
const fromId = id ?? autoId;
|
|
29
30
|
const toId = `${fromId}-to`;
|
|
30
|
-
const isControlled =
|
|
31
|
+
const isControlled = useControlledLatch(valueProp !== void 0);
|
|
31
32
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
32
33
|
const value = isControlled ? valueProp : internalValue;
|
|
33
34
|
const [fromText, setFromText] = React.useState(() => toIsoDate(value?.from));
|
|
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
4
4
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
|
|
5
|
+
import { useControlledLatch } from "../../lib/hooks";
|
|
5
6
|
import { cn } from "../../lib/utils";
|
|
6
7
|
import { Button } from "../general/button";
|
|
7
8
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover";
|
|
@@ -32,7 +33,7 @@ function MonthPicker({
|
|
|
32
33
|
const [open, setOpen] = React.useState(false);
|
|
33
34
|
const autoId = React.useId();
|
|
34
35
|
const inputId = id ?? autoId;
|
|
35
|
-
const isControlled =
|
|
36
|
+
const isControlled = useControlledLatch(valueProp !== void 0);
|
|
36
37
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
37
38
|
const value = isControlled ? valueProp : internalValue;
|
|
38
39
|
const [text, setText] = React.useState(() => toYmText(value));
|
|
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { ArrowRight, CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
4
4
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
|
|
5
|
+
import { useControlledLatch } from "../../lib/hooks";
|
|
5
6
|
import { cn } from "../../lib/utils";
|
|
6
7
|
import { Button } from "../general/button";
|
|
7
8
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover";
|
|
@@ -34,7 +35,7 @@ function MonthRangePicker({
|
|
|
34
35
|
const autoId = React.useId();
|
|
35
36
|
const fromId = id ?? autoId;
|
|
36
37
|
const toId = `${fromId}-to`;
|
|
37
|
-
const isControlled =
|
|
38
|
+
const isControlled = useControlledLatch(valueProp !== void 0);
|
|
38
39
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
39
40
|
const value = isControlled ? valueProp : internalValue;
|
|
40
41
|
const [fromText, setFromText] = React.useState(() => toYmText(value?.from));
|
package/dist/lib/hooks.d.ts
CHANGED
|
@@ -11,5 +11,20 @@ export declare function useDebouncedValue<T>(value: T, delay?: number): T;
|
|
|
11
11
|
* setState is scheduled asynchronously (setTimeout 0 / ms) — Rules of React safe.
|
|
12
12
|
*/
|
|
13
13
|
export declare function useTimeoutFlag(signal: unknown, ms?: number): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Controlled-ness latch for `value`/`defaultValue`/`onValueChange` controls
|
|
16
|
+
* whose empty state is `undefined` (pickers carrying `Date`/`DateRange`).
|
|
17
|
+
*
|
|
18
|
+
* A control counts as controlled once a DEFINED `value` has EVER been passed:
|
|
19
|
+
* - mounted with a defined `value` → controlled, and a later `value={undefined}`
|
|
20
|
+
* stays controlled-EMPTY (not mistaken for uncontrolled);
|
|
21
|
+
* - mounted with `value={undefined}` (an empty form that later restores a
|
|
22
|
+
* saved value) → uncontrolled until the first defined value arrives, then
|
|
23
|
+
* PROMOTES to controlled for good.
|
|
24
|
+
*
|
|
25
|
+
* Fixing controlled-ness at mount (the previous behaviour) silently ignored
|
|
26
|
+
* every later controlled value in the second case.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useControlledLatch(valueIsDefined: boolean): boolean;
|
|
14
29
|
export declare function useMediaQuery(query: string): boolean;
|
|
15
30
|
export declare function useIsMobile(): boolean;
|
package/dist/lib/hooks.js
CHANGED
|
@@ -35,6 +35,11 @@ function useTimeoutFlag(signal, ms = 2e3) {
|
|
|
35
35
|
}, [signal, ms]);
|
|
36
36
|
return Boolean(signal) && active;
|
|
37
37
|
}
|
|
38
|
+
function useControlledLatch(valueIsDefined) {
|
|
39
|
+
const [latched, setLatched] = useState(valueIsDefined);
|
|
40
|
+
if (valueIsDefined && !latched) setLatched(true);
|
|
41
|
+
return valueIsDefined || latched;
|
|
42
|
+
}
|
|
38
43
|
function useMediaQuery(query) {
|
|
39
44
|
const isBrowser = typeof window !== "undefined";
|
|
40
45
|
const getMatch = () => isBrowser ? window.matchMedia(query).matches : false;
|
|
@@ -63,6 +68,7 @@ function useIsMobile() {
|
|
|
63
68
|
return useMediaQuery("(max-width: 767px)");
|
|
64
69
|
}
|
|
65
70
|
export {
|
|
71
|
+
useControlledLatch,
|
|
66
72
|
useDebouncedValue,
|
|
67
73
|
useIsMobile,
|
|
68
74
|
useMediaQuery,
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -914,6 +914,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
914
914
|
readonly file: "components/data-display/card.tsx";
|
|
915
915
|
readonly vocabulary: readonly ["ClassNameProp", "ChildrenProp"];
|
|
916
916
|
};
|
|
917
|
+
readonly CardBarProp: {
|
|
918
|
+
readonly group: "data-display";
|
|
919
|
+
readonly file: "components/data-display/card.tsx";
|
|
920
|
+
readonly vocabulary: readonly ["ClassNameProp", "ChildrenProp"];
|
|
921
|
+
};
|
|
917
922
|
readonly StatCardProp: {
|
|
918
923
|
readonly group: "data-display";
|
|
919
924
|
readonly file: "components/data-display/card.tsx";
|
package/dist/props/registry.js
CHANGED
|
@@ -966,6 +966,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
966
966
|
file: "components/data-display/card.tsx",
|
|
967
967
|
vocabulary: ["ClassNameProp", "ChildrenProp"]
|
|
968
968
|
},
|
|
969
|
+
CardBarProp: {
|
|
970
|
+
group: "data-display",
|
|
971
|
+
file: "components/data-display/card.tsx",
|
|
972
|
+
vocabulary: ["ClassNameProp", "ChildrenProp"]
|
|
973
|
+
},
|
|
969
974
|
StatCardProp: {
|
|
970
975
|
group: "data-display",
|
|
971
976
|
file: "components/data-display/card.tsx",
|
|
@@ -103,6 +103,43 @@
|
|
|
103
103
|
padding-bottom: 0;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/* ── CardBar — positionable tab/toolbar strip (CardBar in card.tsx) ───────
|
|
107
|
+
* Separators come from POSITION: a top bar gets a bottom border, a bottom
|
|
108
|
+
* bar a top border, a middle bar both, a sole child none. `border-block` is
|
|
109
|
+
* the block (top/bottom) axis — RTL-neutral. The card's own overflow:hidden
|
|
110
|
+
* clips the bar to the rounded corners, so it needs no radius of its own. */
|
|
111
|
+
[data-slot="card-bar"] {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: var(--space-inline-md);
|
|
115
|
+
padding: var(--card-space-header-y) var(--card-space-inset);
|
|
116
|
+
border-block: 1px solid hsl(var(--card-border));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-slot="card-bar"]:first-child {
|
|
120
|
+
border-block-start-width: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
[data-slot="card-bar"]:last-child {
|
|
124
|
+
border-block-end-width: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
[data-slot="card-bar-main"] {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: var(--space-inline-sm);
|
|
131
|
+
min-width: 0;
|
|
132
|
+
flex: 1 1 auto;
|
|
133
|
+
overflow-x: auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[data-slot="card-bar-extra"] {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: var(--space-inline-xs);
|
|
140
|
+
flex-shrink: 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
106
143
|
.ui-card-header--banded {
|
|
107
144
|
border-bottom: var(--card-header-border-bottom);
|
|
108
145
|
background-color: hsl(var(--card-header-background) / var(--card-header-background-alpha));
|
|
@@ -39,6 +39,14 @@
|
|
|
39
39
|
min-width: 0;
|
|
40
40
|
/* Cap the control width when a controlWidth is set; otherwise fill the column. */
|
|
41
41
|
max-inline-size: var(--form-control-width, none);
|
|
42
|
+
/* Reserve the control-row height and centre the content so a SHORT control
|
|
43
|
+
* (switch, slider, rating, a bare checkbox/radio — anything thinner than an
|
|
44
|
+
* input) lines up with the label in a horizontal field instead of sitting
|
|
45
|
+
* high. `align-content: center` centres the content row without stretching
|
|
46
|
+
* the control; full-width inputs still fill via the 1fr column, and taller
|
|
47
|
+
* controls (textarea) simply grow past the floor. */
|
|
48
|
+
min-height: var(--control-height);
|
|
49
|
+
align-content: center;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
/* Let controls (and helper/error) shrink within the cell instead of forcing
|