@etoile-dev/react 0.2.3 → 1.0.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 (74) hide show
  1. package/README.md +344 -206
  2. package/dist/Searchbar.d.ts +315 -0
  3. package/dist/Searchbar.js +207 -0
  4. package/dist/context.d.ts +57 -0
  5. package/dist/context.js +32 -0
  6. package/dist/hooks/useEtoileSearch.d.ts +136 -0
  7. package/dist/hooks/useEtoileSearch.js +187 -0
  8. package/dist/index.d.ts +44 -19
  9. package/dist/index.js +37 -12
  10. package/dist/primitives/Content.d.ts +34 -0
  11. package/dist/primitives/Content.js +108 -0
  12. package/dist/primitives/Empty.d.ts +25 -0
  13. package/dist/primitives/Empty.js +25 -0
  14. package/dist/primitives/Error.d.ts +29 -0
  15. package/dist/primitives/Error.js +26 -0
  16. package/dist/primitives/Group.d.ts +30 -0
  17. package/dist/primitives/Group.js +22 -0
  18. package/dist/primitives/Icon.d.ts +21 -0
  19. package/dist/primitives/Icon.js +14 -0
  20. package/dist/primitives/Input.d.ts +32 -0
  21. package/dist/primitives/Input.js +70 -0
  22. package/dist/primitives/Item.d.ts +61 -0
  23. package/dist/primitives/Item.js +76 -0
  24. package/dist/primitives/Kbd.d.ts +20 -0
  25. package/dist/primitives/Kbd.js +13 -0
  26. package/dist/primitives/List.d.ts +35 -0
  27. package/dist/primitives/List.js +37 -0
  28. package/dist/primitives/Loading.d.ts +25 -0
  29. package/dist/primitives/Loading.js +26 -0
  30. package/dist/primitives/Modal.d.ts +39 -0
  31. package/dist/primitives/Modal.js +37 -0
  32. package/dist/primitives/ModalInput.d.ts +61 -0
  33. package/dist/primitives/ModalInput.js +33 -0
  34. package/dist/primitives/Overlay.d.ts +21 -0
  35. package/dist/primitives/Overlay.js +41 -0
  36. package/dist/primitives/Portal.d.ts +28 -0
  37. package/dist/primitives/Portal.js +30 -0
  38. package/dist/primitives/Root.d.ts +116 -0
  39. package/dist/primitives/Root.js +413 -0
  40. package/dist/primitives/Separator.d.ts +19 -0
  41. package/dist/primitives/Separator.js +18 -0
  42. package/dist/primitives/Thumbnail.d.ts +31 -0
  43. package/dist/primitives/Thumbnail.js +59 -0
  44. package/dist/primitives/Trigger.d.ts +28 -0
  45. package/dist/primitives/Trigger.js +35 -0
  46. package/dist/store.d.ts +38 -0
  47. package/dist/store.js +63 -0
  48. package/dist/styles.css +480 -133
  49. package/dist/types.d.ts +3 -31
  50. package/dist/utils/composeRefs.d.ts +12 -0
  51. package/dist/utils/composeRefs.js +27 -0
  52. package/dist/utils/slot.d.ts +22 -0
  53. package/dist/utils/slot.js +58 -0
  54. package/package.json +9 -5
  55. package/dist/Search.d.ts +0 -39
  56. package/dist/Search.js +0 -31
  57. package/dist/components/SearchIcon.d.ts +0 -22
  58. package/dist/components/SearchIcon.js +0 -17
  59. package/dist/components/SearchInput.d.ts +0 -30
  60. package/dist/components/SearchInput.js +0 -59
  61. package/dist/components/SearchKbd.d.ts +0 -30
  62. package/dist/components/SearchKbd.js +0 -24
  63. package/dist/components/SearchResult.d.ts +0 -31
  64. package/dist/components/SearchResult.js +0 -40
  65. package/dist/components/SearchResultThumbnail.d.ts +0 -38
  66. package/dist/components/SearchResultThumbnail.js +0 -38
  67. package/dist/components/SearchResults.d.ts +0 -39
  68. package/dist/components/SearchResults.js +0 -53
  69. package/dist/components/SearchRoot.d.ts +0 -44
  70. package/dist/components/SearchRoot.js +0 -132
  71. package/dist/context/SearchContext.d.ts +0 -55
  72. package/dist/context/SearchContext.js +0 -36
  73. package/dist/hooks/useSearch.d.ts +0 -56
  74. package/dist/hooks/useSearch.js +0 -116
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Input } from "./Input.js";
4
+ import { Icon } from "./Icon.js";
5
+ import { Kbd } from "./Kbd.js";
6
+ /**
7
+ * Pre-composed input row for command palette mode.
8
+ *
9
+ * Renders a search icon, combobox input, and optional keyboard badge in a
10
+ * flex row. A separator border appears automatically when results are
11
+ * visible below (via `:has(+ [role="listbox"])`).
12
+ *
13
+ * Styled via `data-slot="searchbar-modal-input"` — fully independent from
14
+ * the inline `Searchbar` input wrapper.
15
+ *
16
+ * @example Basic usage
17
+ * ```tsx
18
+ * <Searchbar.Modal hotkey="mod+k">
19
+ * <Searchbar.ModalInput />
20
+ * <Searchbar.List>…</Searchbar.List>
21
+ * </Searchbar.Modal>
22
+ * ```
23
+ *
24
+ * @example Custom placeholder, no kbd badge
25
+ * ```tsx
26
+ * <Searchbar.Modal>
27
+ * <Searchbar.ModalInput placeholder="Search paintings…" icon={null} kbd={null} />
28
+ * <Searchbar.List>…</Searchbar.List>
29
+ * </Searchbar.Modal>
30
+ * ```
31
+ */
32
+ export const ModalInput = React.forwardRef(({ placeholder = "Search…", icon = _jsx(Icon, {}), kbd = "Esc", className, ...props }, ref) => (_jsxs("div", { ...props, ref: ref, "data-slot": "searchbar-modal-input", className: className, children: [icon, _jsx(Input, { autoFocus: true, placeholder: placeholder }), kbd !== null && _jsx(Kbd, { children: kbd })] })));
33
+ ModalInput.displayName = "Searchbar.ModalInput";
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ export type SearchbarOverlayProps = {
3
+ className?: string;
4
+ asChild?: boolean;
5
+ } & React.HTMLAttributes<HTMLDivElement>;
6
+ /**
7
+ * Backdrop overlay for command palette / modal mode.
8
+ * Only renders when the search is open.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <Searchbar.Portal>
13
+ * <Searchbar.Overlay className="fixed inset-0 bg-black/40" />
14
+ * <Searchbar.Content>…</Searchbar.Content>
15
+ * </Searchbar.Portal>
16
+ * ```
17
+ */
18
+ export declare const Overlay: React.ForwardRefExoticComponent<{
19
+ className?: string;
20
+ asChild?: boolean;
21
+ } & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { useSearchbarContext, useSearchbarStore } from "../context.js";
4
+ import { Slot } from "../utils/slot.js";
5
+ const PRESENCE_DURATION_MS = 300;
6
+ /**
7
+ * Backdrop overlay for command palette / modal mode.
8
+ * Only renders when the search is open.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <Searchbar.Portal>
13
+ * <Searchbar.Overlay className="fixed inset-0 bg-black/40" />
14
+ * <Searchbar.Content>…</Searchbar.Content>
15
+ * </Searchbar.Portal>
16
+ * ```
17
+ */
18
+ export const Overlay = React.forwardRef(({ className, asChild = false, ...props }, forwardedRef) => {
19
+ const { store, rootId, rootClassName, setOpen } = useSearchbarContext();
20
+ const isOpen = useSearchbarStore(store, (s) => s.open);
21
+ const [present, setPresent] = React.useState(isOpen);
22
+ const mergedClassName = [rootClassName, className].filter(Boolean).join(" ") || undefined;
23
+ React.useEffect(() => {
24
+ if (isOpen) {
25
+ setPresent(true);
26
+ return;
27
+ }
28
+ const timeout = window.setTimeout(() => setPresent(false), PRESENCE_DURATION_MS);
29
+ return () => window.clearTimeout(timeout);
30
+ }, [isOpen]);
31
+ if (!present)
32
+ return null;
33
+ const Comp = asChild ? Slot : "div";
34
+ return (_jsx(Comp, { ...props, ref: forwardedRef, "aria-hidden": "true", className: mergedClassName, "data-state": isOpen ? "open" : "closed", "data-slot": "searchbar-overlay", "data-searchbar-root": rootId, onPointerDown: (event) => {
35
+ props.onPointerDown?.(event);
36
+ if (event.defaultPrevented)
37
+ return;
38
+ setOpen(false);
39
+ } }));
40
+ });
41
+ Overlay.displayName = "Searchbar.Overlay";
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ export type SearchbarPortalProps = {
3
+ /** DOM node to portal into (default: document.body) */
4
+ container?: Element | null;
5
+ children: React.ReactNode;
6
+ };
7
+ /**
8
+ * Renders children into a portal — useful for command palette / modal mode
9
+ * to escape stacking contexts.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <Searchbar.Root>
14
+ * <Searchbar.Trigger />
15
+ * <Searchbar.Portal>
16
+ * <Searchbar.Overlay />
17
+ * <Searchbar.Content>
18
+ * <Searchbar.Input />
19
+ * <Searchbar.List>…</Searchbar.List>
20
+ * </Searchbar.Content>
21
+ * </Searchbar.Portal>
22
+ * </Searchbar.Root>
23
+ * ```
24
+ */
25
+ export declare const Portal: {
26
+ ({ container, children }: SearchbarPortalProps): React.ReactPortal | null;
27
+ displayName: string;
28
+ };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import { createPortal } from "react-dom";
3
+ /**
4
+ * Renders children into a portal — useful for command palette / modal mode
5
+ * to escape stacking contexts.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <Searchbar.Root>
10
+ * <Searchbar.Trigger />
11
+ * <Searchbar.Portal>
12
+ * <Searchbar.Overlay />
13
+ * <Searchbar.Content>
14
+ * <Searchbar.Input />
15
+ * <Searchbar.List>…</Searchbar.List>
16
+ * </Searchbar.Content>
17
+ * </Searchbar.Portal>
18
+ * </Searchbar.Root>
19
+ * ```
20
+ */
21
+ export const Portal = ({ container, children }) => {
22
+ const [mounted, setMounted] = React.useState(false);
23
+ React.useEffect(() => {
24
+ setMounted(true);
25
+ }, []);
26
+ if (!mounted)
27
+ return null;
28
+ return createPortal(children, container ?? document.body);
29
+ };
30
+ Portal.displayName = "Searchbar.Portal";
@@ -0,0 +1,116 @@
1
+ import * as React from "react";
2
+ export type SearchbarRootProps = {
3
+ /** Controlled open state */
4
+ open?: boolean;
5
+ defaultOpen?: boolean;
6
+ onOpenChange?: (open: boolean) => void;
7
+ /** Controlled search query */
8
+ search?: string;
9
+ defaultSearch?: string;
10
+ onSearchChange?: (search: string) => void;
11
+ /** Controlled selected item value */
12
+ value?: string | null;
13
+ defaultValue?: string | null;
14
+ onValueChange?: (value: string | null) => void;
15
+ /** Whether a search is currently in progress */
16
+ isLoading?: boolean;
17
+ /** Current error, if any */
18
+ error?: unknown;
19
+ /**
20
+ * Global keyboard shortcut.
21
+ * Use `"mod+k"` for ⌘K on Mac / Ctrl+K elsewhere, or `"/"` for the widely used search shortcut.
22
+ * Supports: `mod`, `ctrl`, `shift`, `alt` modifiers + any key (e.g. `"mod+k"`, `"/"`).
23
+ *
24
+ * @example `hotkey="mod+k"` — command palette (toggle modal)
25
+ * @example `hotkey="/"` with `hotkeyBehavior="focus"` — focus input (inline searchbar)
26
+ */
27
+ hotkey?: string;
28
+ /**
29
+ * What the hotkey does: `"toggle"` (default) opens/closes the modal; `"focus"` focuses the input.
30
+ * Use `"focus"` for inline Searchbar so the hotkey focuses the input instead of toggling.
31
+ */
32
+ hotkeyBehavior?: "focus" | "toggle";
33
+ /** Called when an item is selected. Receives the item's `value`. */
34
+ onSelect?: (value: string) => void;
35
+ children: React.ReactNode;
36
+ className?: string;
37
+ asChild?: boolean;
38
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect">;
39
+ /**
40
+ * Root of the Searchbar component tree. Manages all search state and provides
41
+ * it to child primitives via an external store (`useSyncExternalStore`).
42
+ *
43
+ * Supports fully controlled, fully uncontrolled, and mixed modes for `open`,
44
+ * `search`, and `value`. Handles keyboard navigation, selection, escape
45
+ * behavior, outside click close, and portal-aware focus boundaries.
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * <Searchbar.Root onSelect={(value) => console.log(value)}>
50
+ * <Searchbar.Input />
51
+ * <Searchbar.List>
52
+ * {items.map((item) => (
53
+ * <Searchbar.Item key={item.id} value={item.id}>{item.title}</Searchbar.Item>
54
+ * ))}
55
+ * <Searchbar.Empty>No results</Searchbar.Empty>
56
+ * </Searchbar.List>
57
+ * </Searchbar.Root>
58
+ * ```
59
+ *
60
+ * @example Command palette
61
+ * ```tsx
62
+ * <Searchbar.Root hotkey="mod+k" className="etoile-search">
63
+ * <Searchbar.Trigger>
64
+ * <Searchbar.Icon />
65
+ * Search paintings…
66
+ * <Searchbar.Kbd />
67
+ * </Searchbar.Trigger>
68
+ * <Searchbar.Portal>
69
+ * <Searchbar.Overlay />
70
+ * <Searchbar.Content aria-label="Search paintings">
71
+ * <Searchbar.Input />
72
+ * <Searchbar.List>
73
+ * <Searchbar.Item value="starry-night">The Starry Night</Searchbar.Item>
74
+ * </Searchbar.List>
75
+ * </Searchbar.Content>
76
+ * </Searchbar.Portal>
77
+ * </Searchbar.Root>
78
+ * ```
79
+ */
80
+ export declare const Root: React.ForwardRefExoticComponent<{
81
+ /** Controlled open state */
82
+ open?: boolean;
83
+ defaultOpen?: boolean;
84
+ onOpenChange?: (open: boolean) => void;
85
+ /** Controlled search query */
86
+ search?: string;
87
+ defaultSearch?: string;
88
+ onSearchChange?: (search: string) => void;
89
+ /** Controlled selected item value */
90
+ value?: string | null;
91
+ defaultValue?: string | null;
92
+ onValueChange?: (value: string | null) => void;
93
+ /** Whether a search is currently in progress */
94
+ isLoading?: boolean;
95
+ /** Current error, if any */
96
+ error?: unknown;
97
+ /**
98
+ * Global keyboard shortcut.
99
+ * Use `"mod+k"` for ⌘K on Mac / Ctrl+K elsewhere, or `"/"` for the widely used search shortcut.
100
+ * Supports: `mod`, `ctrl`, `shift`, `alt` modifiers + any key (e.g. `"mod+k"`, `"/"`).
101
+ *
102
+ * @example `hotkey="mod+k"` — command palette (toggle modal)
103
+ * @example `hotkey="/"` with `hotkeyBehavior="focus"` — focus input (inline searchbar)
104
+ */
105
+ hotkey?: string;
106
+ /**
107
+ * What the hotkey does: `"toggle"` (default) opens/closes the modal; `"focus"` focuses the input.
108
+ * Use `"focus"` for inline Searchbar so the hotkey focuses the input instead of toggling.
109
+ */
110
+ hotkeyBehavior?: "focus" | "toggle";
111
+ /** Called when an item is selected. Receives the item's `value`. */
112
+ onSelect?: (value: string) => void;
113
+ children: React.ReactNode;
114
+ className?: string;
115
+ asChild?: boolean;
116
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect"> & React.RefAttributes<HTMLDivElement>>;