@pushwoosh/dumb-components 1.1.122 → 1.1.123

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.
@@ -77,9 +77,12 @@ function App() {
77
77
  </Button>
78
78
 
79
79
  // Secondary button with icon
80
- <Button
81
- color="secondary"
82
- icon={<PlusIcon />}
80
+ // Note: kit-icons require `size` and `view`. Match icon size to Button size:
81
+ // Button size="field" (default) → icon size="medium"
82
+ // Button size="compact" → icon size="small"
83
+ <Button
84
+ color="secondary"
85
+ icon={<PlusIcon size="medium" view="lined" />}
83
86
  onClick={() => handleAdd()}
84
87
  >
85
88
  Add Item
@@ -94,28 +97,28 @@ function App() {
94
97
  {isDeleting ? 'Deleting...' : 'Delete'}
95
98
  </Button>
96
99
 
97
- // Compact button with right icon
98
- <Button
100
+ // Compact button with right icon (size="small" matches size="compact")
101
+ <Button
99
102
  color="primary"
100
103
  size="compact"
101
- rightIcon={<ArrowIcon />}
104
+ rightIcon={<ArrowIcon size="small" view="lined" />}
102
105
  >
103
106
  Next
104
107
  </Button>
105
108
 
106
109
  // Ghost view button
107
- <Button
110
+ <Button
108
111
  color="primary"
109
112
  view="ghost"
110
- icon={<EditIcon />}
113
+ icon={<EditIcon size="medium" view="lined" />}
111
114
  >
112
115
  Edit
113
116
  </Button>
114
117
 
115
118
  // Icon-only button
116
- <Button
119
+ <Button
117
120
  color="secondary"
118
- icon={<SearchIcon />}
121
+ icon={<SearchIcon size="medium" view="lined" />}
119
122
  onClick={() => handleSearch()}
120
123
  />
121
124
 
@@ -188,8 +191,8 @@ A compact icon-only button with a built-in `CloseIcon` intended for "remove" / "
188
191
  | isDisabled | boolean | false | Whether button is disabled |
189
192
  | isLoading | boolean | false | Whether button shows loading state |
190
193
  | isOpen | boolean | false | Whether button appears in "open" state |
191
- | icon | ReactNode | undefined | Left icon element |
192
- | rightIcon | ReactNode | undefined | Right icon element |
194
+ | icon | ReactNode | undefined | Left icon element. For `@pushwoosh/kit-icons` icons, pass `size` and `view` explicitly (both required). Match icon `size` to Button `size`: `size="medium"` for `field` (default), `size="small"` for `compact`. |
195
+ | rightIcon | ReactNode | undefined | Right icon element. Same icon size/view rules as `icon`. |
193
196
  | children | ReactNode | undefined | Button text content |
194
197
  | boxRef | Ref | undefined | Ref to button element |
195
198
  | as | string \| Component | undefined | Render as different element/component |
@@ -218,6 +221,8 @@ A compact icon-only button with a built-in `CloseIcon` intended for "remove" / "
218
221
  - Don't use loading state without showing loading text to user
219
222
  - Avoid putting buttons inside other interactive elements
220
223
  - Don't use `isDisabled` with `isLoading` simultaneously
224
+ - Don't pass icons without `size` and `view` — they are required on `@pushwoosh/kit-icons` and TypeScript won't catch it (Button's `icon` is typed as `ReactNode`)
225
+ - Don't mismatch icon size to button size: `size="small"` icons belong only in `size="compact"` buttons; `field`-sized buttons need `size="medium"` icons
221
226
  - Avoid very long button text - keep concise
222
227
 
223
228
  ### Integration Tips
@@ -2,4 +2,6 @@ import { type ChipVariant } from './Chip.types';
2
2
  export declare const variants: Record<ChipVariant, {
3
3
  color: string;
4
4
  background: string;
5
+ hoverColor: string;
6
+ hoverBackground: string;
5
7
  }>;
@@ -2,14 +2,20 @@ import { Color } from '@pushwoosh/kit-constants';
2
2
  export const variants = {
3
3
  primary: {
4
4
  color: Color.PRIMARY,
5
- background: Color.BRIGHT_LIGHT
5
+ background: Color.BRIGHT_LIGHT,
6
+ hoverColor: Color.PRIMARY_HOVER,
7
+ hoverBackground: Color.BRIGHT_LIGHT_HOVER
6
8
  },
7
9
  danger: {
8
10
  color: Color.DANGER,
9
- background: Color.DANGER_LIGHT
11
+ background: Color.DANGER_LIGHT,
12
+ hoverColor: Color.DANGER_HOVER,
13
+ hoverBackground: Color.DANGER_LIGHT_HOVER
10
14
  },
11
15
  warning: {
12
16
  color: Color.WARNING,
13
- background: Color.WARNING_LIGHT
17
+ background: Color.WARNING_LIGHT,
18
+ hoverColor: Color.WARNING_HOVER,
19
+ hoverBackground: Color.WARNING_LIGHT_HOVER
14
20
  }
15
21
  };
package/Chip/Chip.d.ts CHANGED
@@ -5,6 +5,8 @@ export declare const Chip: React.ForwardRefExoticComponent<{
5
5
  $onClose?: React.HTMLAttributes<HTMLDivElement>["onClick"];
6
6
  $readOnly?: boolean;
7
7
  $maxWidth?: string;
8
+ $icon?: React.ReactNode;
9
+ $tooltip?: React.ReactNode;
8
10
  as?: string;
9
11
  } & {
10
12
  children?: React.ReactNode | undefined;
package/Chip/Chip.js CHANGED
@@ -1,31 +1,43 @@
1
1
  import React, { forwardRef } from 'react';
2
- import { CloseIcon } from '@pushwoosh/kit-icons';
3
- import { ChipStyled } from './styles';
2
+ import { TooltipTrigger } from '../Tooltip';
3
+ import { ChipCloseIcon, ChipLabel, ChipSlotIcon, ChipStyled } from './styles';
4
4
  export const Chip = forwardRef(({
5
5
  $variant = 'primary',
6
6
  $selected,
7
7
  $readOnly,
8
8
  $onClose,
9
9
  $maxWidth,
10
+ $icon,
11
+ $tooltip,
10
12
  children,
11
13
  as,
12
14
  ...rest
13
15
  }, ref) => {
16
+ const isCloseable = !!$onClose && !$readOnly;
17
+ const hasTooltip = !isCloseable && $tooltip !== undefined;
18
+ if ($readOnly && $onClose) {
19
+ console.error('[Chip]: $onClose is ignored when $readOnly is true — read-only chips have no close button.');
20
+ }
14
21
  return React.createElement(ChipStyled, {
15
22
  ref: ref,
16
23
  "$variant": $variant,
17
24
  "$selected": $selected,
18
- "$closeable": !!$onClose,
25
+ "$hasLeadingIcon": !!$icon,
26
+ "$hasTrailingSlot": isCloseable || hasTooltip,
19
27
  "$readOnly": $readOnly,
20
28
  "$maxWidth": $maxWidth,
21
29
  as: as,
22
30
  ...rest
23
- }, React.createElement("span", {
24
- className: "chipLabel"
25
- }, children), $onClose && React.createElement(CloseIcon, {
31
+ }, $icon && React.createElement(ChipSlotIcon, null, $icon), React.createElement(ChipLabel, {
32
+ "$truncated": $maxWidth !== undefined
33
+ }, children), isCloseable && React.createElement(ChipCloseIcon, {
26
34
  size: "small",
27
35
  onClick: $onClose,
28
- as: "span",
29
- className: "closeIcon"
30
- }));
36
+ forwardedAs: "span"
37
+ }), hasTooltip && React.createElement(ChipSlotIcon, null, React.createElement(TooltipTrigger, {
38
+ view: "question",
39
+ size: "small",
40
+ position: "top-middle",
41
+ content: $tooltip
42
+ })));
31
43
  });
@@ -1,4 +1,4 @@
1
- import { type HTMLAttributes, type PropsWithChildren } from 'react';
1
+ import { type HTMLAttributes, type PropsWithChildren, type ReactNode } from 'react';
2
2
  export type ChipVariant = 'primary' | 'danger' | 'warning';
3
3
  export type ChipProps = PropsWithChildren<{
4
4
  $variant?: ChipVariant;
@@ -7,5 +7,7 @@ export type ChipProps = PropsWithChildren<{
7
7
  $readOnly?: boolean;
8
8
  /** CSS max-width value, e.g. "120px", "50%", "10rem". Enables label truncation with ellipsis. */
9
9
  $maxWidth?: string;
10
+ $icon?: ReactNode;
11
+ $tooltip?: ReactNode;
10
12
  as?: string;
11
13
  }> & HTMLAttributes<HTMLDivElement>;
@@ -21,6 +21,9 @@ interface ChipProps extends PropsWithChildren<HTMLAttributes<HTMLDivElement>> {
21
21
  $selected?: boolean;
22
22
  $onClose?: HTMLAttributes<HTMLDivElement>['onClick'];
23
23
  $readOnly?: boolean;
24
+ $maxWidth?: string;
25
+ $icon?: ReactNode;
26
+ $tooltip?: ReactNode;
24
27
  as?: string;
25
28
  }
26
29
  ```
@@ -116,6 +119,26 @@ function App() {
116
119
  {tag.name}
117
120
  </Chip>
118
121
 
122
+ import { TagIcon } from '@pushwoosh/kit-icons';
123
+
124
+ // Chip with leading icon
125
+ <Chip $variant="primary" $icon={<TagIcon size="small" view="lined" />}>
126
+ Segment
127
+ </Chip>
128
+
129
+ // Icon + removable
130
+ <Chip
131
+ $icon={<TagIcon size="small" view="lined" />}
132
+ $onClose={() => removeTag(tag)}
133
+ >
134
+ {tag}
135
+ </Chip>
136
+
137
+ // Read-only chip with "?" tooltip
138
+ <Chip $readOnly $variant="primary" $tooltip="Explanation for this chip">
139
+ Label
140
+ </Chip>
141
+
119
142
  // Status chips
120
143
  function getStatusChip(status: string) {
121
144
  const variantMap = {
@@ -139,6 +162,9 @@ function getStatusChip(status: string) {
139
162
  | $variant | 'primary' \| 'danger' \| 'warning' | 'primary' | Visual style variant |
140
163
  | $selected | boolean | false | Whether chip appears in selected state |
141
164
  | $onClose | onClick handler | undefined | Makes chip removable with close icon |
165
+ | $maxWidth | string | undefined | CSS max-width; truncates label with ellipsis |
166
+ | $icon | ReactNode | undefined | Leading icon slot; pass a kit-icon with size="small" |
167
+ | $tooltip | ReactNode | undefined | Tooltip content; renders a trailing "?" trigger on read-only chips |
142
168
  | $readOnly | boolean | false | Whether chip is interactive |
143
169
  | as | string | 'div' | HTML element to render as |
144
170
  | children | ReactNode | required | Chip content |
@@ -152,6 +178,18 @@ function getStatusChip(status: string) {
152
178
  | danger | Errors, removable warnings | Error messages, dangerous actions |
153
179
  | warning | Cautions, temporary states | Beta features, warnings, alerts |
154
180
 
181
+ ## Hover States
182
+
183
+ Each variant darkens its background and tint on hover. Hover is only applied to
184
+ interactive chips — it is intentionally disabled when `$selected` (selected chips
185
+ keep the solid `MAIN` background) or `$readOnly` (non-interactive).
186
+
187
+ | Variant | Background → hover | Tint → hover |
188
+ |---------|--------------------|--------------|
189
+ | primary | BRIGHT_LIGHT → BRIGHT_LIGHT_HOVER | PRIMARY → PRIMARY_HOVER |
190
+ | danger | DANGER_LIGHT → DANGER_LIGHT_HOVER | DANGER → DANGER_HOVER |
191
+ | warning | WARNING_LIGHT → WARNING_LIGHT_HOVER | WARNING → WARNING_HOVER |
192
+
155
193
  ## AI Agent Guidelines
156
194
 
157
195
  ### ✅ Recommended Patterns
@@ -161,6 +199,8 @@ function getStatusChip(status: string) {
161
199
  - Keep chip text short and descriptive
162
200
  - Use semantic variants (danger for errors, warning for cautions)
163
201
  - Group related chips together with consistent spacing
202
+ - When adding a leading icon, always use `size="small"` so it fits the chip's 24px height; pass `view` too if the icon defines one
203
+ - Slots are fixed: leading `$icon` on the left; the right side holds exactly one element — the close button on active chips (`$onClose`), or the `$tooltip` "?" trigger on read-only chips. A read-only chip never shows a close button, and the close button + tooltip are mutually exclusive.
164
204
 
165
205
  ### ❌ Common Mistakes to Avoid
166
206
  - Don't use chips for very long text content
package/Chip/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { Chip } from './Chip';
2
+ export type { ChipProps, ChipVariant } from './Chip.types';
package/Chip/styles.d.ts CHANGED
@@ -2,7 +2,13 @@ import type { ChipVariant } from './Chip.types';
2
2
  export declare const ChipStyled: import("styled-components").StyledComponent<"div", any, {
3
3
  $variant: ChipVariant;
4
4
  $selected?: boolean;
5
- $closeable?: boolean;
5
+ $hasLeadingIcon?: boolean;
6
+ $hasTrailingSlot?: boolean;
6
7
  $readOnly?: boolean;
7
8
  $maxWidth?: string;
8
9
  }, never>;
10
+ export declare const ChipSlotIcon: import("styled-components").StyledComponent<"span", any, {}, never>;
11
+ export declare const ChipLabel: import("styled-components").StyledComponent<"span", any, {
12
+ $truncated?: boolean;
13
+ }, never>;
14
+ export declare const ChipCloseIcon: import("styled-components").StyledComponent<import("react").FC<import("@pushwoosh/kit-icons").CloseIconProps>, any, {}, never>;
package/Chip/styles.js CHANGED
@@ -1,14 +1,17 @@
1
1
  import styled from 'styled-components';
2
2
  import { Color } from '@pushwoosh/kit-constants';
3
+ import { CloseIcon } from '@pushwoosh/kit-icons';
3
4
  import { variants } from './Chip.constants';
4
5
  export const ChipStyled = styled.div.withConfig({
5
6
  displayName: "ChipStyled",
6
7
  componentId: "sc-e2g39l-0"
7
- })(["display:inline-flex;min-height:24px;", " padding:0 ", " 0 8px;align-items:center;gap:4px;cursor:", ";border-radius:4px;color:", ";background:", ";font-weight:400;line-height:140%;& > .chipLabel{", "}& > .closeIcon{transition:transform 0.2s;&:hover{transform:scale(1.3);}}"], ({
8
+ })(["display:inline-flex;min-height:24px;", " padding:0 ", " 0 ", ";align-items:center;gap:4px;cursor:", ";border-radius:4px;color:", ";background:", ";font-weight:400;line-height:140%;", ""], ({
8
9
  $maxWidth
9
10
  }) => $maxWidth !== undefined && `max-width: ${$maxWidth};`, ({
10
- $closeable
11
- }) => $closeable ? '4px' : '8px', ({
11
+ $hasTrailingSlot
12
+ }) => $hasTrailingSlot ? '4px' : '8px', ({
13
+ $hasLeadingIcon
14
+ }) => $hasLeadingIcon ? '4px' : '8px', ({
12
15
  $readOnly
13
16
  }) => $readOnly ? 'default' : 'pointer', ({
14
17
  $variant,
@@ -29,11 +32,32 @@ export const ChipStyled = styled.div.withConfig({
29
32
  }
30
33
  return $selected ? Color.MAIN : variants[$variant].background;
31
34
  }, ({
32
- $maxWidth
33
- }) => $maxWidth !== undefined && `
34
- flex: 1 1 0;
35
- min-width: 0;
36
- overflow: hidden;
37
- text-overflow: ellipsis;
38
- white-space: nowrap;
39
- `);
35
+ $variant,
36
+ $selected,
37
+ $readOnly
38
+ }) => !$selected && !$readOnly && `
39
+ &:hover {
40
+ color: ${variants[$variant].hoverColor};
41
+ background: ${variants[$variant].hoverBackground};
42
+ }
43
+ `);
44
+ export const ChipSlotIcon = styled.span.withConfig({
45
+ displayName: "ChipSlotIcon",
46
+ componentId: "sc-e2g39l-1"
47
+ })(["display:inline-flex;flex-shrink:0;"]);
48
+ export const ChipLabel = styled.span.withConfig({
49
+ displayName: "ChipLabel",
50
+ componentId: "sc-e2g39l-2"
51
+ })(["", ""], ({
52
+ $truncated
53
+ }) => $truncated && `
54
+ flex: 1 1 0;
55
+ min-width: 0;
56
+ overflow: hidden;
57
+ text-overflow: ellipsis;
58
+ white-space: nowrap;
59
+ `);
60
+ export const ChipCloseIcon = styled(CloseIcon).withConfig({
61
+ displayName: "ChipCloseIcon",
62
+ componentId: "sc-e2g39l-3"
63
+ })(["flex-shrink:0;transition:transform 0.2s;&:hover{transform:scale(1.3);}"]);
package/index.d.ts CHANGED
@@ -39,7 +39,7 @@ export { FieldBox } from './FieldBox';
39
39
  export { NumberPicker } from './NumberPicker';
40
40
  export { CodeEditor } from './CodeEditor';
41
41
  export { EnhancedInput, SearchInput } from './EnhancedInput';
42
- export { Chip } from './Chip';
42
+ export { Chip, type ChipProps, type ChipVariant } from './Chip';
43
43
  export { RichmediaPreview, useRichmediaHtml } from './RichmediaPreview';
44
44
  export { RichMessageEditor, RichMessageViewer, type DynamicContent, } from './RichMessageEditor';
45
45
  export { FrequencyCappingEdit, FrequencyCappingReadonly, FrequencyCappingInApps, type LocalCapping, type CappingInAppBase, } from './FrequencyCapping';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/dumb-components",
3
- "version": "1.1.122",
3
+ "version": "1.1.123",
4
4
  "description": "React components to build Pushwoosh products",
5
5
  "main": "index.js",
6
6
  "module": "index.js",