@wallarm-org/design-system 0.49.0 → 0.50.0-rc-feature-AS-1033.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.
@@ -73,6 +73,8 @@ const DrawerResizeHandle = ({ ref })=>{
73
73
  children: [
74
74
  /*#__PURE__*/ jsx(TooltipTrigger, {
75
75
  ref: ref,
76
+ "data-slot": "resize-handle",
77
+ "data-resizing": isDragging || void 0,
76
78
  className: cn(drawerResizeHandleVariants({
77
79
  barIsVisible
78
80
  })),
@@ -0,0 +1,30 @@
1
+ /** Fallback width (px) reserved for the "+N" overflow indicator until the real
2
+ * indicator has been measured. Passed to `useOverflowItems.reserveSpace`. */
3
+ export declare const OVERFLOW_RESERVE_SPACE = 80;
4
+ /**
5
+ * Shallow item-by-item equality for two lists. Order-sensitive — items must
6
+ * match at the same index. Used to guard `onOverflow` from re-firing when the
7
+ * hidden set keeps a fresh array identity but the same contents. A `null`
8
+ * previous list is treated as "not equal".
9
+ */
10
+ export declare function areItemsShallowEqual<T>(prev: T[] | null, next: T[]): boolean;
11
+ export interface ResolveVisibleItemsParams<T> {
12
+ /** Full source list, in order. */
13
+ items: T[];
14
+ /** Items the overflow engine decided are visible. */
15
+ visibleItems: T[];
16
+ /** Items the overflow engine collapsed into the indicator. */
17
+ hiddenItems: T[];
18
+ /** Minimum number of items to keep visible regardless of available space. */
19
+ minVisibleItems: number;
20
+ }
21
+ /**
22
+ * Applies the `minVisibleItems` floor to the overflow engine's output. When the
23
+ * engine collapses below the floor (and the list is long enough), force the
24
+ * first `minVisibleItems` to stay visible and recompute the hidden tail.
25
+ * Otherwise the engine's split is returned untouched (same references).
26
+ */
27
+ export declare function resolveVisibleItems<T>({ items, visibleItems, hiddenItems, minVisibleItems, }: ResolveVisibleItemsParams<T>): {
28
+ visibleItems: T[];
29
+ hiddenItems: T[];
30
+ };
@@ -0,0 +1,16 @@
1
+ const OVERFLOW_RESERVE_SPACE = 80;
2
+ function areItemsShallowEqual(prev, next) {
3
+ if (!prev || prev.length !== next.length) return false;
4
+ return next.every((item, index)=>item === prev[index]);
5
+ }
6
+ function resolveVisibleItems({ items, visibleItems, hiddenItems, minVisibleItems }) {
7
+ if (visibleItems.length < minVisibleItems && items.length >= minVisibleItems) return {
8
+ visibleItems: items.slice(0, minVisibleItems),
9
+ hiddenItems: items.slice(minVisibleItems)
10
+ };
11
+ return {
12
+ visibleItems,
13
+ hiddenItems
14
+ };
15
+ }
16
+ export { OVERFLOW_RESERVE_SPACE, areItemsShallowEqual, resolveVisibleItems };
@@ -1,48 +1,52 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { memo, useCallback, useMemo } from "react";
2
+ import { memo, useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import { useOverflowItems } from "../../hooks/index.js";
4
4
  import { cn } from "../../utils/cn.js";
5
+ import { OVERFLOW_RESERVE_SPACE, areItemsShallowEqual, resolveVisibleItems } from "./OverflowList.helpers.js";
5
6
  const OverflowListComponent = ({ items, itemRenderer, overflowRenderer, className, collapseFrom = 'end', minVisibleItems = 0, alwaysRenderOverflow = false, onOverflow, ...props })=>{
6
- const memoizedItemRenderer = useCallback((item)=>{
7
- const index = items.indexOf(item);
8
- return itemRenderer(item, index);
7
+ const indexMap = useMemo(()=>{
8
+ const map = new Map();
9
+ items.forEach((item, index)=>{
10
+ if (!map.has(item)) map.set(item, index);
11
+ });
12
+ return map;
9
13
  }, [
10
- items,
14
+ items
15
+ ]);
16
+ const memoizedItemRenderer = useCallback((item)=>itemRenderer(item, indexMap.get(item) ?? 0), [
17
+ indexMap,
11
18
  itemRenderer
12
19
  ]);
13
- const memoizedMeasurementRenderer = useCallback((item)=>{
14
- const index = items.indexOf(item);
15
- return itemRenderer(item, index);
16
- }, [
17
- items,
20
+ const memoizedMeasurementRenderer = useCallback((item)=>itemRenderer(item, indexMap.get(item) ?? 0), [
21
+ indexMap,
18
22
  itemRenderer
19
23
  ]);
20
24
  const { containerRef, visibleItems, hiddenItems, MeasurementContainer } = useOverflowItems({
21
25
  items,
22
26
  renderItem: memoizedMeasurementRenderer,
23
27
  overflowRenderer: (items)=>overflowRenderer(items),
24
- reserveSpace: 80
28
+ reserveSpace: OVERFLOW_RESERVE_SPACE
25
29
  });
26
- const finalVisibleItems = useMemo(()=>{
27
- if (visibleItems.length < minVisibleItems && items.length >= minVisibleItems) return items.slice(0, minVisibleItems);
28
- return visibleItems;
29
- }, [
30
- visibleItems,
31
- minVisibleItems,
32
- items
33
- ]);
34
- const finalHiddenItems = useMemo(()=>{
35
- if (finalVisibleItems.length !== visibleItems.length) return items.slice(finalVisibleItems.length);
36
- return hiddenItems;
37
- }, [
38
- finalVisibleItems.length,
39
- visibleItems.length,
30
+ const { visibleItems: finalVisibleItems, hiddenItems: finalHiddenItems } = useMemo(()=>resolveVisibleItems({
31
+ items,
32
+ visibleItems,
33
+ hiddenItems,
34
+ minVisibleItems
35
+ }), [
40
36
  items,
41
- hiddenItems
37
+ visibleItems,
38
+ hiddenItems,
39
+ minVisibleItems
42
40
  ]);
43
41
  const finalHiddenCount = finalHiddenItems.length;
44
- useMemo(()=>{
45
- if (onOverflow && finalHiddenItems.length > 0) onOverflow(finalHiddenItems);
42
+ const prevHiddenRef = useRef(null);
43
+ useEffect(()=>{
44
+ if (!onOverflow || 0 === finalHiddenItems.length) {
45
+ prevHiddenRef.current = finalHiddenItems;
46
+ return;
47
+ }
48
+ if (!areItemsShallowEqual(prevHiddenRef.current, finalHiddenItems)) onOverflow(finalHiddenItems);
49
+ prevHiddenRef.current = finalHiddenItems;
46
50
  }, [
47
51
  finalHiddenItems,
48
52
  onOverflow
@@ -65,6 +69,7 @@ const OverflowListComponent = ({ items, itemRenderer, overflowRenderer, classNam
65
69
  /*#__PURE__*/ jsx(MeasurementContainer, {}),
66
70
  /*#__PURE__*/ jsxs("div", {
67
71
  ref: containerRef,
72
+ "data-slot": "overflow-list",
68
73
  className: cn('flex w-full min-w-0', className),
69
74
  ...props,
70
75
  children: [
@@ -1,2 +1,5 @@
1
- import type { FC } from 'react';
2
- export declare const SelectSeparator: FC;
1
+ import type { FC, HTMLAttributes, Ref } from 'react';
2
+ export type SelectSeparatorProps = HTMLAttributes<HTMLHRElement> & {
3
+ ref?: Ref<HTMLHRElement>;
4
+ };
5
+ export declare const SelectSeparator: FC<SelectSeparatorProps>;
@@ -1,10 +1,15 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../utils/cn.js";
2
3
  import { useTestId } from "../../utils/testId.js";
3
- import { DropdownMenuSeparator } from "../DropdownMenu/index.js";
4
- const SelectSeparator = ()=>{
4
+ const SelectSeparator = ({ className, ref, ...props })=>{
5
5
  const testId = useTestId('separator');
6
- return /*#__PURE__*/ jsx(DropdownMenuSeparator, {
7
- "data-testid": testId
6
+ return /*#__PURE__*/ jsx("hr", {
7
+ ref: ref,
8
+ "data-slot": "select-separator",
9
+ "data-testid": testId,
10
+ "aria-orientation": "horizontal",
11
+ className: cn('mx-8 my-4 h-px bg-border-primary border-none', className),
12
+ ...props
8
13
  });
9
14
  };
10
15
  SelectSeparator.displayName = 'SelectSeparator';
@@ -15,5 +15,5 @@ export { SelectOptionIndicator } from './SelectOptionIndicator';
15
15
  export { SelectOptionText } from './SelectOptionText';
16
16
  export { SelectPositioner } from './SelectPositioner';
17
17
  export { SelectSearchInput } from './SelectSearchInput';
18
- export { SelectSeparator } from './SelectSeparator';
18
+ export { SelectSeparator, type SelectSeparatorProps } from './SelectSeparator';
19
19
  export type { SelectDataItem } from './types';
@@ -9,6 +9,7 @@ const TableResizeHandler = ({ header })=>{
9
9
  onMouseDown: header.getResizeHandler(),
10
10
  onTouchStart: header.getResizeHandler(),
11
11
  className: cn(tableResizeHandlerVariants()),
12
+ "data-slot": "resize-handle",
12
13
  "data-testid": testId,
13
14
  "data-resizing": header.column.getIsResizing() || void 0,
14
15
  tabIndex: -1
@@ -20,7 +20,10 @@ const securityEvents = [
20
20
  objectName: 'Rate limiting abuse on the payment endpoint',
21
21
  tags: [
22
22
  'api-abuse',
23
- 'account-takeover'
23
+ 'account-takeover',
24
+ 'credential-stuffing',
25
+ 'scanner',
26
+ 'brute-force'
24
27
  ],
25
28
  isActive: false,
26
29
  requests: 22000,
@@ -40,7 +43,10 @@ const securityEvents = [
40
43
  objectName: 'Mass assignment vulnerability in user profile',
41
44
  tags: [
42
45
  'api-abuse',
43
- 'account-takeover'
46
+ 'account-takeover',
47
+ 'credential-stuffing',
48
+ 'scanner',
49
+ 'brute-force'
44
50
  ],
45
51
  isActive: false,
46
52
  requests: 25000,
@@ -60,7 +66,10 @@ const securityEvents = [
60
66
  objectName: 'Insecure direct object reference in user data',
61
67
  tags: [
62
68
  'api-abuse',
63
- 'account-takeover'
69
+ 'account-takeover',
70
+ 'credential-stuffing',
71
+ 'scanner',
72
+ 'brute-force'
64
73
  ],
65
74
  isActive: true,
66
75
  requests: 30000,
@@ -80,7 +89,10 @@ const securityEvents = [
80
89
  objectName: 'Improper error handling leading to info leak',
81
90
  tags: [
82
91
  'api-abuse',
83
- 'account-takeover'
92
+ 'account-takeover',
93
+ 'credential-stuffing',
94
+ 'scanner',
95
+ 'brute-force'
84
96
  ],
85
97
  isActive: true,
86
98
  requests: 35000,
@@ -100,7 +112,10 @@ const securityEvents = [
100
112
  objectName: 'Broken authentication in the login API',
101
113
  tags: [
102
114
  'api-abuse',
103
- 'account-takeover'
115
+ 'account-takeover',
116
+ 'credential-stuffing',
117
+ 'scanner',
118
+ 'brute-force'
104
119
  ],
105
120
  isActive: true,
106
121
  requests: 20000,
@@ -120,7 +135,10 @@ const securityEvents = [
120
135
  objectName: 'Lack of resource validation in file upload',
121
136
  tags: [
122
137
  'api-abuse',
123
- 'account-takeover'
138
+ 'account-takeover',
139
+ 'credential-stuffing',
140
+ 'scanner',
141
+ 'brute-force'
124
142
  ],
125
143
  isActive: false,
126
144
  requests: 40000,
@@ -140,7 +158,10 @@ const securityEvents = [
140
158
  objectName: 'Server-side request forgery in image proxy',
141
159
  tags: [
142
160
  'api-abuse',
143
- 'account-takeover'
161
+ 'account-takeover',
162
+ 'credential-stuffing',
163
+ 'scanner',
164
+ 'brute-force'
144
165
  ],
145
166
  isActive: false,
146
167
  requests: 50000,
@@ -160,7 +181,10 @@ const securityEvents = [
160
181
  objectName: 'Unvalidated redirects and forwards in auth flow',
161
182
  tags: [
162
183
  'api-abuse',
163
- 'account-takeover'
184
+ 'account-takeover',
185
+ 'credential-stuffing',
186
+ 'scanner',
187
+ 'brute-force'
164
188
  ],
165
189
  isActive: true,
166
190
  requests: 75000,
@@ -180,7 +204,10 @@ const securityEvents = [
180
204
  objectName: 'SQL injection in the user ID',
181
205
  tags: [
182
206
  'api-abuse',
183
- 'account-takeover'
207
+ 'account-takeover',
208
+ 'credential-stuffing',
209
+ 'scanner',
210
+ 'brute-force'
184
211
  ],
185
212
  isActive: false,
186
213
  requests: 15000,
@@ -200,7 +227,10 @@ const securityEvents = [
200
227
  objectName: "Cross-site scripting in the API endpoint",
201
228
  tags: [
202
229
  'api-abuse',
203
- 'account-takeover'
230
+ 'account-takeover',
231
+ 'credential-stuffing',
232
+ 'scanner',
233
+ 'brute-force'
204
234
  ],
205
235
  isActive: false,
206
236
  requests: 18000,
@@ -0,0 +1,15 @@
1
+ export interface CalculateVisibleCountParams {
2
+ /** Width of each item in source order (px). */
3
+ itemWidths: number[];
4
+ /** Gap between flex children of the container (px). */
5
+ gap: number;
6
+ /** Available width of the container (px). */
7
+ availableWidth: number;
8
+ /** Measured width of the '+N' indicator (px); fallback — reserveSpace. */
9
+ indicatorWidth: number;
10
+ }
11
+ /**
12
+ * Pure arithmetic: how many leading items fit before an overflow indicator
13
+ * is required. Does not touch the DOM — safe to call on every resize frame.
14
+ */
15
+ export declare function calculateVisibleCount({ itemWidths, gap, availableWidth, indicatorWidth, }: CalculateVisibleCountParams): number;
@@ -0,0 +1,19 @@
1
+ function calculateVisibleCount({ itemWidths, gap, availableWidth, indicatorWidth }) {
2
+ if (0 === itemWidths.length) return 0;
3
+ if (availableWidth <= 0) return itemWidths.length;
4
+ let total = 0;
5
+ for(let i = 0; i < itemWidths.length; i++)total += (itemWidths[i] ?? 0) + (i > 0 ? gap : 0);
6
+ if (total <= availableWidth) return itemWidths.length;
7
+ const maxWidth = availableWidth - indicatorWidth - gap;
8
+ let accumulated = 0;
9
+ let count = 0;
10
+ for(let i = 0; i < itemWidths.length; i++){
11
+ const widthWithGap = (itemWidths[i] ?? 0) + (i > 0 ? gap : 0);
12
+ if (accumulated + widthWithGap <= maxWidth || 0 === i) {
13
+ accumulated += widthWithGap;
14
+ count++;
15
+ } else break;
16
+ }
17
+ return Math.max(count, 1);
18
+ }
19
+ export { calculateVisibleCount };
@@ -1,93 +1,109 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { useLayoutEffect, useRef, useState } from "react";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useLayoutEffect, useRef, useState } from "react";
3
+ import { calculateVisibleCount } from "./useOverflowItems.helpers.js";
3
4
  function useOverflowItems({ items, renderItem, renderMeasurementItem, overflowRenderer, reserveSpace = 60 }) {
4
5
  const containerRef = useRef(null);
5
6
  const [visibleCount, setVisibleCount] = useState(items.length);
6
- const [measurements, setMeasurements] = useState([]);
7
7
  const measurementRefs = useRef([]);
8
+ const indicatorRef = useRef(null);
9
+ const cacheRef = useRef({
10
+ widths: [],
11
+ gap: 0,
12
+ indicatorWidth: reserveSpace
13
+ });
14
+ const recompute = useCallback(()=>{
15
+ const container = containerRef.current;
16
+ if (!container) return;
17
+ const { widths, gap, indicatorWidth } = cacheRef.current;
18
+ if (0 === widths.length) return;
19
+ const next = calculateVisibleCount({
20
+ itemWidths: widths,
21
+ gap,
22
+ availableWidth: container.offsetWidth,
23
+ indicatorWidth
24
+ });
25
+ setVisibleCount((prev)=>prev === next ? prev : next);
26
+ }, []);
8
27
  useLayoutEffect(()=>{
28
+ const container = containerRef.current;
9
29
  if (0 === items.length) {
30
+ cacheRef.current = {
31
+ widths: [],
32
+ gap: 0,
33
+ indicatorWidth: reserveSpace
34
+ };
10
35
  setVisibleCount(0);
11
- setMeasurements([]);
12
36
  return;
13
37
  }
14
- const widths = measurementRefs.current.slice(0, items.length).map((ref)=>ref?.offsetWidth || 0);
15
- setMeasurements(widths);
38
+ const gap = container ? Number.parseFloat(getComputedStyle(container).gap || '0') || 0 : 0;
39
+ const widths = measurementRefs.current.slice(0, items.length).map((ref)=>ref?.offsetWidth ?? 0);
40
+ const indicatorWidth = indicatorRef.current?.offsetWidth || reserveSpace;
41
+ cacheRef.current = {
42
+ widths,
43
+ gap,
44
+ indicatorWidth
45
+ };
46
+ recompute();
16
47
  }, [
17
- items
48
+ items,
49
+ renderItem,
50
+ renderMeasurementItem,
51
+ overflowRenderer,
52
+ reserveSpace,
53
+ recompute
18
54
  ]);
19
55
  useLayoutEffect(()=>{
20
56
  const container = containerRef.current;
21
- if (!container || 0 === measurements.length) return void setVisibleCount(items.length);
22
- const calculateVisibleItems = ()=>{
23
- const availableWidth = container.offsetWidth;
24
- if (availableWidth <= 0) return void setVisibleCount(items.length);
25
- const computedStyles = window.getComputedStyle(container);
26
- const gap = parseFloat(computedStyles.gap || '0');
27
- let accumulatedWidth = 0;
28
- let count = 0;
29
- for(let i = 0; i < measurements.length; i++){
30
- const itemWidth = measurements[i];
31
- if (!itemWidth) continue;
32
- const widthWithGap = itemWidth + (i > 0 ? gap : 0);
33
- const needsIndicator = i < measurements.length - 1;
34
- let dynamicReserveSpace = reserveSpace;
35
- if (needsIndicator && overflowRenderer) try {
36
- const tempDiv = document.createElement('div');
37
- tempDiv.style.cssText = 'position: absolute; visibility: hidden; top: -9999px;';
38
- tempDiv.style.font = computedStyles.font;
39
- document.body.appendChild(tempDiv);
40
- const overflowItem = items[0];
41
- if (overflowItem) {
42
- const overflowElement = overflowRenderer([
43
- overflowItem
44
- ]);
45
- if (overflowElement && 'object' == typeof overflowElement) tempDiv.textContent = '+1';
46
- }
47
- dynamicReserveSpace = Math.max(reserveSpace, tempDiv.offsetWidth + gap);
48
- document.body.removeChild(tempDiv);
49
- } catch {}
50
- const maxWidth = availableWidth - (needsIndicator ? dynamicReserveSpace : 0);
51
- if (accumulatedWidth + widthWithGap <= maxWidth || 0 === i) {
52
- count++;
53
- accumulatedWidth += widthWithGap;
54
- } else break;
55
- }
56
- setVisibleCount(count || 1);
57
- };
58
- calculateVisibleItems();
59
- const resizeObserver = new ResizeObserver(calculateVisibleItems);
60
- resizeObserver.observe(container);
57
+ if (!container) return;
58
+ let frame = 0;
59
+ const observer = new ResizeObserver(()=>{
60
+ if (frame) return;
61
+ frame = requestAnimationFrame(()=>{
62
+ frame = 0;
63
+ recompute();
64
+ });
65
+ });
66
+ observer.observe(container);
61
67
  return ()=>{
62
- resizeObserver.disconnect();
68
+ if (frame) cancelAnimationFrame(frame);
69
+ observer.disconnect();
63
70
  };
64
71
  }, [
65
- measurements,
66
- items,
67
- reserveSpace,
68
- overflowRenderer
72
+ recompute
69
73
  ]);
70
74
  const visibleItems = items.slice(0, visibleCount);
71
75
  const hiddenItems = items.slice(visibleCount);
72
76
  const hiddenCount = hiddenItems.length;
73
- const MeasurementContainer = ()=>{
77
+ const MeasurementContainer = useCallback(()=>{
74
78
  if (0 === items.length) return null;
75
79
  const renderMeasure = renderMeasurementItem || renderItem;
76
- return /*#__PURE__*/ jsx("div", {
80
+ return /*#__PURE__*/ jsxs("div", {
77
81
  className: "absolute invisible pointer-events-none",
78
82
  "aria-hidden": "true",
79
- children: items.map((item, index)=>{
80
- const key = `${index}`;
81
- return /*#__PURE__*/ jsx("div", {
82
- ref: (el)=>{
83
- measurementRefs.current[index] = el;
84
- },
83
+ children: [
84
+ items.map((item, index)=>{
85
+ const key = `${index}`;
86
+ return /*#__PURE__*/ jsx("div", {
87
+ ref: (el)=>{
88
+ measurementRefs.current[index] = el;
89
+ },
90
+ className: "inline-flex",
91
+ children: renderMeasure(item)
92
+ }, key);
93
+ }),
94
+ overflowRenderer && /*#__PURE__*/ jsx("div", {
95
+ ref: indicatorRef,
85
96
  className: "inline-flex",
86
- children: renderMeasure(item)
87
- }, key);
88
- })
97
+ children: overflowRenderer(items)
98
+ })
99
+ ]
89
100
  });
90
- };
101
+ }, [
102
+ items,
103
+ renderItem,
104
+ renderMeasurementItem,
105
+ overflowRenderer
106
+ ]);
91
107
  return {
92
108
  containerRef,
93
109
  visibleItems,
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.48.0",
3
- "generatedAt": "2026-05-28T00:36:49.945Z",
2
+ "version": "0.49.0",
3
+ "generatedAt": "2026-05-28T07:33:40.912Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Accordion",
@@ -18174,6 +18174,11 @@
18174
18174
  "code": "() => {\n return (\n <VStack gap={12}>\n <Drawer width={800}>\n <DrawerTrigger asChild>\n <Button>Open Resizable Drawer (as number)</Button>\n </DrawerTrigger>\n\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Resizable Drawer</DrawerTitle>\n </DrawerHeader>\n\n <DrawerBody>\n <div className='py-12'>\n <p className='mb-16'>Drag the left edge to resize this drawer.</p>\n <ContentPlaceholder height={300} />\n </div>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n\n <Drawer width='900px'>\n <DrawerTrigger asChild>\n <Button>Open Resizable Drawer (900px)</Button>\n </DrawerTrigger>\n\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Resizable Drawer with \"900px\" width</DrawerTitle>\n </DrawerHeader>\n\n <DrawerBody>\n <div className='py-12'>\n <p className='mb-16'>Width is set as \"900px\" string. Drag the left edge to resize.</p>\n <ContentPlaceholder height={300} />\n </div>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n\n <Drawer width='50%'>\n <DrawerTrigger asChild>\n <Button>Open Resizable Drawer (50%)</Button>\n </DrawerTrigger>\n\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Resizable Drawer with 50% width</DrawerTitle>\n </DrawerHeader>\n\n <DrawerBody>\n <div className='py-12'>\n <p className='mb-16'>\n Width is set as \"50%\". Drag the left edge to resize - it will convert to pixels.\n </p>\n <ContentPlaceholder height={300} />\n </div>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n </VStack>\n );\n}",
18175
18175
  "description": "Resizable drawer"
18176
18176
  },
18177
+ {
18178
+ "name": "ResizableWithOverflowList",
18179
+ "code": "() => {\n const [open, setOpen] = useState(true);\n\n return (\n <Drawer width={480} open={open} onOpenChange={setOpen}>\n <DrawerTrigger asChild>\n <Button>Open Resizable Drawer with OverflowList</Button>\n </DrawerTrigger>\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Resizable Drawer with OverflowList</DrawerTitle>\n </DrawerHeader>\n <DrawerBody>\n <p className='mb-16'>Drag the left edge — the tag list reflows live.</p>\n <Attribute>\n <AttributeLabel>Attack types</AttributeLabel>\n <AttributeValue>\n <OverflowList\n className='gap-4'\n items={DRAWER_TAGS}\n itemRenderer={item => <Tag key={item}>{item}</Tag>}\n overflowRenderer={renderDrawerOverflow}\n />\n </AttributeValue>\n </Attribute>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n );\n}",
18180
+ "description": "Resizable drawer with an OverflowList — drag the left edge to reflow tags."
18181
+ },
18177
18182
  {
18178
18183
  "name": "Scrollable",
18179
18184
  "code": "() => {\n return (\n <Drawer>\n <DrawerTrigger asChild>\n <Button>Open Drawer with Scroll</Button>\n </DrawerTrigger>\n\n <DrawerContent>\n <DrawerHeader>\n <DrawerTitle>Scrollable Content</DrawerTitle>\n </DrawerHeader>\n\n <DrawerBody>\n <div className='flex flex-col gap-16 py-12'>\n {Array.from({ length: 20 }).map((_, i) => (\n <ContentPlaceholder key={i} height={100} />\n ))}\n </div>\n </DrawerBody>\n\n <DrawerFooter>\n <DrawerClose asChild>\n <Button variant='ghost' color='neutral' size='large'>\n Close\n </Button>\n </DrawerClose>\n <Button variant='primary' color='brand' size='large'>\n Confirm\n </Button>\n </DrawerFooter>\n </DrawerContent>\n </Drawer>\n );\n}",
@@ -30600,7 +30605,33 @@
30600
30605
  ],
30601
30606
  "variants": [],
30602
30607
  "subComponents": [],
30603
- "examples": []
30608
+ "examples": [
30609
+ {
30610
+ "name": "Basic",
30611
+ "code": "() => (\n <div className='w-640'>\n <OverflowList\n className='gap-4'\n items={TAGS}\n itemRenderer={item => <Tag key={item}>{item}</Tag>}\n overflowRenderer={renderOverflowPopover}\n />\n </div>\n)",
30612
+ "description": "Basic list — all items fit in a wide container."
30613
+ },
30614
+ {
30615
+ "name": "Collapsed",
30616
+ "code": "() => (\n <div className='w-200'>\n <OverflowList\n className='gap-4'\n items={TAGS}\n itemRenderer={item => <Tag key={item}>{item}</Tag>}\n overflowRenderer={renderOverflowPopover}\n />\n </div>\n)",
30617
+ "description": "Narrow container — most items collapse into \"+N\"."
30618
+ },
30619
+ {
30620
+ "name": "CollapseFromStart",
30621
+ "code": "() => (\n <div className='w-240'>\n <OverflowList\n className='gap-4'\n collapseFrom='start'\n items={TAGS}\n itemRenderer={item => <Tag key={item}>{item}</Tag>}\n overflowRenderer={renderOverflowPopover}\n />\n </div>\n)",
30622
+ "description": "Collapse from the start of the list."
30623
+ },
30624
+ {
30625
+ "name": "MinVisibleItems",
30626
+ "code": "() => (\n <div className='w-160 overflow-hidden rounded-2 border border-border-primary p-12'>\n <OverflowList\n className='gap-4'\n minVisibleItems={1}\n items={TAGS}\n itemRenderer={item => (\n <Tag key={item} className='shrink-0 whitespace-nowrap'>\n {item}\n </Tag>\n )}\n overflowRenderer={renderOverflowPopoverInline}\n />\n </div>\n)",
30627
+ "description": "Guaranteed minimum number of visible items. The container is deliberately\nnarrow: `minVisibleItems={1}` keeps at least one tag visible (the rest\ncollapse into \"+N\") even when the space is tight."
30628
+ },
30629
+ {
30630
+ "name": "ResizableContainer",
30631
+ "code": "() => (\n <div\n data-testid='resizable-wrapper'\n className='overflow-hidden rounded-2 border border-border-primary p-12'\n style={{ width: 500, minWidth: 80, maxWidth: 800, resize: 'horizontal' }}\n >\n <OverflowList\n className='gap-4'\n items={TAGS}\n itemRenderer={item => <Tag key={item}>{item}</Tag>}\n overflowRenderer={renderOverflowPopover}\n />\n </div>\n)",
30632
+ "description": "Resizable container: drag the bottom-right corner of the box to see the list\nreflow live. The data-testid is used by E2E to resize programmatically."
30633
+ }
30634
+ ]
30604
30635
  },
30605
30636
  {
30606
30637
  "name": "OverflowTooltip",
@@ -37765,7 +37796,283 @@
37765
37796
  "importPath": "@wallarm-org/design-system/Select",
37766
37797
  "props": [],
37767
37798
  "variants": [],
37768
- "subComponents": [],
37799
+ "subComponents": [
37800
+ {
37801
+ "name": "SelectSeparator",
37802
+ "props": [
37803
+ {
37804
+ "name": "defaultChecked",
37805
+ "type": "boolean | undefined",
37806
+ "required": false
37807
+ },
37808
+ {
37809
+ "name": "defaultValue",
37810
+ "type": "string | number | readonly string[] | undefined",
37811
+ "required": false
37812
+ },
37813
+ {
37814
+ "name": "suppressContentEditableWarning",
37815
+ "type": "boolean | undefined",
37816
+ "required": false
37817
+ },
37818
+ {
37819
+ "name": "suppressHydrationWarning",
37820
+ "type": "boolean | undefined",
37821
+ "required": false
37822
+ },
37823
+ {
37824
+ "name": "accessKey",
37825
+ "type": "string | undefined",
37826
+ "required": false
37827
+ },
37828
+ {
37829
+ "name": "autoCapitalize",
37830
+ "type": "\"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}) | undefined",
37831
+ "required": false
37832
+ },
37833
+ {
37834
+ "name": "autoFocus",
37835
+ "type": "boolean | undefined",
37836
+ "required": false
37837
+ },
37838
+ {
37839
+ "name": "contentEditable",
37840
+ "type": "Booleanish | \"inherit\" | \"plaintext-only\" | undefined",
37841
+ "required": false
37842
+ },
37843
+ {
37844
+ "name": "contextMenu",
37845
+ "type": "string | undefined",
37846
+ "required": false
37847
+ },
37848
+ {
37849
+ "name": "dir",
37850
+ "type": "string | undefined",
37851
+ "required": false
37852
+ },
37853
+ {
37854
+ "name": "draggable",
37855
+ "type": "Booleanish | undefined",
37856
+ "required": false
37857
+ },
37858
+ {
37859
+ "name": "enterKeyHint",
37860
+ "type": "\"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\" | undefined",
37861
+ "required": false
37862
+ },
37863
+ {
37864
+ "name": "hidden",
37865
+ "type": "boolean | undefined",
37866
+ "required": false
37867
+ },
37868
+ {
37869
+ "name": "id",
37870
+ "type": "string | undefined",
37871
+ "required": false
37872
+ },
37873
+ {
37874
+ "name": "lang",
37875
+ "type": "string | undefined",
37876
+ "required": false
37877
+ },
37878
+ {
37879
+ "name": "nonce",
37880
+ "type": "string | undefined",
37881
+ "required": false
37882
+ },
37883
+ {
37884
+ "name": "slot",
37885
+ "type": "string | undefined",
37886
+ "required": false
37887
+ },
37888
+ {
37889
+ "name": "spellCheck",
37890
+ "type": "Booleanish | undefined",
37891
+ "required": false
37892
+ },
37893
+ {
37894
+ "name": "tabIndex",
37895
+ "type": "number | undefined",
37896
+ "required": false
37897
+ },
37898
+ {
37899
+ "name": "title",
37900
+ "type": "string | undefined",
37901
+ "required": false
37902
+ },
37903
+ {
37904
+ "name": "translate",
37905
+ "type": "\"yes\" | \"no\" | undefined",
37906
+ "required": false
37907
+ },
37908
+ {
37909
+ "name": "radioGroup",
37910
+ "type": "string | undefined",
37911
+ "required": false
37912
+ },
37913
+ {
37914
+ "name": "role",
37915
+ "type": "AriaRole | undefined",
37916
+ "required": false
37917
+ },
37918
+ {
37919
+ "name": "about",
37920
+ "type": "string | undefined",
37921
+ "required": false
37922
+ },
37923
+ {
37924
+ "name": "content",
37925
+ "type": "string | undefined",
37926
+ "required": false
37927
+ },
37928
+ {
37929
+ "name": "datatype",
37930
+ "type": "string | undefined",
37931
+ "required": false
37932
+ },
37933
+ {
37934
+ "name": "inlist",
37935
+ "type": "any",
37936
+ "required": false
37937
+ },
37938
+ {
37939
+ "name": "prefix",
37940
+ "type": "string | undefined",
37941
+ "required": false
37942
+ },
37943
+ {
37944
+ "name": "property",
37945
+ "type": "string | undefined",
37946
+ "required": false
37947
+ },
37948
+ {
37949
+ "name": "rel",
37950
+ "type": "string | undefined",
37951
+ "required": false
37952
+ },
37953
+ {
37954
+ "name": "resource",
37955
+ "type": "string | undefined",
37956
+ "required": false
37957
+ },
37958
+ {
37959
+ "name": "rev",
37960
+ "type": "string | undefined",
37961
+ "required": false
37962
+ },
37963
+ {
37964
+ "name": "typeof",
37965
+ "type": "string | undefined",
37966
+ "required": false
37967
+ },
37968
+ {
37969
+ "name": "vocab",
37970
+ "type": "string | undefined",
37971
+ "required": false
37972
+ },
37973
+ {
37974
+ "name": "autoCorrect",
37975
+ "type": "string | undefined",
37976
+ "required": false
37977
+ },
37978
+ {
37979
+ "name": "autoSave",
37980
+ "type": "string | undefined",
37981
+ "required": false
37982
+ },
37983
+ {
37984
+ "name": "color",
37985
+ "type": "string | undefined",
37986
+ "required": false
37987
+ },
37988
+ {
37989
+ "name": "itemProp",
37990
+ "type": "string | undefined",
37991
+ "required": false
37992
+ },
37993
+ {
37994
+ "name": "itemScope",
37995
+ "type": "boolean | undefined",
37996
+ "required": false
37997
+ },
37998
+ {
37999
+ "name": "itemType",
38000
+ "type": "string | undefined",
38001
+ "required": false
38002
+ },
38003
+ {
38004
+ "name": "itemID",
38005
+ "type": "string | undefined",
38006
+ "required": false
38007
+ },
38008
+ {
38009
+ "name": "itemRef",
38010
+ "type": "string | undefined",
38011
+ "required": false
38012
+ },
38013
+ {
38014
+ "name": "results",
38015
+ "type": "number | undefined",
38016
+ "required": false
38017
+ },
38018
+ {
38019
+ "name": "security",
38020
+ "type": "string | undefined",
38021
+ "required": false
38022
+ },
38023
+ {
38024
+ "name": "unselectable",
38025
+ "type": "\"off\" | \"on\" | undefined",
38026
+ "required": false
38027
+ },
38028
+ {
38029
+ "name": "popover",
38030
+ "type": "\"\" | \"auto\" | \"manual\" | \"hint\" | undefined",
38031
+ "required": false
38032
+ },
38033
+ {
38034
+ "name": "popoverTargetAction",
38035
+ "type": "\"toggle\" | \"show\" | \"hide\" | undefined",
38036
+ "required": false
38037
+ },
38038
+ {
38039
+ "name": "popoverTarget",
38040
+ "type": "string | undefined",
38041
+ "required": false
38042
+ },
38043
+ {
38044
+ "name": "inert",
38045
+ "type": "boolean | undefined",
38046
+ "required": false,
38047
+ "description": "@see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert"
38048
+ },
38049
+ {
38050
+ "name": "inputMode",
38051
+ "type": "\"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
38052
+ "required": false,
38053
+ "description": "Hints at the type of data that might be entered by the user while editing the element or its contents"
38054
+ },
38055
+ {
38056
+ "name": "is",
38057
+ "type": "string | undefined",
38058
+ "required": false,
38059
+ "description": "Specify that a standard HTML element should behave like a defined custom built-in element"
38060
+ },
38061
+ {
38062
+ "name": "exportparts",
38063
+ "type": "string | undefined",
38064
+ "required": false,
38065
+ "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts}"
38066
+ },
38067
+ {
38068
+ "name": "part",
38069
+ "type": "string | undefined",
38070
+ "required": false,
38071
+ "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part}"
38072
+ }
38073
+ ]
38074
+ }
38075
+ ],
37769
38076
  "examples": [
37770
38077
  {
37771
38078
  "name": "Basic",
@@ -51627,6 +51934,11 @@
51627
51934
  "name": "ColumnResizing",
51628
51935
  "code": "() => {\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n\n return (\n <Table\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n />\n );\n}"
51629
51936
  },
51937
+ {
51938
+ "name": "ColumnResizingWithOverflowList",
51939
+ "code": "() => {\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n\n const columns = useMemo<TableColumnDef<SecurityEvent>[]>(\n () => [\n ...securityColumns.slice(0, 1),\n securityColumnHelper.display({\n id: 'tags',\n header: 'Tags',\n size: 240,\n cell: ({ row }) => (\n <OverflowList\n className='gap-4'\n items={row.original.tags}\n itemRenderer={(item: string) => <Tag key={item}>{item}</Tag>}\n overflowRenderer={renderTableTagsOverflow}\n />\n ),\n }),\n ...securityColumns.slice(1),\n ],\n [],\n );\n\n return (\n <Table\n data={securityEvents}\n columns={columns}\n getRowId={row => row.id}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n />\n );\n}",
51940
+ "description": "Resizable column whose cell hosts an OverflowList — resize it to reflow tags."
51941
+ },
51630
51942
  {
51631
51943
  "name": "ColumnResizingWithPinning",
51632
51944
  "code": "() => {\n const [columnSizing, setColumnSizing] = useState<TableColumnSizingState>({});\n const [columnPinning, setColumnPinning] = useState<TableColumnPinningState>({\n left: ['objectName'],\n });\n\n return (\n <Table\n className='max-w-920'\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n columnSizing={columnSizing}\n onColumnSizingChange={setColumnSizing}\n columnPinning={columnPinning}\n onColumnPinningChange={setColumnPinning}\n />\n );\n}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.49.0",
3
+ "version": "0.50.0-rc-feature-AS-1033.1",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",