@firecms/ui 3.1.0 → 3.2.0-canary.4c3b8f2
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/index.es.js +141 -22
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +141 -22
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/components/DateTimeField.tsx +7 -2
- package/src/components/Tabs.tsx +121 -33
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.2.0-canary.4c3b8f2",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"index.css",
|
|
115
115
|
"tailwind.config.js"
|
|
116
116
|
],
|
|
117
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "b86dabf4d939a8db1d84f4581c8cc3cfcccf0891",
|
|
118
118
|
"publishConfig": {
|
|
119
119
|
"access": "public"
|
|
120
120
|
}
|
|
@@ -51,7 +51,7 @@ export const DateTimeField: React.FC<DateTimeFieldProps> = ({
|
|
|
51
51
|
const [focused, setFocused] = useState(false);
|
|
52
52
|
const [internalValue, setInternalValue] = useState<string>("");
|
|
53
53
|
const [isTyping, setIsTyping] = useState(false);
|
|
54
|
-
const invalidValue = value !== undefined && value !== null && !(value instanceof Date);
|
|
54
|
+
const invalidValue = value !== undefined && value !== null && (!(value instanceof Date) || isNaN((value as Date).getTime()));
|
|
55
55
|
|
|
56
56
|
useInjectStyles("DateTimeField", inputStyles);
|
|
57
57
|
|
|
@@ -84,7 +84,12 @@ export const DateTimeField: React.FC<DateTimeFieldProps> = ({
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
const formatter = new Intl.DateTimeFormat("en-CA", options);
|
|
87
|
-
|
|
87
|
+
let parts: Intl.DateTimeFormatPart[];
|
|
88
|
+
try {
|
|
89
|
+
parts = formatter.formatToParts(dateValue);
|
|
90
|
+
} catch {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
88
93
|
|
|
89
94
|
const getPart = (type: string) => parts.find(p => p.type === type)?.value ?? "";
|
|
90
95
|
|
package/src/components/Tabs.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useRef, useState, useEffect } from "react";
|
|
2
2
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
3
|
import { cls } from "../util";
|
|
4
4
|
import { defaultBorderMixin } from "../styles";
|
|
5
|
+
import { IconButton } from "./IconButton";
|
|
6
|
+
import { ChevronLeftIcon, ChevronRightIcon } from "../icons";
|
|
5
7
|
|
|
6
8
|
export type TabsProps = {
|
|
7
9
|
value: string,
|
|
@@ -12,22 +14,108 @@ export type TabsProps = {
|
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export function Tabs({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
value,
|
|
18
|
+
onValueChange,
|
|
19
|
+
className,
|
|
20
|
+
innerClassName,
|
|
21
|
+
children
|
|
22
|
+
}: TabsProps) {
|
|
23
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
24
|
+
const [showLeftScroll, setShowLeftScroll] = useState(false);
|
|
25
|
+
const [showRightScroll, setShowRightScroll] = useState(false);
|
|
26
|
+
const [isScrollable, setIsScrollable] = useState(false);
|
|
27
|
+
|
|
28
|
+
const checkScroll = () => {
|
|
29
|
+
if (scrollContainerRef.current) {
|
|
30
|
+
const { scrollLeft, scrollWidth, clientWidth } = scrollContainerRef.current;
|
|
31
|
+
setShowLeftScroll(scrollLeft > 0);
|
|
32
|
+
setShowRightScroll(Math.ceil(scrollLeft + clientWidth) < scrollWidth);
|
|
33
|
+
setIsScrollable(scrollWidth > clientWidth);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
checkScroll();
|
|
39
|
+
window.addEventListener("resize", checkScroll);
|
|
40
|
+
|
|
41
|
+
let observer: ResizeObserver;
|
|
42
|
+
if (scrollContainerRef.current) {
|
|
43
|
+
observer = new ResizeObserver(checkScroll);
|
|
44
|
+
observer.observe(scrollContainerRef.current);
|
|
45
|
+
if (scrollContainerRef.current.firstElementChild) {
|
|
46
|
+
observer.observe(scrollContainerRef.current.firstElementChild);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return () => {
|
|
51
|
+
window.removeEventListener("resize", checkScroll);
|
|
52
|
+
observer?.disconnect();
|
|
53
|
+
};
|
|
54
|
+
}, [children]);
|
|
55
|
+
|
|
56
|
+
const scroll = (direction: "left" | "right") => {
|
|
57
|
+
if (scrollContainerRef.current) {
|
|
58
|
+
const container = scrollContainerRef.current;
|
|
59
|
+
const scrollAmount = Math.max(container.clientWidth / 2, 200);
|
|
60
|
+
const targetScroll = container.scrollLeft + (direction === "left" ? -scrollAmount : scrollAmount);
|
|
61
|
+
|
|
62
|
+
container.scrollTo({
|
|
63
|
+
left: targetScroll,
|
|
64
|
+
behavior: "smooth"
|
|
65
|
+
});
|
|
66
|
+
// checkScroll will be called by onScroll event
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return <TabsPrimitive.Root value={value} onValueChange={onValueChange} className={cls("flex flex-row items-center min-w-0", className)}>
|
|
71
|
+
{isScrollable && (
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
disabled={!showLeftScroll}
|
|
75
|
+
onClick={() => scroll("left")}
|
|
76
|
+
className={cls(
|
|
77
|
+
"flex-shrink-0 z-10 flex items-center justify-center rounded-md px-0.5 py-1.5 transition-all h-10 w-6",
|
|
78
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400",
|
|
79
|
+
"disabled:pointer-events-none disabled:opacity-0",
|
|
80
|
+
"text-surface-600 dark:text-surface-400 hover:bg-surface-200 dark:hover:bg-surface-800",
|
|
81
|
+
"mr-1 bg-surface-50 dark:bg-surface-900 border", defaultBorderMixin
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
<ChevronLeftIcon size="small" />
|
|
85
|
+
</button>
|
|
86
|
+
)}
|
|
87
|
+
<div
|
|
88
|
+
ref={scrollContainerRef}
|
|
89
|
+
className="flex-1 overflow-x-auto no-scrollbar min-w-0"
|
|
90
|
+
onScroll={checkScroll}
|
|
91
|
+
style={{ scrollbarWidth: "none", msOverflowStyle: "none" }}
|
|
92
|
+
>
|
|
93
|
+
<TabsPrimitive.List className={cls(
|
|
94
|
+
"border",
|
|
95
|
+
defaultBorderMixin,
|
|
96
|
+
"gap-2",
|
|
97
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-surface-50 p-1 text-surface-600 dark:bg-surface-900 dark:text-surface-400",
|
|
98
|
+
innerClassName)
|
|
99
|
+
}>
|
|
100
|
+
{children}
|
|
101
|
+
</TabsPrimitive.List>
|
|
102
|
+
</div>
|
|
103
|
+
{isScrollable && (
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
disabled={!showRightScroll}
|
|
107
|
+
onClick={() => scroll("right")}
|
|
108
|
+
className={cls(
|
|
109
|
+
"flex-shrink-0 z-10 flex items-center justify-center rounded-md px-0.5 py-1.5 transition-all h-10 w-6",
|
|
110
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400",
|
|
111
|
+
"disabled:pointer-events-none disabled:opacity-0",
|
|
112
|
+
"text-surface-600 dark:text-surface-400 hover:bg-surface-200 dark:hover:bg-surface-800",
|
|
113
|
+
"ml-1 bg-surface-50 dark:bg-surface-900 border", defaultBorderMixin
|
|
114
|
+
)}
|
|
115
|
+
>
|
|
116
|
+
<ChevronRightIcon size="small" />
|
|
117
|
+
</button>
|
|
118
|
+
)}
|
|
31
119
|
</TabsPrimitive.Root>
|
|
32
120
|
}
|
|
33
121
|
|
|
@@ -40,23 +128,23 @@ export type TabProps = {
|
|
|
40
128
|
};
|
|
41
129
|
|
|
42
130
|
export function Tab({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
131
|
+
value,
|
|
132
|
+
className,
|
|
133
|
+
innerClassName,
|
|
134
|
+
children,
|
|
135
|
+
disabled
|
|
136
|
+
}: TabProps) {
|
|
49
137
|
return <TabsPrimitive.Trigger value={value}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
138
|
+
disabled={disabled}
|
|
139
|
+
className={cls(
|
|
140
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-white transition-all",
|
|
141
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400 focus-visible:ring-offset-2",
|
|
142
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
143
|
+
"data-[state=active]:bg-white data-[state=active]:text-surface-900 dark:data-[state=active]:bg-surface-950 dark:data-[state=active]:text-surface-50",
|
|
144
|
+
// "data-[state=active]:border",
|
|
145
|
+
// defaultBorderMixin,
|
|
146
|
+
className,
|
|
147
|
+
innerClassName)}>
|
|
60
148
|
{children}
|
|
61
149
|
</TabsPrimitive.Trigger>;
|
|
62
150
|
}
|