@versini/ui-bubble 9.0.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,11 +64,11 @@ function App() {
64
64
  <Bubble
65
65
  kind="right"
66
66
  tail
67
- footer={{
68
- Sent: "12:00 PM",
69
- Delivered: "12:01 PM",
70
- Read: "12:02 PM"
71
- }}
67
+ footer={[
68
+ { key: "Sent", value: "12:00 PM" },
69
+ { key: "Delivered", value: "12:01 PM" },
70
+ { key: "Read", value: "12:02 PM" }
71
+ ]}
72
72
  >
73
73
  Message with delivery status footer.
74
74
  </Bubble>
@@ -137,7 +137,7 @@ function ChatExample() {
137
137
  key={message.id}
138
138
  kind={message.kind}
139
139
  tail
140
- footer={{ Time: message.time }}
140
+ footer={[{ key: "Time", value: message.time }]}
141
141
  copyToClipboard
142
142
  >
143
143
  {message.text}
@@ -152,22 +152,23 @@ function ChatExample() {
152
152
 
153
153
  ```tsx
154
154
  import { Bubble } from "@versini/ui-bubble/bubble";
155
- import { BUBBLE_FOOTER_EMPTY } from "@versini/ui-bubble/constants";
155
+ import { FOOTER_EMPTY } from "@versini/ui-bubble/constants";
156
156
 
157
157
  function AdvancedFooterExample() {
158
158
  return (
159
159
  <div className="space-y-4">
160
- {/* Structured footer with empty row */}
160
+ {/* Structured footer with empty row and value-only item */}
161
161
  <Bubble
162
162
  kind="right"
163
163
  tail
164
- footer={{
165
- "Message ID": "msg-123",
166
- Sent: "2:30 PM",
167
- Status: BUBBLE_FOOTER_EMPTY, // Empty row that maintains height
168
- Delivered: "2:31 PM",
169
- Read: "2:35 PM"
170
- }}
164
+ footer={[
165
+ { key: "Message ID", value: "msg-123" },
166
+ { key: "Sent", value: "2:30 PM" },
167
+ FOOTER_EMPTY, // Empty row that maintains height
168
+ { key: "Delivered", value: "2:31 PM" },
169
+ { key: "Read", value: "2:35 PM" },
170
+ { value: "12/22/2025 2:36 PM EDT" } // Value only, no key displayed
171
+ ]}
171
172
  >
172
173
  Message with detailed tracking information.
173
174
  </Bubble>
@@ -266,20 +267,40 @@ function CopyFunctionalityExample() {
266
267
 
267
268
  ### Bubble Props
268
269
 
269
- | Prop | Type | Default | Description |
270
- | ------------------------ | ---------------------------------------------------------- | ---------- | ------------------------------------------------------- |
271
- | children | `React.ReactNode` | - | The text to render in the bubble |
272
- | kind | `"left" \| "right"` | `"left"` | The type of Bubble (changes color and chevron location) |
273
- | tail | `boolean` | `false` | Whether or not the Bubble should have a tail |
274
- | copyToClipboard | `boolean \| string \| ((text: any) => void)` | - | Copy functionality configuration |
275
- | footer | `{ [key: string]: string \| number \| undefined \| null }` | - | Object depicting the extra rows for the Bubble footer |
276
- | rawFooter | `React.ReactNode` | - | Same as "footer" but accepts raw JSX |
277
- | noMaxWidth | `boolean` | `false` | Whether to disable default responsive max-width |
278
- | className | `string` | - | CSS class(es) to add to the main component wrapper |
279
- | contentClassName | `string` | - | CSS class(es) to add to the content wrapper |
280
- | copyToClipboardMode | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | The mode of Copy Button |
281
- | copyToClipboardFocusMode | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | The focus mode for the Copy Button |
270
+ | Prop | Type | Default | Description |
271
+ | ------------------------ | ----------------------------------------------- | ---------- | ------------------------------------------------------- |
272
+ | children | `React.ReactNode` | - | The text to render in the bubble |
273
+ | kind | `"left" \| "right"` | `"left"` | The type of Bubble (changes color and chevron location) |
274
+ | tail | `boolean` | `false` | Whether or not the Bubble should have a tail |
275
+ | copyToClipboard | `boolean \| string \| ((text: any) => void)` | - | Copy functionality configuration |
276
+ | footer | `BubbleFooter` (see below) | - | Array of footer items for the Bubble |
277
+ | rawFooter | `React.ReactNode` | - | Same as "footer" but accepts raw JSX |
278
+ | noMaxWidth | `boolean` | `false` | Whether to disable default responsive max-width |
279
+ | className | `string` | - | CSS class(es) to add to the main component wrapper |
280
+ | contentClassName | `string` | - | CSS class(es) to add to the content wrapper |
281
+ | copyToClipboardMode | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | The mode of Copy Button |
282
+ | copyToClipboardFocusMode | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | The focus mode for the Copy Button |
283
+
284
+ ### Footer Types
285
+
286
+ The `footer` prop accepts an array of footer items with the following types:
287
+
288
+ ```typescript
289
+ // Key-value pair: renders as "key: value"
290
+ {
291
+ key: string;
292
+ value: string | number;
293
+ }
294
+
295
+ // Value only: renders just the value without a key prefix
296
+ {
297
+ value: string | number;
298
+ }
299
+
300
+ // Empty row: maintains height for layout consistency
301
+ FOOTER_EMPTY;
302
+ ```
282
303
 
283
304
  ### Special Values
284
305
 
285
- - `BUBBLE_FOOTER_EMPTY` - Import from `@versini/ui-bubble/constants` to create an empty footer row that maintains height
306
+ - `FOOTER_EMPTY` - Import from `@versini/ui-bubble/constants` to create an empty footer row that maintains height
@@ -1,12 +1,12 @@
1
1
  /*!
2
- @versini/ui-bubble v9.0.1
2
+ @versini/ui-bubble v10.0.0
3
3
  © 2025 gizmette.com
4
4
  */
5
5
  try {
6
6
  if (!window.__VERSINI_UI_BUBBLE__) {
7
7
  window.__VERSINI_UI_BUBBLE__ = {
8
- version: "9.0.1",
9
- buildTime: "12/22/2025 10:34 AM EST",
8
+ version: "10.0.0",
9
+ buildTime: "12/22/2025 02:27 PM EST",
10
10
  homepage: "https://www.npmjs.com/package/@versini/ui-bubble",
11
11
  license: "MIT",
12
12
  };
@@ -1,12 +1,12 @@
1
1
  /*!
2
- @versini/ui-bubble v9.0.1
2
+ @versini/ui-bubble v10.0.0
3
3
  © 2025 gizmette.com
4
4
  */
5
5
  try {
6
6
  if (!window.__VERSINI_UI_BUBBLE__) {
7
7
  window.__VERSINI_UI_BUBBLE__ = {
8
- version: "9.0.1",
9
- buildTime: "12/22/2025 10:34 AM EST",
8
+ version: "10.0.0",
9
+ buildTime: "12/22/2025 02:27 PM EST",
10
10
  homepage: "https://www.npmjs.com/package/@versini/ui-bubble",
11
11
  license: "MIT",
12
12
  };
@@ -19,7 +19,6 @@ import { jsx, jsxs } from "react/jsx-runtime";
19
19
  import { ButtonIcon } from "@versini/ui-button/button-icon";
20
20
  import { IconCopied, IconCopy } from "@versini/ui-icons";
21
21
  import { useEffect, useState } from "react";
22
- import { BUBBLE_FOOTER_EMPTY } from "../BubbleConstants/BubbleConstants.js";
23
22
  import { getBubbleClasses } from "./utilities.js";
24
23
 
25
24
  ;// CONCATENATED MODULE: external "react/jsx-runtime"
@@ -30,8 +29,6 @@ import { getBubbleClasses } from "./utilities.js";
30
29
 
31
30
  ;// CONCATENATED MODULE: external "react"
32
31
 
33
- ;// CONCATENATED MODULE: external "../BubbleConstants/BubbleConstants.js"
34
-
35
32
  ;// CONCATENATED MODULE: external "./utilities.js"
36
33
 
37
34
  ;// CONCATENATED MODULE: ./src/components/Bubble/Bubble.tsx
@@ -40,7 +37,16 @@ import { getBubbleClasses } from "./utilities.js";
40
37
 
41
38
 
42
39
 
43
-
40
+ /**
41
+ * Type guard to check if a footer item is the BUBBLE_FOOTER_EMPTY constant.
42
+ */ const isFooterEmpty = (item)=>{
43
+ return typeof item === "object" && item !== null && "isEmpty" in item && item.isEmpty === true;
44
+ };
45
+ /**
46
+ * Type guard to check if a footer item has a key (key-value pair).
47
+ */ const isFooterKeyValue = (item)=>{
48
+ return typeof item === "object" && item !== null && "key" in item && "value" in item;
49
+ };
44
50
  const Bubble = ({ children, kind = "left", className, contentClassName, footer, rawFooter, copyToClipboard, copyToClipboardFocusMode = "system", copyToClipboardMode = "system", noMaxWidth = false, tail = false, gradient })=>{
45
51
  const [copied, setCopied] = useState(false);
46
52
  const bubbleClasses = getBubbleClasses({
@@ -86,10 +92,11 @@ const Bubble = ({ children, kind = "left", className, contentClassName, footer,
86
92
  className: bubbleClasses.main,
87
93
  children: children
88
94
  }),
89
- footer && Object.keys(footer).map((key, idx)=>{
90
- const footerValue = footer[key];
91
- // Handle the FOOTER_EMPTY case
92
- if (footerValue === BUBBLE_FOOTER_EMPTY) {
95
+ footer && footer.map((item, idx)=>{
96
+ /**
97
+ * Handle FOOTER_EMPTY - creates an empty row that maintains height.
98
+ * This prevents layout shifts when the last bubble is received.
99
+ */ if (isFooterEmpty(item)) {
93
100
  return /*#__PURE__*/ jsx("div", {
94
101
  className: "prose-p:m-0",
95
102
  children: /*#__PURE__*/ jsx("p", {
@@ -100,20 +107,34 @@ const Bubble = ({ children, kind = "left", className, contentClassName, footer,
100
107
  children: "\xa0"
101
108
  })
102
109
  })
103
- }, `${key}-${idx}`);
110
+ }, `footer-empty-${idx}`);
111
+ }
112
+ /**
113
+ * Handle key-value pairs: `{ key: "Sent", value: "12:00 PM" }`
114
+ * Renders as "Sent: 12:00 PM"
115
+ */ if (isFooterKeyValue(item)) {
116
+ return /*#__PURE__*/ jsx("div", {
117
+ className: "prose-p:m-0",
118
+ children: /*#__PURE__*/ jsxs("p", {
119
+ className: bubbleClasses.footer,
120
+ children: [
121
+ item.key,
122
+ ": ",
123
+ item.value
124
+ ]
125
+ })
126
+ }, `footer-kv-${idx}`);
104
127
  }
105
- // Handle normal footer values (only if they exist)
106
- return footerValue ? /*#__PURE__*/ jsx("div", {
128
+ /**
129
+ * Handle value-only items: `{ value: "12:00 PM" }`
130
+ * Renders just the value without a key prefix.
131
+ */ return /*#__PURE__*/ jsx("div", {
107
132
  className: "prose-p:m-0",
108
- children: /*#__PURE__*/ jsxs("p", {
133
+ children: /*#__PURE__*/ jsx("p", {
109
134
  className: bubbleClasses.footer,
110
- children: [
111
- key,
112
- ": ",
113
- footerValue
114
- ]
135
+ children: item.value
115
136
  })
116
- }, `${key}-${idx}`) : null;
137
+ }, `footer-val-${idx}`);
117
138
  }),
118
139
  rawFooter && rawFooter
119
140
  ]
@@ -1,3 +1,28 @@
1
+ import type { BUBBLE_FOOTER_EMPTY } from "../BubbleConstants/BubbleConstants";
2
+ /**
3
+ * A footer item with both key and value displayed as "key: value".
4
+ */
5
+ export type BubbleFooterKeyValue = {
6
+ key: string;
7
+ value: string | number;
8
+ };
9
+ /**
10
+ * A footer item with only the value displayed (no key).
11
+ */
12
+ export type BubbleFooterValueOnly = {
13
+ value: string | number;
14
+ };
15
+ /**
16
+ * A single footer item that can be:
17
+ * - A key-value pair: `{ key: "Sent", value: "12:00 PM" }` → renders as "Sent: 12:00 PM"
18
+ * - A value-only item: `{ value: "12:00 PM" }` → renders as "12:00 PM"
19
+ * - An empty placeholder: `FOOTER_EMPTY` → renders an invisible row that maintains height
20
+ */
21
+ export type BubbleFooterItem = BubbleFooterKeyValue | BubbleFooterValueOnly | typeof BUBBLE_FOOTER_EMPTY;
22
+ /**
23
+ * Array of footer items for the Bubble component.
24
+ */
25
+ export type BubbleFooter = BubbleFooterItem[];
1
26
  export type BubbleProps = {
2
27
  /**
3
28
  * The text to render in the bubble.
@@ -33,27 +58,29 @@ export type BubbleProps = {
33
58
  */
34
59
  copyToClipboardMode?: "dark" | "light" | "system" | "alt-system";
35
60
  /**
36
- * Object depicting the extra rows for the Bubble.
61
+ * Array of footer items to display below the bubble content.
37
62
  * @example
38
63
  * ```tsx
39
- * import { BUBBLE_FOOTER_EMPTY } from "@versini/ui-bubble/constants";
64
+ * import { FOOTER_EMPTY } from "@versini/ui-bubble/constants";
40
65
  *
41
66
  * <Bubble
42
- * footer={{
43
- * "Sent": "12:00 PM",
44
- * "Delivered": "12:01 PM",
45
- * "Verified": BUBBLE_FOOTER_EMPTY,
46
- * "Read": "12:02 PM",
47
- * }}
67
+ * footer={[
68
+ * { key: "Sent", value: "12:00 PM" },
69
+ * { key: "Delivered", value: "12:01 PM" },
70
+ * FOOTER_EMPTY,
71
+ * { key: "Read", value: "12:02 PM" },
72
+ * { value: "12/22/2025 12:03 PM EDT" },
73
+ * ]}
48
74
  * >Hello World</Bubble>
49
75
  * ```
50
- * This will create a footer with 4 rows, with the row before last being
51
- * empty but taking the full height of a normal row.
52
- *
76
+ * This will create a footer with 5 rows:
77
+ * - "Sent: 12:00 PM"
78
+ * - "Delivered: 12:01 PM"
79
+ * - An empty row (maintains height for layout consistency)
80
+ * - "Read: 12:02 PM"
81
+ * - "12/22/2025 12:03 PM EDT" (value only, no key)
53
82
  */
54
- footer?: {
55
- [key: string]: string | number | undefined | null;
56
- };
83
+ footer?: BubbleFooter;
57
84
  /**
58
85
  * The gradient intensity applied to the Bubble background.
59
86
  * - "light": Subtle gradient with 80% opacity at the top
@@ -1,12 +1,12 @@
1
1
  /*!
2
- @versini/ui-bubble v9.0.1
2
+ @versini/ui-bubble v10.0.0
3
3
  © 2025 gizmette.com
4
4
  */
5
5
  try {
6
6
  if (!window.__VERSINI_UI_BUBBLE__) {
7
7
  window.__VERSINI_UI_BUBBLE__ = {
8
- version: "9.0.1",
9
- buildTime: "12/22/2025 10:34 AM EST",
8
+ version: "10.0.0",
9
+ buildTime: "12/22/2025 02:27 PM EST",
10
10
  homepage: "https://www.npmjs.com/package/@versini/ui-bubble",
11
11
  license: "MIT",
12
12
  };
@@ -1,12 +1,12 @@
1
1
  /*!
2
- @versini/ui-bubble v9.0.1
2
+ @versini/ui-bubble v10.0.0
3
3
  © 2025 gizmette.com
4
4
  */
5
5
  try {
6
6
  if (!window.__VERSINI_UI_BUBBLE__) {
7
7
  window.__VERSINI_UI_BUBBLE__ = {
8
- version: "9.0.1",
9
- buildTime: "12/22/2025 10:34 AM EST",
8
+ version: "10.0.0",
9
+ buildTime: "12/22/2025 02:27 PM EST",
10
10
  homepage: "https://www.npmjs.com/package/@versini/ui-bubble",
11
11
  license: "MIT",
12
12
  };
@@ -1 +1,20 @@
1
- export declare const BUBBLE_FOOTER_EMPTY = "FOOTER_EMPTY";
1
+ /**
2
+ * A special footer item that creates an empty row maintaining height.
3
+ * Useful for layout consistency when a row needs to be reserved but empty.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * import { BUBBLE_FOOTER_EMPTY } from "@versini/ui-bubble/constants";
8
+ *
9
+ * <Bubble
10
+ * footer={[
11
+ * { key: "Sent", value: "12:00 PM" },
12
+ * BUBBLE_FOOTER_EMPTY,
13
+ * { key: "Read", value: "12:02 PM" },
14
+ * ]}
15
+ * >Hello World</Bubble>
16
+ * ```
17
+ */
18
+ export declare const BUBBLE_FOOTER_EMPTY: {
19
+ readonly isEmpty: true;
20
+ };
@@ -1,12 +1,12 @@
1
1
  /*!
2
- @versini/ui-bubble v9.0.1
2
+ @versini/ui-bubble v10.0.0
3
3
  © 2025 gizmette.com
4
4
  */
5
5
  try {
6
6
  if (!window.__VERSINI_UI_BUBBLE__) {
7
7
  window.__VERSINI_UI_BUBBLE__ = {
8
- version: "9.0.1",
9
- buildTime: "12/22/2025 10:34 AM EST",
8
+ version: "10.0.0",
9
+ buildTime: "12/22/2025 02:27 PM EST",
10
10
  homepage: "https://www.npmjs.com/package/@versini/ui-bubble",
11
11
  license: "MIT",
12
12
  };
@@ -18,6 +18,24 @@ try {
18
18
 
19
19
  ;// CONCATENATED MODULE: ./src/components/BubbleConstants/BubbleConstants.tsx
20
20
  // Side-effect free constants for ui-bubble.
21
- const BUBBLE_FOOTER_EMPTY = "FOOTER_EMPTY";
21
+ /**
22
+ * A special footer item that creates an empty row maintaining height.
23
+ * Useful for layout consistency when a row needs to be reserved but empty.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * import { BUBBLE_FOOTER_EMPTY } from "@versini/ui-bubble/constants";
28
+ *
29
+ * <Bubble
30
+ * footer={[
31
+ * { key: "Sent", value: "12:00 PM" },
32
+ * BUBBLE_FOOTER_EMPTY,
33
+ * { key: "Read", value: "12:02 PM" },
34
+ * ]}
35
+ * >Hello World</Bubble>
36
+ * ```
37
+ */ const BUBBLE_FOOTER_EMPTY = {
38
+ isEmpty: true
39
+ };
22
40
 
23
41
  export { BUBBLE_FOOTER_EMPTY };
@@ -1,4 +1,5 @@
1
1
  export * from "../common/constants";
2
2
  export * from "./Bubble/Bubble";
3
3
  export type * from "./Bubble/BubbleTypes";
4
+ export type { BubbleFooter, BubbleFooterItem, BubbleFooterKeyValue, BubbleFooterValueOnly, } from "./Bubble/BubbleTypes";
4
5
  export { BUBBLE_FOOTER_EMPTY } from "./BubbleConstants/BubbleConstants";
@@ -1,12 +1,12 @@
1
1
  /*!
2
- @versini/ui-bubble v9.0.1
2
+ @versini/ui-bubble v10.0.0
3
3
  © 2025 gizmette.com
4
4
  */
5
5
  try {
6
6
  if (!window.__VERSINI_UI_BUBBLE__) {
7
7
  window.__VERSINI_UI_BUBBLE__ = {
8
- version: "9.0.1",
9
- buildTime: "12/22/2025 10:34 AM EST",
8
+ version: "10.0.0",
9
+ buildTime: "12/22/2025 02:27 PM EST",
10
10
  homepage: "https://www.npmjs.com/package/@versini/ui-bubble",
11
11
  license: "MIT",
12
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-bubble",
3
- "version": "9.0.1",
3
+ "version": "10.0.0",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -58,5 +58,5 @@
58
58
  "sideEffects": [
59
59
  "**/*.css"
60
60
  ],
61
- "gitHead": "934b8175b9c5408b0bb8fb5d15a1b198d36be6b9"
61
+ "gitHead": "14e58b399a479e202fc1e384ad6e8170665d0244"
62
62
  }