@ctrl-ui/react 0.8.10 → 0.8.12

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 (98) hide show
  1. package/dist/action-bar.js +2 -2
  2. package/dist/activity.js +1 -1
  3. package/dist/blocks/design-canvas-inspector.js +2 -2
  4. package/dist/blocks/design-canvas.js +2 -2
  5. package/dist/blocks/discussion.js +1 -1
  6. package/dist/blocks/file-explorer.js +2 -2
  7. package/dist/blocks/settings.js +3 -3
  8. package/dist/chat-composer-editor/extensions/mention.js +1 -1
  9. package/dist/chat-composer.js +1 -1
  10. package/dist/chat-layout.js +1 -1
  11. package/dist/context.js +1 -1
  12. package/dist/dynamic-notification.js +1 -1
  13. package/dist/email/theme.js +1 -1
  14. package/dist/filter-bar/parts.js +1 -1
  15. package/dist/filter-bar.js +1 -1
  16. package/dist/inline-citation.js +1 -1
  17. package/dist/knob-contracts/emoji-picker-knobs.d.ts +1 -1
  18. package/dist/knob-contracts/emoji-picker-knobs.js +1 -0
  19. package/dist/knob-contracts/icon-picker-knobs.d.ts +4 -0
  20. package/dist/knob-contracts/icon-picker-knobs.js +9 -0
  21. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  22. package/dist/knob-contracts/markdown-knobs.js +3 -1
  23. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  24. package/dist/knob-contracts/tabs-knobs.js +2 -0
  25. package/dist/lib/phone-input-format.js +1 -1
  26. package/dist/skin.d.ts +2 -0
  27. package/dist/styles/index.css +3 -1
  28. package/dist/styles/recipes/accordion.css +13 -1
  29. package/dist/styles/recipes/badge.css +1 -0
  30. package/dist/styles/recipes/button.css +20 -4
  31. package/dist/styles/recipes/code.css +0 -15
  32. package/dist/styles/recipes/collapsible.css +15 -0
  33. package/dist/styles/recipes/emoji-picker.css +84 -1
  34. package/dist/styles/recipes/field.css +15 -2
  35. package/dist/styles/recipes/icon-picker.css +101 -0
  36. package/dist/styles/recipes/markdown.css +66 -0
  37. package/dist/styles/recipes/popup-motion.css +10 -0
  38. package/dist/styles/recipes/scroll-area.css +53 -27
  39. package/dist/styles/recipes/tabs-motion.css +99 -0
  40. package/dist/styles/recipes/tabs.css +38 -10
  41. package/dist/styles/recipes/tree.css +13 -1
  42. package/dist/ui/alert-dialog.js +1 -1
  43. package/dist/ui/alert.js +1 -1
  44. package/dist/ui/autocomplete.js +1 -1
  45. package/dist/ui/breadcrumb.js +1 -1
  46. package/dist/ui/button.d.ts +6 -3
  47. package/dist/ui/button.js +10 -6
  48. package/dist/ui/card.js +1 -1
  49. package/dist/ui/code-diff.js +1 -1
  50. package/dist/ui/code.js +2 -2
  51. package/dist/ui/collapsible.js +1 -2
  52. package/dist/ui/color-picker.js +1 -1
  53. package/dist/ui/combobox.js +1 -1
  54. package/dist/ui/context-menu.js +1 -1
  55. package/dist/ui/dialog.js +1 -1
  56. package/dist/ui/drawer.js +1 -1
  57. package/dist/ui/dropdown-menu.js +1 -1
  58. package/dist/ui/emoji-icon-picker.d.ts +13 -0
  59. package/dist/ui/emoji-icon-picker.js +50 -0
  60. package/dist/ui/emoji-picker.d.ts +14 -3
  61. package/dist/ui/emoji-picker.js +194 -15
  62. package/dist/ui/empty.js +1 -1
  63. package/dist/ui/hover-card.js +1 -1
  64. package/dist/ui/icon-picker.d.ts +35 -0
  65. package/dist/ui/icon-picker.js +140 -0
  66. package/dist/ui/input-group.js +1 -1
  67. package/dist/ui/item.js +1 -1
  68. package/dist/ui/markdown-elements.js +6 -10
  69. package/dist/ui/markdown.d.ts +6 -5
  70. package/dist/ui/markdown.js +12 -4
  71. package/dist/ui/menubar.js +1 -1
  72. package/dist/ui/morphing-panel.js +1 -1
  73. package/dist/ui/navigation-menu.js +1 -1
  74. package/dist/ui/phone-input.js +2 -2
  75. package/dist/ui/popover.d.ts +3 -0
  76. package/dist/ui/popover.js +37 -2
  77. package/dist/ui/rich-tooltip.js +1 -1
  78. package/dist/ui/scroll-area.d.ts +4 -1
  79. package/dist/ui/scroll-area.js +21 -6
  80. package/dist/ui/select.js +1 -1
  81. package/dist/ui/sheet.js +1 -1
  82. package/dist/ui/sidebar-menu.js +1 -1
  83. package/dist/ui/sidebar-provider.js +2 -2
  84. package/dist/ui/sidebar-resize-rail.js +1 -1
  85. package/dist/ui/sidebar.js +1 -1
  86. package/dist/ui/tabs.d.ts +4 -0
  87. package/dist/ui/tabs.js +69 -23
  88. package/dist/ui/timeline.js +1 -1
  89. package/dist/ui/toast.js +1 -1
  90. package/dist/ui/toggle.d.ts +1 -1
  91. package/dist/ui/toggle.js +2 -1
  92. package/dist/ui/toolbar.d.ts +1 -1
  93. package/dist/ui/toolbar.js +1 -1
  94. package/dist/ui/tooltip.js +1 -1
  95. package/dist/ui/tree.js +1 -1
  96. package/dist/ui/trigger-menu.js +1 -1
  97. package/dist/user-ask.js +2 -2
  98. package/package.json +1 -1
package/dist/ui/button.js CHANGED
@@ -1,14 +1,15 @@
1
1
  "use client";
2
- import { skinAdornment } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { skinAdornment } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { Children, Fragment, createContext, isValidElement, useContext } from "react";
6
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
6
  import { Button as Button$1 } from "@base-ui/react/button";
8
7
  import { useRender } from "@base-ui/react/use-render";
8
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/ui/button.tsx
10
10
  const ButtonTrackContext = createContext(false);
11
11
  const buttonShapes = ["default", "circle"];
12
+ const buttonLayouts = ["inline", "stacked"];
12
13
  const buttonStructureClasses = "isolate inline-flex shrink-0 items-center justify-center overflow-visible whitespace-nowrap";
13
14
  const buttonTrackStructureClasses = "data-[track=hover]:relative data-[track=hover]:isolate [&_[data-track-item]]:[isolation:auto]";
14
15
  const buttonContentClasses = "relative z-[1] inline-flex min-w-0 items-center justify-center gap-[inherit]";
@@ -36,7 +37,7 @@ function ButtonBody({ layer, wrap = true, children }) {
36
37
  const content = wrapButtonText(children);
37
38
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [layer, wrapsContent ? /* @__PURE__ */ jsx(ButtonContent, { children: content }) : content] });
38
39
  }
39
- function Button({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", type = "button", disabled, render, nativeButton, className, children, ...props }) {
40
+ function Button({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", layout = "inline", type = "button", disabled, render, nativeButton, className, children, ...props }) {
40
41
  const skin = useSkin();
41
42
  const layer = skinAdornment(skin, "button", "layer", {
42
43
  variant,
@@ -54,6 +55,7 @@ function Button({ variant = "quiet", size = "sm", tone = "neutral", active = fal
54
55
  "data-active": active ? "true" : void 0,
55
56
  "data-icon-only": iconOnly ? "true" : void 0,
56
57
  "data-shape": shape,
58
+ "data-layout": layout,
57
59
  "data-variant": variant,
58
60
  "data-tone": tone,
59
61
  "data-size": size,
@@ -68,7 +70,7 @@ function Button({ variant = "quiet", size = "sm", tone = "neutral", active = fal
68
70
  })
69
71
  });
70
72
  }
71
- function ButtonLink({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", render, className, children, ...props }) {
73
+ function ButtonLink({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", layout = "inline", render, className, children, ...props }) {
72
74
  const skin = useSkin();
73
75
  const tracksHover = useContext(ButtonTrackContext);
74
76
  return useRender({
@@ -84,6 +86,7 @@ function ButtonLink({ variant = "quiet", size = "sm", tone = "neutral", active =
84
86
  "data-active": active ? "true" : void 0,
85
87
  "data-icon-only": iconOnly ? "true" : void 0,
86
88
  "data-shape": shape,
89
+ "data-layout": layout,
87
90
  "data-variant": variant,
88
91
  "data-tone": tone,
89
92
  "data-size": size,
@@ -98,7 +101,7 @@ function ButtonLink({ variant = "quiet", size = "sm", tone = "neutral", active =
98
101
  }
99
102
  });
100
103
  }
101
- function ButtonLabel({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", className, children, ...props }) {
104
+ function ButtonLabel({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", layout = "inline", className, children, ...props }) {
102
105
  const skin = useSkin();
103
106
  const tracksHover = useContext(ButtonTrackContext);
104
107
  return /* @__PURE__ */ jsx("label", {
@@ -111,6 +114,7 @@ function ButtonLabel({ variant = "quiet", size = "sm", tone = "neutral", active
111
114
  "data-active": active ? "true" : void 0,
112
115
  "data-icon-only": iconOnly ? "true" : void 0,
113
116
  "data-shape": shape,
117
+ "data-layout": layout,
114
118
  "data-variant": variant,
115
119
  "data-tone": tone,
116
120
  "data-size": size,
@@ -125,4 +129,4 @@ function ButtonLabel({ variant = "quiet", size = "sm", tone = "neutral", active
125
129
  });
126
130
  }
127
131
  //#endregion
128
- export { Button, ButtonLabel, ButtonLink, ButtonTrackContext, buttonContentClasses, buttonShapes, buttonStructureClasses, buttonTrackStructureClasses };
132
+ export { Button, ButtonLabel, ButtonLink, ButtonTrackContext, buttonContentClasses, buttonLayouts, buttonShapes, buttonStructureClasses, buttonTrackStructureClasses };
package/dist/ui/card.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { jsx } from "react/jsx-runtime";
4
3
  import { useRender } from "@base-ui/react/use-render";
4
+ import { jsx } from "react/jsx-runtime";
5
5
  //#region src/ui/card.tsx
6
6
  function Card({ variant = "default", className, ...props }) {
7
7
  return /* @__PURE__ */ jsx("div", {
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { highlightToTokens, mergeCodeTokenLineWithEmphasis } from "../lib/code-tokens.js";
4
3
  import { ScrollArea } from "./scroll-area.js";
4
+ import { highlightToTokens, mergeCodeTokenLineWithEmphasis } from "../lib/code-tokens.js";
5
5
  import { CodeCopy, CodeFloatingCopy, CodeTokenLine } from "./code.js";
6
6
  import { buildDiffFromFiles, buildDiffFromPatch, diffRunEnd } from "../lib/diff.js";
7
7
  import { useEffect, useMemo, useRef, useState } from "react";
package/dist/ui/code.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
+ import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard.js";
2
3
  import { cn } from "../lib/cn.js";
3
4
  import { Button } from "./button.js";
4
5
  import { LiveStatus } from "./live-status.js";
5
- import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard.js";
6
- import { highlightToTokens } from "../lib/code-tokens.js";
7
6
  import { ScrollArea } from "./scroll-area.js";
7
+ import { highlightToTokens } from "../lib/code-tokens.js";
8
8
  import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
9
9
  import { Children, createContext, isValidElement, useContext, useEffect, useMemo, useRef, useState } from "react";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { jsx } from "react/jsx-runtime";
4
3
  import { useRender } from "@base-ui/react/use-render";
4
+ import { jsx } from "react/jsx-runtime";
5
5
  import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
6
6
  //#region src/ui/collapsible.tsx
7
7
  function CollapsibleTriggerElement({ triggerProps, open, render, className, children }) {
@@ -52,7 +52,6 @@ function CollapsibleTrigger({ render, className, children, ...props }) {
52
52
  }
53
53
  function CollapsibleContent({ className, children, ...props }) {
54
54
  return /* @__PURE__ */ jsx(Collapsible$1.Panel, {
55
- className: "h-[var(--collapsible-panel-height)] overflow-hidden data-ending-style:h-0 data-starting-style:h-0",
56
55
  ...props,
57
56
  render: (renderProps, state) => /* @__PURE__ */ jsx("div", {
58
57
  "data-control-ui": "collapsible",
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { Button } from "./button.js";
6
6
  import { Input } from "./input.js";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { ScrollArea } from "./scroll-area.js";
6
6
  import { popupItemStructureClasses } from "../surface-variants.js";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { popupItemStructureClasses } from "../surface-variants.js";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
package/dist/ui/dialog.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute, skinAdornment } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute, skinAdornment } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { Button } from "./button.js";
6
6
  import { PopupCloseButton } from "../popup-parts.js";
package/dist/ui/drawer.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { createContext, useContext } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { popupItemStructureClasses } from "../surface-variants.js";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -0,0 +1,13 @@
1
+ import { EmojiPicker } from "./emoji-picker.js";
2
+ import { IconPicker } from "./icon-picker.js";
3
+ import { Tabs, TabsList } from "./tabs.js";
4
+ import { ComponentProps } from "react";
5
+ //#region src/ui/emoji-icon-picker.d.ts
6
+ export declare function EmojiIconPicker({ children, defaultValue, ...props }: ComponentProps<typeof Tabs<"emoji" | "icons">>): import("react").JSX.Element;
7
+ export declare function EmojiIconPickerTabs({ emojiLabel, iconsLabel, ...props }: Omit<ComponentProps<typeof TabsList>, "children"> & {
8
+ emojiLabel?: string;
9
+ iconsLabel?: string;
10
+ }): import("react").JSX.Element;
11
+ export declare function EmojiIconPickerEmoji(props: ComponentProps<typeof EmojiPicker>): import("react").JSX.Element;
12
+ export declare function EmojiIconPickerIcons(props: ComponentProps<typeof IconPicker>): import("react").JSX.Element;
13
+ //#endregion
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { PopoverViewport } from "./popover.js";
3
+ import { EmojiPicker } from "./emoji-picker.js";
4
+ import { IconPicker } from "./icon-picker.js";
5
+ import { Tabs, TabsList, TabsPanel, TabsTab } from "./tabs.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/ui/emoji-icon-picker.tsx
8
+ function EmojiIconPicker({ children, defaultValue = "emoji", ...props }) {
9
+ return /* @__PURE__ */ jsx(Tabs, {
10
+ "data-control-ui": "emoji-icon-picker",
11
+ "data-control-family": "tabs",
12
+ "data-slot": "root",
13
+ defaultValue,
14
+ ...props,
15
+ children: /* @__PURE__ */ jsx(PopoverViewport, { children })
16
+ });
17
+ }
18
+ function EmojiIconPickerTabs({ emojiLabel = "Emoji", iconsLabel = "Icons", ...props }) {
19
+ return /* @__PURE__ */ jsxs(TabsList, {
20
+ size: "xs",
21
+ className: "mx-2 mt-2",
22
+ "aria-label": "Choose emoji or icon",
23
+ ...props,
24
+ children: [/* @__PURE__ */ jsx(TabsTab, {
25
+ value: "emoji",
26
+ children: emojiLabel
27
+ }), /* @__PURE__ */ jsx(TabsTab, {
28
+ value: "icons",
29
+ children: iconsLabel
30
+ })]
31
+ });
32
+ }
33
+ function EmojiIconPickerEmoji(props) {
34
+ return /* @__PURE__ */ jsx(TabsPanel, {
35
+ value: "emoji",
36
+ "data-slide": void 0,
37
+ className: "mt-0",
38
+ children: /* @__PURE__ */ jsx(EmojiPicker, { ...props })
39
+ });
40
+ }
41
+ function EmojiIconPickerIcons(props) {
42
+ return /* @__PURE__ */ jsx(TabsPanel, {
43
+ value: "icons",
44
+ "data-slide": void 0,
45
+ className: "mt-0",
46
+ children: /* @__PURE__ */ jsx(IconPicker, { ...props })
47
+ });
48
+ }
49
+ //#endregion
50
+ export { EmojiIconPicker, EmojiIconPickerEmoji, EmojiIconPickerIcons, EmojiIconPickerTabs };
@@ -1,16 +1,27 @@
1
1
  import { EmojiPickerKnobStyle } from "../knob-contracts/emoji-picker-knobs.js";
2
2
  import { CSSProperties, ComponentProps } from "react";
3
- import { EmojiPicker as EmojiPicker$1 } from "frimousse";
3
+ import { Emoji, EmojiPicker as EmojiPicker$1 } from "frimousse";
4
4
  //#region src/ui/emoji-picker.d.ts
5
5
  type EmojiPickerStyle = {
6
6
  style?: CSSProperties & EmojiPickerKnobStyle;
7
7
  };
8
8
  export type EmojiPickerProps = Omit<ComponentProps<typeof EmojiPicker$1.Root>, "style"> & EmojiPickerStyle;
9
- export declare function EmojiPicker({ className, columns, ...props }: EmojiPickerProps): import("react").JSX.Element;
9
+ export declare function EmojiPicker({ className, columns, style, ref, onEmojiSelect, resolveEmojiData, ...props }: EmojiPickerProps): import("react").JSX.Element;
10
+ export declare function EmojiPickerCategories({ "aria-label": ariaLabel, onKeyDown, ...props }: ComponentProps<"div">): import("react").JSX.Element | null;
11
+ export type EmojiPickerRecentProps = ComponentProps<"div"> & {
12
+ emojis: readonly Emoji[];
13
+ label?: string;
14
+ };
15
+ export type EmojiPickerReactionsProps = ComponentProps<"div"> & {
16
+ emojis: readonly Emoji[];
17
+ onEmojiSelect: (emoji: Emoji) => void;
18
+ };
19
+ export declare function EmojiPickerReactions({ emojis, onEmojiSelect, "aria-label": ariaLabel, onKeyDown, ...props }: EmojiPickerReactionsProps): import("react").JSX.Element;
20
+ export declare function EmojiPickerRecent({ emojis, label, ...props }: EmojiPickerRecentProps): import("react").JSX.Element | null;
10
21
  export type EmojiPickerSearchProps = Omit<ComponentProps<typeof EmojiPicker$1.Search>, "style"> & EmojiPickerStyle;
11
22
  export declare function EmojiPickerSearch({ className, placeholder, "aria-label": ariaLabel, ...props }: EmojiPickerSearchProps): import("react").JSX.Element;
12
23
  export type EmojiPickerContentProps = Omit<ComponentProps<typeof EmojiPicker$1.Viewport>, "style"> & EmojiPickerStyle;
13
- export declare function EmojiPickerContent({ className, ...props }: EmojiPickerContentProps): import("react").JSX.Element;
24
+ export declare function EmojiPickerContent({ className, ref, ...props }: EmojiPickerContentProps): import("react").JSX.Element;
14
25
  export type EmojiPickerFooterProps = Omit<ComponentProps<"div">, "style" | "children"> & EmojiPickerStyle & {
15
26
  placeholder?: string;
16
27
  };
@@ -1,18 +1,180 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Spinner } from "./spinner.js";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
- import { SearchIcon } from "lucide-react";
6
- import { EmojiPicker as EmojiPicker$1 } from "frimousse";
4
+ import { Toolbar } from "@base-ui/react/toolbar";
5
+ import { createContext, useContext, useImperativeHandle, useRef, useState } from "react";
6
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
+ import { FlagIcon, HandIcon, HashIcon, HeartIcon, LeafIcon, LightbulbIcon, PizzaIcon, PlaneIcon, SearchIcon, SmileIcon, VolleyballIcon } from "lucide-react";
8
+ import { EmojiPicker as EmojiPicker$1, defaultEmojiDataResolver } from "frimousse";
7
9
  //#region src/ui/emoji-picker.tsx
8
- function EmojiPicker({ className, columns = 9, ...props }) {
9
- return /* @__PURE__ */ jsx(EmojiPicker$1.Root, {
10
+ const EmojiPickerContext = createContext(null);
11
+ function useEmojiPicker() {
12
+ const picker = useContext(EmojiPickerContext);
13
+ if (!picker) throw new Error("EmojiPicker parts must be rendered inside <EmojiPicker>.");
14
+ return picker;
15
+ }
16
+ function EmojiPicker({ className, columns = 8, style, ref, onEmojiSelect, resolveEmojiData = defaultEmojiDataResolver, ...props }) {
17
+ const rootRef = useRef(null);
18
+ const viewportRef = useRef(null);
19
+ const [loadState, setLoadState] = useState({ status: "loading" });
20
+ const [retryAttempt, setRetryAttempt] = useState(0);
21
+ useImperativeHandle(ref, () => {
22
+ if (!rootRef.current) throw new Error("EmojiPicker root is not mounted.");
23
+ return rootRef.current;
24
+ });
25
+ const loadEmojiData = async (locale, options) => {
26
+ setLoadState({ status: "loading" });
27
+ try {
28
+ const data = await resolveEmojiData(locale, options);
29
+ if (!options.signal?.aborted) setLoadState({
30
+ status: "ready",
31
+ data
32
+ });
33
+ return data;
34
+ } catch (error) {
35
+ if (!options.signal?.aborted) setLoadState({ status: "error" });
36
+ throw error;
37
+ }
38
+ };
39
+ const retryEmojiData = () => {
40
+ setLoadState({ status: "loading" });
41
+ setRetryAttempt((attempt) => attempt + 1);
42
+ };
43
+ const selectEmoji = (emoji) => onEmojiSelect?.(emoji);
44
+ const pickerStyle = {
45
+ "--_emoji-picker-columns": columns,
46
+ ...style
47
+ };
48
+ return /* @__PURE__ */ jsx(EmojiPickerContext.Provider, {
49
+ value: {
50
+ columns,
51
+ rootRef,
52
+ viewportRef,
53
+ loadState,
54
+ retryEmojiData,
55
+ selectEmoji
56
+ },
57
+ children: /* @__PURE__ */ jsx(EmojiPicker$1.Root, {
58
+ "data-control-ui": "emoji-picker",
59
+ "data-control-family": "emoji-picker",
60
+ "data-slot": "root",
61
+ columns,
62
+ className: cn("isolate flex flex-col", className),
63
+ ...props,
64
+ ref: rootRef,
65
+ style: pickerStyle,
66
+ onEmojiSelect: selectEmoji,
67
+ resolveEmojiData: loadEmojiData
68
+ }, retryAttempt)
69
+ });
70
+ }
71
+ function stopPickerNavigation(event) {
72
+ if (event.key !== "Escape" && event.key !== "Tab") event.stopPropagation();
73
+ }
74
+ const categoryIcons = /* @__PURE__ */ new Map([
75
+ [0, SmileIcon],
76
+ [1, HandIcon],
77
+ [3, LeafIcon],
78
+ [4, PizzaIcon],
79
+ [5, PlaneIcon],
80
+ [6, VolleyballIcon],
81
+ [7, LightbulbIcon],
82
+ [8, HeartIcon],
83
+ [9, FlagIcon]
84
+ ]);
85
+ function emojiCategoryRows(emojiData, columns) {
86
+ let rowOffset = 0;
87
+ return emojiData.categories.flatMap((category) => {
88
+ const emojiCount = emojiData.emojis.filter((emoji) => emoji.category === category.index).length;
89
+ if (emojiCount === 0) return [];
90
+ const startRow = rowOffset;
91
+ rowOffset += Math.ceil(emojiCount / columns);
92
+ return [{
93
+ ...category,
94
+ startRow
95
+ }];
96
+ });
97
+ }
98
+ function EmojiPickerCategories({ "aria-label": ariaLabel = "Emoji categories", onKeyDown, ...props }) {
99
+ const { columns, loadState, rootRef, viewportRef } = useEmojiPicker();
100
+ if (loadState.status !== "ready") return null;
101
+ const categories = emojiCategoryRows(loadState.data, columns);
102
+ const scrollToCategory = (startRow, categoryIndex) => {
103
+ const root = rootRef.current;
104
+ const viewport = viewportRef.current;
105
+ if (!root || !viewport) return;
106
+ const metrics = getComputedStyle(root);
107
+ const rowHeight = Number.parseFloat(metrics.getPropertyValue("--frimousse-row-height"));
108
+ const headerHeight = Number.parseFloat(metrics.getPropertyValue("--frimousse-category-header-height"));
109
+ viewport.scrollTo({ top: startRow * rowHeight + categoryIndex * headerHeight });
110
+ };
111
+ return /* @__PURE__ */ jsx(Toolbar.Root, {
112
+ "data-control-ui": "emoji-picker",
113
+ "data-control-family": "emoji-picker",
114
+ "data-slot": "categories",
115
+ "aria-label": ariaLabel,
116
+ ...props,
117
+ onKeyDown: (event) => {
118
+ onKeyDown?.(event);
119
+ stopPickerNavigation(event);
120
+ },
121
+ children: categories.map((category, categoryIndex) => {
122
+ const CategoryIcon = categoryIcons.get(category.index) ?? HashIcon;
123
+ return /* @__PURE__ */ jsx(Toolbar.Button, {
124
+ type: "button",
125
+ "aria-label": category.label,
126
+ "data-control-ui": "emoji-picker",
127
+ "data-control-family": "emoji-picker",
128
+ "data-slot": "category",
129
+ onClick: () => scrollToCategory(category.startRow, categoryIndex),
130
+ children: /* @__PURE__ */ jsx(CategoryIcon, { "aria-hidden": "true" })
131
+ }, category.index);
132
+ })
133
+ });
134
+ }
135
+ function EmojiPickerReactions({ emojis, onEmojiSelect, "aria-label": ariaLabel = "Reactions", onKeyDown, ...props }) {
136
+ return /* @__PURE__ */ jsx(Toolbar.Root, {
137
+ "aria-label": ariaLabel,
10
138
  "data-control-ui": "emoji-picker",
11
139
  "data-control-family": "emoji-picker",
12
140
  "data-slot": "root",
13
- columns,
14
- className: cn("isolate flex h-72 w-fit flex-col", className),
15
- ...props
141
+ "data-variant": "reactions",
142
+ ...props,
143
+ onKeyDown: (event) => {
144
+ onKeyDown?.(event);
145
+ stopPickerNavigation(event);
146
+ },
147
+ children: emojis.map((emoji) => /* @__PURE__ */ jsx(Toolbar.Button, {
148
+ type: "button",
149
+ "aria-label": emoji.label,
150
+ "data-control-ui": "emoji-picker",
151
+ "data-control-family": "emoji-picker",
152
+ "data-slot": "emoji",
153
+ onClick: () => onEmojiSelect(emoji),
154
+ children: emoji.emoji
155
+ }, emoji.emoji))
156
+ });
157
+ }
158
+ function EmojiPickerRecent({ emojis, label = "Recently used", ...props }) {
159
+ const picker = useEmojiPicker();
160
+ if (emojis.length === 0) return null;
161
+ return /* @__PURE__ */ jsxs("div", {
162
+ "data-control-ui": "emoji-picker",
163
+ "data-control-family": "emoji-picker",
164
+ "data-slot": "recent",
165
+ ...props,
166
+ children: [/* @__PURE__ */ jsx("div", {
167
+ "data-control-ui": "emoji-picker",
168
+ "data-control-family": "emoji-picker",
169
+ "data-slot": "recent-label",
170
+ children: label
171
+ }), /* @__PURE__ */ jsx(EmojiPickerReactions, {
172
+ "data-control-ui": "emoji-picker",
173
+ "data-slot": "recent-grid",
174
+ emojis: emojis.slice(0, picker.columns),
175
+ "aria-label": label,
176
+ onEmojiSelect: picker.selectEmoji
177
+ })]
16
178
  });
17
179
  }
18
180
  function EmojiPickerSearch({ className, placeholder = "Search emoji…", "aria-label": ariaLabel = "Search emoji", ...props }) {
@@ -49,6 +211,7 @@ function EmojiPickerRow({ children, className, ...props }) {
49
211
  }
50
212
  function EmojiPickerEmoji({ emoji, className, ...props }) {
51
213
  return /* @__PURE__ */ jsx("button", {
214
+ type: "button",
52
215
  "data-control-ui": "emoji-picker",
53
216
  "data-control-family": "emoji-picker",
54
217
  "data-slot": "emoji",
@@ -68,14 +231,30 @@ function EmojiPickerCategoryHeader({ category, className, ...props }) {
68
231
  children: category.label
69
232
  });
70
233
  }
71
- function EmojiPickerContent({ className, ...props }) {
72
- return /* @__PURE__ */ jsxs(EmojiPicker$1.Viewport, {
234
+ function EmojiPickerContent({ className, ref, ...props }) {
235
+ const { viewportRef, loadState, retryEmojiData } = useEmojiPicker();
236
+ useImperativeHandle(ref, () => {
237
+ if (!viewportRef.current) throw new Error("EmojiPicker viewport is not mounted.");
238
+ return viewportRef.current;
239
+ });
240
+ return /* @__PURE__ */ jsx(EmojiPicker$1.Viewport, {
73
241
  "data-control-ui": "emoji-picker",
74
242
  "data-control-family": "emoji-picker",
75
243
  "data-slot": "content",
76
- className: cn("relative flex-1 outline-none", className),
244
+ className: cn("relative min-h-0 flex-1 outline-none", className),
77
245
  ...props,
78
- children: [
246
+ ref: viewportRef,
247
+ children: loadState.status === "error" ? /* @__PURE__ */ jsxs("div", {
248
+ "data-control-ui": "emoji-picker",
249
+ "data-control-family": "emoji-picker",
250
+ "data-slot": "error",
251
+ role: "alert",
252
+ children: [/* @__PURE__ */ jsx("span", { children: "Couldn’t load emoji." }), /* @__PURE__ */ jsx("button", {
253
+ type: "button",
254
+ onClick: retryEmojiData,
255
+ children: "Try again"
256
+ })]
257
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
79
258
  /* @__PURE__ */ jsx(EmojiPicker$1.Loading, {
80
259
  "data-control-ui": "emoji-picker",
81
260
  "data-control-family": "emoji-picker",
@@ -101,7 +280,7 @@ function EmojiPickerContent({ className, ...props }) {
101
280
  CategoryHeader: EmojiPickerCategoryHeader
102
281
  }
103
282
  })
104
- ]
283
+ ] })
105
284
  });
106
285
  }
107
286
  function EmojiPickerFooter({ className, placeholder = "Pick an emoji…", ...props }) {
@@ -111,7 +290,7 @@ function EmojiPickerFooter({ className, placeholder = "Pick an emoji…", ...pro
111
290
  "data-slot": "footer",
112
291
  className: cn("flex min-w-0 items-center", className),
113
292
  ...props,
114
- children: /* @__PURE__ */ jsx(EmojiPicker$1.ActiveEmoji, { children: ({ emoji }) => emoji ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
293
+ children: /* @__PURE__ */ jsx(EmojiPicker$1.ActiveEmoji, { children: ({ emoji }) => emoji ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
115
294
  "data-control-ui": "emoji-picker",
116
295
  "data-control-family": "emoji-picker",
117
296
  "data-slot": "footer-emoji",
@@ -133,4 +312,4 @@ function EmojiPickerFooter({ className, placeholder = "Pick an emoji…", ...pro
133
312
  });
134
313
  }
135
314
  //#endregion
136
- export { EmojiPicker, EmojiPickerContent, EmojiPickerFooter, EmojiPickerSearch };
315
+ export { EmojiPicker, EmojiPickerCategories, EmojiPickerContent, EmojiPickerFooter, EmojiPickerReactions, EmojiPickerRecent, EmojiPickerSearch };
package/dist/ui/empty.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { jsx } from "react/jsx-runtime";
4
3
  import { useRender } from "@base-ui/react/use-render";
4
+ import { jsx } from "react/jsx-runtime";
5
5
  //#region src/ui/empty.tsx
6
6
  function Empty({ className, ...props }) {
7
7
  return /* @__PURE__ */ jsx("div", {
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { controlEffectsAttribute } from "../skin.js";
3
2
  import { cn } from "../lib/cn.js";
3
+ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { PreviewCard } from "@base-ui/react/preview-card";
@@ -0,0 +1,35 @@
1
+ import { ControlledChoice } from "../control-props.js";
2
+ import { IconPickerKnobStyle } from "../knob-contracts/icon-picker-knobs.js";
3
+ import { Input } from "./input.js";
4
+ import { CSSProperties, ComponentProps, ReactNode } from "react";
5
+ //#region src/ui/icon-picker.d.ts
6
+ export type IconPickerItem = {
7
+ value: string;
8
+ label: string;
9
+ icon: ReactNode;
10
+ keywords?: readonly string[];
11
+ };
12
+ export type IconPickerColor = {
13
+ value: string;
14
+ label: string;
15
+ color: string;
16
+ };
17
+ type IconPickerStyle = {
18
+ style?: CSSProperties & IconPickerKnobStyle;
19
+ };
20
+ export type IconPickerProps = Omit<ComponentProps<"div">, "style" | "defaultValue" | "onChange"> & IconPickerStyle & ControlledChoice & {
21
+ items: readonly IconPickerItem[];
22
+ columns?: number;
23
+ };
24
+ export declare function IconPicker({ items, columns, value, defaultValue, onValueChange, className, style, children, ...props }: IconPickerProps): import("react").JSX.Element;
25
+ export type IconPickerSearchProps = Omit<ComponentProps<typeof Input>, "value" | "defaultValue" | "onValueChange">;
26
+ export declare function IconPickerSearch({ placeholder, "aria-label": ariaLabel, className, onChange, ...props }: IconPickerSearchProps): import("react").JSX.Element;
27
+ export type IconPickerContentProps = Omit<ComponentProps<"div">, "style" | "children"> & IconPickerStyle & {
28
+ empty?: ReactNode;
29
+ };
30
+ export declare function IconPickerContent({ className, empty, "aria-label": ariaLabel, ...props }: IconPickerContentProps): import("react").JSX.Element;
31
+ export type IconPickerColorsProps = Omit<ComponentProps<"div">, "style" | "defaultValue" | "onChange" | "children"> & IconPickerStyle & ControlledChoice & {
32
+ colors: readonly IconPickerColor[];
33
+ };
34
+ export declare function IconPickerColors({ colors, "aria-label": ariaLabel, ...props }: IconPickerColorsProps): import("react").JSX.Element;
35
+ //#endregion