cherry-styled-components 0.2.15 → 0.2.17

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.
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ "use client";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { DynamicIcon as DynamicIcon$1, iconNames } from "lucide-react/dynamic";
5
+ //#region src/lib/icon-dynamic.tsx
6
+ var KEBAB_NAMES = new Set(iconNames);
7
+ function toPascal(name) {
8
+ return name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
9
+ }
10
+ var KEBAB_BY_PASCAL = new Map(iconNames.map((name) => [toPascal(name), name]));
11
+ function resolveIconName(name) {
12
+ if (KEBAB_NAMES.has(name)) return name;
13
+ return KEBAB_BY_PASCAL.get(toPascal(name)) ?? null;
14
+ }
15
+ /**
16
+ * Renders a lucide icon chosen at runtime (CMS content, user settings). The
17
+ * glyph is fetched on demand through `lucide-react/dynamic`, so it appears
18
+ * after hydration once its chunk has loaded, and never in server-rendered
19
+ * HTML. For any icon known when the code is written, use
20
+ * `<Icon icon={Glyph} />` from the main entry instead. In a `$icon`-style
21
+ * prop, pass it as an element: `$icon={<DynamicIcon name={n} />}`.
22
+ */ function DynamicIcon({ name, "aria-label": ariaLabel, ...props }) {
23
+ const iconName = resolveIconName(name);
24
+ if (!iconName) return null;
25
+ return /*#__PURE__*/ jsx(DynamicIcon$1, {
26
+ name: iconName,
27
+ ...props,
28
+ "aria-label": ariaLabel,
29
+ "aria-hidden": !ariaLabel,
30
+ role: ariaLabel ? "img" : void 0
31
+ });
32
+ }
33
+ //#endregion
34
+ export { DynamicIcon };
package/dist/icon.d.ts CHANGED
@@ -1,11 +1,45 @@
1
- import { icons } from 'lucide-react';
2
- export type IconProps = keyof typeof icons;
3
- interface Props {
4
- name: IconProps;
1
+ import { ComponentType, ReactElement } from 'react';
2
+ /**
3
+ * The props Cherry hands to an icon component. Every `lucide-react` export
4
+ * satisfies this, and so does any SVG component that takes a `size`.
5
+ */
6
+ export interface IconGlyphProps {
7
+ size?: number;
8
+ color?: string;
9
+ className?: string;
10
+ "aria-label"?: string;
11
+ "aria-hidden"?: boolean;
12
+ role?: "img";
13
+ }
14
+ /**
15
+ * An icon component: `import { ArrowRight } from "lucide-react"`, or a
16
+ * component of your own that accepts `IconGlyphProps`.
17
+ */
18
+ export type IconComponent = ComponentType<IconGlyphProps>;
19
+ /**
20
+ * What a component's `$icon`-style prop accepts: an `IconComponent`, which
21
+ * the component renders through `Icon` at the size it chooses, or a rendered
22
+ * element such as `<DynamicIcon name={n} />`, which is used as given.
23
+ */
24
+ export type IconSource = IconComponent | ReactElement;
25
+ export interface IconProps {
26
+ /** An imported lucide component, or any component taking `IconGlyphProps`. */
27
+ icon: IconComponent;
5
28
  color?: string;
6
29
  size?: number;
7
30
  className?: string;
8
31
  "aria-label"?: string;
9
32
  }
10
- declare const Icon: ({ name, color, size, className, "aria-label": ariaLabel, }: Props) => import("react").JSX.Element | null;
33
+ /**
34
+ * Renders one icon component. It costs exactly that glyph and renders on the
35
+ * server. Decorative unless labelled; a labelled glyph is announced as an
36
+ * image. For an icon chosen at runtime, see `DynamicIcon` in the
37
+ * `cherry-styled-components/dynamic` subpath.
38
+ */
39
+ declare const Icon: ({ icon: Glyph, color, size, className, "aria-label": ariaLabel, }: IconProps) => import("react").JSX.Element;
40
+ /**
41
+ * Renders an `IconSource`: an element as given, a component through `Icon`
42
+ * with `props`. For components that forward a `$icon`-style prop.
43
+ */
44
+ export declare function renderIcon(source: IconSource, props?: Omit<IconProps, "icon">): ReactElement;
11
45
  export { Icon };
package/dist/icon.js CHANGED
@@ -1,17 +1,29 @@
1
+ "use client";
2
+ "use client";
1
3
  import { jsx } from "react/jsx-runtime";
2
- import { icons } from "lucide-react";
4
+ import { isValidElement } from "react";
3
5
  //#region src/lib/icon.tsx
4
- var Icon = ({ name, color, size, className, "aria-label": ariaLabel }) => {
5
- const LucideIcon = icons[name];
6
- if (!LucideIcon) return null;
7
- return /*#__PURE__*/ jsx(LucideIcon, {
8
- color,
9
- size,
10
- className,
11
- "aria-label": ariaLabel,
12
- "aria-hidden": !ariaLabel,
13
- role: ariaLabel ? "img" : void 0
6
+ /**
7
+ * Renders one icon component. It costs exactly that glyph and renders on the
8
+ * server. Decorative unless labelled; a labelled glyph is announced as an
9
+ * image. For an icon chosen at runtime, see `DynamicIcon` in the
10
+ * `cherry-styled-components/dynamic` subpath.
11
+ */ var Icon = ({ icon: Glyph, color, size, className, "aria-label": ariaLabel }) => /*#__PURE__*/ jsx(Glyph, {
12
+ color,
13
+ size,
14
+ className,
15
+ "aria-label": ariaLabel,
16
+ "aria-hidden": !ariaLabel,
17
+ role: ariaLabel ? "img" : void 0
18
+ });
19
+ /**
20
+ * Renders an `IconSource`: an element as given, a component through `Icon`
21
+ * with `props`. For components that forward a `$icon`-style prop.
22
+ */ function renderIcon(source, props) {
23
+ return /*#__PURE__*/ isValidElement(source) ? source : /*#__PURE__*/ jsx(Icon, {
24
+ icon: source,
25
+ ...props
14
26
  });
15
- };
27
+ }
16
28
  //#endregion
17
- export { Icon };
29
+ export { Icon, renderIcon };
package/dist/index.js CHANGED
@@ -7,9 +7,8 @@ import { useBelowBreakpoint, useMediaQuery } from "./utils/use-media-query.js";
7
7
  import { useOnClickOutside } from "./utils/use-on-click-outside.js";
8
8
  import { CherryThemeProvider, ThemeContext } from "./styled-components/theme-provider.js";
9
9
  import { ClientThemeProvider } from "./styled-components/client-theme-provider.js";
10
- import { StyledComponentsRegistry } from "./styled-components/registry.js";
11
10
  import { createThemeInitScript, resolveTheme, themeInitScript } from "./styled-components/theme-init.js";
12
- import { Icon } from "./icon.js";
11
+ import { Icon, renderIcon } from "./icon.js";
13
12
  import { Accordion } from "./accordion.js";
14
13
  import { Avatar } from "./avatar.js";
15
14
  import { Dropzone, matchesAccept } from "./dropzone.js";
@@ -45,4 +44,4 @@ import { Textarea } from "./textarea.js";
45
44
  import { ThemeToggle } from "./theme-toggle.js";
46
45
  import { StyledNotificationItem, StyledNotifications, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, useToastNotifications } from "./toast.js";
47
46
  import { Toggle } from "./toggle.js";
48
- export { Accordion, Avatar, AvatarDropzone, Box, Button, Callout, ChatContext, ChatInput, ChatLauncher, ChatMessage, ChatMessageList, ChatPanel, ChatProvider, ChatSource, ChatSources, ChatTyping, CherryThemeProvider, ClientThemeProvider, Col, Container, Dropzone, Flex, GlobalStyles, Grid, Icon, IconButton, Input, MaxWidth, Modal, Password, Prose, Range, Select, Space, Spinner, StyledComponentsRegistry, StyledIconWrapper, StyledInputWrapper, StyledLabel, StyledNotificationItem, StyledNotifications, TabContent, Tabs, Textarea, ThemeContext, ThemeToggle, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, Toggle, alpha, breakpoints, buttonStyles, chatTextStyles, colors, colorsDark, createThemeInitScript, createTypographyStyle, darkFilledTextRule, errorInteractiveStyles, filledTextColor, fontSizes, fonts, formElementHeightStyles, fullWidthStyles, generateAlignContentStyles, generateAlignItemsStyles, generateColSpanStyles, generateColsStyles, generateDirectionStyles, generateGapStyles, generateJustifyContentStyles, generatePaddingStyles, iconButtonStyles, interactiveStyles, lineHeights, matchesAccept, mq, proseStyles, resetButton, resetInput, resolveShowcaseCommand, resolveTheme, shade, shadows, shadowsDark, showcaseCommands, spacing, statusBorderStyles, styledBlockquote, styledButton, styledButtonBig, styledCode, styledH1, styledH2, styledH3, styledH4, styledH5, styledH6, styledHero1, styledHero2, styledHero3, styledInput, styledInputBig, styledSmall, styledStrong, styledText, theme, themeDark, themeInitScript, thinScrollbar, tint, useBelowBreakpoint, useChat, useLockBodyScroll, useMediaQuery, useOnClickOutside, useToastNotifications };
47
+ export { Accordion, Avatar, AvatarDropzone, Box, Button, Callout, ChatContext, ChatInput, ChatLauncher, ChatMessage, ChatMessageList, ChatPanel, ChatProvider, ChatSource, ChatSources, ChatTyping, CherryThemeProvider, ClientThemeProvider, Col, Container, Dropzone, Flex, GlobalStyles, Grid, Icon, IconButton, Input, MaxWidth, Modal, Password, Prose, Range, Select, Space, Spinner, StyledIconWrapper, StyledInputWrapper, StyledLabel, StyledNotificationItem, StyledNotifications, TabContent, Tabs, Textarea, ThemeContext, ThemeToggle, ToastNotifications, ToastNotificationsContext, ToastNotificationsProvider, Toggle, alpha, breakpoints, buttonStyles, chatTextStyles, colors, colorsDark, createThemeInitScript, createTypographyStyle, darkFilledTextRule, errorInteractiveStyles, filledTextColor, fontSizes, fonts, formElementHeightStyles, fullWidthStyles, generateAlignContentStyles, generateAlignItemsStyles, generateColSpanStyles, generateColsStyles, generateDirectionStyles, generateGapStyles, generateJustifyContentStyles, generatePaddingStyles, iconButtonStyles, interactiveStyles, lineHeights, matchesAccept, mq, proseStyles, renderIcon, resetButton, resetInput, resolveShowcaseCommand, resolveTheme, shade, shadows, shadowsDark, showcaseCommands, spacing, statusBorderStyles, styledBlockquote, styledButton, styledButtonBig, styledCode, styledH1, styledH2, styledH3, styledH4, styledH5, styledH6, styledHero1, styledHero2, styledHero3, styledInput, styledInputBig, styledSmall, styledStrong, styledText, theme, themeDark, themeInitScript, thinScrollbar, tint, useBelowBreakpoint, useChat, useLockBodyScroll, useMediaQuery, useOnClickOutside, useToastNotifications };
package/dist/input.js CHANGED
@@ -5,10 +5,11 @@ import { Icon } from "./icon.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { forwardRef } from "react";
7
7
  import styled, { css } from "styled-components";
8
+ import { CalendarDays, Check } from "lucide-react";
8
9
  //#region src/lib/input.tsx
9
10
  var StyledInputWrapper = styled.span.withConfig({
10
11
  displayName: "input__StyledInputWrapper",
11
- componentId: "sc-5352dea9-0"
12
+ componentId: "sc-8a4d191a-0"
12
13
  })([
13
14
  `display:inline-flex;flex-wrap:`,
14
15
  `;align-items:center;`,
@@ -20,7 +21,7 @@ var StyledInputWrapper = styled.span.withConfig({
20
21
  ], ({ type }) => type === "checkbox" || type === "radio" ? "nowrap" : "wrap", ({ $label }) => $label && css([`gap:5px;align-items:flex-start;align-content:flex-start;`]), ({ $size }) => $size === "small" ? "14px" : "17px", ({ $size }) => $size === "small" ? "18px" : "24px", ({ $size }) => $size === "small" ? "18px" : "24px", ({ $fullWidth }) => fullWidthStyles($fullWidth));
21
22
  var StyledLabel = styled.label.withConfig({
22
23
  displayName: "input__StyledLabel",
23
- componentId: "sc-5352dea9-1"
24
+ componentId: "sc-8a4d191a-1"
24
25
  })([
25
26
  `display:inline-block;color:`,
26
27
  `;font-size:`,
@@ -29,7 +30,7 @@ var StyledLabel = styled.label.withConfig({
29
30
  ], ({ theme }) => theme.colors.grayDark, ({ theme }) => theme.fontSizes.small.lg, ({ theme }) => theme.lineHeights.small.lg);
30
31
  var StyledInput = styled.input.withConfig({
31
32
  displayName: "input__StyledInput",
32
- componentId: "sc-5352dea9-2"
33
+ componentId: "sc-8a4d191a-2"
33
34
  })([
34
35
  ``,
35
36
  `;`,
@@ -38,7 +39,7 @@ var StyledInput = styled.input.withConfig({
38
39
  `;background:`,
39
40
  `;border:solid 2px `,
40
41
  `;box-shadow:0 0 0 0px `,
41
- `;transition:all 0.3s ease;word-break:keep-all;white-space:nowrap;display:inline-flex;&::-webkit-datetime-edit{padding:0;margin:0;vertical-align:middle;}&::-webkit-datetime-edit-fields-wrapper{padding:4px 0;margin:0;vertical-align:middle;}&::-webkit-calendar-picker-indicator{background:transparent;cursor:pointer;position:absolute;right:15px;top:50%;transform:translateY(-50%);width:24px;height:24px;}&::-webkit-datetime-edit-text{color:`,
42
+ `;transition:all 0.3s ease;word-break:keep-all;white-space:nowrap;display:inline-flex;&::-webkit-datetime-edit{padding:0;margin:0;vertical-align:middle;}&::-webkit-datetime-edit-fields-wrapper{padding:4px 0;margin:0;vertical-align:middle;}&::-webkit-date-and-time-value{text-align:left;margin:0;}&::-webkit-calendar-picker-indicator{background:transparent;cursor:pointer;position:absolute;right:15px;top:50%;transform:translateY(-50%);width:24px;height:24px;}&::-webkit-datetime-edit-text{color:`,
42
43
  `;}&::-webkit-datetime-edit-month-field{&:focus{background:`,
43
44
  `;color:`,
44
45
  `;border-radius:4px;}}&::-webkit-datetime-edit-day-field{&:focus{background:`,
@@ -85,7 +86,7 @@ var StyledInput = styled.input.withConfig({
85
86
  `, ({ $fullWidth }) => fullWidthStyles($fullWidth));
86
87
  var StyledIconWrapper = styled.span.withConfig({
87
88
  displayName: "input__StyledIconWrapper",
88
- componentId: "sc-5352dea9-3"
89
+ componentId: "sc-8a4d191a-3"
89
90
  })([
90
91
  `display:inline-flex;position:relative;line-height:0;min-width:fit-content;& em{display:block;border-radius:50%;background:`,
91
92
  `;}& svg,& em{object-fit:contain;position:absolute;top:50%;left:50%;color:`,
@@ -93,7 +94,7 @@ var StyledIconWrapper = styled.span.withConfig({
93
94
  ], ({ theme }) => theme.colors.primary, ({ theme }) => theme.colors.primary);
94
95
  var StyledRadioCheckboxInput = styled.input.withConfig({
95
96
  displayName: "input__StyledRadioCheckboxInput",
96
- componentId: "sc-5352dea9-4"
97
+ componentId: "sc-8a4d191a-4"
97
98
  })([
98
99
  ``,
99
100
  `;display:inline-block;background:`,
@@ -178,7 +179,7 @@ var StyledRadioCheckboxInput = styled.input.withConfig({
178
179
  });
179
180
  var StyledCustomIconWrapper = styled.span.withConfig({
180
181
  displayName: "input__StyledCustomIconWrapper",
181
- componentId: "sc-5352dea9-5"
182
+ componentId: "sc-8a4d191a-5"
182
183
  })([
183
184
  `position:relative;`,
184
185
  `;& svg{position:absolute;top:50%;transform:translateY(-50%);pointer-events:none;width:24px;height:24px;object-fit:contain;color:`,
@@ -195,7 +196,7 @@ function LocalInput({ ...props }, ref) {
195
196
  ...props,
196
197
  "aria-invalid": props.$error || void 0,
197
198
  ref
198
- }), !props.disabled && props.type === "checkbox" ? /*#__PURE__*/ jsx(Icon, { name: "Check" }) : /*#__PURE__*/ jsx("em", {})] }), props.$label && /*#__PURE__*/ jsx(StyledLabel, {
199
+ }), !props.disabled && props.type === "checkbox" ? /*#__PURE__*/ jsx(Icon, { icon: Check }) : /*#__PURE__*/ jsx("em", {})] }), props.$label && /*#__PURE__*/ jsx(StyledLabel, {
199
200
  htmlFor: props.id,
200
201
  children: props.$label
201
202
  })]
@@ -222,7 +223,7 @@ function LocalInput({ ...props }, ref) {
222
223
  }),
223
224
  props.$iconPosition === "right" && props.$icon && props.$icon,
224
225
  (props.type === "date" || props.type === "datetime-local" || props.type === "month" || props.type === "week" || props.type === "time") && /*#__PURE__*/ jsx(Icon, {
225
- name: "CalendarDays",
226
+ icon: CalendarDays,
226
227
  className: "icon-calendar"
227
228
  })
228
229
  ]
package/dist/modal.js CHANGED
@@ -9,6 +9,7 @@ import { IconButton } from "./icon-button.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
11
11
  import styled, { css, keyframes } from "styled-components";
12
+ import { X } from "lucide-react";
12
13
  import { createPortal } from "react-dom";
13
14
  //#region src/lib/modal.tsx
14
15
  var emptySubscribe = () => () => {};
@@ -21,7 +22,7 @@ var innerRiseIn = keyframes([`from{transform:translateY(40px);}to{transform:tran
21
22
  var innerSinkOut = keyframes([`from{transform:translateY(0);}to{transform:translateY(40px);}`]);
22
23
  var StyledModal = styled.div.withConfig({
23
24
  displayName: "modal__StyledModal",
24
- componentId: "sc-1bbfce8-0"
25
+ componentId: "sc-937855ae-0"
25
26
  })([
26
27
  `position:fixed;top:0;left:0;width:100%;height:100%;background:`,
27
28
  `;display:flex;justify-content:center;align-items:center;z-index:1010;animation:`,
@@ -35,11 +36,11 @@ var StyledModal = styled.div.withConfig({
35
36
  ], ({ theme }) => alpha(theme.colors.primary, 50), ({ $isClosing }) => $isClosing ? overlayFadeOut : overlayFadeIn, ({ $isClosing }) => $isClosing && css([`pointer-events:none;`]), ({ theme }) => theme.colors.light, ({ theme }) => theme.spacing.radius.lg, ({ $isClosing }) => $isClosing ? innerSinkOut : innerRiseIn, mq("lg"), ({ $width }) => $width && css([`max-width:`, `px;`], $width));
36
37
  var StyledModalClose = styled(IconButton).withConfig({
37
38
  displayName: "modal__StyledModalClose",
38
- componentId: "sc-1bbfce8-1"
39
+ componentId: "sc-937855ae-1"
39
40
  })([`position:absolute;top:20px;right:20px;z-index:10;`]);
40
41
  var StyledModalTitle = styled.h2.withConfig({
41
42
  displayName: "modal__StyledModalTitle",
42
- componentId: "sc-1bbfce8-2"
43
+ componentId: "sc-937855ae-2"
43
44
  })([
44
45
  `--divider-color:`,
45
46
  `;margin:0 0 15px 0;padding:0 0 15px 0;color:`,
@@ -48,7 +49,7 @@ var StyledModalTitle = styled.h2.withConfig({
48
49
  ], ({ theme }) => theme.colors.grayLight, ({ theme }) => theme.colors.dark, ({ theme }) => styledH5(theme));
49
50
  var StyledModalContent = styled.div.withConfig({
50
51
  displayName: "modal__StyledModalContent",
51
- componentId: "sc-1bbfce8-3"
52
+ componentId: "sc-937855ae-3"
52
53
  })([
53
54
  `max-height:calc(100svh - 200px);overflow-y:auto;padding:5px;margin:-5px;--divider-color:`,
54
55
  `;`,
@@ -101,7 +102,7 @@ function Modal({ children, $isOpen, $onClose, $title, $width, $hideCloseButton,
101
102
  onClick: closeModal,
102
103
  className: "modal-close",
103
104
  "aria-label": "Close Modal",
104
- children: /*#__PURE__*/ jsx(Icon, { name: "X" })
105
+ children: /*#__PURE__*/ jsx(Icon, { icon: X })
105
106
  })
106
107
  }),
107
108
  $title && /*#__PURE__*/ jsx(StyledModalTitle, {
package/dist/password.js CHANGED
@@ -7,6 +7,7 @@ import { Input } from "./input.js";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { forwardRef, useState } from "react";
9
9
  import styled, { css } from "styled-components";
10
+ import { Eye, EyeOff } from "lucide-react";
10
11
  //#region src/lib/password.tsx
11
12
  var passwordSizes = {
12
13
  small: {
@@ -27,7 +28,7 @@ var passwordSizes = {
27
28
  };
28
29
  var StyledPassword = styled.span.withConfig({
29
30
  displayName: "password__StyledPassword",
30
- componentId: "sc-b3beabaa-0"
31
+ componentId: "sc-ab5e6dd3-0"
31
32
  })([
32
33
  `position:relative;display:inline-flex;& input{padding-right:`,
33
34
  `;}`,
@@ -38,7 +39,7 @@ var StyledPassword = styled.span.withConfig({
38
39
  }, ({ $fullWidth }) => fullWidthStyles($fullWidth));
39
40
  var StyledPasswordReveal = styled.span.withConfig({
40
41
  displayName: "password__StyledPasswordReveal",
41
- componentId: "sc-b3beabaa-1"
42
+ componentId: "sc-ab5e6dd3-1"
42
43
  })([`position:absolute;bottom:0;display:flex;align-items:center;`, ``], ({ $size }) => {
43
44
  const { height, inset } = passwordSizes[$size ?? "default"];
44
45
  return css([
@@ -65,7 +66,7 @@ function LocalPassword({ ...props }, ref) {
65
66
  "aria-pressed": show,
66
67
  title: show ? "Hide password" : "Show password",
67
68
  onClick: () => setShow((prev) => !prev),
68
- children: /*#__PURE__*/ jsx(Icon, { name: show ? "EyeOff" : "Eye" })
69
+ children: /*#__PURE__*/ jsx(Icon, { icon: show ? EyeOff : Eye })
69
70
  })
70
71
  })]
71
72
  });
package/dist/select.js CHANGED
@@ -6,10 +6,11 @@ import { StyledInputWrapper, StyledLabel } from "./input.js";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { forwardRef } from "react";
8
8
  import styled from "styled-components";
9
+ import { ChevronDown } from "lucide-react";
9
10
  //#region src/lib/select.tsx
10
11
  var StyledSelect = styled.select.withConfig({
11
12
  displayName: "select__StyledSelect",
12
- componentId: "sc-65975d95-0"
13
+ componentId: "sc-ea1826c7-0"
13
14
  })([
14
15
  ``,
15
16
  `;`,
@@ -38,7 +39,7 @@ var StyledSelect = styled.select.withConfig({
38
39
  `, ({ $fullWidth }) => fullWidthStyles($fullWidth));
39
40
  var StyledIconWrapper = styled.span.withConfig({
40
41
  displayName: "select__StyledIconWrapper",
41
- componentId: "sc-65975d95-1"
42
+ componentId: "sc-ea1826c7-1"
42
43
  })([
43
44
  `position:relative;`,
44
45
  ` & svg{position:absolute;top:50%;right:15px;width:24px;height:24px;color:`,
@@ -59,7 +60,7 @@ function LocalSelect({ ...props }, ref) {
59
60
  "aria-invalid": props.$error || void 0,
60
61
  ref,
61
62
  children: props.children
62
- }), /*#__PURE__*/ jsx(Icon, { name: "ChevronDown" })]
63
+ }), /*#__PURE__*/ jsx(Icon, { icon: ChevronDown })]
63
64
  })]
64
65
  });
65
66
  }
package/dist/spinner.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { IconProps } from './icon';
1
+ import { IconComponent } from './icon';
2
2
  export interface SpinnerProps {
3
- /** Lucide icon to rotate. */
4
- name?: IconProps;
3
+ /** The icon to rotate: an imported lucide component. Defaults to `LoaderCircle`. */
4
+ icon?: IconComponent;
5
5
  color?: string;
6
6
  size?: number;
7
7
  className?: string;
8
8
  "aria-label"?: string;
9
9
  }
10
- declare const Spinner: ({ name, ...props }: SpinnerProps) => import("react").JSX.Element;
10
+ declare const Spinner: ({ icon, ...props }: SpinnerProps) => import("react").JSX.Element;
11
11
  export { Spinner };
package/dist/spinner.js CHANGED
@@ -3,18 +3,19 @@
3
3
  import { Icon } from "./icon.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import styled, { keyframes } from "styled-components";
6
+ import { LoaderCircle } from "lucide-react";
6
7
  //#region src/lib/spinner.tsx
7
8
  var spin = keyframes([`0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}`]);
8
9
  var StyledSpinner = styled(Icon).withConfig({
9
10
  displayName: "spinner__StyledSpinner",
10
- componentId: "sc-bb636095-0"
11
+ componentId: "sc-af2b088c-0"
11
12
  })([
12
13
  `color:`,
13
14
  `;animation:`,
14
15
  ` 1s linear infinite;@media (prefers-reduced-motion:reduce){animation:none;}`
15
16
  ], ({ theme }) => theme.colors.dark, spin);
16
- var Spinner = ({ name = "LoaderCircle", ...props }) => /*#__PURE__*/ jsx(StyledSpinner, {
17
- name,
17
+ var Spinner = ({ icon = LoaderCircle, ...props }) => /*#__PURE__*/ jsx(StyledSpinner, {
18
+ icon,
18
19
  ...props
19
20
  });
20
21
  //#endregion
@@ -1,4 +1,3 @@
1
1
  export * from './client-theme-provider';
2
- export * from './registry';
3
2
  export * from './theme-init';
4
3
  export * from './theme-provider';
@@ -6,10 +6,11 @@ import { Icon } from "./icon.js";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { forwardRef, useContext, useEffect } from "react";
8
8
  import styled, { css } from "styled-components";
9
+ import { MoonStar, Sun } from "lucide-react";
9
10
  //#region src/lib/theme-toggle.tsx
10
11
  var StyledThemeToggle = styled.button.withConfig({
11
12
  displayName: "theme-toggle__StyledThemeToggle",
12
- componentId: "sc-f8700a2a-0"
13
+ componentId: "sc-85f678d3-0"
13
14
  })([
14
15
  ``,
15
16
  ` `,
@@ -45,7 +46,7 @@ function LocalThemeToggle({ onClick, $shortcut = false, ...props }, ref) {
45
46
  onClick?.(event);
46
47
  },
47
48
  ref,
48
- children: [/*#__PURE__*/ jsx(Icon, { name: "Sun" }), /*#__PURE__*/ jsx(Icon, { name: "MoonStar" })]
49
+ children: [/*#__PURE__*/ jsx(Icon, { icon: Sun }), /*#__PURE__*/ jsx(Icon, { icon: MoonStar })]
49
50
  });
50
51
  }
51
52
  var ThemeToggle = /*#__PURE__*/ forwardRef(LocalThemeToggle);
package/dist/toast.d.ts CHANGED
@@ -27,14 +27,14 @@ interface ToastNotificationsProviderProps {
27
27
  }
28
28
  declare function ToastNotificationsProvider({ children, }: ToastNotificationsProviderProps): React.JSX.Element;
29
29
  export declare const StyledNotifications: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components/dist/types').Merged<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "style"> & {
30
- style?: import('styled-components/dist/types').CSSPropertiesWithVars | React.CSSProperties | undefined;
30
+ style?: React.CSSProperties | import('styled-components/dist/types').CSSPropertiesWithVars | undefined;
31
31
  }, {
32
32
  theme: Theme;
33
33
  $align?: ToastAlign;
34
34
  $bottom?: boolean;
35
35
  }>> & string;
36
36
  export declare const StyledNotificationItem: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components/dist/types').Merged<Omit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "style"> & {
37
- style?: import('styled-components/dist/types').CSSPropertiesWithVars | React.CSSProperties | undefined;
37
+ style?: React.CSSProperties | import('styled-components/dist/types').CSSPropertiesWithVars | undefined;
38
38
  }, {
39
39
  theme: Theme;
40
40
  $color: ToastColor;
package/dist/toast.js CHANGED
@@ -5,12 +5,13 @@ import { IconButton } from "./icon-button.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
7
7
  import styled, { css } from "styled-components";
8
+ import { CircleCheck, CircleX, Info, TriangleAlert, X } from "lucide-react";
8
9
  //#region src/lib/toast.tsx
9
10
  var statusIcons = {
10
- success: "CircleCheck",
11
- error: "CircleX",
12
- warning: "TriangleAlert",
13
- info: "Info"
11
+ success: CircleCheck,
12
+ error: CircleX,
13
+ warning: TriangleAlert,
14
+ info: Info
14
15
  };
15
16
  var EXIT_DURATION_MS = 400;
16
17
  var ToastNotificationsContext = /*#__PURE__*/ createContext({
@@ -52,11 +53,11 @@ function ToastNotificationsProvider({ children }) {
52
53
  }
53
54
  var StyledNotifications = styled.ul.withConfig({
54
55
  displayName: "toast__StyledNotifications",
55
- componentId: "sc-623506e7-0"
56
+ componentId: "sc-5ff21b00-0"
56
57
  })([`position:fixed;z-index:99999;margin:0;padding:0;list-style:none;left:20px;right:20px;pointer-events:none;`, ``], ({ $bottom }) => $bottom ? css([`bottom:20px;`]) : css([`top:20px;`]));
57
58
  var StyledNotificationItem = styled.li.withConfig({
58
59
  displayName: "toast__StyledNotificationItem",
59
- componentId: "sc-623506e7-1"
60
+ componentId: "sc-5ff21b00-1"
60
61
  })([
61
62
  `display:grid;grid-template-rows:0fr;justify-items:center;margin:0;padding:0;opacity:0;pointer-events:none;transform:translateY(`,
62
63
  `);transition:opacity 0.2s ease,transform 0.2s ease,grid-template-rows 0.25s ease 0.15s,margin 0.25s ease 0.15s;`,
@@ -126,7 +127,7 @@ function NotificationItem({ notification, $align, $bottom }) {
126
127
  children: [
127
128
  /*#__PURE__*/ jsx("span", {
128
129
  className: "status-icon",
129
- children: /*#__PURE__*/ jsx(Icon, { name: statusIcons[notification.color] || statusIcons.info })
130
+ children: /*#__PURE__*/ jsx(Icon, { icon: statusIcons[notification.color] ?? statusIcons.info })
130
131
  }),
131
132
  /*#__PURE__*/ jsx("span", {
132
133
  className: "message",
@@ -137,7 +138,7 @@ function NotificationItem({ notification, $align, $bottom }) {
137
138
  className: "close-button",
138
139
  "aria-label": "Dismiss notification",
139
140
  onClick: () => setPhase("exiting"),
140
- children: /*#__PURE__*/ jsx(Icon, { name: "X" })
141
+ children: /*#__PURE__*/ jsx(Icon, { icon: X })
141
142
  })
142
143
  ]
143
144
  })
@@ -2,6 +2,46 @@
2
2
  "use client";
3
3
  import { useEffect, useRef } from "react";
4
4
  //#region src/lib/utils/use-on-click-outside.tsx
5
+ var NATIVE_PICKER_SELECTOR = [
6
+ "select",
7
+ "input[type=\"date\"]",
8
+ "input[type=\"datetime-local\"]",
9
+ "input[type=\"month\"]",
10
+ "input[type=\"time\"]",
11
+ "input[type=\"week\"]"
12
+ ].join(",");
13
+ var RECENT_PRESS_MS = 400;
14
+ var subscriberCount = 0;
15
+ var lastPressAt = Number.NEGATIVE_INFINITY;
16
+ var pickerBlurredAt = Number.NEGATIVE_INFINITY;
17
+ var capture = {
18
+ capture: true,
19
+ passive: true
20
+ };
21
+ function notePress() {
22
+ lastPressAt = performance.now();
23
+ }
24
+ function noteFocusOut(event) {
25
+ const target = event.target;
26
+ if (target instanceof Element && target.matches(NATIVE_PICKER_SELECTOR)) pickerBlurredAt = performance.now();
27
+ }
28
+ function subscribeToPresses() {
29
+ if (subscriberCount++ > 0) return;
30
+ document.addEventListener("pointerdown", notePress, capture);
31
+ document.addEventListener("touchstart", notePress, capture);
32
+ document.addEventListener("focusout", noteFocusOut, true);
33
+ }
34
+ function unsubscribeFromPresses() {
35
+ if (--subscriberCount > 0) return;
36
+ document.removeEventListener("pointerdown", notePress, capture);
37
+ document.removeEventListener("touchstart", notePress, capture);
38
+ document.removeEventListener("focusout", noteFocusOut, true);
39
+ }
40
+ function isStrayPickerEvent() {
41
+ const now = performance.now();
42
+ const active = document.activeElement;
43
+ return (active instanceof Element && active.matches(NATIVE_PICKER_SELECTOR) || now - pickerBlurredAt < RECENT_PRESS_MS) && now - lastPressAt >= RECENT_PRESS_MS;
44
+ }
5
45
  function useOnClickOutside(refs, cb) {
6
46
  const latest = useRef({
7
47
  refs,
@@ -16,11 +56,14 @@ function useOnClickOutside(refs, cb) {
16
56
  useEffect(() => {
17
57
  function handleClickOutside(event) {
18
58
  const { refs, cb } = latest.current;
59
+ if (isStrayPickerEvent()) return;
19
60
  if (refs.every((ref) => !ref.current || !ref.current.contains(event.target))) cb();
20
61
  }
62
+ subscribeToPresses();
21
63
  document.addEventListener("mousedown", handleClickOutside);
22
64
  return () => {
23
65
  document.removeEventListener("mousedown", handleClickOutside);
66
+ unsubscribeFromPresses();
24
67
  };
25
68
  }, []);
26
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cherry-styled-components",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Cherry is a design system for the modern web. Designed in Figma, built in React using Typescript.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -10,6 +10,14 @@
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js"
13
+ },
14
+ "./next": {
15
+ "types": "./dist/styled-components/registry.d.ts",
16
+ "import": "./dist/styled-components/registry.js"
17
+ },
18
+ "./dynamic": {
19
+ "types": "./dist/icon-dynamic.d.ts",
20
+ "import": "./dist/icon-dynamic.js"
13
21
  }
14
22
  },
15
23
  "files": [
@@ -20,7 +28,7 @@
20
28
  "homepage": "https://cherry.al",
21
29
  "repository": "https://github.com/cherry-design-system/styled-components",
22
30
  "license": "MIT",
23
- "packageManager": "pnpm@11.20.0",
31
+ "packageManager": "pnpm@12.3.4",
24
32
  "engines": {
25
33
  "node": ">=18.0.0"
26
34
  },
@@ -44,33 +52,32 @@
44
52
  "format:check": "prettier --check .",
45
53
  "screenshots": "node scripts/screenshot-previews.cjs"
46
54
  },
47
- "dependencies": {
48
- "lucide-react": "^1.28.0"
49
- },
50
55
  "peerDependencies": {
56
+ "lucide-react": "^1.31.0",
51
57
  "react": "^18.0.0 || ^19.0.0",
52
58
  "react-dom": "^18.0.0 || ^19.0.0",
53
59
  "styled-components": "^6.0.0"
54
60
  },
55
61
  "devDependencies": {
56
- "@swc/plugin-styled-components": "^12.15.0",
57
- "@types/node": "^26.1.2",
62
+ "@swc/plugin-styled-components": "^14.0.0",
63
+ "@types/node": "^26.4.1",
58
64
  "@types/react": "^19.2.18",
59
- "@types/react-dom": "^19.2.4",
65
+ "@types/react-dom": "^19.2.7",
60
66
  "@typescript/typescript6": "^6.0.2",
61
67
  "@vitejs/plugin-react-swc": "^4.3.3",
62
68
  "eslint-plugin-react-hooks": "^7.1.1",
63
- "eslint-plugin-react-refresh": "^0.5.3",
64
- "next": "^16.3.0",
65
- "playwright-core": "^1.62.1",
69
+ "eslint-plugin-react-refresh": "^0.5.6",
70
+ "lucide-react": "^1.41.0",
71
+ "next": "^16.3.4",
72
+ "playwright-core": "^1.63.0",
66
73
  "prettier": "^3.9.6",
67
74
  "react": "^19.2.8",
68
75
  "react-dom": "^19.2.8",
69
76
  "rollup-plugin-preserve-directives": "^0.4.0",
70
- "sharp": "^0.35.3",
71
- "styled-components": "^6.5.0",
77
+ "sharp": "^0.35.4",
78
+ "styled-components": "^6.5.3",
72
79
  "typescript": "^7.0.2",
73
- "vite": "^8.2.0",
74
- "vite-plugin-dts": "^5.0.3"
80
+ "vite": "^8.2.2",
81
+ "vite-plugin-dts": "^5.1.0"
75
82
  }
76
83
  }