@blurrow/ui 0.2.0 → 0.2.1
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 +48 -36
- package/dist/Accordion.d.ts +17 -0
- package/dist/Accordion.js +138 -0
- package/dist/Accordion.js.map +1 -0
- package/dist/Alert.d.ts +13 -0
- package/dist/Alert.js +69 -0
- package/dist/Alert.js.map +1 -0
- package/dist/AuthorCard.d.ts +12 -0
- package/dist/AuthorCard.js +57 -0
- package/dist/AuthorCard.js.map +1 -0
- package/dist/Avatar.d.ts +21 -0
- package/dist/Avatar.js +96 -0
- package/dist/Avatar.js.map +1 -0
- package/dist/Badge.d.ts +12 -0
- package/dist/Badge.js +46 -0
- package/dist/Badge.js.map +1 -0
- package/dist/Banner.d.ts +15 -0
- package/dist/Banner.js +68 -0
- package/dist/Banner.js.map +1 -0
- package/dist/Breadcrumb.d.ts +13 -0
- package/dist/Breadcrumb.js +45 -0
- package/dist/Breadcrumb.js.map +1 -0
- package/dist/Button.d.ts +19 -0
- package/dist/Button.js +282 -0
- package/dist/Button.js.map +1 -0
- package/dist/ButtonGroup.d.ts +19 -0
- package/dist/ButtonGroup.js +62 -0
- package/dist/ButtonGroup.js.map +1 -0
- package/dist/Card.d.ts +40 -0
- package/dist/Card.js +175 -0
- package/dist/Card.js.map +1 -0
- package/dist/CardShell.d.ts +18 -0
- package/dist/CardShell.js +58 -0
- package/dist/CardShell.js.map +1 -0
- package/dist/Carousel.d.ts +22 -0
- package/dist/Carousel.js +247 -0
- package/dist/Carousel.js.map +1 -0
- package/dist/Checkbox.d.ts +15 -0
- package/dist/Checkbox.js +84 -0
- package/dist/Checkbox.js.map +1 -0
- package/dist/CloseButton.d.ts +12 -0
- package/dist/CloseButton.js +63 -0
- package/dist/CloseButton.js.map +1 -0
- package/dist/CodeBlock.d.ts +12 -0
- package/dist/CodeBlock.js +221 -0
- package/dist/CodeBlock.js.map +1 -0
- package/dist/ColorPicker.d.ts +24 -0
- package/dist/ColorPicker.js +366 -0
- package/dist/ColorPicker.js.map +1 -0
- package/dist/Combobox.d.ts +34 -0
- package/dist/Combobox.js +255 -0
- package/dist/Combobox.js.map +1 -0
- package/dist/CommandPalette.d.ts +25 -0
- package/dist/CommandPalette.js +304 -0
- package/dist/CommandPalette.js.map +1 -0
- package/dist/ContextMenu.d.ts +16 -0
- package/dist/ContextMenu.js +209 -0
- package/dist/ContextMenu.js.map +1 -0
- package/dist/DataList.d.ts +15 -0
- package/dist/DataList.js +42 -0
- package/dist/DataList.js.map +1 -0
- package/dist/DatePicker.d.ts +65 -0
- package/dist/DatePicker.js +391 -0
- package/dist/DatePicker.js.map +1 -0
- package/dist/DateRangePicker.d.ts +34 -0
- package/dist/DateRangePicker.js +180 -0
- package/dist/DateRangePicker.js.map +1 -0
- package/dist/Drawer.d.ts +17 -0
- package/dist/Drawer.js +132 -0
- package/dist/Drawer.js.map +1 -0
- package/dist/DropdownMenu.d.ts +23 -0
- package/dist/DropdownMenu.js +185 -0
- package/dist/DropdownMenu.js.map +1 -0
- package/dist/EmptyState.d.ts +13 -0
- package/dist/EmptyState.js +55 -0
- package/dist/EmptyState.js.map +1 -0
- package/dist/FieldMessage.d.ts +26 -0
- package/dist/FieldMessage.js +95 -0
- package/dist/FieldMessage.js.map +1 -0
- package/dist/FileUpload.d.ts +32 -0
- package/dist/FileUpload.js +253 -0
- package/dist/FileUpload.js.map +1 -0
- package/dist/Floating.d.ts +32 -0
- package/dist/Floating.js +95 -0
- package/dist/Floating.js.map +1 -0
- package/dist/HoverCard.d.ts +18 -0
- package/dist/HoverCard.js +90 -0
- package/dist/HoverCard.js.map +1 -0
- package/dist/Input.d.ts +17 -0
- package/dist/Input.js +96 -0
- package/dist/Input.js.map +1 -0
- package/dist/Kbd.d.ts +9 -0
- package/dist/Kbd.js +24 -0
- package/dist/Kbd.js.map +1 -0
- package/dist/Modal.d.ts +15 -0
- package/dist/Modal.js +109 -0
- package/dist/Modal.js.map +1 -0
- package/dist/NumberInput.d.ts +23 -0
- package/dist/NumberInput.js +162 -0
- package/dist/NumberInput.js.map +1 -0
- package/dist/OtpInput.d.ts +26 -0
- package/dist/OtpInput.js +173 -0
- package/dist/OtpInput.js.map +1 -0
- package/dist/Pagination.d.ts +11 -0
- package/dist/Pagination.js +148 -0
- package/dist/Pagination.js.map +1 -0
- package/dist/Popover.d.ts +13 -0
- package/dist/Popover.js +98 -0
- package/dist/Popover.js.map +1 -0
- package/dist/Progress.d.ts +12 -0
- package/dist/Progress.js +61 -0
- package/dist/Progress.js.map +1 -0
- package/dist/QuoteCard.d.ts +13 -0
- package/dist/QuoteCard.js +92 -0
- package/dist/QuoteCard.js.map +1 -0
- package/dist/Radio.d.ts +19 -0
- package/dist/Radio.js +161 -0
- package/dist/Radio.js.map +1 -0
- package/dist/RadioCards.d.ts +27 -0
- package/dist/RadioCards.js +205 -0
- package/dist/RadioCards.js.map +1 -0
- package/dist/ReactionBar.d.ts +15 -0
- package/dist/ReactionBar.js +92 -0
- package/dist/ReactionBar.js.map +1 -0
- package/dist/ReadingProgress.d.ts +8 -0
- package/dist/ReadingProgress.js +51 -0
- package/dist/ReadingProgress.js.map +1 -0
- package/dist/ReadingTime.d.ts +9 -0
- package/dist/ReadingTime.js +39 -0
- package/dist/ReadingTime.js.map +1 -0
- package/dist/Select.d.ts +20 -0
- package/dist/Select.js +244 -0
- package/dist/Select.js.map +1 -0
- package/dist/Separator.d.ts +14 -0
- package/dist/Separator.js +33 -0
- package/dist/Separator.js.map +1 -0
- package/dist/ShareButtons.d.ts +10 -0
- package/dist/ShareButtons.js +107 -0
- package/dist/ShareButtons.js.map +1 -0
- package/dist/Sidebar.d.ts +30 -0
- package/dist/Sidebar.js +140 -0
- package/dist/Sidebar.js.map +1 -0
- package/dist/Skeleton.d.ts +15 -0
- package/dist/Skeleton.js +67 -0
- package/dist/Skeleton.js.map +1 -0
- package/dist/Slider.d.ts +27 -0
- package/dist/Slider.js +182 -0
- package/dist/Slider.js.map +1 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +24 -0
- package/dist/Spinner.js.map +1 -0
- package/dist/SpotlightCard.d.ts +17 -0
- package/dist/SpotlightCard.js +87 -0
- package/dist/SpotlightCard.js.map +1 -0
- package/dist/Stat.d.ts +19 -0
- package/dist/Stat.js +71 -0
- package/dist/Stat.js.map +1 -0
- package/dist/StatCard.d.ts +24 -0
- package/dist/StatCard.js +212 -0
- package/dist/StatCard.js.map +1 -0
- package/dist/Steps.d.ts +14 -0
- package/dist/Steps.js +94 -0
- package/dist/Steps.js.map +1 -0
- package/dist/Table.d.ts +21 -0
- package/dist/Table.js +93 -0
- package/dist/Table.js.map +1 -0
- package/dist/TableOfContents.d.ts +17 -0
- package/dist/TableOfContents.js +92 -0
- package/dist/TableOfContents.js.map +1 -0
- package/dist/Tabs.d.ts +17 -0
- package/dist/Tabs.js +123 -0
- package/dist/Tabs.js.map +1 -0
- package/dist/TagInput.d.ts +16 -0
- package/dist/TagInput.js +169 -0
- package/dist/TagInput.js.map +1 -0
- package/dist/Textarea.d.ts +13 -0
- package/dist/Textarea.js +74 -0
- package/dist/Textarea.js.map +1 -0
- package/dist/TimePicker.d.ts +28 -0
- package/dist/TimePicker.js +264 -0
- package/dist/TimePicker.js.map +1 -0
- package/dist/Timeline.d.ts +19 -0
- package/dist/Timeline.js +76 -0
- package/dist/Timeline.js.map +1 -0
- package/dist/Toast.d.ts +18 -0
- package/dist/Toast.js +318 -0
- package/dist/Toast.js.map +1 -0
- package/dist/Toggle.d.ts +13 -0
- package/dist/Toggle.js +84 -0
- package/dist/Toggle.js.map +1 -0
- package/dist/Tooltip.d.ts +13 -0
- package/dist/Tooltip.js +138 -0
- package/dist/Tooltip.js.map +1 -0
- package/dist/cli.js +172 -5
- package/dist/index.d.ts +68 -631
- package/dist/index.js +67 -5558
- package/dist/index.js.map +1 -1
- package/dist/registry/Accordion.tsx +6 -6
- package/dist/registry/Alert.tsx +13 -10
- package/dist/registry/AuthorCard.tsx +7 -7
- package/dist/registry/Avatar.tsx +5 -5
- package/dist/registry/Badge.tsx +12 -15
- package/dist/registry/Banner.tsx +15 -18
- package/dist/registry/Breadcrumb.tsx +3 -3
- package/dist/registry/Button.tsx +58 -69
- package/dist/registry/ButtonGroup.tsx +9 -9
- package/dist/registry/Card.tsx +15 -15
- package/dist/registry/CardShell.tsx +6 -6
- package/dist/registry/Carousel.tsx +19 -15
- package/dist/registry/Checkbox.tsx +14 -9
- package/dist/registry/CloseButton.tsx +2 -2
- package/dist/registry/CodeBlock.tsx +15 -15
- package/dist/registry/ColorPicker.tsx +404 -0
- package/dist/registry/Combobox.tsx +318 -0
- package/dist/registry/CommandPalette.tsx +31 -24
- package/dist/registry/ContextMenu.tsx +241 -0
- package/dist/registry/DataList.tsx +4 -4
- package/dist/registry/DatePicker.tsx +494 -0
- package/dist/registry/DateRangePicker.tsx +232 -0
- package/dist/registry/Drawer.tsx +17 -15
- package/dist/registry/DropdownMenu.tsx +39 -77
- package/dist/registry/EmptyState.tsx +8 -8
- package/dist/registry/FieldMessage.tsx +123 -0
- package/dist/registry/FileUpload.tsx +318 -0
- package/dist/registry/Floating.tsx +134 -0
- package/dist/registry/HoverCard.tsx +113 -0
- package/dist/registry/Input.tsx +54 -43
- package/dist/registry/Kbd.tsx +4 -5
- package/dist/registry/Modal.tsx +17 -15
- package/dist/registry/NumberInput.tsx +183 -0
- package/dist/registry/OtpInput.tsx +219 -0
- package/dist/registry/Pagination.tsx +15 -18
- package/dist/registry/Popover.tsx +40 -45
- package/dist/registry/Progress.tsx +4 -4
- package/dist/registry/QuoteCard.tsx +7 -7
- package/dist/registry/Radio.tsx +24 -21
- package/dist/registry/RadioCards.tsx +247 -0
- package/dist/registry/ReactionBar.tsx +5 -5
- package/dist/registry/ReadingProgress.tsx +2 -2
- package/dist/registry/ReadingTime.tsx +2 -2
- package/dist/registry/Select.tsx +28 -53
- package/dist/registry/Separator.tsx +52 -0
- package/dist/registry/ShareButtons.tsx +2 -2
- package/dist/registry/Sidebar.tsx +18 -18
- package/dist/registry/Skeleton.tsx +1 -1
- package/dist/registry/Slider.tsx +229 -0
- package/dist/registry/Spinner.tsx +32 -0
- package/dist/registry/SpotlightCard.tsx +6 -6
- package/dist/registry/Stat.tsx +4 -4
- package/dist/registry/StatCard.tsx +19 -19
- package/dist/registry/Steps.tsx +7 -7
- package/dist/registry/Table.tsx +12 -10
- package/dist/registry/TableOfContents.tsx +8 -8
- package/dist/registry/Tabs.tsx +6 -6
- package/dist/registry/TagInput.tsx +9 -9
- package/dist/registry/Textarea.tsx +5 -5
- package/dist/registry/TimePicker.tsx +315 -0
- package/dist/registry/Timeline.tsx +14 -14
- package/dist/registry/Toast.tsx +15 -12
- package/dist/registry/Toggle.tsx +7 -10
- package/dist/registry/Tooltip.tsx +21 -73
- package/dist/registry/styles.css +868 -62
- package/dist/registry/useFloatingPanel.ts +112 -0
- package/dist/styles.css +868 -62
- package/dist/useFloatingPanel.d.ts +24 -0
- package/dist/useFloatingPanel.js +89 -0
- package/dist/useFloatingPanel.js.map +1 -0
- package/dist/useModalBehavior.d.ts +15 -0
- package/dist/useModalBehavior.js +57 -0
- package/dist/useModalBehavior.js.map +1 -0
- package/package.json +1 -1
package/dist/Tooltip.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/Tooltip.tsx
|
|
4
|
+
import React, { useState, useRef, useCallback, useEffect, useId } from "react";
|
|
5
|
+
import { ENTER_FROM, LAYER, ORIGIN, Portal, useAnchoredPosition } from "./Floating.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
function Tooltip({
|
|
8
|
+
content,
|
|
9
|
+
position = "top",
|
|
10
|
+
children,
|
|
11
|
+
className = ""
|
|
12
|
+
}) {
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const [visible, setVisible] = useState(false);
|
|
15
|
+
const [mounted, setMounted] = useState(false);
|
|
16
|
+
const triggerRef = useRef(null);
|
|
17
|
+
const enterTimer = useRef(null);
|
|
18
|
+
const exitTimer = useRef(null);
|
|
19
|
+
const autoHideTimer = useRef(null);
|
|
20
|
+
const touchActiveRef = useRef(false);
|
|
21
|
+
const uid = useId();
|
|
22
|
+
const hide = useCallback(() => {
|
|
23
|
+
if (enterTimer.current) clearTimeout(enterTimer.current);
|
|
24
|
+
if (autoHideTimer.current) clearTimeout(autoHideTimer.current);
|
|
25
|
+
setVisible(false);
|
|
26
|
+
exitTimer.current = setTimeout(() => {
|
|
27
|
+
setMounted(false);
|
|
28
|
+
setOpen(false);
|
|
29
|
+
}, 150);
|
|
30
|
+
}, []);
|
|
31
|
+
const show = useCallback((fromTouch = false) => {
|
|
32
|
+
if (exitTimer.current) clearTimeout(exitTimer.current);
|
|
33
|
+
if (autoHideTimer.current) clearTimeout(autoHideTimer.current);
|
|
34
|
+
enterTimer.current = setTimeout(() => setOpen(true), fromTouch ? 0 : 80);
|
|
35
|
+
if (fromTouch) {
|
|
36
|
+
autoHideTimer.current = setTimeout(() => {
|
|
37
|
+
touchActiveRef.current = false;
|
|
38
|
+
hide();
|
|
39
|
+
}, 2400);
|
|
40
|
+
}
|
|
41
|
+
}, [hide]);
|
|
42
|
+
if (open && !mounted) setMounted(true);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (open) {
|
|
45
|
+
requestAnimationFrame(() => requestAnimationFrame(() => setVisible(true)));
|
|
46
|
+
}
|
|
47
|
+
}, [open]);
|
|
48
|
+
const { setFloating, side } = useAnchoredPosition(triggerRef, { open: mounted, side: position, align: "center", gap: 7 });
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!open) return;
|
|
51
|
+
const onDocPointer = (e) => {
|
|
52
|
+
if (!triggerRef.current?.contains(e.target)) {
|
|
53
|
+
touchActiveRef.current = false;
|
|
54
|
+
hide();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener("pointerdown", onDocPointer);
|
|
58
|
+
return () => document.removeEventListener("pointerdown", onDocPointer);
|
|
59
|
+
}, [open, hide]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!open) return;
|
|
62
|
+
const onKey = (e) => {
|
|
63
|
+
if (e.key === "Escape") hide();
|
|
64
|
+
};
|
|
65
|
+
document.addEventListener("keydown", onKey);
|
|
66
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
67
|
+
}, [open, hide]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
return () => {
|
|
70
|
+
if (enterTimer.current) clearTimeout(enterTimer.current);
|
|
71
|
+
if (exitTimer.current) clearTimeout(exitTimer.current);
|
|
72
|
+
if (autoHideTimer.current) clearTimeout(autoHideTimer.current);
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
75
|
+
const handlePointerEnter = (e) => {
|
|
76
|
+
if (e.pointerType === "touch") return;
|
|
77
|
+
show(false);
|
|
78
|
+
};
|
|
79
|
+
const handlePointerLeave = (e) => {
|
|
80
|
+
if (e.pointerType === "touch") return;
|
|
81
|
+
if (touchActiveRef.current) return;
|
|
82
|
+
hide();
|
|
83
|
+
};
|
|
84
|
+
const handlePointerDown = (e) => {
|
|
85
|
+
if (e.pointerType !== "touch") return;
|
|
86
|
+
touchActiveRef.current = true;
|
|
87
|
+
if (open) {
|
|
88
|
+
hide();
|
|
89
|
+
} else {
|
|
90
|
+
show(true);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
return /* @__PURE__ */ jsxs(
|
|
94
|
+
"div",
|
|
95
|
+
{
|
|
96
|
+
ref: triggerRef,
|
|
97
|
+
className: ["relative inline-flex", className].join(" "),
|
|
98
|
+
onPointerEnter: handlePointerEnter,
|
|
99
|
+
onPointerLeave: handlePointerLeave,
|
|
100
|
+
onPointerDown: handlePointerDown,
|
|
101
|
+
onFocus: () => show(false),
|
|
102
|
+
onBlur: () => {
|
|
103
|
+
if (!touchActiveRef.current) hide();
|
|
104
|
+
},
|
|
105
|
+
children: [
|
|
106
|
+
React.isValidElement(children) ? React.cloneElement(children, {
|
|
107
|
+
"aria-describedby": mounted ? `${uid}-tip` : void 0
|
|
108
|
+
}) : children,
|
|
109
|
+
mounted && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
110
|
+
"div",
|
|
111
|
+
{
|
|
112
|
+
ref: setFloating,
|
|
113
|
+
id: `${uid}-tip`,
|
|
114
|
+
role: "tooltip",
|
|
115
|
+
className: "px-2.5 py-1.5 rounded-[9px] text-ink-inverse text-[11.5px] font-medium whitespace-nowrap pointer-events-none transition-[opacity,transform] duration-150 ease-[var(--ease-spring)]",
|
|
116
|
+
style: {
|
|
117
|
+
position: "fixed",
|
|
118
|
+
top: 0,
|
|
119
|
+
left: 0,
|
|
120
|
+
zIndex: LAYER.tooltip,
|
|
121
|
+
transformOrigin: ORIGIN[side],
|
|
122
|
+
opacity: visible ? 1 : 0,
|
|
123
|
+
transform: visible ? "none" : `${ENTER_FROM[side]} scale(0.95)`,
|
|
124
|
+
background: "var(--face-inverse)",
|
|
125
|
+
boxShadow: "var(--tooltip-shadow)",
|
|
126
|
+
textShadow: "var(--emboss-inverse)"
|
|
127
|
+
},
|
|
128
|
+
children: content
|
|
129
|
+
}
|
|
130
|
+
) })
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
Tooltip
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=Tooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Tooltip.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useRef, useCallback, useEffect, useId } from \"react\";\nimport { ENTER_FROM, LAYER, ORIGIN, Portal, useAnchoredPosition } from \"./Floating\";\n\ntype TooltipPosition = \"top\" | \"bottom\" | \"left\" | \"right\";\n\ninterface TooltipProps {\n content: string;\n position?: TooltipPosition;\n children: React.ReactNode;\n className?: string;\n}\n\nexport function Tooltip({\n content,\n position = \"top\",\n children,\n className = \"\",\n}: TooltipProps) {\n const [open, setOpen] = useState(false);\n const [visible, setVisible] = useState(false);\n const [mounted, setMounted] = useState(false);\n const triggerRef = useRef<HTMLDivElement>(null);\n const enterTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const exitTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const autoHideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const touchActiveRef = useRef(false);\n const uid = useId();\n\n const hide = useCallback(() => {\n if (enterTimer.current) clearTimeout(enterTimer.current);\n if (autoHideTimer.current) clearTimeout(autoHideTimer.current);\n setVisible(false);\n exitTimer.current = setTimeout(() => {\n setMounted(false);\n setOpen(false);\n }, 150);\n }, []);\n\n const show = useCallback((fromTouch = false) => {\n if (exitTimer.current) clearTimeout(exitTimer.current);\n if (autoHideTimer.current) clearTimeout(autoHideTimer.current);\n enterTimer.current = setTimeout(() => setOpen(true), fromTouch ? 0 : 80);\n if (fromTouch) {\n autoHideTimer.current = setTimeout(() => {\n touchActiveRef.current = false;\n hide();\n }, 2400);\n }\n }, [hide]);\n\n // Mounted during render; the effect only waits for it to paint\n if (open && !mounted) setMounted(true);\n\n useEffect(() => {\n if (open) {\n requestAnimationFrame(() => requestAnimationFrame(() => setVisible(true)));\n }\n }, [open]);\n\n /* Portalled to the body and placed before its first paint, flipping\n to the other side when there's no room */\n const { setFloating, side } = useAnchoredPosition(triggerRef, { open: mounted, side: position, align: \"center\", gap: 7 });\n\n useEffect(() => {\n if (!open) return;\n const onDocPointer = (e: PointerEvent) => {\n if (!triggerRef.current?.contains(e.target as Node)) {\n touchActiveRef.current = false;\n hide();\n }\n };\n document.addEventListener(\"pointerdown\", onDocPointer);\n return () => document.removeEventListener(\"pointerdown\", onDocPointer);\n }, [open, hide]);\n\n // Escape dismisses without moving focus (WCAG 1.4.13)\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") hide();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [open, hide]);\n\n useEffect(() => {\n return () => {\n if (enterTimer.current) clearTimeout(enterTimer.current);\n if (exitTimer.current) clearTimeout(exitTimer.current);\n if (autoHideTimer.current) clearTimeout(autoHideTimer.current);\n };\n }, []);\n\n const handlePointerEnter = (e: React.PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n show(false);\n };\n\n const handlePointerLeave = (e: React.PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n if (touchActiveRef.current) return;\n hide();\n };\n\n const handlePointerDown = (e: React.PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n touchActiveRef.current = true;\n if (open) {\n hide();\n } else {\n show(true);\n }\n };\n\n return (\n <div\n ref={triggerRef}\n className={[\"relative inline-flex\", className].join(\" \")}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n onPointerDown={handlePointerDown}\n onFocus={() => show(false)}\n onBlur={() => {\n if (!touchActiveRef.current) hide();\n }}\n >\n {React.isValidElement(children)\n ? React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {\n \"aria-describedby\": mounted ? `${uid}-tip` : undefined,\n })\n : children}\n {mounted && (\n <Portal>\n <div\n ref={setFloating}\n id={`${uid}-tip`}\n role=\"tooltip\"\n className=\"px-2.5 py-1.5 rounded-[9px] text-ink-inverse text-[11.5px] font-medium whitespace-nowrap pointer-events-none transition-[opacity,transform] duration-150 ease-[var(--ease-spring)]\"\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n zIndex: LAYER.tooltip,\n transformOrigin: ORIGIN[side],\n opacity: visible ? 1 : 0,\n transform: visible ? \"none\" : `${ENTER_FROM[side]} scale(0.95)`,\n background: \"var(--face-inverse)\",\n boxShadow: \"var(--tooltip-shadow)\",\n textShadow: \"var(--emboss-inverse)\",\n }}\n >\n {content}\n </div>\n </Portal>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -211,10 +211,111 @@ var registry = [
|
|
|
211
211
|
ex("input-demo", "default"),
|
|
212
212
|
ex("input-with-label", "with label and hint"),
|
|
213
213
|
ex("input-with-icon", "with icon"),
|
|
214
|
+
ex("input-layered", "layered, next to a button"),
|
|
214
215
|
ex("input-error", "error"),
|
|
215
216
|
ex("input-disabled", "disabled")
|
|
216
217
|
]
|
|
217
218
|
},
|
|
219
|
+
{
|
|
220
|
+
slug: "number-input",
|
|
221
|
+
name: "number input",
|
|
222
|
+
category: "forms",
|
|
223
|
+
description: "a field for numbers, with two little keys that step it up and down. hold one to keep going, or use the arrow keys.",
|
|
224
|
+
file: "NumberInput",
|
|
225
|
+
usage: `<NumberInput label="seats" value={seats} onChange={setSeats} min={1} max={20} />`,
|
|
226
|
+
examples: [
|
|
227
|
+
ex("number-input-demo", "default"),
|
|
228
|
+
ex("number-input-units", "units and decimals", "prefix and suffix hold a unit; step sets the size of each nudge and how many decimals show."),
|
|
229
|
+
ex("number-input-error", "error")
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
slug: "date-picker",
|
|
234
|
+
name: "date picker",
|
|
235
|
+
category: "forms",
|
|
236
|
+
description: "a field that opens a calendar. arrows move by day and week, page up and down flip the month.",
|
|
237
|
+
file: "DatePicker",
|
|
238
|
+
usage: `<DatePicker label="publish on" value={date} onChange={setDate} />`,
|
|
239
|
+
examples: [
|
|
240
|
+
ex("date-picker-demo", "default"),
|
|
241
|
+
ex("date-picker-limits", "limits", "min, max and isdatedisabled keep people to the days that work."),
|
|
242
|
+
ex("calendar-demo", "calendar on its own", "the grid is exported as Calendar, for when the month should always be showing.")
|
|
243
|
+
]
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
slug: "date-range-picker",
|
|
247
|
+
name: "date range picker",
|
|
248
|
+
category: "forms",
|
|
249
|
+
description: "two months side by side. click where it starts and where it ends, and the days between sink into a well.",
|
|
250
|
+
file: "DateRangePicker",
|
|
251
|
+
usage: `<DateRangePicker label="report period" value={range} onChange={setRange} />`,
|
|
252
|
+
examples: [
|
|
253
|
+
ex("date-range-picker-demo", "with presets"),
|
|
254
|
+
ex("date-range-picker-single", "one month", "singlemonth keeps the panel narrow; min stops you booking the past.")
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
slug: "time-picker",
|
|
259
|
+
name: "time picker",
|
|
260
|
+
category: "forms",
|
|
261
|
+
description: "columns of hours and minutes, with am and pm if you want them. the value is always a plain \u201C14:30\u201D.",
|
|
262
|
+
file: "TimePicker",
|
|
263
|
+
usage: `<TimePicker label="reminder" value={time} onChange={setTime} />`,
|
|
264
|
+
examples: [
|
|
265
|
+
ex("time-picker-demo", "default"),
|
|
266
|
+
ex("time-picker-12h", "12-hour, with limits")
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
slug: "color-picker",
|
|
271
|
+
name: "color picker",
|
|
272
|
+
category: "forms",
|
|
273
|
+
description: "a field holding a chip of the colour. open it for a saturation area, a hue strip, a hex field and swatches.",
|
|
274
|
+
file: "ColorPicker",
|
|
275
|
+
usage: `<ColorPicker label="accent colour" value={color} onChange={setColor} />`,
|
|
276
|
+
examples: [
|
|
277
|
+
ex("color-picker-demo", "default"),
|
|
278
|
+
ex("color-picker-inline", "inline", "inline puts the panel straight on the page, with no field in front."),
|
|
279
|
+
ex("color-picker-swatches", "custom swatches", "pass your own palette as swatches, or [] to hide the row.")
|
|
280
|
+
]
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
slug: "slider",
|
|
284
|
+
name: "slider",
|
|
285
|
+
category: "forms",
|
|
286
|
+
description: "a raised knob riding in a groove. give it two numbers and it becomes a range with a knob at each end.",
|
|
287
|
+
file: "Slider",
|
|
288
|
+
usage: `<Slider label="volume" value={volume} onChange={setVolume} />`,
|
|
289
|
+
examples: [
|
|
290
|
+
ex("slider-demo", "default"),
|
|
291
|
+
ex("slider-range", "range", "pass a pair of numbers and you get two knobs that can't cross."),
|
|
292
|
+
ex("slider-marks", "marks and disabled")
|
|
293
|
+
]
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
slug: "combobox",
|
|
297
|
+
name: "combobox",
|
|
298
|
+
category: "forms",
|
|
299
|
+
description: "a select you can type into. the list narrows as you go, and the bit that matched turns bold.",
|
|
300
|
+
file: "Combobox",
|
|
301
|
+
usage: `<Combobox label="language" options={options} value={value} onChange={setValue} />`,
|
|
302
|
+
examples: [
|
|
303
|
+
ex("combobox-demo", "default"),
|
|
304
|
+
ex("combobox-descriptions", "descriptions and keywords", "keywords match without being shown, so \u201Cgmt\u201D finds london.")
|
|
305
|
+
]
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
slug: "otp-input",
|
|
309
|
+
name: "otp input",
|
|
310
|
+
category: "forms",
|
|
311
|
+
description: "a row of little wells for a verification code. typing moves you along, and pasting the whole code fills them all.",
|
|
312
|
+
file: "OtpInput",
|
|
313
|
+
usage: `<OtpInput length={6} onComplete={verify} />`,
|
|
314
|
+
examples: [
|
|
315
|
+
ex("otp-input-demo", "default"),
|
|
316
|
+
ex("otp-input-alphanumeric", "letters and masking")
|
|
317
|
+
]
|
|
318
|
+
},
|
|
218
319
|
{
|
|
219
320
|
slug: "textarea",
|
|
220
321
|
name: "textarea",
|
|
@@ -292,6 +393,30 @@ var registry = [
|
|
|
292
393
|
ex("tag-input-max", "limited", "maxtags caps how many tags can be added.")
|
|
293
394
|
]
|
|
294
395
|
},
|
|
396
|
+
{
|
|
397
|
+
slug: "radio-cards",
|
|
398
|
+
name: "radio cards",
|
|
399
|
+
category: "forms",
|
|
400
|
+
description: "bigger choices, like a plan. the options sit in one well and a raised thumb springs to the one you pick.",
|
|
401
|
+
file: "RadioCards",
|
|
402
|
+
usage: `<RadioCards label="plan" options={plans} value={plan} onChange={setPlan} />`,
|
|
403
|
+
examples: [
|
|
404
|
+
ex("radio-cards-demo", "default"),
|
|
405
|
+
ex("radio-cards-columns", "columns and icons")
|
|
406
|
+
]
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
slug: "file-upload",
|
|
410
|
+
name: "file upload",
|
|
411
|
+
category: "forms",
|
|
412
|
+
description: "a well you drop files into. each one lands in a list with its size, a progress bar while it uploads and a key to remove it.",
|
|
413
|
+
file: "FileUpload",
|
|
414
|
+
usage: `<FileUpload label="attachments" upload={(file, onProgress) => send(file, onProgress)} />`,
|
|
415
|
+
examples: [
|
|
416
|
+
ex("file-upload-demo", "with uploading", "pass upload and the bars fill as you report progress; name a file \u201Cfail\u201D to see an error."),
|
|
417
|
+
ex("file-upload-single", "one image")
|
|
418
|
+
]
|
|
419
|
+
},
|
|
295
420
|
/* ── Navigation ──────────────────────────────────────────────── */
|
|
296
421
|
{
|
|
297
422
|
slug: "tabs",
|
|
@@ -428,6 +553,24 @@ var registry = [
|
|
|
428
553
|
ex("dropdown-menu-with-icons", "with icons")
|
|
429
554
|
]
|
|
430
555
|
},
|
|
556
|
+
{
|
|
557
|
+
slug: "context-menu",
|
|
558
|
+
name: "context menu",
|
|
559
|
+
category: "overlays",
|
|
560
|
+
description: "the dropdown menu's panel, opened where you right-click. long-press works on touch, shift f10 on a keyboard.",
|
|
561
|
+
file: "ContextMenu",
|
|
562
|
+
usage: `<ContextMenu items={items} onSelect={handle}>\u2026</ContextMenu>`,
|
|
563
|
+
examples: [ex("context-menu-demo", "default")]
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
slug: "hover-card",
|
|
567
|
+
name: "hover card",
|
|
568
|
+
category: "overlays",
|
|
569
|
+
description: "a preview that floats up when you rest on a link or a name, and stays while you move into it.",
|
|
570
|
+
file: "HoverCard",
|
|
571
|
+
usage: `<HoverCard trigger={<a href="/ada">ada</a>}>\u2026</HoverCard>`,
|
|
572
|
+
examples: [ex("hover-card-demo", "default")]
|
|
573
|
+
},
|
|
431
574
|
{
|
|
432
575
|
slug: "toast",
|
|
433
576
|
name: "toast",
|
|
@@ -496,6 +639,18 @@ toast({ title: "post saved", variant: "success" });`,
|
|
|
496
639
|
ex("skeleton-card", "cards")
|
|
497
640
|
]
|
|
498
641
|
},
|
|
642
|
+
{
|
|
643
|
+
slug: "spinner",
|
|
644
|
+
name: "spinner",
|
|
645
|
+
category: "feedback",
|
|
646
|
+
description: "a small arc going round, for waits too short or too vague for a progress bar. it takes the text colour around it.",
|
|
647
|
+
file: "Spinner",
|
|
648
|
+
usage: `<Spinner label="loading" />`,
|
|
649
|
+
examples: [
|
|
650
|
+
ex("spinner-demo", "default"),
|
|
651
|
+
ex("spinner-in-button", "in a button")
|
|
652
|
+
]
|
|
653
|
+
},
|
|
499
654
|
{
|
|
500
655
|
slug: "empty-state",
|
|
501
656
|
name: "empty state",
|
|
@@ -549,6 +704,18 @@ toast({ title: "post saved", variant: "success" });`,
|
|
|
549
704
|
usage: `<Table columns={columns} rows={rows} title="recent posts" />`,
|
|
550
705
|
examples: [ex("table-demo", "default")]
|
|
551
706
|
},
|
|
707
|
+
{
|
|
708
|
+
slug: "separator",
|
|
709
|
+
name: "separator",
|
|
710
|
+
category: "data display",
|
|
711
|
+
description: "a groove scored into the surface, with room for a word in the middle.",
|
|
712
|
+
file: "Separator",
|
|
713
|
+
usage: `<Separator label="or" />`,
|
|
714
|
+
examples: [
|
|
715
|
+
ex("separator-demo", "with a label"),
|
|
716
|
+
ex("separator-vertical", "vertical")
|
|
717
|
+
]
|
|
718
|
+
},
|
|
552
719
|
{
|
|
553
720
|
slug: "data-list",
|
|
554
721
|
name: "data list",
|
|
@@ -996,7 +1163,7 @@ function parse(argv) {
|
|
|
996
1163
|
return { command: rest[0], args: rest.slice(1), flags };
|
|
997
1164
|
}
|
|
998
1165
|
var HELP = `
|
|
999
|
-
${c.bold("blurrow")} ${c.gray(`v${"0.2.
|
|
1166
|
+
${c.bold("blurrow")} ${c.gray(`v${"0.2.1"}`)} ${c.gray("tactile react components")}
|
|
1000
1167
|
|
|
1001
1168
|
${c.bold("usage")}
|
|
1002
1169
|
npx @blurrow/ui ${c.cyan("<command>")} [options] ${c.gray("or, once installed: npx blurrow <command>")}
|
|
@@ -1025,7 +1192,7 @@ ${c.bold("examples")}
|
|
|
1025
1192
|
`;
|
|
1026
1193
|
function header(title) {
|
|
1027
1194
|
log.line(`
|
|
1028
|
-
${c.bold("blurrow")} ${c.gray("\xB7")} ${title}${c.gray(` v${"0.2.
|
|
1195
|
+
${c.bold("blurrow")} ${c.gray("\xB7")} ${title}${c.gray(` v${"0.2.1"}`)}`);
|
|
1029
1196
|
}
|
|
1030
1197
|
function requireProject(flags) {
|
|
1031
1198
|
const p = useProject(readProject(flags.cwd));
|
|
@@ -1331,14 +1498,14 @@ function diff(names, flags) {
|
|
|
1331
1498
|
const shipped = sourceFile(e.file);
|
|
1332
1499
|
const local = fs3.readFileSync(path3.join(p.root, dir, shipped.name), "utf8");
|
|
1333
1500
|
if (local === shipped.code) {
|
|
1334
|
-
log.ok(`${e.slug} ${c.gray("matches v0.2.
|
|
1501
|
+
log.ok(`${e.slug} ${c.gray("matches v0.2.1")}`);
|
|
1335
1502
|
continue;
|
|
1336
1503
|
}
|
|
1337
1504
|
changed++;
|
|
1338
1505
|
const lines = diffLines(shipped.code, local);
|
|
1339
1506
|
const plus = lines.filter((l) => l.kind === "+").length;
|
|
1340
1507
|
const minus = lines.filter((l) => l.kind === "-").length;
|
|
1341
|
-
log.warn(`${c.bold(e.slug)} ${c.green(`+${plus}`)} ${c.red(`-${minus}`)} ${c.gray(`vs v${"0.2.
|
|
1508
|
+
log.warn(`${c.bold(e.slug)} ${c.green(`+${plus}`)} ${c.red(`-${minus}`)} ${c.gray(`vs v${"0.2.1"}`)}`);
|
|
1342
1509
|
if (names.length) printHunks(hunks(lines));
|
|
1343
1510
|
}
|
|
1344
1511
|
log.line();
|
|
@@ -1445,7 +1612,7 @@ function doctor(flags) {
|
|
|
1445
1612
|
}
|
|
1446
1613
|
async function main() {
|
|
1447
1614
|
const { command, args, flags } = parse(process.argv.slice(2));
|
|
1448
|
-
if (command === "-v" || command === "--version") return log.line("0.2.
|
|
1615
|
+
if (command === "-v" || command === "--version") return log.line("0.2.1");
|
|
1449
1616
|
if (!command || command === "help" || command === "-h" || command === "--help") return log.line(HELP);
|
|
1450
1617
|
switch (command) {
|
|
1451
1618
|
case "init":
|