beacon-ui 3.4.8 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.5.0] - 2026-01-09
9
+
10
+ ### Added
11
+ - Toast component with support for default, success, error, and warning variants
12
+ - Toast component includes action buttons, dismiss controls, and configurable icons
13
+ - Toast component supports border styling via `showBorder` prop
14
+ - Tab and TabItem components for navigation and content organization
15
+ - Tab component supports segmented control patterns with consistent styling
16
+
17
+ ### Changed
18
+ - Toast component updated with improved typography (regular font size) and padding (8px vertical, 12px horizontal)
19
+ - Toast component height now calculated dynamically from padding and line height instead of fixed height
20
+ - Select component dropdown items now have size-specific styling:
21
+ - Small/Medium: 16px icons, extra-small/small font sizes, 8px/12px padding
22
+ - Large/XL: 20px icons, regular font size, 12px/16px padding
23
+ - Input component iconOnly mode now maintains square dimensions (width equals height) for all size variants
24
+ - Slider component enhanced with desktop hover preview tooltip showing value at cursor position
25
+ - Slider component enhanced with mobile long-press preview (500ms) showing value before setting
26
+
27
+ ### Fixed
28
+ - Select component dropdown items now correctly match design specifications for each size variant
29
+ - Input component iconOnly mode now properly sizes to square dimensions matching component guidelines
30
+
31
+ ## [3.5.1] - 2026-01-09
32
+
33
+ ### Changed
34
+ - Updated design token variables across the system
35
+
8
36
  ## [3.4.8] - 2026-01-08
9
37
 
10
38
  ### Added
@@ -208,7 +208,7 @@ export function Button({ variant = "filled", size = "md", cornerRadius = 2, star
208
208
  }
209
209
  else if (variant === "outline") {
210
210
  stateStyles.backgroundColor = "var(--bg-primary)";
211
- stateStyles.color = "var(--fg-neutral)";
211
+ stateStyles.color = "var(--fg-on-action)";
212
212
  stateStyles.borderColor = "var(--bg-primary)";
213
213
  }
214
214
  else if (variant === "link") {
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAA+C,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAGrG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAWD,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC5G,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;CAChE;AAmED,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,IAAW,EACX,MAAM,EAAE,UAAU,EAClB,SAAgB,EAChB,aAAoB,EACpB,WAAkB,EAClB,SAAS,EACT,OAAO,EACP,aAAa,EACb,OAAY,EACZ,QAAQ,EACR,SAAgB,EAChB,YAAY,EACZ,SAAS,EACT,KAAK,EACL,EAAE,EACF,GAAG,IAAI,EACR,EAAE,WAAW,2CA4Tb"}
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAA+C,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAGrG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAWD,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC5G,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;CAChE;AAmED,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,IAAW,EACX,MAAM,EAAE,UAAU,EAClB,SAAgB,EAChB,aAAoB,EACpB,WAAkB,EAClB,SAAS,EACT,OAAO,EACP,aAAa,EACb,OAAY,EACZ,QAAQ,EACR,SAAgB,EAChB,YAAY,EACZ,SAAS,EACT,KAAK,EACL,EAAE,EACF,GAAG,IAAI,EACR,EAAE,WAAW,2CA0Vb"}
@@ -187,34 +187,65 @@ export function Select({ label, size = "md", status: statusProp, showLabel = tru
187
187
  ? CORNER_RADIUS_MAP[cornerRadius]
188
188
  : sizeConfig.borderRadius,
189
189
  overflow: "hidden",
190
- zIndex: 1000,
190
+ zIndex: 9999,
191
191
  };
192
192
  return baseStyles;
193
193
  }, [isOpen, status, sizeConfig, cornerRadius]);
194
194
  const getOptionStyles = (isSelected, isHovered, isLast, isFirst) => {
195
- const borderRadius = cornerRadius !== undefined
196
- ? CORNER_RADIUS_MAP[cornerRadius]
197
- : sizeConfig.borderRadius;
195
+ // Border radius for dropdown items: use corner-radius-200 for lg/xl, otherwise use sizeConfig.borderRadius
196
+ const itemBorderRadius = (size === "lg" || size === "xl")
197
+ ? "var(--corner-radius-200)"
198
+ : (cornerRadius !== undefined ? CORNER_RADIUS_MAP[cornerRadius] : sizeConfig.borderRadius);
199
+ // Padding based on size
200
+ let padding;
201
+ if (size === "sm") {
202
+ padding = "var(--spacing-200)"; // 8px all around
203
+ }
204
+ else if (size === "md") {
205
+ padding = "var(--spacing-300) var(--spacing-200)"; // 12px vertical, 8px horizontal
206
+ }
207
+ else {
208
+ // lg and xl
209
+ padding = "var(--spacing-400) var(--spacing-300)"; // 16px vertical, 12px horizontal
210
+ }
211
+ // Gap based on size
212
+ const gap = size === "sm" ? "var(--spacing-100)" : "var(--spacing-200)";
213
+ // Font size and line height based on size
214
+ let fontSize;
215
+ let lineHeight;
216
+ if (size === "sm") {
217
+ fontSize = "var(--fonts-body-extra-small-text-size)";
218
+ lineHeight = "var(--fonts-body-extra-small-line-height)";
219
+ }
220
+ else if (size === "md") {
221
+ fontSize = "var(--fonts-body-small-text-size)";
222
+ lineHeight = "var(--fonts-body-small-line-height)";
223
+ }
224
+ else {
225
+ // lg and xl
226
+ fontSize = "var(--fonts-body-regular-text-size)";
227
+ lineHeight = "var(--fonts-body-regular-line-height)";
228
+ }
198
229
  const baseStyles = {
199
230
  display: "flex",
200
231
  alignItems: "center",
201
- gap: size === "sm" || size === "md" ? "var(--spacing-100)" : "var(--spacing-200)",
202
- padding: size === "sm" || size === "md" ? "var(--spacing-200)" : "var(--spacing-200) var(--spacing-200) var(--spacing-300) var(--spacing-200)",
232
+ gap,
233
+ padding,
203
234
  cursor: "pointer",
204
235
  border: "none",
205
236
  borderTop: isFirst ? "var(--border-width-25) solid var(--border-strong-100)" : "none",
206
237
  borderLeft: "var(--border-width-25) solid var(--border-strong-100)",
207
238
  borderRight: "var(--border-width-25) solid var(--border-strong-100)",
208
239
  borderBottom: isLast ? "var(--border-width-25) solid var(--border-strong-100)" : "var(--border-width-25) solid var(--border-strong-100)",
209
- borderTopLeftRadius: isFirst ? borderRadius : 0,
210
- borderTopRightRadius: isFirst ? borderRadius : 0,
211
- borderBottomLeftRadius: isLast ? borderRadius : 0,
212
- borderBottomRightRadius: isLast ? borderRadius : 0,
240
+ borderTopLeftRadius: isFirst ? itemBorderRadius : 0,
241
+ borderTopRightRadius: isFirst ? itemBorderRadius : 0,
242
+ borderBottomLeftRadius: isLast ? itemBorderRadius : 0,
243
+ borderBottomRightRadius: isLast ? itemBorderRadius : 0,
213
244
  backgroundColor: isSelected ? "var(--bg-primary-tonal)" : "var(--bg-page-primary)",
214
245
  color: isSelected ? "var(--fg-primary-on-tonal)" : "var(--fg-neutral-tertiary)",
215
246
  fontFamily: "var(--font-secondary)",
216
- fontSize: size === "sm" || size === "md" ? "var(--body-extra-small-text-size)" : size === "lg" ? "var(--body-small-text-size)" : "var(--body-regular-text-size)",
217
- lineHeight: size === "sm" || size === "md" ? "var(--body-extra-small-line-height)" : size === "lg" ? "var(--body-small-line-height)" : "var(--body-regular-line-height)",
247
+ fontSize,
248
+ lineHeight,
218
249
  transition: "background-color 0.15s ease, color 0.15s ease",
219
250
  };
220
251
  if (isHovered && !isSelected) {
@@ -230,6 +261,7 @@ export function Select({ label, size = "md", status: statusProp, showLabel = tru
230
261
  gap: "var(--spacing-100)",
231
262
  width: fullWidth ? "100%" : "fit-content",
232
263
  position: "relative",
264
+ overflow: "visible",
233
265
  }, children: [showLabel && label && (_jsx("label", { htmlFor: selectId, style: labelStyles, children: label })), _jsxs("button", { id: selectId, type: "button", onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { ...containerStyles, ...style }, className: className, "aria-expanded": isOpen || status === "active", "aria-haspopup": "listbox", "aria-disabled": status === "disabled", "aria-labelledby": showLabel && label ? undefined : undefined, disabled: status === "disabled", ...rest, children: [showStartIcon && (_jsx("div", { style: {
234
266
  color: status === "disabled" ? "var(--fg-disabled)" : "var(--fg-neutral)",
235
267
  flexShrink: 0,
@@ -253,24 +285,25 @@ export function Select({ label, size = "md", status: statusProp, showLabel = tru
253
285
  const isHovered = hoveredOptionIndex === index;
254
286
  const isLast = index === options.length - 1;
255
287
  const isFirst = index === 0;
256
- return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, style: getOptionStyles(isSelected, isHovered, isLast, isFirst), onClick: () => handleOptionClick(option.value), onMouseEnter: () => setHoveredOptionIndex(index), onMouseLeave: () => setHoveredOptionIndex(null), children: [option.icon && (_jsx("div", { style: {
288
+ return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, style: {
289
+ ...getOptionStyles(isSelected, isHovered, isLast, isFirst),
290
+ touchAction: "manipulation", // Allow touch scrolling while still enabling taps
291
+ }, onClick: () => handleOptionClick(option.value), onMouseEnter: () => setHoveredOptionIndex(index), onMouseLeave: () => setHoveredOptionIndex(null), children: [option.icon && (_jsx("div", { style: {
257
292
  display: "flex",
258
293
  alignItems: "center",
259
294
  justifyContent: "center",
260
- ...(typeof sizeConfig.iconSize === "number"
261
- ? { width: `${sizeConfig.iconSize}px`, height: `${sizeConfig.iconSize}px` }
262
- : { width: `var(--icon-size-${sizeConfig.iconSize})`, height: `var(--icon-size-${sizeConfig.iconSize})` }),
295
+ width: size === "sm" || size === "md" ? "16px" : "20px",
296
+ height: size === "sm" || size === "md" ? "16px" : "20px",
263
297
  flexShrink: 0,
264
298
  color: isSelected ? "var(--fg-primary-on-tonal)" : "var(--fg-neutral-tertiary)",
265
299
  }, children: option.icon })), _jsx("span", { style: { flex: "1 0 0", minWidth: 0, textAlign: "left" }, children: option.label }), isSelected && (_jsx("div", { style: {
266
300
  display: "flex",
267
301
  alignItems: "center",
268
302
  justifyContent: "center",
269
- ...(typeof sizeConfig.iconSize === "number"
270
- ? { width: `${sizeConfig.iconSize}px`, height: `${sizeConfig.iconSize}px` }
271
- : { width: `var(--icon-size-${sizeConfig.iconSize})`, height: `var(--icon-size-${sizeConfig.iconSize})` }),
303
+ width: size === "sm" || size === "md" ? "16px" : "20px",
304
+ height: size === "sm" || size === "md" ? "16px" : "20px",
272
305
  flexShrink: 0,
273
306
  color: "var(--fg-primary-on-tonal)",
274
- }, children: _jsx(CheckIcon, { size: sizeConfig.iconSize }) }))] }, option.value));
307
+ }, children: _jsx(CheckIcon, { size: size === "sm" || size === "md" ? 16 : 20 }) }))] }, option.value));
275
308
  }) }))] }));
276
309
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../src/components/Slider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA0C,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAGtF,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IACtD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD,wBAAgB,MAAM,CAAC,EACrB,KAAU,EACV,GAAO,EACP,GAAS,EACT,IAAQ,EACR,KAAa,EACb,UAAU,EACV,QAAQ,EACR,WAAmB,EACnB,SAAiB,EACjB,SAAc,EACd,UAAU,EACV,SAAgB,EAChB,KAAgB,EAChB,MAAM,EAAE,UAAU,EAClB,QAAgB,EAChB,SAAS,EACT,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,GAAG,IAAI,EACR,EAAE,WAAW,2CAocb"}
1
+ {"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../src/components/Slider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqD,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAGjG,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IACtD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD,wBAAgB,MAAM,CAAC,EACrB,KAAU,EACV,GAAO,EACP,GAAS,EACT,IAAQ,EACR,KAAa,EACb,UAAU,EACV,QAAQ,EACR,WAAmB,EACnB,SAAiB,EACjB,SAAc,EACd,UAAU,EACV,SAAgB,EAChB,KAAgB,EAChB,MAAM,EAAE,UAAU,EAClB,QAAgB,EAChB,SAAS,EACT,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,GAAG,IAAI,EACR,EAAE,WAAW,2CAkrBb"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback, useMemo, useRef } from "react";
3
+ import { useState, useCallback, useMemo, useRef, useEffect } from "react";
4
4
  import { useThemeSafe } from "../providers/ThemeProvider";
5
5
  const TRACK_HEIGHT = 8;
6
6
  const THUMB_SIZE = 16;
@@ -10,6 +10,12 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
10
10
  const [internalStatus, setInternalStatus] = useState("default");
11
11
  const [isDragging, setIsDragging] = useState(false);
12
12
  const [activeThumb, setActiveThumb] = useState(null);
13
+ const [previewValue, setPreviewValue] = useState(null);
14
+ const [previewPosition, setPreviewPosition] = useState(null);
15
+ const [isLongPressing, setIsLongPressing] = useState(false);
16
+ const longPressTimerRef = useRef(null);
17
+ const touchStartPositionRef = useRef(null);
18
+ const isLongPressActiveRef = useRef(false);
13
19
  const trackRef = useRef(null);
14
20
  const status = statusProp ?? internalStatus;
15
21
  const isDisabled = disabled || status === "disabled";
@@ -49,6 +55,9 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
49
55
  const rawValue = min + percentage * (max - min);
50
56
  return Math.round(rawValue / step) * step;
51
57
  }, [min, max, step, value]);
58
+ const getValueFromTouchPosition = useCallback((clientX) => {
59
+ return getValueFromPosition(clientX);
60
+ }, [getValueFromPosition]);
52
61
  const handleTrackClick = useCallback((e) => {
53
62
  if (isDisabled)
54
63
  return;
@@ -68,12 +77,130 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
68
77
  onChange?.(newValue);
69
78
  }
70
79
  }, [isDisabled, getValueFromPosition, range, currentRangeValue, onChange]);
80
+ const handleTrackMouseMove = useCallback((e) => {
81
+ if (isDisabled || !showTooltip || isDragging)
82
+ return;
83
+ const newValue = getValueFromPosition(e.clientX);
84
+ const rect = trackRef.current?.getBoundingClientRect();
85
+ if (rect) {
86
+ const percentage = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
87
+ setPreviewValue(newValue);
88
+ setPreviewPosition(percentage * 100);
89
+ }
90
+ }, [isDisabled, showTooltip, isDragging, getValueFromPosition]);
91
+ const handleTrackMouseLeave = useCallback(() => {
92
+ setPreviewValue(null);
93
+ setPreviewPosition(null);
94
+ }, []);
95
+ const handleTrackTouchStart = useCallback((e) => {
96
+ if (isDisabled)
97
+ return;
98
+ const touch = e.touches[0];
99
+ if (!touch)
100
+ return;
101
+ touchStartPositionRef.current = touch.clientX;
102
+ setIsLongPressing(false);
103
+ // Clear any existing timer
104
+ if (longPressTimerRef.current) {
105
+ clearTimeout(longPressTimerRef.current);
106
+ }
107
+ // Set up long-press timer
108
+ isLongPressActiveRef.current = false;
109
+ longPressTimerRef.current = setTimeout(() => {
110
+ if (touchStartPositionRef.current !== null && showTooltip) {
111
+ isLongPressActiveRef.current = true;
112
+ setIsLongPressing(true);
113
+ const newValue = getValueFromTouchPosition(touchStartPositionRef.current);
114
+ const rect = trackRef.current?.getBoundingClientRect();
115
+ if (rect) {
116
+ const percentage = Math.max(0, Math.min(1, (touchStartPositionRef.current - rect.left) / rect.width));
117
+ setPreviewValue(newValue);
118
+ setPreviewPosition(percentage * 100);
119
+ }
120
+ }
121
+ }, 500); // 500ms for long-press
122
+ // Handle immediate touch (for quick taps)
123
+ const handleTouchEnd = (endEvent) => {
124
+ const wasLongPress = isLongPressActiveRef.current;
125
+ if (longPressTimerRef.current) {
126
+ clearTimeout(longPressTimerRef.current);
127
+ longPressTimerRef.current = null;
128
+ }
129
+ if (wasLongPress) {
130
+ // Long-press was active, set the value
131
+ endEvent.preventDefault();
132
+ if (touchStartPositionRef.current !== null) {
133
+ const newValue = getValueFromTouchPosition(touchStartPositionRef.current);
134
+ if (range && currentRangeValue) {
135
+ const [start, end] = currentRangeValue;
136
+ const distToStart = Math.abs(newValue - start);
137
+ const distToEnd = Math.abs(newValue - end);
138
+ if (distToStart < distToEnd) {
139
+ onChange?.([newValue, end]);
140
+ }
141
+ else {
142
+ onChange?.([start, newValue]);
143
+ }
144
+ }
145
+ else {
146
+ onChange?.(newValue);
147
+ }
148
+ }
149
+ setIsLongPressing(false);
150
+ isLongPressActiveRef.current = false;
151
+ setPreviewValue(null);
152
+ setPreviewPosition(null);
153
+ }
154
+ else {
155
+ // Quick tap, set value immediately
156
+ const endTouch = endEvent.changedTouches[0];
157
+ if (endTouch) {
158
+ const newValue = getValueFromTouchPosition(endTouch.clientX);
159
+ if (range && currentRangeValue) {
160
+ const [start, end] = currentRangeValue;
161
+ const distToStart = Math.abs(newValue - start);
162
+ const distToEnd = Math.abs(newValue - end);
163
+ if (distToStart < distToEnd) {
164
+ onChange?.([newValue, end]);
165
+ }
166
+ else {
167
+ onChange?.([start, newValue]);
168
+ }
169
+ }
170
+ else {
171
+ onChange?.(newValue);
172
+ }
173
+ }
174
+ }
175
+ touchStartPositionRef.current = null;
176
+ document.removeEventListener("touchend", handleTouchEnd);
177
+ document.removeEventListener("touchcancel", handleTouchEnd);
178
+ document.removeEventListener("touchmove", handleTouchMove);
179
+ };
180
+ const handleTouchMove = (moveEvent) => {
181
+ // If user moves finger, cancel long-press
182
+ if (longPressTimerRef.current) {
183
+ clearTimeout(longPressTimerRef.current);
184
+ longPressTimerRef.current = null;
185
+ }
186
+ setIsLongPressing(false);
187
+ isLongPressActiveRef.current = false;
188
+ setPreviewValue(null);
189
+ setPreviewPosition(null);
190
+ touchStartPositionRef.current = null;
191
+ };
192
+ document.addEventListener("touchend", handleTouchEnd);
193
+ document.addEventListener("touchcancel", handleTouchEnd);
194
+ document.addEventListener("touchmove", handleTouchMove, { passive: true });
195
+ }, [isDisabled, showTooltip, getValueFromTouchPosition, range, currentRangeValue, onChange, isLongPressing]);
71
196
  const handleThumbMouseDown = useCallback((thumb) => (e) => {
72
197
  if (isDisabled)
73
198
  return;
74
199
  e.preventDefault();
75
200
  setIsDragging(true);
76
201
  setActiveThumb(thumb);
202
+ setPreviewValue(null);
203
+ setPreviewPosition(null);
77
204
  if (!statusProp) {
78
205
  setInternalStatus("active");
79
206
  }
@@ -106,6 +233,57 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
106
233
  document.addEventListener("mousemove", handleMouseMove);
107
234
  document.addEventListener("mouseup", handleMouseUp);
108
235
  }, [isDisabled, statusProp, getValueFromPosition, range, currentRangeValue, onChange]);
236
+ const handleThumbTouchStart = useCallback((thumb) => (e) => {
237
+ if (isDisabled)
238
+ return;
239
+ e.preventDefault();
240
+ setIsDragging(true);
241
+ setActiveThumb(thumb);
242
+ setPreviewValue(null);
243
+ setPreviewPosition(null);
244
+ setIsLongPressing(false);
245
+ isLongPressActiveRef.current = false;
246
+ if (longPressTimerRef.current) {
247
+ clearTimeout(longPressTimerRef.current);
248
+ longPressTimerRef.current = null;
249
+ }
250
+ if (!statusProp) {
251
+ setInternalStatus("active");
252
+ }
253
+ const handleTouchMove = (moveEvent) => {
254
+ if (moveEvent.touches.length === 0)
255
+ return;
256
+ const touch = moveEvent.touches[0];
257
+ const newValue = getValueFromTouchPosition(touch.clientX);
258
+ if (range && currentRangeValue) {
259
+ const [start, end] = currentRangeValue;
260
+ if (thumb === "start") {
261
+ const clampedValue = Math.min(newValue, end);
262
+ onChange?.([clampedValue, end]);
263
+ }
264
+ else {
265
+ const clampedValue = Math.max(newValue, start);
266
+ onChange?.([start, clampedValue]);
267
+ }
268
+ }
269
+ else {
270
+ onChange?.(newValue);
271
+ }
272
+ };
273
+ const handleTouchEnd = () => {
274
+ setIsDragging(false);
275
+ setActiveThumb(null);
276
+ if (!statusProp) {
277
+ setInternalStatus("default");
278
+ }
279
+ document.removeEventListener("touchmove", handleTouchMove);
280
+ document.removeEventListener("touchend", handleTouchEnd);
281
+ document.removeEventListener("touchcancel", handleTouchEnd);
282
+ };
283
+ document.addEventListener("touchmove", handleTouchMove, { passive: false });
284
+ document.addEventListener("touchend", handleTouchEnd);
285
+ document.addEventListener("touchcancel", handleTouchEnd);
286
+ }, [isDisabled, statusProp, getValueFromTouchPosition, range, currentRangeValue, onChange]);
109
287
  const startPercentage = useMemo(() => {
110
288
  return ((startValue - min) / (max - min)) * 100;
111
289
  }, [startValue, min, max]);
@@ -230,6 +408,14 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
230
408
  }
231
409
  return String(val);
232
410
  }, [stepLabels, min, step]);
411
+ // Cleanup long-press timer on unmount
412
+ useEffect(() => {
413
+ return () => {
414
+ if (longPressTimerRef.current) {
415
+ clearTimeout(longPressTimerRef.current);
416
+ }
417
+ };
418
+ }, []);
233
419
  const { id, ...restProps } = rest;
234
420
  return (_jsxs("div", { className: className, style: {
235
421
  display: "flex",
@@ -243,7 +429,21 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
243
429
  paddingBottom: "2px",
244
430
  height: "8px",
245
431
  overflow: "visible",
246
- }, onClick: handleTrackClick, children: _jsxs("div", { style: trackStyles, children: [_jsx("div", { style: selectionStyles }), showSteps && (_jsx("div", { style: {
432
+ touchAction: "none", // Prevent default touch behaviors for better dragging
433
+ }, onClick: handleTrackClick, onTouchStart: handleTrackTouchStart, onMouseMove: handleTrackMouseMove, onMouseLeave: handleTrackMouseLeave, children: _jsxs("div", { style: trackStyles, children: [_jsx("div", { style: selectionStyles }), showTooltip && previewValue !== null && previewPosition !== null && !isDragging && (_jsxs("div", { style: {
434
+ ...tooltipStyles,
435
+ left: `${previewPosition}%`,
436
+ }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(previewValue) }), _jsx("div", { style: {
437
+ position: "absolute",
438
+ bottom: "-6px",
439
+ left: "50%",
440
+ transform: "translateX(-50%)",
441
+ width: 0,
442
+ height: 0,
443
+ borderLeft: "6px solid transparent",
444
+ borderRight: "6px solid transparent",
445
+ borderTop: "6px solid var(--bg-page-tertiary)",
446
+ } })] })), showSteps && (_jsx("div", { style: {
247
447
  position: "absolute",
248
448
  top: 0,
249
449
  left: 0,
@@ -268,7 +468,8 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
268
468
  }) })), range ? (_jsxs(_Fragment, { children: [_jsx("div", { role: "slider", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": startValue, "aria-valuetext": getTooltipText(startValue), "aria-labelledby": id ? `${id}-label` : undefined, "aria-label": id ? undefined : `${restProps["aria-label"] || label} start`, tabIndex: isDisabled ? -1 : 0, style: {
269
469
  ...thumbStyles,
270
470
  left: `${startPercentage}%`,
271
- }, onMouseDown: handleThumbMouseDown("start"), children: showTooltip && (status === "active" || status === "hover") && (_jsxs("div", { style: { ...tooltipStyles, left: "50%" }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(startValue) }), _jsx("div", { style: {
471
+ touchAction: "none", // Prevent default touch behaviors for better dragging
472
+ }, onMouseDown: handleThumbMouseDown("start"), onTouchStart: handleThumbTouchStart("start"), children: showTooltip && (status === "active" || status === "hover") && (_jsxs("div", { style: { ...tooltipStyles, left: "50%" }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(startValue) }), _jsx("div", { style: {
272
473
  position: "absolute",
273
474
  bottom: "-6px",
274
475
  left: "50%",
@@ -281,7 +482,8 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
281
482
  } })] })) }), _jsx("div", { role: "slider", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": endValue, "aria-valuetext": getTooltipText(endValue), "aria-labelledby": id ? `${id}-label` : undefined, "aria-label": id ? undefined : `${restProps["aria-label"] || label} end`, tabIndex: isDisabled ? -1 : 0, style: {
282
483
  ...thumbStyles,
283
484
  left: `${endPercentage}%`,
284
- }, onMouseDown: handleThumbMouseDown("end"), children: showTooltip && (status === "active" || status === "hover") && (_jsxs("div", { style: { ...tooltipStyles, left: "50%" }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(endValue) }), _jsx("div", { style: {
485
+ touchAction: "none", // Prevent default touch behaviors for better dragging
486
+ }, onMouseDown: handleThumbMouseDown("end"), onTouchStart: handleThumbTouchStart("end"), children: showTooltip && (status === "active" || status === "hover") && (_jsxs("div", { style: { ...tooltipStyles, left: "50%" }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(endValue) }), _jsx("div", { style: {
285
487
  position: "absolute",
286
488
  bottom: "-6px",
287
489
  left: "50%",
@@ -294,7 +496,8 @@ export function Slider({ value = 60, min = 0, max = 100, step = 1, range = false
294
496
  } })] })) })] })) : (_jsx("div", { role: "none", "aria-hidden": "true", tabIndex: -1, style: {
295
497
  ...thumbStyles,
296
498
  left: `${startPercentage}%`,
297
- }, onMouseDown: handleThumbMouseDown("end"), children: showTooltip && (status === "active" || status === "hover") && (_jsxs("div", { style: { ...tooltipStyles, left: "50%" }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(currentSingleValue ?? 60) }), _jsx("div", { style: {
499
+ touchAction: "none", // Prevent default touch behaviors for better dragging
500
+ }, onMouseDown: handleThumbMouseDown("end"), onTouchStart: handleThumbTouchStart("end"), children: showTooltip && (status === "active" || status === "hover") && (_jsxs("div", { style: { ...tooltipStyles, left: "50%" }, children: [_jsx("p", { style: { margin: 0, textAlign: "center" }, children: getTooltipText(currentSingleValue ?? 60) }), _jsx("div", { style: {
298
501
  position: "absolute",
299
502
  bottom: "-6px",
300
503
  left: "50%",
@@ -0,0 +1,9 @@
1
+ import { ComponentPropsWithRef, ReactElement } from "react";
2
+ import type { TabItemProps } from "./TabItem";
3
+ export interface TabProps extends ComponentPropsWithRef<"div"> {
4
+ children: ReactElement<TabItemProps> | ReactElement<TabItemProps>[];
5
+ size?: TabItemProps["size"];
6
+ tabStyle?: TabItemProps["tabStyle"];
7
+ }
8
+ export declare function Tab({ children, size, tabStyle, className, style: inlineStyle, ...rest }: TabProps): import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=Tab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../src/components/Tab.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,qBAAqB,EAAE,YAAY,EAAgC,MAAM,OAAO,CAAC;AAEnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,WAAW,QAAS,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAC5D,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;IACpE,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;CACrC;AAED,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,IAAW,EACX,QAAoB,EACpB,SAAS,EACT,KAAK,EAAE,WAAW,EAClB,GAAG,IAAI,EACR,EAAE,QAAQ,2CAgCV"}
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useMemo, cloneElement, isValidElement } from "react";
4
+ import { useThemeSafe } from "../providers/ThemeProvider";
5
+ export function Tab({ children, size = "sm", tabStyle = "default", className, style: inlineStyle, ...rest }) {
6
+ useThemeSafe();
7
+ const containerStyles = useMemo(() => {
8
+ return {
9
+ display: "inline-flex",
10
+ gap: 0,
11
+ backgroundColor: "var(--bg-page-primary)",
12
+ border: "var(--border-width-25) solid var(--border-strong-200)",
13
+ borderRadius: tabStyle === "pill" ? "var(--corner-radius-200)" : "var(--corner-radius-200)",
14
+ padding: "2px",
15
+ overflow: "hidden",
16
+ ...inlineStyle,
17
+ };
18
+ }, [tabStyle, inlineStyle]);
19
+ const childrenArray = Array.isArray(children) ? children : [children];
20
+ return (_jsx("div", { className: className, style: containerStyles, role: "tablist", ...rest, children: childrenArray.map((child, index) => {
21
+ if (isValidElement(child)) {
22
+ return cloneElement(child, {
23
+ key: child.key || index,
24
+ size: child.props.size ?? size,
25
+ tabStyle: child.props.tabStyle ?? tabStyle,
26
+ });
27
+ }
28
+ return child;
29
+ }) }));
30
+ }
@@ -0,0 +1,13 @@
1
+ import { ComponentPropsWithRef } from "react";
2
+ export type TabItemState = "default" | "active" | "hover" | "disabled";
3
+ export type TabItemSize = "sm" | "md";
4
+ export type TabItemStyle = "default" | "pill";
5
+ export interface TabItemProps extends Omit<ComponentPropsWithRef<"button">, "onClick"> {
6
+ tabName?: string;
7
+ state?: TabItemState;
8
+ size?: TabItemSize;
9
+ tabStyle?: TabItemStyle;
10
+ onClick?: () => void;
11
+ }
12
+ export declare function TabItem({ tabName, state: stateProp, size, tabStyle, onClick, className, style: inlineStyle, disabled, onMouseEnter, onMouseLeave, ...rest }: TabItemProps): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=TabItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabItem.d.ts","sourceRoot":"","sources":["../../src/components/TabItem.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqB,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAGjE,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AACvE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAyBD,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,KAAK,EAAE,SAAS,EAChB,IAAW,EACX,QAAoB,EACpB,OAAO,EACP,SAAS,EACT,KAAK,EAAE,WAAW,EAClB,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,GAAG,IAAI,EACR,EAAE,YAAY,2CAgEd"}
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import { useThemeSafe } from "../providers/ThemeProvider";
5
+ const SIZE_CONFIG = {
6
+ sm: {
7
+ fontSize: "var(--fonts-body-small-text-size)",
8
+ lineHeight: "var(--fonts-body-small-line-height)",
9
+ paddingY: "var(--spacing-100)",
10
+ paddingX: "var(--spacing-200)",
11
+ },
12
+ md: {
13
+ fontSize: "var(--fonts-body-regular-text-size)",
14
+ lineHeight: "var(--fonts-body-regular-line-height)",
15
+ paddingY: "var(--spacing-200)",
16
+ paddingX: "var(--spacing-300)",
17
+ },
18
+ };
19
+ export function TabItem({ tabName, state: stateProp, size = "sm", tabStyle = "default", onClick, className, style: inlineStyle, disabled, onMouseEnter, onMouseLeave, ...rest }) {
20
+ useThemeSafe();
21
+ const [internalHover, setInternalHover] = useState(false);
22
+ const sizeConfig = SIZE_CONFIG[size];
23
+ const isDisabled = disabled || stateProp === "disabled";
24
+ const isActive = stateProp === "active";
25
+ const isHovered = stateProp === "hover" || (internalHover && !stateProp && !isDisabled && !isActive);
26
+ const effectiveState = stateProp ?? (isHovered ? "hover" : "default");
27
+ const buttonStyles = useMemo(() => {
28
+ const baseStyles = {
29
+ flex: "1",
30
+ padding: `${sizeConfig.paddingY} ${sizeConfig.paddingX}`,
31
+ border: "none",
32
+ backgroundColor: "transparent",
33
+ color: isDisabled ? "var(--fg-disabled)" : isActive ? "var(--fg-primary-on-tonal)" : "var(--fg-neutral)",
34
+ fontFamily: "var(--font-secondary)",
35
+ fontSize: sizeConfig.fontSize,
36
+ lineHeight: sizeConfig.lineHeight,
37
+ fontWeight: "var(--font-weight-secondary-medium)",
38
+ cursor: isDisabled ? "not-allowed" : "pointer",
39
+ transition: "background-color 0.15s ease, color 0.15s ease",
40
+ borderRadius: tabStyle === "pill" ? "var(--corner-radius-100)" : "0",
41
+ whiteSpace: "nowrap",
42
+ ...inlineStyle,
43
+ };
44
+ if (isActive) {
45
+ baseStyles.backgroundColor = "var(--bg-primary-tonal)";
46
+ }
47
+ else if (isHovered) {
48
+ baseStyles.backgroundColor = "var(--bg-page-secondary)";
49
+ }
50
+ return baseStyles;
51
+ }, [sizeConfig, isDisabled, isActive, isHovered, tabStyle, inlineStyle]);
52
+ const handleMouseEnter = (e) => {
53
+ if (!isDisabled && !stateProp) {
54
+ setInternalHover(true);
55
+ }
56
+ onMouseEnter?.(e);
57
+ };
58
+ const handleMouseLeave = (e) => {
59
+ if (!stateProp) {
60
+ setInternalHover(false);
61
+ }
62
+ onMouseLeave?.(e);
63
+ };
64
+ return (_jsx("button", { type: "button", onClick: onClick, disabled: isDisabled, className: className, style: buttonStyles, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...rest, children: tabName }));
65
+ }
@@ -0,0 +1,54 @@
1
+ import { type ComponentPropsWithRef } from "react";
2
+ export type ToastVariant = "default" | "success" | "error" | "warning";
3
+ export interface ToastProps extends ComponentPropsWithRef<"div"> {
4
+ /**
5
+ * Visual tone of the toast.
6
+ */
7
+ variant?: ToastVariant;
8
+ /**
9
+ * Primary message content.
10
+ */
11
+ message?: React.ReactNode;
12
+ /**
13
+ * Optional action label (e.g. "Undo").
14
+ */
15
+ actionLabel?: string;
16
+ /**
17
+ * Called when the action is clicked.
18
+ */
19
+ onAction?: () => void;
20
+ /**
21
+ * Toggle visibility of the action chip.
22
+ */
23
+ showAction?: boolean;
24
+ /**
25
+ * Show the dismiss control.
26
+ */
27
+ dismissible?: boolean;
28
+ /**
29
+ * Called when the close control is clicked.
30
+ */
31
+ onDismiss?: () => void;
32
+ /**
33
+ * Override the default leading icon.
34
+ */
35
+ leadingIcon?: React.ReactNode;
36
+ /**
37
+ * Hide the leading icon.
38
+ */
39
+ showIcon?: boolean;
40
+ /**
41
+ * Override the default close icon.
42
+ */
43
+ closeIcon?: React.ReactNode;
44
+ /**
45
+ * Stretch to the parent's width.
46
+ */
47
+ fullWidth?: boolean;
48
+ /**
49
+ * Show border around the toast.
50
+ */
51
+ showBorder?: boolean;
52
+ }
53
+ export declare function Toast({ variant, message, actionLabel, onAction, showAction, dismissible, onDismiss, leadingIcon, showIcon, closeIcon, fullWidth, showBorder, className, style, role, ...rest }: ToastProps): import("react/jsx-runtime").JSX.Element;
54
+ //# sourceMappingURL=Toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../src/components/Toast.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAI5D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvE,MAAM,WAAW,UAAW,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAC9D;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;OAEG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA8CD,wBAAgB,KAAK,CAAC,EACpB,OAAmB,EACnB,OAAsB,EACtB,WAAoB,EACpB,QAAQ,EACR,UAAiB,EACjB,WAAkB,EAClB,SAAS,EACT,WAAW,EACX,QAAe,EACf,SAAS,EACT,SAAiB,EACjB,UAAkB,EAClB,SAAS,EACT,KAAK,EACL,IAAe,EACf,GAAG,IAAI,EACR,EAAE,UAAU,2CA4HZ"}
@@ -0,0 +1,116 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { useThemeSafe } from "../providers/ThemeProvider";
5
+ import { CircleErrorIcon, CloseIcon } from "../icons";
6
+ const VARIANT_CONFIG = {
7
+ default: {
8
+ background: "var(--bg-page-secondary)",
9
+ text: "var(--fg-neutral)",
10
+ iconColor: "var(--fg-neutral-tertiary)",
11
+ actionColor: "var(--fg-primary)",
12
+ closeColor: "var(--fg-neutral-tertiary)",
13
+ icon: _jsx(CircleErrorIcon, { size: 20 }),
14
+ },
15
+ success: {
16
+ background: "var(--bg-success)",
17
+ text: "var(--fg-on-action)",
18
+ iconColor: "var(--fg-on-action)",
19
+ actionColor: "var(--fg-on-action)",
20
+ closeColor: "var(--fg-on-action)",
21
+ icon: _jsx(CircleErrorIcon, { size: 20 }),
22
+ },
23
+ error: {
24
+ background: "var(--bg-critical)",
25
+ text: "var(--fg-on-action)",
26
+ iconColor: "var(--fg-on-action)",
27
+ actionColor: "var(--fg-on-action)",
28
+ closeColor: "var(--fg-on-action)",
29
+ icon: _jsx(CircleErrorIcon, { size: 20 }),
30
+ },
31
+ warning: {
32
+ background: "var(--bg-warning)",
33
+ text: "var(--fg-on-action)",
34
+ iconColor: "var(--fg-on-action)",
35
+ actionColor: "var(--fg-on-action)",
36
+ closeColor: "var(--fg-on-action)",
37
+ icon: _jsx(CircleErrorIcon, { size: 20 }),
38
+ },
39
+ };
40
+ export function Toast({ variant = "default", message = "Toast Info", actionLabel = "Undo", onAction, showAction = true, dismissible = true, onDismiss, leadingIcon, showIcon = true, closeIcon, fullWidth = false, showBorder = false, className, style, role = "status", ...rest }) {
41
+ useThemeSafe();
42
+ const config = VARIANT_CONFIG[variant];
43
+ const containerStyles = useMemo(() => {
44
+ const baseStyles = {
45
+ display: "flex",
46
+ alignItems: "center",
47
+ gap: "var(--spacing-200)",
48
+ padding: "var(--spacing-200) var(--spacing-300)",
49
+ borderRadius: "var(--corner-radius-200)",
50
+ boxShadow: "var(--drop-shadow-200)",
51
+ backgroundColor: config.background,
52
+ color: config.text,
53
+ width: fullWidth ? "100%" : "300px",
54
+ ...style,
55
+ };
56
+ if (showBorder) {
57
+ baseStyles.border = "var(--border-width-25) solid var(--border-strong-200)";
58
+ }
59
+ return baseStyles;
60
+ }, [config.background, config.text, fullWidth, showBorder, style]);
61
+ const messageStyles = useMemo(() => {
62
+ return {
63
+ flex: "1 1 0%",
64
+ minWidth: 0,
65
+ fontFamily: "var(--font-secondary)",
66
+ fontSize: "var(--fonts-body-regular-text-size)",
67
+ lineHeight: "var(--fonts-body-regular-line-height)",
68
+ fontWeight: "var(--font-weight-secondary-regular)",
69
+ color: config.text,
70
+ whiteSpace: "nowrap",
71
+ overflow: "hidden",
72
+ textOverflow: "ellipsis",
73
+ };
74
+ }, [config.text]);
75
+ const actionStyles = useMemo(() => {
76
+ return {
77
+ display: "inline-flex",
78
+ alignItems: "center",
79
+ justifyContent: "center",
80
+ gap: "var(--spacing-100)",
81
+ padding: "var(--spacing-100) var(--spacing-200)",
82
+ borderRadius: "var(--corner-radius-100)",
83
+ border: "none",
84
+ backgroundColor: "transparent",
85
+ color: config.actionColor,
86
+ fontFamily: "var(--font-secondary)",
87
+ fontSize: "var(--body-small-text-size)",
88
+ lineHeight: "var(--body-small-line-height)",
89
+ fontWeight: "var(--font-weight-secondary-medium)",
90
+ cursor: onAction ? "pointer" : "default",
91
+ transition: "opacity 0.15s ease",
92
+ };
93
+ }, [config.actionColor, onAction]);
94
+ const iconNode = showIcon ? leadingIcon ?? config.icon : null;
95
+ return (_jsxs("div", { role: role, "aria-live": role === "alert" ? "assertive" : "polite", className: className, style: containerStyles, ...rest, children: [iconNode && (_jsx("span", { style: {
96
+ display: "inline-flex",
97
+ alignItems: "center",
98
+ justifyContent: "center",
99
+ width: "20px",
100
+ height: "20px",
101
+ flexShrink: 0,
102
+ color: config.iconColor,
103
+ }, children: iconNode })), _jsx("span", { style: messageStyles, children: message }), showAction && actionLabel && (_jsx("button", { type: "button", onClick: onAction, style: actionStyles, "aria-label": actionLabel, children: actionLabel })), dismissible && (_jsx("button", { type: "button", onClick: onDismiss, style: {
104
+ display: "inline-flex",
105
+ alignItems: "center",
106
+ justifyContent: "center",
107
+ width: "20px",
108
+ height: "20px",
109
+ borderRadius: "var(--corner-radius-100)",
110
+ border: "none",
111
+ backgroundColor: "transparent",
112
+ color: config.closeColor,
113
+ cursor: onDismiss ? "pointer" : "default",
114
+ flexShrink: 0,
115
+ }, "aria-label": "Dismiss notification", children: closeIcon ?? _jsx(CloseIcon, { size: 20 }) }))] }));
116
+ }
package/dist/index.d.ts CHANGED
@@ -11,6 +11,9 @@ export { Menu, type MenuItem as MenuItemData } from "./components/Menu";
11
11
  export { MenuItem, type MenuItemProps, type MenuItemState } from "./components/MenuItem";
12
12
  export { RadioButton } from "./components/RadioButton";
13
13
  export { Slider } from "./components/Slider";
14
+ export { Toast } from "./components/Toast";
15
+ export { Tab } from "./components/Tab";
16
+ export { TabItem } from "./components/TabItem";
14
17
  export type { ButtonProps, ButtonVariant, ButtonSize, CornerRadiusStep, JustifyContent, ButtonState, ButtonColor } from "./components/Button";
15
18
  export type { ButtonIconProps } from "./components/ButtonIcon";
16
19
  export type { CardProps, CardStatus, CardShadow } from "./components/Card";
@@ -23,6 +26,9 @@ export type { ChipProps, ChipSize, ChipColor } from "./components/Chip";
23
26
  export type { MenuProps } from "./components/Menu";
24
27
  export type { RadioButtonProps, RadioButtonStatus } from "./components/RadioButton";
25
28
  export type { SliderProps, SliderStatus } from "./components/Slider";
29
+ export type { ToastProps, ToastVariant } from "./components/Toast";
30
+ export type { TabProps } from "./components/Tab";
31
+ export type { TabItemProps, TabItemState, TabItemSize, TabItemStyle } from "./components/TabItem";
26
32
  export { ThemeProvider, useTheme, useThemeSafe } from "./providers/ThemeProvider";
27
33
  export type { Theme, HueVariant } from "./tokens/types";
28
34
  export type { ColorPrimitive, SemanticColor, SpacingToken, BackgroundToken, ForegroundToken, BorderToken, } from "./tokens/types";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC9I,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC3E,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC/F,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC3G,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACxE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpF,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGlF,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,eAAe,EACf,eAAe,EACf,WAAW,GACZ,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC9I,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC3E,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC/F,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC3G,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACxE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpF,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGlG,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGlF,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,eAAe,EACf,eAAe,EACf,WAAW,GACZ,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -12,5 +12,8 @@ export { Menu } from "./components/Menu";
12
12
  export { MenuItem } from "./components/MenuItem";
13
13
  export { RadioButton } from "./components/RadioButton";
14
14
  export { Slider } from "./components/Slider";
15
+ export { Toast } from "./components/Toast";
16
+ export { Tab } from "./components/Tab";
17
+ export { TabItem } from "./components/TabItem";
15
18
  // Providers
16
19
  export { ThemeProvider, useTheme, useThemeSafe } from "./providers/ThemeProvider";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beacon-ui",
3
- "version": "3.4.8",
3
+ "version": "3.5.1",
4
4
  "description": "Beacon Design System - Components and tokens",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -82,6 +82,11 @@
82
82
  --util-overlay-light: var(--color-alpha-neutral-black-300);
83
83
  --util-overlay-medium: var(--color-alpha-neutral-black-500);
84
84
  --util-overlay-strong: var(--color-alpha-neutral-black-700);
85
+ --util-bright-alpha-100: var(--color-alpha-neutral-white-100);
86
+ --util-bright-alpha-200: var(--color-alpha-neutral-white-200);
87
+ --util-bright-alpha-300: var(--color-alpha-neutral-white-300);
88
+ --util-bright-alpha-400: var(--color-alpha-neutral-white-400);
89
+ --util-brigh-alpha-500: var(--color-alpha-neutral-white-500);
85
90
  --shadow-none: var(--color-alpha-neutral-black-0);
86
91
  --shadow-subtle: var(--color-alpha-neutral-black-100);
87
92
  --shadow-normal: var(--color-alpha-neutral-black-200);
@@ -82,6 +82,11 @@
82
82
  --util-overlay-light: var(--color-alpha-neutral-black-300);
83
83
  --util-overlay-medium: var(--color-alpha-neutral-black-500);
84
84
  --util-overlay-strong: var(--color-alpha-neutral-black-700);
85
+ --util-bright-alpha-100: var(--color-alpha-neutral-black-100);
86
+ --util-bright-alpha-200: var(--color-alpha-neutral-black-200);
87
+ --util-bright-alpha-300: var(--color-alpha-neutral-black-300);
88
+ --util-bright-alpha-400: var(--color-alpha-neutral-black-400);
89
+ --util-brigh-alpha-500: var(--color-alpha-neutral-black-500);
85
90
  --shadow-none: var(--color-alpha-neutral-black-0);
86
91
  --shadow-subtle: var(--color-alpha-neutral-black-100);
87
92
  --shadow-normal: var(--color-alpha-neutral-black-200);
@@ -404,6 +404,11 @@
404
404
  --util-overlay-light: var(--color-alpha-neutral-black-300);
405
405
  --util-overlay-medium: var(--color-alpha-neutral-black-500);
406
406
  --util-overlay-strong: var(--color-alpha-neutral-black-700);
407
+ --util-bright-alpha-100: var(--color-alpha-neutral-black-100);
408
+ --util-bright-alpha-200: var(--color-alpha-neutral-black-200);
409
+ --util-bright-alpha-300: var(--color-alpha-neutral-black-300);
410
+ --util-bright-alpha-400: var(--color-alpha-neutral-black-400);
411
+ --util-brigh-alpha-500: var(--color-alpha-neutral-black-500);
407
412
  --shadow-none: var(--color-alpha-neutral-black-0);
408
413
  --shadow-subtle: var(--color-alpha-neutral-black-100);
409
414
  --shadow-normal: var(--color-alpha-neutral-black-200);
@@ -495,6 +500,11 @@
495
500
  --util-overlay-light: var(--color-alpha-neutral-black-300);
496
501
  --util-overlay-medium: var(--color-alpha-neutral-black-500);
497
502
  --util-overlay-strong: var(--color-alpha-neutral-black-700);
503
+ --util-bright-alpha-100: var(--color-alpha-neutral-white-100);
504
+ --util-bright-alpha-200: var(--color-alpha-neutral-white-200);
505
+ --util-bright-alpha-300: var(--color-alpha-neutral-white-300);
506
+ --util-bright-alpha-400: var(--color-alpha-neutral-white-400);
507
+ --util-brigh-alpha-500: var(--color-alpha-neutral-white-500);
498
508
  --shadow-none: var(--color-alpha-neutral-black-0);
499
509
  --shadow-subtle: var(--color-alpha-neutral-black-100);
500
510
  --shadow-normal: var(--color-alpha-neutral-black-200);