@blurrow/ui 0.1.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 +60 -20
- 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 +1644 -0
- 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 +140 -0
- package/dist/registry/Alert.tsx +95 -0
- package/dist/registry/AuthorCard.tsx +67 -0
- package/dist/registry/Avatar.tsx +116 -0
- package/dist/registry/Badge.tsx +55 -0
- package/dist/registry/Banner.tsx +86 -0
- package/dist/registry/Breadcrumb.tsx +60 -0
- package/dist/registry/Button.tsx +361 -0
- package/dist/registry/ButtonGroup.tsx +82 -0
- package/dist/registry/Card.tsx +241 -0
- package/dist/registry/CardShell.tsx +78 -0
- package/dist/registry/Carousel.tsx +296 -0
- package/dist/registry/Checkbox.tsx +104 -0
- package/dist/registry/CloseButton.tsx +68 -0
- package/dist/registry/CodeBlock.tsx +202 -0
- package/dist/registry/ColorPicker.tsx +404 -0
- package/dist/registry/Combobox.tsx +318 -0
- package/dist/registry/CommandPalette.tsx +356 -0
- package/dist/registry/ContextMenu.tsx +241 -0
- package/dist/registry/DataList.tsx +49 -0
- package/dist/registry/DatePicker.tsx +494 -0
- package/dist/registry/DateRangePicker.tsx +232 -0
- package/dist/registry/Drawer.tsx +166 -0
- package/dist/registry/DropdownMenu.tsx +219 -0
- package/dist/registry/EmptyState.tsx +55 -0
- 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 +108 -0
- package/dist/registry/Kbd.tsx +23 -0
- package/dist/registry/Modal.tsx +130 -0
- package/dist/registry/NumberInput.tsx +183 -0
- package/dist/registry/OtpInput.tsx +219 -0
- package/dist/registry/Pagination.tsx +160 -0
- package/dist/registry/Popover.tsx +116 -0
- package/dist/registry/Progress.tsx +66 -0
- package/dist/registry/QuoteCard.tsx +93 -0
- package/dist/registry/Radio.tsx +205 -0
- package/dist/registry/RadioCards.tsx +247 -0
- package/dist/registry/ReactionBar.tsx +101 -0
- package/dist/registry/ReadingProgress.tsx +51 -0
- package/dist/registry/ReadingTime.tsx +35 -0
- package/dist/registry/Select.tsx +265 -0
- package/dist/registry/Separator.tsx +52 -0
- package/dist/registry/ShareButtons.tsx +134 -0
- package/dist/registry/Sidebar.tsx +182 -0
- package/dist/registry/Skeleton.tsx +83 -0
- package/dist/registry/Slider.tsx +229 -0
- package/dist/registry/Spinner.tsx +32 -0
- package/dist/registry/SpotlightCard.tsx +106 -0
- package/dist/registry/Stat.tsx +82 -0
- package/dist/registry/StatCard.tsx +256 -0
- package/dist/registry/Steps.tsx +109 -0
- package/dist/registry/Table.tsx +123 -0
- package/dist/registry/TableOfContents.tsx +116 -0
- package/dist/registry/Tabs.tsx +136 -0
- package/dist/registry/TagInput.tsx +191 -0
- package/dist/registry/Textarea.tsx +84 -0
- package/dist/registry/TimePicker.tsx +315 -0
- package/dist/registry/Timeline.tsx +102 -0
- package/dist/registry/Toast.tsx +388 -0
- package/dist/registry/Toggle.tsx +96 -0
- package/dist/registry/Tooltip.tsx +160 -0
- package/dist/registry/styles.css +1207 -0
- package/dist/registry/useFloatingPanel.ts +112 -0
- package/dist/registry/useModalBehavior.ts +77 -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 +23 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/CommandPalette.tsx"],"sourcesContent":["\"use client\";\n\nimport React, {\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useMemo,\n useCallback,\n useId,\n} from \"react\";\nimport { Portal } from \"./Floating\";\nimport { Kbd } from \"./Kbd\";\nimport { useModalBehavior } from \"./useModalBehavior\";\n\ninterface CommandItem {\n key: string;\n label: string;\n description?: string;\n icon?: React.ReactNode;\n shortcut?: string;\n onSelect?: () => void;\n}\n\ninterface CommandSection {\n title: string;\n items: CommandItem[];\n}\n\ninterface CommandPaletteProps {\n open: boolean;\n onClose: () => void;\n sections: CommandSection[];\n placeholder?: string;\n className?: string;\n}\n\nconst CLOSE_MS = 240;\n\nexport function CommandPalette({\n open,\n onClose,\n sections,\n placeholder = \"search commands…\",\n className = \"\",\n}: CommandPaletteProps) {\n const [mounted, setMounted] = useState(false);\n const [visible, setVisible] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const dialogRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const uid = useId();\n\n // Filter sections based on query\n const filtered = useMemo(() => {\n const q = query.toLowerCase().trim();\n if (!q) return sections;\n return sections\n .map((section) => ({\n ...section,\n items: section.items.filter(\n (item) =>\n item.label.toLowerCase().includes(q) ||\n item.description?.toLowerCase().includes(q),\n ),\n }))\n .filter((section) => section.items.length > 0);\n }, [sections, query]);\n\n // Flat list of all visible items for keyboard nav\n const flatItems = useMemo(\n () => filtered.flatMap((s) => s.items),\n [filtered],\n );\n\n // Mount / unmount lifecycle (matches Modal pattern); every opening starts fresh\n const [wasOpen, setWasOpen] = useState(false);\n if (open !== wasOpen) {\n setWasOpen(open);\n if (open) {\n setMounted(true);\n setQuery(\"\");\n setActiveIndex(0);\n } else {\n setVisible(false);\n }\n }\n\n useEffect(() => {\n if (open || !mounted) return;\n const t = setTimeout(() => setMounted(false), CLOSE_MS);\n return () => clearTimeout(t);\n }, [open, mounted]);\n\n // Enter animation (matches Modal pattern)\n useLayoutEffect(() => {\n if (!mounted || !open || visible) return;\n\n const el = dialogRef.current;\n if (el) {\n void el.getBoundingClientRect();\n }\n\n const id = requestAnimationFrame(() => {\n setVisible(true);\n });\n return () => cancelAnimationFrame(id);\n }, [mounted, open, visible]);\n\n // Reset active index when filter changes\n useEffect(() => {\n setActiveIndex(0);\n }, [query]);\n\n // Scroll active item into view\n useEffect(() => {\n if (!listRef.current) return;\n const activeEl = listRef.current.querySelector(\n `[data-command-index=\"${activeIndex}\"]`,\n );\n if (activeEl) {\n activeEl.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // Handle selection\n const handleSelect = useCallback(\n (item: CommandItem) => {\n item.onSelect?.();\n onClose();\n },\n [onClose],\n );\n\n // Keyboard navigation\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setActiveIndex((i) => (i + 1 >= flatItems.length ? 0 : i + 1));\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setActiveIndex((i) =>\n i - 1 < 0 ? Math.max(flatItems.length - 1, 0) : i - 1,\n );\n break;\n case \"Enter\":\n e.preventDefault();\n if (flatItems[activeIndex]) {\n handleSelect(flatItems[activeIndex]);\n }\n break;\n }\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [open, onClose, flatItems, activeIndex, handleSelect]);\n\n // Escape, focus trap + restore, scroll lock\n useModalBehavior({ open, ready: visible, ref: dialogRef, onClose, initialFocus: inputRef });\n\n if (!mounted) return null;\n\n let itemIndex = 0;\n\n return (\n <Portal>\n <div className=\"fixed inset-0 z-90 flex items-start justify-center pt-[min(20vh,140px)] px-4\">\n {/* Backdrop */}\n <div\n className={[\n \"absolute inset-0 backdrop-blur-[6px]\",\n \"transition-opacity ease-out\",\n visible ? \"opacity-100 duration-300\" : \"opacity-0 duration-200\",\n ].join(\" \")}\n style={{ background: \"var(--scrim)\" }}\n aria-hidden\n onClick={onClose}\n />\n\n {/* Palette panel */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Command palette\"\n className={[\n \"relative z-10 w-full max-w-lg rounded-[20px] p-1.5 overflow-hidden\",\n \"transition-[opacity,transform,scale,translate] ease-[var(--ease-spring)]\",\n visible\n ? \"opacity-100 scale-100 translate-y-0 duration-300\"\n : \"opacity-0 scale-[0.94] translate-y-3 duration-200\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")}\n style={{ background: \"var(--panel-bezel)\", boxShadow: \"var(--modal-shadow)\" }}\n >\n <div\n className=\"bg-panel-face rounded-[14px] overflow-hidden\"\n style={{ boxShadow: \"var(--panel-face-shadow)\" }}\n >\n {/* Search input */}\n <div className=\"flex items-center gap-3 px-4 py-3.5\">\n <svg\n className=\"shrink-0 text-ink-faint\"\n width=\"14\"\n height=\"14\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16z\"\n />\n </svg>\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={`${uid}-list`}\n aria-activedescendant={flatItems[activeIndex] ? `${uid}-item-${activeIndex}` : undefined}\n aria-autocomplete=\"list\"\n className=\"flex-1 min-w-0 text-[14.5px] font-medium text-ink placeholder:text-ink-faint placeholder:font-normal bg-transparent border-none outline-none\"\n />\n <Kbd className=\"shrink-0\">esc</Kbd>\n </div>\n\n {/* Separator */}\n <div className=\"border-b border-line\" />\n\n {/* Results */}\n <div\n ref={listRef}\n id={`${uid}-list`}\n role=\"listbox\"\n className=\"max-h-[340px] overflow-y-auto scroll-thin py-2 px-2\"\n >\n {filtered.length === 0 ? (\n <div className=\"py-10 flex flex-col items-center gap-1 text-center\">\n <p className=\"text-[13px] font-medium text-ink-label\">\n nothing for “{query}”\n </p>\n <p className=\"text-[12px] text-ink-faint\">try a shorter or different word.</p>\n </div>\n ) : (\n filtered.map((section, si) => (\n <div key={section.title} role=\"group\" aria-label={section.title} className={si > 0 ? \"mt-3\" : \"\"}>\n <div aria-hidden className=\"px-3 py-1.5 text-[11.5px] font-medium text-ink-faint lowercase select-none\">\n {section.title}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {section.items.map((item) => {\n const currentIndex = itemIndex++;\n const isActive = currentIndex === activeIndex;\n return (\n <div\n key={item.key}\n id={`${uid}-item-${currentIndex}`}\n role=\"option\"\n aria-selected={isActive}\n data-command-index={currentIndex}\n onClick={() => handleSelect(item)}\n onMouseMove={() => setActiveIndex(currentIndex)}\n className={[\n \"group/cmd flex items-center gap-3 w-full text-left px-3 py-2 rounded-[10px] cursor-pointer\",\n \"transition-[background,box-shadow] duration-100\",\n isActive ? \"bg-surface-hover\" : \"\",\n ].join(\" \")}\n style={{ boxShadow: isActive ? \"var(--panel-face-shadow)\" : undefined }}\n >\n {item.icon && (\n <span\n className={[\n \"shrink-0 w-7 h-7 rounded-[8px] flex items-center justify-center transition-colors duration-100\",\n isActive ? \"text-ink\" : \"text-ink-muted\",\n ].join(\" \")}\n style={{ background: \"var(--face)\", boxShadow: \"var(--face-shadow)\" }}\n >\n {item.icon}\n </span>\n )}\n <div className=\"flex-1 min-w-0 flex flex-col\">\n <span className=\"text-[13px] font-medium text-ink truncate\">\n {item.label}\n </span>\n {item.description && (\n <span className=\"text-[12px] text-ink-muted truncate\">\n {item.description}\n </span>\n )}\n </div>\n {item.shortcut && <Kbd className=\"shrink-0\">{item.shortcut}</Kbd>}\n {!item.shortcut && (\n <svg\n aria-hidden\n width=\"12\"\n height=\"12\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n viewBox=\"0 0 24 24\"\n className={[\n \"shrink-0 text-ink-faint transition-all duration-150\",\n isActive ? \"opacity-100 translate-x-0\" : \"opacity-0 -translate-x-1\",\n ].join(\" \")}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n )}\n </div>\n );\n })}\n </div>\n </div>\n ))\n )}\n </div>\n\n {/* Key hints — teach the palette while it's open */}\n <div\n className=\"flex items-center gap-4 px-4 py-2.5 border-t border-line text-[11.5px] text-ink-muted select-none\"\n style={{ background: \"var(--palette-tray)\" }}\n >\n <span className=\"inline-flex items-center gap-1.5\">\n <Kbd>↑</Kbd>\n <Kbd>↓</Kbd>\n Navigate\n </span>\n <span className=\"inline-flex items-center gap-1.5\">\n <Kbd>↵</Kbd>\n Open\n </span>\n <span className=\"ml-auto tabular-nums text-ink-faint\">\n {flatItems.length} {flatItems.length === 1 ? \"result\" : \"results\"}\n </span>\n </div>\n </div>\n </div>\n </div>\n </Portal>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { DropdownMenuItem } from './DropdownMenu.js';
|
|
4
|
+
|
|
5
|
+
type ContextMenuItem = DropdownMenuItem;
|
|
6
|
+
interface ContextMenuProps {
|
|
7
|
+
/** The area that answers a right-click */
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
items: ContextMenuItem[];
|
|
10
|
+
onSelect?: (key: string) => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
declare function ContextMenu({ children, items, onSelect, disabled, className }: ContextMenuProps): react_jsx_runtime.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { ContextMenu, type ContextMenuItem };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/ContextMenu.tsx
|
|
4
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
5
|
+
import { LAYER, Portal } from "./Floating.js";
|
|
6
|
+
import { Kbd } from "./Kbd.js";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var PAD = 8;
|
|
9
|
+
var LONG_PRESS = 500;
|
|
10
|
+
function ContextMenu({ children, items, onSelect, disabled = false, className = "" }) {
|
|
11
|
+
const uid = useId();
|
|
12
|
+
const areaRef = useRef(null);
|
|
13
|
+
const menuRef = useRef(null);
|
|
14
|
+
const [point, setPoint] = useState(null);
|
|
15
|
+
const [mounted, setMounted] = useState(false);
|
|
16
|
+
const [visible, setVisible] = useState(false);
|
|
17
|
+
const press = useRef(null);
|
|
18
|
+
const open = point !== null;
|
|
19
|
+
if (open && !mounted) setMounted(true);
|
|
20
|
+
if (!open && visible) setVisible(false);
|
|
21
|
+
const enabledItems = () => Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]:not([disabled])') ?? []);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (open) {
|
|
24
|
+
const id = requestAnimationFrame(
|
|
25
|
+
() => requestAnimationFrame(() => {
|
|
26
|
+
setVisible(true);
|
|
27
|
+
menuRef.current?.querySelector('[role="menuitem"]:not([disabled])')?.focus();
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
return () => cancelAnimationFrame(id);
|
|
31
|
+
}
|
|
32
|
+
const t = setTimeout(() => setMounted(false), 160);
|
|
33
|
+
return () => clearTimeout(t);
|
|
34
|
+
}, [open]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!open) return;
|
|
37
|
+
const shut = (e) => {
|
|
38
|
+
if (menuRef.current?.contains(e.target)) return;
|
|
39
|
+
setPoint(null);
|
|
40
|
+
};
|
|
41
|
+
const onScroll = () => setPoint(null);
|
|
42
|
+
document.addEventListener("pointerdown", shut);
|
|
43
|
+
window.addEventListener("scroll", onScroll, true);
|
|
44
|
+
window.addEventListener("resize", onScroll);
|
|
45
|
+
window.addEventListener("blur", onScroll);
|
|
46
|
+
return () => {
|
|
47
|
+
document.removeEventListener("pointerdown", shut);
|
|
48
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
49
|
+
window.removeEventListener("resize", onScroll);
|
|
50
|
+
window.removeEventListener("blur", onScroll);
|
|
51
|
+
};
|
|
52
|
+
}, [open]);
|
|
53
|
+
const attach = (el) => {
|
|
54
|
+
menuRef.current = el;
|
|
55
|
+
if (!el || !point) return;
|
|
56
|
+
const w = el.offsetWidth;
|
|
57
|
+
const h = el.offsetHeight;
|
|
58
|
+
const vw = document.documentElement.clientWidth;
|
|
59
|
+
const vh = window.innerHeight;
|
|
60
|
+
const flipX = point.x + w > vw - PAD;
|
|
61
|
+
const flipY = point.y + h > vh - PAD;
|
|
62
|
+
el.style.left = `${Math.max(PAD, flipX ? point.x - w : point.x)}px`;
|
|
63
|
+
el.style.top = `${Math.max(PAD, flipY ? point.y - h : point.y)}px`;
|
|
64
|
+
el.style.transformOrigin = `${flipY ? "bottom" : "top"} ${flipX ? "right" : "left"}`;
|
|
65
|
+
};
|
|
66
|
+
const openAt = (x, y) => {
|
|
67
|
+
if (disabled) return;
|
|
68
|
+
setPoint({ x, y });
|
|
69
|
+
};
|
|
70
|
+
const close = (refocus = true) => {
|
|
71
|
+
setPoint(null);
|
|
72
|
+
if (refocus) areaRef.current?.focus({ preventScroll: true });
|
|
73
|
+
};
|
|
74
|
+
const onMenuKeyDown = (e) => {
|
|
75
|
+
const list = enabledItems();
|
|
76
|
+
const at = list.indexOf(document.activeElement);
|
|
77
|
+
const go = (i) => list[(i + list.length) % list.length]?.focus();
|
|
78
|
+
switch (e.key) {
|
|
79
|
+
case "ArrowDown":
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
go(at + 1);
|
|
82
|
+
break;
|
|
83
|
+
case "ArrowUp":
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
go(at - 1);
|
|
86
|
+
break;
|
|
87
|
+
case "Home":
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
go(0);
|
|
90
|
+
break;
|
|
91
|
+
case "End":
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
go(list.length - 1);
|
|
94
|
+
break;
|
|
95
|
+
case "Escape":
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
e.stopPropagation();
|
|
98
|
+
close();
|
|
99
|
+
break;
|
|
100
|
+
case "Tab":
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
close();
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const cancelPress = () => {
|
|
107
|
+
if (press.current) clearTimeout(press.current);
|
|
108
|
+
};
|
|
109
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
110
|
+
/* @__PURE__ */ jsxs(
|
|
111
|
+
"div",
|
|
112
|
+
{
|
|
113
|
+
ref: areaRef,
|
|
114
|
+
tabIndex: 0,
|
|
115
|
+
"aria-haspopup": "menu",
|
|
116
|
+
"aria-expanded": open,
|
|
117
|
+
"aria-controls": open ? `${uid}-menu` : void 0,
|
|
118
|
+
"aria-describedby": `${uid}-how`,
|
|
119
|
+
className: ["outline-none focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-page", className].join(" "),
|
|
120
|
+
onContextMenu: (e) => {
|
|
121
|
+
if (disabled) return;
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
openAt(e.clientX, e.clientY);
|
|
124
|
+
},
|
|
125
|
+
onKeyDown: (e) => {
|
|
126
|
+
if (e.key === "ContextMenu" || e.shiftKey && e.key === "F10") {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
const r = areaRef.current.getBoundingClientRect();
|
|
129
|
+
openAt(r.left + 16, r.top + 16);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
onPointerDown: (e) => {
|
|
133
|
+
if (e.pointerType !== "touch") return;
|
|
134
|
+
const { clientX, clientY } = e;
|
|
135
|
+
cancelPress();
|
|
136
|
+
press.current = setTimeout(() => openAt(clientX, clientY), LONG_PRESS);
|
|
137
|
+
},
|
|
138
|
+
onPointerUp: cancelPress,
|
|
139
|
+
onPointerMove: cancelPress,
|
|
140
|
+
onPointerCancel: cancelPress,
|
|
141
|
+
children: [
|
|
142
|
+
children,
|
|
143
|
+
/* @__PURE__ */ jsx("span", { id: `${uid}-how`, className: "sr-only", children: "right-click, long-press, or press shift f10 for options" })
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
),
|
|
147
|
+
mounted && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
148
|
+
"div",
|
|
149
|
+
{
|
|
150
|
+
ref: attach,
|
|
151
|
+
id: `${uid}-menu`,
|
|
152
|
+
role: "menu",
|
|
153
|
+
onKeyDown: onMenuKeyDown,
|
|
154
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
155
|
+
className: "min-w-[200px] rounded-[16px] p-1.5 transition-[opacity,transform] duration-150 ease-[var(--ease-spring)]",
|
|
156
|
+
style: {
|
|
157
|
+
position: "fixed",
|
|
158
|
+
top: 0,
|
|
159
|
+
left: 0,
|
|
160
|
+
zIndex: LAYER.floating,
|
|
161
|
+
opacity: visible ? 1 : 0,
|
|
162
|
+
transform: visible ? "none" : "scale(0.94)",
|
|
163
|
+
background: "var(--panel-bezel)",
|
|
164
|
+
boxShadow: "var(--overlay-shadow)"
|
|
165
|
+
},
|
|
166
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex flex-col rounded-[11px] bg-panel-face p-1", style: { boxShadow: "var(--panel-face-shadow)" }, children: items.map(
|
|
167
|
+
(item) => item.divider ? /* @__PURE__ */ jsx("div", { role: "separator", className: "mx-2 my-1 h-px bg-line" }, item.key) : /* @__PURE__ */ jsxs(
|
|
168
|
+
"button",
|
|
169
|
+
{
|
|
170
|
+
type: "button",
|
|
171
|
+
role: "menuitem",
|
|
172
|
+
tabIndex: -1,
|
|
173
|
+
disabled: item.disabled,
|
|
174
|
+
onClick: () => {
|
|
175
|
+
onSelect?.(item.key);
|
|
176
|
+
close();
|
|
177
|
+
},
|
|
178
|
+
onMouseEnter: (e) => e.currentTarget.focus(),
|
|
179
|
+
className: [
|
|
180
|
+
"group/item flex w-full cursor-pointer items-center gap-2.5 rounded-[8px] px-2.5 py-2 text-[13px] font-medium outline-none",
|
|
181
|
+
"transition-[background,color] duration-100 disabled:pointer-events-none disabled:opacity-35",
|
|
182
|
+
item.danger ? "text-danger focus:bg-danger-wash" : "text-ink-label focus:bg-surface-active/80 focus:text-ink"
|
|
183
|
+
].join(" "),
|
|
184
|
+
children: [
|
|
185
|
+
item.icon && /* @__PURE__ */ jsx(
|
|
186
|
+
"span",
|
|
187
|
+
{
|
|
188
|
+
className: [
|
|
189
|
+
"flex h-4 w-4 shrink-0 items-center justify-center transition-colors duration-100",
|
|
190
|
+
item.danger ? "text-danger-soft" : "text-ink-faint group-focus/item:text-ink-label"
|
|
191
|
+
].join(" "),
|
|
192
|
+
children: item.icon
|
|
193
|
+
}
|
|
194
|
+
),
|
|
195
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: item.label }),
|
|
196
|
+
item.shortcut && /* @__PURE__ */ jsx(Kbd, { className: "shrink-0", children: item.shortcut })
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
item.key
|
|
200
|
+
)
|
|
201
|
+
) })
|
|
202
|
+
}
|
|
203
|
+
) })
|
|
204
|
+
] });
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
ContextMenu
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=ContextMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/ContextMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useId, useRef, useState } from \"react\";\nimport type { DropdownMenuItem } from \"./DropdownMenu\";\nimport { LAYER, Portal } from \"./Floating\";\nimport { Kbd } from \"./Kbd\";\n\nexport type ContextMenuItem = DropdownMenuItem;\n\ninterface ContextMenuProps {\n /** The area that answers a right-click */\n children: React.ReactNode;\n items: ContextMenuItem[];\n onSelect?: (key: string) => void;\n disabled?: boolean;\n className?: string;\n}\n\nconst PAD = 8;\nconst LONG_PRESS = 500;\n\n/* The same panel as DropdownMenu, opened where you right-click instead of\n under a button. A long press opens it on touch, and the context-menu\n key or shift+F10 opens it from the keyboard at the focused element.\n It grows out of the corner nearest the pointer and flips to stay on\n screen. */\nexport function ContextMenu({ children, items, onSelect, disabled = false, className = \"\" }: ContextMenuProps) {\n const uid = useId();\n const areaRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement | null>(null);\n const [point, setPoint] = useState<{ x: number; y: number } | null>(null);\n const [mounted, setMounted] = useState(false);\n const [visible, setVisible] = useState(false);\n const press = useRef<ReturnType<typeof setTimeout> | null>(null);\n const open = point !== null;\n\n if (open && !mounted) setMounted(true);\n if (!open && visible) setVisible(false);\n\n const enabledItems = () =>\n Array.from(menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([disabled])') ?? []);\n\n useEffect(() => {\n if (open) {\n const id = requestAnimationFrame(() =>\n requestAnimationFrame(() => {\n setVisible(true);\n menuRef.current?.querySelector<HTMLElement>('[role=\"menuitem\"]:not([disabled])')?.focus();\n }),\n );\n return () => cancelAnimationFrame(id);\n }\n const t = setTimeout(() => setMounted(false), 160);\n return () => clearTimeout(t);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const shut = (e: Event) => {\n if (menuRef.current?.contains(e.target as Node)) return;\n setPoint(null);\n };\n const onScroll = () => setPoint(null);\n document.addEventListener(\"pointerdown\", shut);\n window.addEventListener(\"scroll\", onScroll, true);\n window.addEventListener(\"resize\", onScroll);\n window.addEventListener(\"blur\", onScroll);\n return () => {\n document.removeEventListener(\"pointerdown\", shut);\n window.removeEventListener(\"scroll\", onScroll, true);\n window.removeEventListener(\"resize\", onScroll);\n window.removeEventListener(\"blur\", onScroll);\n };\n }, [open]);\n\n /* Placed as soon as it's attached: beside the pointer, flipped left or\n up if it would run off the edge. Only writes to the element, so it\n can run on every render without setting state. */\n const attach = (el: HTMLDivElement | null) => {\n menuRef.current = el;\n if (!el || !point) return;\n const w = el.offsetWidth;\n const h = el.offsetHeight;\n const vw = document.documentElement.clientWidth;\n const vh = window.innerHeight;\n const flipX = point.x + w > vw - PAD;\n const flipY = point.y + h > vh - PAD;\n el.style.left = `${Math.max(PAD, flipX ? point.x - w : point.x)}px`;\n el.style.top = `${Math.max(PAD, flipY ? point.y - h : point.y)}px`;\n el.style.transformOrigin = `${flipY ? \"bottom\" : \"top\"} ${flipX ? \"right\" : \"left\"}`;\n };\n\n const openAt = (x: number, y: number) => {\n if (disabled) return;\n setPoint({ x, y });\n };\n\n const close = (refocus = true) => {\n setPoint(null);\n if (refocus) areaRef.current?.focus({ preventScroll: true });\n };\n\n const onMenuKeyDown = (e: React.KeyboardEvent) => {\n const list = enabledItems();\n const at = list.indexOf(document.activeElement as HTMLElement);\n const go = (i: number) => list[(i + list.length) % list.length]?.focus();\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n go(at + 1);\n break;\n case \"ArrowUp\":\n e.preventDefault();\n go(at - 1);\n break;\n case \"Home\":\n e.preventDefault();\n go(0);\n break;\n case \"End\":\n e.preventDefault();\n go(list.length - 1);\n break;\n case \"Escape\":\n e.preventDefault();\n e.stopPropagation();\n close();\n break;\n case \"Tab\":\n e.preventDefault();\n close();\n break;\n }\n };\n\n const cancelPress = () => {\n if (press.current) clearTimeout(press.current);\n };\n\n return (\n <>\n <div\n ref={areaRef}\n tabIndex={0}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? `${uid}-menu` : undefined}\n aria-describedby={`${uid}-how`}\n className={[\"outline-none focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-page\", className].join(\" \")}\n onContextMenu={(e) => {\n if (disabled) return;\n e.preventDefault();\n openAt(e.clientX, e.clientY);\n }}\n onKeyDown={(e) => {\n if (e.key === \"ContextMenu\" || (e.shiftKey && e.key === \"F10\")) {\n e.preventDefault();\n const r = areaRef.current!.getBoundingClientRect();\n openAt(r.left + 16, r.top + 16);\n }\n }}\n onPointerDown={(e) => {\n if (e.pointerType !== \"touch\") return;\n const { clientX, clientY } = e;\n cancelPress();\n press.current = setTimeout(() => openAt(clientX, clientY), LONG_PRESS);\n }}\n onPointerUp={cancelPress}\n onPointerMove={cancelPress}\n onPointerCancel={cancelPress}\n >\n {children}\n <span id={`${uid}-how`} className=\"sr-only\">\n right-click, long-press, or press shift f10 for options\n </span>\n </div>\n\n {mounted && (\n <Portal>\n <div\n ref={attach}\n id={`${uid}-menu`}\n role=\"menu\"\n onKeyDown={onMenuKeyDown}\n onContextMenu={(e) => e.preventDefault()}\n className=\"min-w-[200px] rounded-[16px] p-1.5 transition-[opacity,transform] duration-150 ease-[var(--ease-spring)]\"\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n zIndex: LAYER.floating,\n opacity: visible ? 1 : 0,\n transform: visible ? \"none\" : \"scale(0.94)\",\n background: \"var(--panel-bezel)\",\n boxShadow: \"var(--overlay-shadow)\",\n }}\n >\n <div className=\"flex flex-col rounded-[11px] bg-panel-face p-1\" style={{ boxShadow: \"var(--panel-face-shadow)\" }}>\n {items.map((item) =>\n item.divider ? (\n <div key={item.key} role=\"separator\" className=\"mx-2 my-1 h-px bg-line\" />\n ) : (\n <button\n key={item.key}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n disabled={item.disabled}\n onClick={() => {\n onSelect?.(item.key);\n close();\n }}\n onMouseEnter={(e) => e.currentTarget.focus()}\n className={[\n \"group/item flex w-full cursor-pointer items-center gap-2.5 rounded-[8px] px-2.5 py-2 text-[13px] font-medium outline-none\",\n \"transition-[background,color] duration-100 disabled:pointer-events-none disabled:opacity-35\",\n item.danger ? \"text-danger focus:bg-danger-wash\" : \"text-ink-label focus:bg-surface-active/80 focus:text-ink\",\n ].join(\" \")}\n >\n {item.icon && (\n <span\n className={[\n \"flex h-4 w-4 shrink-0 items-center justify-center transition-colors duration-100\",\n item.danger ? \"text-danger-soft\" : \"text-ink-faint group-focus/item:text-ink-label\",\n ].join(\" \")}\n >\n {item.icon}\n </span>\n )}\n <span className=\"flex-1 text-left\">{item.label}</span>\n {item.shortcut && <Kbd className=\"shrink-0\">{item.shortcut}</Kbd>}\n </button>\n ),\n )}\n </div>\n </div>\n </Portal>\n )}\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface DataListItem {
|
|
5
|
+
label: string;
|
|
6
|
+
value: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
interface DataListProps {
|
|
9
|
+
items: DataListItem[];
|
|
10
|
+
columns?: 1 | 2;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function DataList({ items, columns, className }: DataListProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { DataList };
|
package/dist/DataList.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// ../../components/ui/DataList.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function DataList({ items, columns = 1, className = "" }) {
|
|
4
|
+
return /* @__PURE__ */ jsx(
|
|
5
|
+
"div",
|
|
6
|
+
{
|
|
7
|
+
className: ["rounded-[18px] p-1.5", className].filter(Boolean).join(" "),
|
|
8
|
+
style: { background: "var(--panel-bezel)", boxShadow: "var(--panel-shadow)" },
|
|
9
|
+
children: /* @__PURE__ */ jsx(
|
|
10
|
+
"dl",
|
|
11
|
+
{
|
|
12
|
+
className: [
|
|
13
|
+
// 1px gaps over a hairline-coloured face draw the grid lines, so
|
|
14
|
+
// borders never double up whatever the item count
|
|
15
|
+
"grid gap-px bg-ink/[0.05] rounded-[13px] overflow-hidden",
|
|
16
|
+
columns === 2 ? "grid-cols-1 sm:grid-cols-2" : "grid-cols-1"
|
|
17
|
+
].join(" "),
|
|
18
|
+
style: { boxShadow: "var(--panel-face-shadow)" },
|
|
19
|
+
children: items.map((item, i) => /* @__PURE__ */ jsxs(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
className: [
|
|
23
|
+
"flex flex-col gap-1.5 px-5 py-4 bg-panel-face",
|
|
24
|
+
// An odd last cell in two columns spans the row instead of leaving a hole
|
|
25
|
+
columns === 2 && items.length % 2 === 1 && i === items.length - 1 ? "sm:col-span-2" : ""
|
|
26
|
+
].join(" "),
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ jsx("dt", { className: "text-[12px] text-ink-muted", children: item.label }),
|
|
29
|
+
/* @__PURE__ */ jsx("dd", { className: "min-h-6 flex items-center text-[14px] font-medium text-ink tracking-[-0.01em] tabular-nums", children: item.value })
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
i
|
|
33
|
+
))
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
DataList
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=DataList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/DataList.tsx"],"sourcesContent":["import React from \"react\";\n\ninterface DataListItem {\n label: string;\n value: React.ReactNode;\n}\n\ninterface DataListProps {\n items: DataListItem[];\n columns?: 1 | 2;\n className?: string;\n}\n\nexport function DataList({ items, columns = 1, className = \"\" }: DataListProps) {\n return (\n <div\n className={[\"rounded-[18px] p-1.5\", className].filter(Boolean).join(\" \")}\n style={{ background: \"var(--panel-bezel)\", boxShadow: \"var(--panel-shadow)\" }}\n >\n <dl\n className={[\n // 1px gaps over a hairline-coloured face draw the grid lines, so\n // borders never double up whatever the item count\n \"grid gap-px bg-ink/[0.05] rounded-[13px] overflow-hidden\",\n columns === 2 ? \"grid-cols-1 sm:grid-cols-2\" : \"grid-cols-1\",\n ].join(\" \")}\n style={{ boxShadow: \"var(--panel-face-shadow)\" }}\n >\n {items.map((item, i) => (\n <div\n key={i}\n className={[\n \"flex flex-col gap-1.5 px-5 py-4 bg-panel-face\",\n // An odd last cell in two columns spans the row instead of leaving a hole\n columns === 2 && items.length % 2 === 1 && i === items.length - 1 ? \"sm:col-span-2\" : \"\",\n ].join(\" \")}\n >\n <dt className=\"text-[12px] text-ink-muted\">\n {item.label}\n </dt>\n <dd className=\"min-h-6 flex items-center text-[14px] font-medium text-ink tracking-[-0.01em] tabular-nums\">\n {item.value}\n </dd>\n </div>\n ))}\n </dl>\n </div>\n );\n}\n"],"mappings":";AA6BU,SAQE,KARF;AAhBH,SAAS,SAAS,EAAE,OAAO,UAAU,GAAG,YAAY,GAAG,GAAkB;AAC9E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,wBAAwB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MACvE,OAAO,EAAE,YAAY,sBAAsB,WAAW,sBAAsB;AAAA,MAE5E;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA;AAAA;AAAA,YAGT;AAAA,YACA,YAAY,IAAI,+BAA+B;AAAA,UACjD,EAAE,KAAK,GAAG;AAAA,UACV,OAAO,EAAE,WAAW,2BAA2B;AAAA,UAE9C,gBAAM,IAAI,CAAC,MAAM,MAChB;AAAA,YAAC;AAAA;AAAA,cAEC,WAAW;AAAA,gBACT;AAAA;AAAA,gBAEA,YAAY,KAAK,MAAM,SAAS,MAAM,KAAK,MAAM,MAAM,SAAS,IAAI,kBAAkB;AAAA,cACxF,EAAE,KAAK,GAAG;AAAA,cAEV;AAAA,oCAAC,QAAG,WAAU,8BACX,eAAK,OACR;AAAA,gBACA,oBAAC,QAAG,WAAU,8FACX,eAAK,OACR;AAAA;AAAA;AAAA,YAZK;AAAA,UAaP,CACD;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
declare const startOfDay: (d: Date) => Date;
|
|
4
|
+
declare const sameDay: (a?: Date | null, b?: Date | null) => boolean;
|
|
5
|
+
declare const toISODate: (d: Date) => string;
|
|
6
|
+
interface DateRange {
|
|
7
|
+
from: Date | null;
|
|
8
|
+
to: Date | null;
|
|
9
|
+
}
|
|
10
|
+
interface CalendarProps {
|
|
11
|
+
value?: Date | null;
|
|
12
|
+
/** Shade a span of days instead of marking one (see DateRangePicker) */
|
|
13
|
+
range?: DateRange;
|
|
14
|
+
onChange?: (date: Date) => void;
|
|
15
|
+
/** The day under the pointer or keyboard, for previewing a range */
|
|
16
|
+
onDayHover?: (date: Date | null) => void;
|
|
17
|
+
/** The month shown first; defaults to the selected date, or today */
|
|
18
|
+
defaultMonth?: Date;
|
|
19
|
+
/** How many months sit side by side */
|
|
20
|
+
months?: 1 | 2;
|
|
21
|
+
min?: Date;
|
|
22
|
+
max?: Date;
|
|
23
|
+
/** Grey out and skip particular days, like weekends */
|
|
24
|
+
isDateDisabled?: (date: Date) => boolean;
|
|
25
|
+
/** 0 is Sunday, 1 is Monday */
|
|
26
|
+
weekStartsOn?: 0 | 1;
|
|
27
|
+
locale?: string;
|
|
28
|
+
/** Move focus onto the grid as soon as it mounts */
|
|
29
|
+
autoFocus?: boolean;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function Calendar({ value, range, onChange, onDayHover, defaultMonth, months, min, max, isDateDisabled, weekStartsOn, locale, autoFocus, className, }: CalendarProps): react_jsx_runtime.JSX.Element;
|
|
33
|
+
declare const CalendarIcon: ({ className }: {
|
|
34
|
+
className?: string;
|
|
35
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
36
|
+
declare function ClearKey({ label, onClear }: {
|
|
37
|
+
label: string;
|
|
38
|
+
onClear: () => void;
|
|
39
|
+
}): react_jsx_runtime.JSX.Element;
|
|
40
|
+
interface DatePickerProps {
|
|
41
|
+
value?: Date | null;
|
|
42
|
+
defaultValue?: Date | null;
|
|
43
|
+
onChange?: (date: Date | null) => void;
|
|
44
|
+
label?: string;
|
|
45
|
+
hint?: string;
|
|
46
|
+
error?: string;
|
|
47
|
+
placeholder?: string;
|
|
48
|
+
min?: Date;
|
|
49
|
+
max?: Date;
|
|
50
|
+
isDateDisabled?: (date: Date) => boolean;
|
|
51
|
+
weekStartsOn?: 0 | 1;
|
|
52
|
+
locale?: string;
|
|
53
|
+
/** How the chosen date reads in the field */
|
|
54
|
+
format?: Intl.DateTimeFormatOptions;
|
|
55
|
+
/** Submit the date with a form, as yyyy-mm-dd */
|
|
56
|
+
name?: string;
|
|
57
|
+
/** Show a small key in the field that empties it */
|
|
58
|
+
clearable?: boolean;
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
fullWidth?: boolean;
|
|
61
|
+
className?: string;
|
|
62
|
+
}
|
|
63
|
+
declare function DatePicker({ value, defaultValue, onChange, label, hint, error, placeholder, min, max, isDateDisabled, weekStartsOn, locale, format, name, clearable, disabled, fullWidth, className, }: DatePickerProps): react_jsx_runtime.JSX.Element;
|
|
64
|
+
|
|
65
|
+
export { Calendar, CalendarIcon, ClearKey, DatePicker, type DateRange, sameDay, startOfDay, toISODate };
|