@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.
Files changed (271) hide show
  1. package/README.md +48 -36
  2. package/dist/Accordion.d.ts +17 -0
  3. package/dist/Accordion.js +138 -0
  4. package/dist/Accordion.js.map +1 -0
  5. package/dist/Alert.d.ts +13 -0
  6. package/dist/Alert.js +69 -0
  7. package/dist/Alert.js.map +1 -0
  8. package/dist/AuthorCard.d.ts +12 -0
  9. package/dist/AuthorCard.js +57 -0
  10. package/dist/AuthorCard.js.map +1 -0
  11. package/dist/Avatar.d.ts +21 -0
  12. package/dist/Avatar.js +96 -0
  13. package/dist/Avatar.js.map +1 -0
  14. package/dist/Badge.d.ts +12 -0
  15. package/dist/Badge.js +46 -0
  16. package/dist/Badge.js.map +1 -0
  17. package/dist/Banner.d.ts +15 -0
  18. package/dist/Banner.js +68 -0
  19. package/dist/Banner.js.map +1 -0
  20. package/dist/Breadcrumb.d.ts +13 -0
  21. package/dist/Breadcrumb.js +45 -0
  22. package/dist/Breadcrumb.js.map +1 -0
  23. package/dist/Button.d.ts +19 -0
  24. package/dist/Button.js +282 -0
  25. package/dist/Button.js.map +1 -0
  26. package/dist/ButtonGroup.d.ts +19 -0
  27. package/dist/ButtonGroup.js +62 -0
  28. package/dist/ButtonGroup.js.map +1 -0
  29. package/dist/Card.d.ts +40 -0
  30. package/dist/Card.js +175 -0
  31. package/dist/Card.js.map +1 -0
  32. package/dist/CardShell.d.ts +18 -0
  33. package/dist/CardShell.js +58 -0
  34. package/dist/CardShell.js.map +1 -0
  35. package/dist/Carousel.d.ts +22 -0
  36. package/dist/Carousel.js +247 -0
  37. package/dist/Carousel.js.map +1 -0
  38. package/dist/Checkbox.d.ts +15 -0
  39. package/dist/Checkbox.js +84 -0
  40. package/dist/Checkbox.js.map +1 -0
  41. package/dist/CloseButton.d.ts +12 -0
  42. package/dist/CloseButton.js +63 -0
  43. package/dist/CloseButton.js.map +1 -0
  44. package/dist/CodeBlock.d.ts +12 -0
  45. package/dist/CodeBlock.js +221 -0
  46. package/dist/CodeBlock.js.map +1 -0
  47. package/dist/ColorPicker.d.ts +24 -0
  48. package/dist/ColorPicker.js +366 -0
  49. package/dist/ColorPicker.js.map +1 -0
  50. package/dist/Combobox.d.ts +34 -0
  51. package/dist/Combobox.js +255 -0
  52. package/dist/Combobox.js.map +1 -0
  53. package/dist/CommandPalette.d.ts +25 -0
  54. package/dist/CommandPalette.js +304 -0
  55. package/dist/CommandPalette.js.map +1 -0
  56. package/dist/ContextMenu.d.ts +16 -0
  57. package/dist/ContextMenu.js +209 -0
  58. package/dist/ContextMenu.js.map +1 -0
  59. package/dist/DataList.d.ts +15 -0
  60. package/dist/DataList.js +42 -0
  61. package/dist/DataList.js.map +1 -0
  62. package/dist/DatePicker.d.ts +65 -0
  63. package/dist/DatePicker.js +391 -0
  64. package/dist/DatePicker.js.map +1 -0
  65. package/dist/DateRangePicker.d.ts +34 -0
  66. package/dist/DateRangePicker.js +180 -0
  67. package/dist/DateRangePicker.js.map +1 -0
  68. package/dist/Drawer.d.ts +17 -0
  69. package/dist/Drawer.js +132 -0
  70. package/dist/Drawer.js.map +1 -0
  71. package/dist/DropdownMenu.d.ts +23 -0
  72. package/dist/DropdownMenu.js +185 -0
  73. package/dist/DropdownMenu.js.map +1 -0
  74. package/dist/EmptyState.d.ts +13 -0
  75. package/dist/EmptyState.js +55 -0
  76. package/dist/EmptyState.js.map +1 -0
  77. package/dist/FieldMessage.d.ts +26 -0
  78. package/dist/FieldMessage.js +95 -0
  79. package/dist/FieldMessage.js.map +1 -0
  80. package/dist/FileUpload.d.ts +32 -0
  81. package/dist/FileUpload.js +253 -0
  82. package/dist/FileUpload.js.map +1 -0
  83. package/dist/Floating.d.ts +32 -0
  84. package/dist/Floating.js +95 -0
  85. package/dist/Floating.js.map +1 -0
  86. package/dist/HoverCard.d.ts +18 -0
  87. package/dist/HoverCard.js +90 -0
  88. package/dist/HoverCard.js.map +1 -0
  89. package/dist/Input.d.ts +17 -0
  90. package/dist/Input.js +96 -0
  91. package/dist/Input.js.map +1 -0
  92. package/dist/Kbd.d.ts +9 -0
  93. package/dist/Kbd.js +24 -0
  94. package/dist/Kbd.js.map +1 -0
  95. package/dist/Modal.d.ts +15 -0
  96. package/dist/Modal.js +109 -0
  97. package/dist/Modal.js.map +1 -0
  98. package/dist/NumberInput.d.ts +23 -0
  99. package/dist/NumberInput.js +162 -0
  100. package/dist/NumberInput.js.map +1 -0
  101. package/dist/OtpInput.d.ts +26 -0
  102. package/dist/OtpInput.js +173 -0
  103. package/dist/OtpInput.js.map +1 -0
  104. package/dist/Pagination.d.ts +11 -0
  105. package/dist/Pagination.js +148 -0
  106. package/dist/Pagination.js.map +1 -0
  107. package/dist/Popover.d.ts +13 -0
  108. package/dist/Popover.js +98 -0
  109. package/dist/Popover.js.map +1 -0
  110. package/dist/Progress.d.ts +12 -0
  111. package/dist/Progress.js +61 -0
  112. package/dist/Progress.js.map +1 -0
  113. package/dist/QuoteCard.d.ts +13 -0
  114. package/dist/QuoteCard.js +92 -0
  115. package/dist/QuoteCard.js.map +1 -0
  116. package/dist/Radio.d.ts +19 -0
  117. package/dist/Radio.js +161 -0
  118. package/dist/Radio.js.map +1 -0
  119. package/dist/RadioCards.d.ts +27 -0
  120. package/dist/RadioCards.js +205 -0
  121. package/dist/RadioCards.js.map +1 -0
  122. package/dist/ReactionBar.d.ts +15 -0
  123. package/dist/ReactionBar.js +92 -0
  124. package/dist/ReactionBar.js.map +1 -0
  125. package/dist/ReadingProgress.d.ts +8 -0
  126. package/dist/ReadingProgress.js +51 -0
  127. package/dist/ReadingProgress.js.map +1 -0
  128. package/dist/ReadingTime.d.ts +9 -0
  129. package/dist/ReadingTime.js +39 -0
  130. package/dist/ReadingTime.js.map +1 -0
  131. package/dist/Select.d.ts +20 -0
  132. package/dist/Select.js +244 -0
  133. package/dist/Select.js.map +1 -0
  134. package/dist/Separator.d.ts +14 -0
  135. package/dist/Separator.js +33 -0
  136. package/dist/Separator.js.map +1 -0
  137. package/dist/ShareButtons.d.ts +10 -0
  138. package/dist/ShareButtons.js +107 -0
  139. package/dist/ShareButtons.js.map +1 -0
  140. package/dist/Sidebar.d.ts +30 -0
  141. package/dist/Sidebar.js +140 -0
  142. package/dist/Sidebar.js.map +1 -0
  143. package/dist/Skeleton.d.ts +15 -0
  144. package/dist/Skeleton.js +67 -0
  145. package/dist/Skeleton.js.map +1 -0
  146. package/dist/Slider.d.ts +27 -0
  147. package/dist/Slider.js +182 -0
  148. package/dist/Slider.js.map +1 -0
  149. package/dist/Spinner.d.ts +12 -0
  150. package/dist/Spinner.js +24 -0
  151. package/dist/Spinner.js.map +1 -0
  152. package/dist/SpotlightCard.d.ts +17 -0
  153. package/dist/SpotlightCard.js +87 -0
  154. package/dist/SpotlightCard.js.map +1 -0
  155. package/dist/Stat.d.ts +19 -0
  156. package/dist/Stat.js +71 -0
  157. package/dist/Stat.js.map +1 -0
  158. package/dist/StatCard.d.ts +24 -0
  159. package/dist/StatCard.js +212 -0
  160. package/dist/StatCard.js.map +1 -0
  161. package/dist/Steps.d.ts +14 -0
  162. package/dist/Steps.js +94 -0
  163. package/dist/Steps.js.map +1 -0
  164. package/dist/Table.d.ts +21 -0
  165. package/dist/Table.js +93 -0
  166. package/dist/Table.js.map +1 -0
  167. package/dist/TableOfContents.d.ts +17 -0
  168. package/dist/TableOfContents.js +92 -0
  169. package/dist/TableOfContents.js.map +1 -0
  170. package/dist/Tabs.d.ts +17 -0
  171. package/dist/Tabs.js +123 -0
  172. package/dist/Tabs.js.map +1 -0
  173. package/dist/TagInput.d.ts +16 -0
  174. package/dist/TagInput.js +169 -0
  175. package/dist/TagInput.js.map +1 -0
  176. package/dist/Textarea.d.ts +13 -0
  177. package/dist/Textarea.js +74 -0
  178. package/dist/Textarea.js.map +1 -0
  179. package/dist/TimePicker.d.ts +28 -0
  180. package/dist/TimePicker.js +264 -0
  181. package/dist/TimePicker.js.map +1 -0
  182. package/dist/Timeline.d.ts +19 -0
  183. package/dist/Timeline.js +76 -0
  184. package/dist/Timeline.js.map +1 -0
  185. package/dist/Toast.d.ts +18 -0
  186. package/dist/Toast.js +318 -0
  187. package/dist/Toast.js.map +1 -0
  188. package/dist/Toggle.d.ts +13 -0
  189. package/dist/Toggle.js +84 -0
  190. package/dist/Toggle.js.map +1 -0
  191. package/dist/Tooltip.d.ts +13 -0
  192. package/dist/Tooltip.js +138 -0
  193. package/dist/Tooltip.js.map +1 -0
  194. package/dist/cli.js +172 -5
  195. package/dist/index.d.ts +68 -631
  196. package/dist/index.js +67 -5558
  197. package/dist/index.js.map +1 -1
  198. package/dist/registry/Accordion.tsx +6 -6
  199. package/dist/registry/Alert.tsx +13 -10
  200. package/dist/registry/AuthorCard.tsx +7 -7
  201. package/dist/registry/Avatar.tsx +5 -5
  202. package/dist/registry/Badge.tsx +12 -15
  203. package/dist/registry/Banner.tsx +15 -18
  204. package/dist/registry/Breadcrumb.tsx +3 -3
  205. package/dist/registry/Button.tsx +58 -69
  206. package/dist/registry/ButtonGroup.tsx +9 -9
  207. package/dist/registry/Card.tsx +15 -15
  208. package/dist/registry/CardShell.tsx +6 -6
  209. package/dist/registry/Carousel.tsx +19 -15
  210. package/dist/registry/Checkbox.tsx +14 -9
  211. package/dist/registry/CloseButton.tsx +2 -2
  212. package/dist/registry/CodeBlock.tsx +15 -15
  213. package/dist/registry/ColorPicker.tsx +404 -0
  214. package/dist/registry/Combobox.tsx +318 -0
  215. package/dist/registry/CommandPalette.tsx +31 -24
  216. package/dist/registry/ContextMenu.tsx +241 -0
  217. package/dist/registry/DataList.tsx +4 -4
  218. package/dist/registry/DatePicker.tsx +494 -0
  219. package/dist/registry/DateRangePicker.tsx +232 -0
  220. package/dist/registry/Drawer.tsx +17 -15
  221. package/dist/registry/DropdownMenu.tsx +39 -77
  222. package/dist/registry/EmptyState.tsx +8 -8
  223. package/dist/registry/FieldMessage.tsx +123 -0
  224. package/dist/registry/FileUpload.tsx +318 -0
  225. package/dist/registry/Floating.tsx +134 -0
  226. package/dist/registry/HoverCard.tsx +113 -0
  227. package/dist/registry/Input.tsx +54 -43
  228. package/dist/registry/Kbd.tsx +4 -5
  229. package/dist/registry/Modal.tsx +17 -15
  230. package/dist/registry/NumberInput.tsx +183 -0
  231. package/dist/registry/OtpInput.tsx +219 -0
  232. package/dist/registry/Pagination.tsx +15 -18
  233. package/dist/registry/Popover.tsx +40 -45
  234. package/dist/registry/Progress.tsx +4 -4
  235. package/dist/registry/QuoteCard.tsx +7 -7
  236. package/dist/registry/Radio.tsx +24 -21
  237. package/dist/registry/RadioCards.tsx +247 -0
  238. package/dist/registry/ReactionBar.tsx +5 -5
  239. package/dist/registry/ReadingProgress.tsx +2 -2
  240. package/dist/registry/ReadingTime.tsx +2 -2
  241. package/dist/registry/Select.tsx +28 -53
  242. package/dist/registry/Separator.tsx +52 -0
  243. package/dist/registry/ShareButtons.tsx +2 -2
  244. package/dist/registry/Sidebar.tsx +18 -18
  245. package/dist/registry/Skeleton.tsx +1 -1
  246. package/dist/registry/Slider.tsx +229 -0
  247. package/dist/registry/Spinner.tsx +32 -0
  248. package/dist/registry/SpotlightCard.tsx +6 -6
  249. package/dist/registry/Stat.tsx +4 -4
  250. package/dist/registry/StatCard.tsx +19 -19
  251. package/dist/registry/Steps.tsx +7 -7
  252. package/dist/registry/Table.tsx +12 -10
  253. package/dist/registry/TableOfContents.tsx +8 -8
  254. package/dist/registry/Tabs.tsx +6 -6
  255. package/dist/registry/TagInput.tsx +9 -9
  256. package/dist/registry/Textarea.tsx +5 -5
  257. package/dist/registry/TimePicker.tsx +315 -0
  258. package/dist/registry/Timeline.tsx +14 -14
  259. package/dist/registry/Toast.tsx +15 -12
  260. package/dist/registry/Toggle.tsx +7 -10
  261. package/dist/registry/Tooltip.tsx +21 -73
  262. package/dist/registry/styles.css +868 -62
  263. package/dist/registry/useFloatingPanel.ts +112 -0
  264. package/dist/styles.css +868 -62
  265. package/dist/useFloatingPanel.d.ts +24 -0
  266. package/dist/useFloatingPanel.js +89 -0
  267. package/dist/useFloatingPanel.js.map +1 -0
  268. package/dist/useModalBehavior.d.ts +15 -0
  269. package/dist/useModalBehavior.js +57 -0
  270. package/dist/useModalBehavior.js.map +1 -0
  271. package/package.json +1 -1
@@ -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.0"}`)} ${c.gray("tactile react components")}
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.0"}`)}`);
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.0")}`);
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.0"}`)}`);
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.0");
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":