@dreamtree-org/twreact-ui 1.3.10 → 1.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +30 -0
  2. package/ai-skills/dreamtree-ui.md +12 -7
  3. package/dist/ai-schema.json +137 -21
  4. package/dist/charts/index.js +195 -66
  5. package/dist/components/charts/CartesianChart.d.ts.map +1 -1
  6. package/dist/components/charts/ChartFrame.d.ts.map +1 -1
  7. package/dist/components/charts/useChartLayout.d.ts +25 -0
  8. package/dist/components/charts/useChartLayout.d.ts.map +1 -0
  9. package/dist/components/core/Button.d.ts.map +1 -1
  10. package/dist/components/core/Checkbox.d.ts.map +1 -1
  11. package/dist/components/core/ColorPicker.d.ts.map +1 -1
  12. package/dist/components/core/Input.d.ts.map +1 -1
  13. package/dist/components/core/Loader.d.ts.map +1 -1
  14. package/dist/components/core/LocationPicker.d.ts.map +1 -1
  15. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  16. package/dist/components/core/Radio.d.ts.map +1 -1
  17. package/dist/components/core/Rate.d.ts.map +1 -1
  18. package/dist/components/core/RoundedTag.d.ts.map +1 -1
  19. package/dist/components/core/Skeleton.d.ts.map +1 -1
  20. package/dist/components/core/Slider.d.ts.map +1 -1
  21. package/dist/components/core/SpeechToText.d.ts.map +1 -1
  22. package/dist/components/core/Switch.d.ts.map +1 -1
  23. package/dist/components/core/Tabs.d.ts +2 -5
  24. package/dist/components/core/Tabs.d.ts.map +1 -1
  25. package/dist/components/core/Textarea.d.ts.map +1 -1
  26. package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
  27. package/dist/components/navigation/FootNav.d.ts.map +1 -1
  28. package/dist/components/utility/Avatar.d.ts +8 -0
  29. package/dist/components/utility/Avatar.d.ts.map +1 -1
  30. package/dist/components/utility/AvatarGroup.d.ts +4 -0
  31. package/dist/components/utility/AvatarGroup.d.ts.map +1 -0
  32. package/dist/components/utility/Badge.d.ts.map +1 -1
  33. package/dist/components/utility/Card.d.ts.map +1 -1
  34. package/dist/components/utility/Carousel.d.ts.map +1 -1
  35. package/dist/components/utility/FileUpload.d.ts.map +1 -1
  36. package/dist/components/utility/Pagination.d.ts.map +1 -1
  37. package/dist/components/utility/Sortable.d.ts.map +1 -1
  38. package/dist/components/utility/Stepper.d.ts.map +1 -1
  39. package/dist/esm/components/blocks/EmptyState.js +1 -1
  40. package/dist/esm/components/blocks/ErrorState.js +1 -1
  41. package/dist/esm/components/blocks/LoadingState.js +4 -4
  42. package/dist/esm/components/blocks/StatCard.js +6 -6
  43. package/dist/esm/components/blocks/StatusPill.js +5 -8
  44. package/dist/esm/components/charts/CartesianChart.js +85 -62
  45. package/dist/esm/components/charts/ChartFrame.js +12 -5
  46. package/dist/esm/components/charts/useChartLayout.js +105 -0
  47. package/dist/esm/components/core/Button.js +49 -25
  48. package/dist/esm/components/core/Checkbox.js +71 -45
  49. package/dist/esm/components/core/ColorPicker.js +31 -42
  50. package/dist/esm/components/core/Input.js +89 -22
  51. package/dist/esm/components/core/Loader.js +30 -44
  52. package/dist/esm/components/core/LocationPicker.js +82 -16
  53. package/dist/esm/components/core/ProgressBar.js +8 -12
  54. package/dist/esm/components/core/Radio.js +10 -12
  55. package/dist/esm/components/core/Rate.js +72 -4
  56. package/dist/esm/components/core/RoundedTag.js +5 -5
  57. package/dist/esm/components/core/Skeleton.js +13 -14
  58. package/dist/esm/components/core/Slider.js +24 -6
  59. package/dist/esm/components/core/SpeechToText.js +68 -16
  60. package/dist/esm/components/core/Switch.js +88 -16
  61. package/dist/esm/components/core/Tabs.js +180 -33
  62. package/dist/esm/components/core/Textarea.js +10 -2
  63. package/dist/esm/components/navigation/Breadcrumbs.js +97 -35
  64. package/dist/esm/components/navigation/FootNav.js +102 -13
  65. package/dist/esm/components/utility/Avatar.js +14 -12
  66. package/dist/esm/components/utility/AvatarGroup.js +56 -0
  67. package/dist/esm/components/utility/Badge.js +43 -23
  68. package/dist/esm/components/utility/Card.js +32 -8
  69. package/dist/esm/components/utility/Carousel.js +139 -29
  70. package/dist/esm/components/utility/FileUpload.js +106 -31
  71. package/dist/esm/components/utility/Pagination.js +31 -13
  72. package/dist/esm/components/utility/Sortable.js +6 -2
  73. package/dist/esm/components/utility/Stepper.js +110 -47
  74. package/dist/esm/hooks/useSliderValues.js +8 -2
  75. package/dist/esm/hooks/useSortable.js +107 -5
  76. package/dist/esm/index.js +1 -0
  77. package/dist/esm/utils/touchTarget.js +4 -1
  78. package/dist/hooks/useSliderValues.d.ts +6 -2
  79. package/dist/hooks/useSliderValues.d.ts.map +1 -1
  80. package/dist/hooks/useSortable.d.ts +11 -1
  81. package/dist/hooks/useSortable.d.ts.map +1 -1
  82. package/dist/index.css +1 -1
  83. package/dist/index.d.ts +3 -2
  84. package/dist/index.esm.css +1 -1
  85. package/dist/index.esm.js +2341 -1191
  86. package/dist/index.js +2341 -1190
  87. package/dist/types/charts.d.ts +3 -1
  88. package/dist/types/charts.d.ts.map +1 -1
  89. package/dist/types/index.d.ts +56 -7
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/doc/README.md +1 -0
  92. package/mcp/catalog.snapshot.json +279 -80
  93. package/package.json +1 -1
@@ -1,10 +1,12 @@
1
1
  'use client';
2
- import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2, slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
4
  import React from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { PRESS_FEEDBACK } from '../../utils/touchTarget.js';
6
7
 
7
- var _excluded = ["items", "className"];
8
+ var _excluded = ["items", "linkAs", "hideOnScroll", "badgeMax", "className", "onFocus"],
9
+ _excluded2 = ["className"];
8
10
  var hasBadge = function hasBadge(badge) {
9
11
  return badge === 0 || Boolean(badge);
10
12
  };
@@ -17,34 +19,100 @@ var passthrough = function passthrough(item) {
17
19
  return /^(aria|data)-/.test(key);
18
20
  }));
19
21
  };
22
+ // Numeric counts above `max` read `${max}+` so a big number can't widen the tab.
23
+ var formatBadge = function formatBadge(badge, max) {
24
+ return typeof badge === 'number' && typeof max === 'number' && badge > max ? "".concat(max, "+") : badge;
25
+ };
20
26
  var Badge = function Badge(_ref3) {
21
- var badge = _ref3.badge;
27
+ var badge = _ref3.badge,
28
+ max = _ref3.max;
22
29
  return jsx("span", {
23
- className: "absolute -top-1.5 left-full -ml-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-600 px-1 text-micro font-semibold text-white",
30
+ className: "absolute -top-1.5 left-full -ml-2 flex h-4 min-w-4 items-center justify-center whitespace-nowrap rounded-full bg-error-600 px-1 text-micro font-semibold tabular-nums text-white",
24
31
  "data-slot": "badge",
25
- children: badge
32
+ children: formatBadge(badge, max)
26
33
  });
27
34
  };
35
+ // Scroll distance (px) that counts as a direction change, and the offset
36
+ // before the bar may hide (so it never hides at the very top of the page).
37
+ var SCROLL_DELTA = 8;
38
+ var HIDE_AFTER = 64;
39
+ // Hide on scroll down, show on scroll up. Passive listener; SSR-safe (the
40
+ // effect only runs in the browser).
41
+ function useHideOnScroll(enabled) {
42
+ var _React$useState = React.useState(false),
43
+ _React$useState2 = _slicedToArray(_React$useState, 2),
44
+ hidden = _React$useState2[0],
45
+ setHidden = _React$useState2[1];
46
+ React.useEffect(function () {
47
+ if (!enabled || typeof window === 'undefined') {
48
+ setHidden(false);
49
+ return undefined;
50
+ }
51
+ var lastY = window.scrollY || 0;
52
+ var onScroll = function onScroll() {
53
+ var y = window.scrollY || 0;
54
+ var delta = y - lastY;
55
+ if (Math.abs(delta) < SCROLL_DELTA) return;
56
+ setHidden(delta > 0 && y > HIDE_AFTER);
57
+ lastY = y;
58
+ };
59
+ window.addEventListener('scroll', onScroll, {
60
+ passive: true
61
+ });
62
+ return function () {
63
+ return window.removeEventListener('scroll', onScroll);
64
+ };
65
+ }, [enabled]);
66
+ return [hidden, setHidden];
67
+ }
28
68
  var FootNav = /*#__PURE__*/React.forwardRef(function (_ref4, ref) {
29
69
  var _ref4$items = _ref4.items,
30
70
  items = _ref4$items === void 0 ? [] : _ref4$items,
71
+ linkAs = _ref4.linkAs,
72
+ _ref4$hideOnScroll = _ref4.hideOnScroll,
73
+ hideOnScroll = _ref4$hideOnScroll === void 0 ? false : _ref4$hideOnScroll,
74
+ _ref4$badgeMax = _ref4.badgeMax,
75
+ badgeMax = _ref4$badgeMax === void 0 ? 99 : _ref4$badgeMax,
31
76
  className = _ref4.className,
77
+ onFocus = _ref4.onFocus,
32
78
  props = _objectWithoutProperties(_ref4, _excluded);
79
+ var _useHideOnScroll = useHideOnScroll(hideOnScroll),
80
+ _useHideOnScroll2 = _slicedToArray(_useHideOnScroll, 2),
81
+ hidden = _useHideOnScroll2[0],
82
+ setHidden = _useHideOnScroll2[1];
83
+ // Keyboard focus moving into a hidden bar brings it back.
84
+ var handleFocus = function handleFocus(e) {
85
+ if (hidden) setHidden(false);
86
+ onFocus === null || onFocus === void 0 || onFocus(e);
87
+ };
33
88
  return jsx("nav", _objectSpread2(_objectSpread2({
34
89
  ref: ref,
35
- className: cn('fixed bottom-0 left-0 right-0 bg-surface border-t border-line px-2 py-2 md:hidden', className)
90
+ "aria-label": "Primary",
91
+ "data-hidden": hideOnScroll ? String(hidden) : undefined,
92
+ className: cn('fixed bottom-0 left-0 right-0 z-sticky border-t border-line px-2 pb-safe md:hidden',
93
+ // Translucent where backdrop blur is supported, solid surface otherwise.
94
+ 'bg-surface supports-[backdrop-filter]:bg-surface/80 supports-[backdrop-filter]:backdrop-blur', hideOnScroll && 'transition-transform duration-base ease-standard motion-reduce:transition-none', hideOnScroll && hidden && 'translate-y-full', className),
95
+ onFocus: handleFocus
36
96
  }, props), {}, {
37
97
  children: jsx("div", {
38
- className: "flex items-stretch justify-around gap-1",
98
+ className: "flex items-stretch justify-around gap-1 py-2",
39
99
  children: items.map(function (item, index) {
40
- var _item$id;
100
+ var _item$to, _item$id;
41
101
  var isAction = item.variant === 'action';
42
- return jsxs("button", _objectSpread2(_objectSpread2({
43
- type: "button",
102
+ var isLink = item.href !== undefined || item.to !== undefined;
103
+ var Item = isLink ? linkAs || 'a' : 'button';
104
+ var linkProps = isLink ? _objectSpread2({
105
+ href: item.href
106
+ }, linkAs ? {
107
+ to: (_item$to = item.to) !== null && _item$to !== void 0 ? _item$to : item.href
108
+ } : {}) : {
109
+ type: 'button'
110
+ };
111
+ return jsxs(Item, _objectSpread2(_objectSpread2(_objectSpread2({}, linkProps), {}, {
44
112
  "aria-current": item.active ? 'page' : undefined,
45
113
  "data-variant": isAction ? 'action' : undefined
46
114
  }, passthrough(item)), {}, {
47
- className: cn('relative flex min-w-0 flex-1 flex-col items-center gap-1 rounded-md px-1 py-2 text-xs transition-colors', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1 motion-reduce:transition-none', isAction ? 'group justify-start py-0 font-medium text-muted' : item.active ? 'bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300' : 'font-medium text-muted hover:bg-gray-100 hover:text-foreground dark:hover:bg-gray-800'),
115
+ className: cn('relative flex min-w-0 flex-1 flex-col items-center gap-1 rounded-md px-1 py-2 text-xs no-underline', PRESS_FEEDBACK, 'transition duration-fast ease-standard motion-reduce:transition-none', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1 dark:focus-visible:ring-offset-gray-900', isAction ? 'group justify-start py-0 font-medium text-muted' : item.active ? 'bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300' : 'font-medium text-muted hover:bg-gray-100 hover:text-foreground dark:hover:bg-gray-800'),
48
116
  onClick: function onClick() {
49
117
  var _item$onClick;
50
118
  return (_item$onClick = item.onClick) === null || _item$onClick === void 0 ? void 0 : _item$onClick.call(item, item);
@@ -56,13 +124,15 @@ var FootNav = /*#__PURE__*/React.forwardRef(function (_ref4, ref) {
56
124
  className: "relative -mt-7 inline-flex h-14 w-14 items-center justify-center rounded-full bg-primary-600 text-2xl text-white shadow-lg ring-4 ring-surface transition-colors group-hover:bg-primary-700 motion-reduce:transition-none",
57
125
  "data-slot": "icon",
58
126
  children: [item.icon, hasBadge(item.badge) && jsx(Badge, {
59
- badge: item.badge
127
+ badge: item.badge,
128
+ max: badgeMax
60
129
  })]
61
130
  }) : jsxs("span", {
62
131
  className: "relative inline-flex text-lg",
63
132
  "data-slot": "icon",
64
133
  children: [item.icon, hasBadge(item.badge) && jsx(Badge, {
65
- badge: item.badge
134
+ badge: item.badge,
135
+ max: badgeMax
66
136
  })]
67
137
  }), jsx("span", {
68
138
  className: "block max-w-full truncate",
@@ -74,5 +144,24 @@ var FootNav = /*#__PURE__*/React.forwardRef(function (_ref4, ref) {
74
144
  }));
75
145
  });
76
146
  FootNav.displayName = 'FootNav';
147
+ // Reserves the bar's height plus the bottom safe area at the end of the page
148
+ // content, so nothing scrolls under the fixed bar. Hidden from `md` up, like
149
+ // the bar itself.
150
+ var FootNavSpacer = /*#__PURE__*/React.forwardRef(function (_ref5, ref) {
151
+ var className = _ref5.className,
152
+ props = _objectWithoutProperties(_ref5, _excluded2);
153
+ return jsx("div", _objectSpread2(_objectSpread2({
154
+ ref: ref,
155
+ "aria-hidden": "true",
156
+ "data-slot": "footnav-spacer",
157
+ className: cn('pb-safe md:hidden', className)
158
+ }, props), {}, {
159
+ children: jsx("div", {
160
+ className: "h-20"
161
+ })
162
+ }));
163
+ });
164
+ FootNavSpacer.displayName = 'FootNav.Spacer';
165
+ FootNav.Spacer = FootNavSpacer;
77
166
 
78
167
  export { FootNav as default };
@@ -6,6 +6,15 @@ import { cn } from '../../utils/cn.js';
6
6
  import { User } from 'lucide-react';
7
7
 
8
8
  var _excluded = ["src", "alt", "name", "size", "shape", "status", "className"];
9
+ // Internal (not exported from the package): shared with AvatarGroup's "+N" tile.
10
+ var AVATAR_SIZES = {
11
+ xs: 'h-6 w-6 text-xs',
12
+ sm: 'h-8 w-8 text-sm',
13
+ md: 'h-10 w-10 text-base',
14
+ lg: 'h-12 w-12 text-lg',
15
+ xl: 'h-16 w-16 text-xl',
16
+ '2xl': 'h-20 w-20 text-2xl'
17
+ };
9
18
  var Avatar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
10
19
  var src = _ref.src,
11
20
  alt = _ref.alt,
@@ -33,14 +42,7 @@ var Avatar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
33
42
  setImgFailed(false);
34
43
  }
35
44
  var showImage = Boolean(src) && !imgFailed;
36
- var sizes = {
37
- xs: 'h-6 w-6 text-xs',
38
- sm: 'h-8 w-8 text-sm',
39
- md: 'h-10 w-10 text-base',
40
- lg: 'h-12 w-12 text-lg',
41
- xl: 'h-16 w-16 text-xl',
42
- '2xl': 'h-20 w-20 text-2xl'
43
- };
45
+ var sizes = AVATAR_SIZES;
44
46
  var getInitials = function getInitials(name) {
45
47
  if (!name) return '';
46
48
  return name.split(' ').map(function (word) {
@@ -49,7 +51,7 @@ var Avatar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
49
51
  };
50
52
  var statusColors = {
51
53
  online: 'bg-success-500',
52
- offline: 'bg-gray-400',
54
+ offline: 'bg-gray-400 dark:bg-gray-500',
53
55
  away: 'bg-warning-500',
54
56
  busy: 'bg-error-500'
55
57
  };
@@ -65,7 +67,7 @@ var Avatar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
65
67
  ref: ref,
66
68
  className: "relative inline-block",
67
69
  children: [jsx("div", _objectSpread2(_objectSpread2({
68
- className: cn('flex items-center justify-center bg-gray-100 text-primary-600 font-medium', sizes[size], {
70
+ className: cn('flex items-center justify-center bg-gray-100 text-primary-600 font-medium dark:bg-gray-800 dark:text-primary-300', sizes[size], {
69
71
  'rounded-full': shape === 'circle',
70
72
  'rounded-md': shape === 'square',
71
73
  'rounded-lg': shape === 'rounded'
@@ -93,10 +95,10 @@ var Avatar = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
93
95
  })
94
96
  })
95
97
  })), status && jsx("span", {
96
- className: cn('absolute bottom-0 right-0 rounded-full border-2 border-white', statusColors[status], statusSizes[size])
98
+ className: cn('absolute bottom-0 right-0 rounded-full border-2 border-surface', statusColors[status], statusSizes[size])
97
99
  })]
98
100
  });
99
101
  });
100
102
  Avatar.displayName = 'Avatar';
101
103
 
102
- export { Avatar as default };
104
+ export { AVATAR_SIZES, Avatar as default };
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+ import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { jsxs } from 'react/jsx-runtime';
4
+ import React from 'react';
5
+ import { cn } from '../../utils/cn.js';
6
+ import { AVATAR_SIZES } from './Avatar.js';
7
+
8
+ var _excluded = ["children", "max", "size", "className"];
9
+ // Overlap grows with the avatar so the stack reads the same at every size.
10
+ var OVERLAP = {
11
+ xs: '-space-x-1.5',
12
+ sm: '-space-x-2',
13
+ md: '-space-x-2',
14
+ lg: '-space-x-3',
15
+ xl: '-space-x-4',
16
+ '2xl': '-space-x-5'
17
+ };
18
+ // Each avatar is ringed in the surface colour so it reads as cut out of the
19
+ // one behind it (dark-mode aware).
20
+ var RING = 'ring-2 ring-surface';
21
+ var AvatarGroup = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
22
+ var children = _ref.children,
23
+ max = _ref.max,
24
+ _ref$size = _ref.size,
25
+ size = _ref$size === void 0 ? 'md' : _ref$size,
26
+ className = _ref.className,
27
+ props = _objectWithoutProperties(_ref, _excluded);
28
+ var avatars = React.Children.toArray(children).filter(React.isValidElement);
29
+ var limit = typeof max === 'number' && max >= 0 ? max : avatars.length;
30
+ var visible = avatars.slice(0, limit);
31
+ var overflow = avatars.length - visible.length;
32
+ var sizeKey = AVATAR_SIZES[size] ? size : 'md';
33
+ return jsxs("div", _objectSpread2(_objectSpread2({
34
+ ref: ref,
35
+ role: "group",
36
+ className: cn('flex items-center', OVERLAP[sizeKey], className)
37
+ }, props), {}, {
38
+ children: [visible.map(function (child) {
39
+ var _child$props$size;
40
+ return /*#__PURE__*/React.cloneElement(child, {
41
+ // An avatar's own `size` wins; otherwise the group's size applies.
42
+ size: (_child$props$size = child.props.size) !== null && _child$props$size !== void 0 ? _child$props$size : size,
43
+ className: cn(RING, child.props.className)
44
+ });
45
+ }), overflow > 0 && jsxs("span", {
46
+ role: "img",
47
+ "aria-label": "".concat(overflow, " more"),
48
+ "data-slot": "overflow",
49
+ className: cn('relative inline-flex shrink-0 items-center justify-center rounded-full bg-gray-200 font-medium text-gray-700 dark:bg-gray-700 dark:text-gray-100', RING, AVATAR_SIZES[sizeKey]),
50
+ children: ["+", overflow]
51
+ })]
52
+ }));
53
+ });
54
+ AvatarGroup.displayName = 'AvatarGroup';
55
+
56
+ export { AvatarGroup as default };
@@ -1,10 +1,33 @@
1
1
  'use client';
2
2
  import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
- import { jsx } from 'react/jsx-runtime';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
4
  import React from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
6
 
7
- var _excluded = ["children", "variant", "size", "rounded", "className"];
7
+ var _excluded = ["children", "variant", "size", "rounded", "dot", "icon", "max", "className"];
8
+ // Light: 100 fill + 800 text (≥4.5:1). Dark: a tinted 900 fill + 200 text.
9
+ var variants = {
10
+ "default": 'bg-gray-100 text-primary-800 dark:bg-gray-800 dark:text-gray-100',
11
+ primary: 'bg-primary-100 text-primary-800 dark:bg-primary-900/40 dark:text-primary-200',
12
+ secondary: 'bg-secondary-100 text-secondary-800 dark:bg-secondary-800 dark:text-secondary-100',
13
+ success: 'bg-success-100 text-success-800 dark:bg-success-900/40 dark:text-success-200',
14
+ warning: 'bg-warning-100 text-warning-800 dark:bg-warning-900/40 dark:text-warning-200',
15
+ error: 'bg-error-100 text-error-800 dark:bg-error-900/40 dark:text-error-200',
16
+ info: 'bg-info-100 text-info-800 dark:bg-info-900/40 dark:text-info-200',
17
+ outline: 'border border-gray-300 text-gray-700 dark:border-gray-600 dark:text-gray-200'
18
+ };
19
+ var sizes = {
20
+ xs: 'px-1.5 py-px text-xs',
21
+ sm: 'px-2 py-0.5 text-xs',
22
+ md: 'px-2.5 py-0.5 text-sm',
23
+ lg: 'px-3 py-1 text-base',
24
+ xl: 'px-3.5 py-1 text-lg',
25
+ '2xl': 'px-4 py-1.5 text-xl'
26
+ };
27
+ // A numeric count above `max` reads `${max}+` (e.g. 99+).
28
+ var formatCount = function formatCount(children, max) {
29
+ return typeof children === 'number' && typeof max === 'number' && children > max ? "".concat(max, "+") : children;
30
+ };
8
31
  var Badge = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
9
32
  var children = _ref.children,
10
33
  _ref$variant = _ref.variant,
@@ -13,34 +36,31 @@ var Badge = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
13
36
  size = _ref$size === void 0 ? 'md' : _ref$size,
14
37
  _ref$rounded = _ref.rounded,
15
38
  rounded = _ref$rounded === void 0 ? true : _ref$rounded,
39
+ _ref$dot = _ref.dot,
40
+ dot = _ref$dot === void 0 ? false : _ref$dot,
41
+ icon = _ref.icon,
42
+ max = _ref.max,
16
43
  className = _ref.className,
17
44
  props = _objectWithoutProperties(_ref, _excluded);
18
- var variants = {
19
- "default": 'bg-gray-100 text-primary-800',
20
- primary: 'bg-primary-100 text-primary-800',
21
- secondary: 'bg-secondary-100 text-secondary-800',
22
- success: 'bg-success-100 text-success-800',
23
- warning: 'bg-warning-100 text-warning-800',
24
- error: 'bg-error-100 text-error-800',
25
- info: 'bg-info-100 text-info-800',
26
- outline: 'border border-gray-300'
27
- };
28
- var sizes = {
29
- xs: 'px-1.5 py-px text-xs',
30
- sm: 'px-2 py-0.5 text-xs',
31
- md: 'px-2.5 py-0.5 text-sm',
32
- lg: 'px-3 py-1 text-base',
33
- xl: 'px-3.5 py-1 text-lg',
34
- '2xl': 'px-4 py-1.5 text-xl'
35
- };
36
- return jsx("span", _objectSpread2(_objectSpread2({
45
+ var hasLead = dot || Boolean(icon);
46
+ return jsxs("span", _objectSpread2(_objectSpread2({
37
47
  ref: ref,
38
48
  className: cn('inline-flex items-center font-medium', variants[variant], sizes[size] || sizes.md, {
39
49
  'rounded-full': rounded,
40
- 'rounded-md': !rounded
50
+ 'rounded-md': !rounded,
51
+ 'gap-1': hasLead
41
52
  }, className)
42
53
  }, props), {}, {
43
- children: children
54
+ children: [dot && jsx("span", {
55
+ className: "h-1.5 w-1.5 shrink-0 rounded-full bg-current",
56
+ "aria-hidden": "true",
57
+ "data-slot": "dot"
58
+ }), icon && jsx("span", {
59
+ className: "inline-flex h-3.5 w-3.5 shrink-0 items-center",
60
+ "aria-hidden": "true",
61
+ "data-slot": "icon",
62
+ children: icon
63
+ }), formatCount(children, max)]
44
64
  }));
45
65
  });
46
66
  Badge.displayName = 'Badge';
@@ -1,26 +1,50 @@
1
1
  'use client';
2
- import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2, defineProperty as _defineProperty } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
  import React from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { PRESS_FEEDBACK } from '../../utils/touchTarget.js';
6
7
 
7
- var _excluded = ["children", "className", "hover"],
8
+ var _excluded = ["children", "className", "hover", "interactive", "onClick", "onKeyDown"],
8
9
  _excluded2 = ["children", "className"],
9
10
  _excluded3 = ["children", "className"],
10
11
  _excluded4 = ["children", "className"],
11
12
  _excluded5 = ["children", "className"],
12
13
  _excluded6 = ["children", "className"];
14
+ // Interactive cards: hover lift, press shrink and a keyboard focus ring. The
15
+ // transition list covers both the shadow and the press transform.
16
+ var INTERACTIVE_CLASSES = cn('cursor-pointer hover:shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900', PRESS_FEEDBACK, 'transition-[box-shadow,transform] duration-fast ease-standard motion-reduce:transition-none');
13
17
  var Card = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
14
18
  var children = _ref.children,
15
19
  className = _ref.className,
16
20
  _ref$hover = _ref.hover,
17
21
  hover = _ref$hover === void 0 ? false : _ref$hover,
22
+ _ref$interactive = _ref.interactive,
23
+ interactive = _ref$interactive === void 0 ? false : _ref$interactive,
24
+ onClick = _ref.onClick,
25
+ onKeyDown = _ref.onKeyDown,
18
26
  props = _objectWithoutProperties(_ref, _excluded);
27
+ // A clickable interactive card with no role of its own becomes a keyboard
28
+ // operable button (Tab to it, Enter / Space to activate).
29
+ var actsAsButton = interactive && typeof onClick === 'function' && props.role === undefined;
30
+ var handleKeyDown = function handleKeyDown(e) {
31
+ onKeyDown === null || onKeyDown === void 0 || onKeyDown(e);
32
+ if (!actsAsButton || e.defaultPrevented || e.target !== e.currentTarget) return;
33
+ if (e.key === 'Enter' || e.key === ' ') {
34
+ e.preventDefault();
35
+ onClick(e);
36
+ }
37
+ };
19
38
  return jsx("div", _objectSpread2(_objectSpread2({
20
39
  ref: ref,
21
- className: cn('rounded-lg border border-line bg-surface shadow-sm', {
22
- 'hover:shadow-md transition-shadow': hover
23
- }, className)
40
+ role: actsAsButton ? 'button' : undefined,
41
+ tabIndex: actsAsButton ? 0 : undefined,
42
+ onClick: onClick,
43
+ onKeyDown: actsAsButton ? handleKeyDown : onKeyDown,
44
+ "data-interactive": interactive || undefined,
45
+ className: cn('rounded-card border border-line bg-surface shadow-sm', _defineProperty({
46
+ 'hover:shadow-md transition-shadow': hover && !interactive
47
+ }, INTERACTIVE_CLASSES, interactive), className)
24
48
  }, props), {}, {
25
49
  children: children
26
50
  }));
@@ -31,7 +55,7 @@ var CardHeader = function CardHeader(_ref2) {
31
55
  className = _ref2.className,
32
56
  props = _objectWithoutProperties(_ref2, _excluded2);
33
57
  return jsx("div", _objectSpread2(_objectSpread2({
34
- className: cn('px-6 py-4 border-b border-line', className)
58
+ className: cn('px-4 sm:px-6 py-4 border-b border-line', className)
35
59
  }, props), {}, {
36
60
  children: children
37
61
  }));
@@ -61,7 +85,7 @@ var CardContent = function CardContent(_ref5) {
61
85
  className = _ref5.className,
62
86
  props = _objectWithoutProperties(_ref5, _excluded5);
63
87
  return jsx("div", _objectSpread2(_objectSpread2({
64
- className: cn('px-6 py-4', className)
88
+ className: cn('px-4 sm:px-6 py-4', className)
65
89
  }, props), {}, {
66
90
  children: children
67
91
  }));
@@ -71,7 +95,7 @@ var CardFooter = function CardFooter(_ref6) {
71
95
  className = _ref6.className,
72
96
  props = _objectWithoutProperties(_ref6, _excluded6);
73
97
  return jsx("div", _objectSpread2(_objectSpread2({
74
- className: cn('px-6 py-4 border-t border-line', className)
98
+ className: cn('px-4 sm:px-6 py-4 border-t border-line', className)
75
99
  }, props), {}, {
76
100
  children: children
77
101
  }));