@wallarm-org/design-system 0.48.0-rc-feature-shell.1 → 0.48.0-rc-feature-AS-1033.2

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.
@@ -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';
@@ -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
2
  "version": "0.47.0",
3
- "generatedAt": "2026-05-26T19:49:46.334Z",
3
+ "generatedAt": "2026-05-27T20:13:56.529Z",
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",
@@ -36356,7 +36387,283 @@
36356
36387
  "importPath": "@wallarm-org/design-system/Select",
36357
36388
  "props": [],
36358
36389
  "variants": [],
36359
- "subComponents": [],
36390
+ "subComponents": [
36391
+ {
36392
+ "name": "SelectSeparator",
36393
+ "props": [
36394
+ {
36395
+ "name": "defaultChecked",
36396
+ "type": "boolean | undefined",
36397
+ "required": false
36398
+ },
36399
+ {
36400
+ "name": "defaultValue",
36401
+ "type": "string | number | readonly string[] | undefined",
36402
+ "required": false
36403
+ },
36404
+ {
36405
+ "name": "suppressContentEditableWarning",
36406
+ "type": "boolean | undefined",
36407
+ "required": false
36408
+ },
36409
+ {
36410
+ "name": "suppressHydrationWarning",
36411
+ "type": "boolean | undefined",
36412
+ "required": false
36413
+ },
36414
+ {
36415
+ "name": "accessKey",
36416
+ "type": "string | undefined",
36417
+ "required": false
36418
+ },
36419
+ {
36420
+ "name": "autoCapitalize",
36421
+ "type": "\"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}) | undefined",
36422
+ "required": false
36423
+ },
36424
+ {
36425
+ "name": "autoFocus",
36426
+ "type": "boolean | undefined",
36427
+ "required": false
36428
+ },
36429
+ {
36430
+ "name": "contentEditable",
36431
+ "type": "Booleanish | \"inherit\" | \"plaintext-only\" | undefined",
36432
+ "required": false
36433
+ },
36434
+ {
36435
+ "name": "contextMenu",
36436
+ "type": "string | undefined",
36437
+ "required": false
36438
+ },
36439
+ {
36440
+ "name": "dir",
36441
+ "type": "string | undefined",
36442
+ "required": false
36443
+ },
36444
+ {
36445
+ "name": "draggable",
36446
+ "type": "Booleanish | undefined",
36447
+ "required": false
36448
+ },
36449
+ {
36450
+ "name": "enterKeyHint",
36451
+ "type": "\"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\" | undefined",
36452
+ "required": false
36453
+ },
36454
+ {
36455
+ "name": "hidden",
36456
+ "type": "boolean | undefined",
36457
+ "required": false
36458
+ },
36459
+ {
36460
+ "name": "id",
36461
+ "type": "string | undefined",
36462
+ "required": false
36463
+ },
36464
+ {
36465
+ "name": "lang",
36466
+ "type": "string | undefined",
36467
+ "required": false
36468
+ },
36469
+ {
36470
+ "name": "nonce",
36471
+ "type": "string | undefined",
36472
+ "required": false
36473
+ },
36474
+ {
36475
+ "name": "slot",
36476
+ "type": "string | undefined",
36477
+ "required": false
36478
+ },
36479
+ {
36480
+ "name": "spellCheck",
36481
+ "type": "Booleanish | undefined",
36482
+ "required": false
36483
+ },
36484
+ {
36485
+ "name": "tabIndex",
36486
+ "type": "number | undefined",
36487
+ "required": false
36488
+ },
36489
+ {
36490
+ "name": "title",
36491
+ "type": "string | undefined",
36492
+ "required": false
36493
+ },
36494
+ {
36495
+ "name": "translate",
36496
+ "type": "\"yes\" | \"no\" | undefined",
36497
+ "required": false
36498
+ },
36499
+ {
36500
+ "name": "radioGroup",
36501
+ "type": "string | undefined",
36502
+ "required": false
36503
+ },
36504
+ {
36505
+ "name": "role",
36506
+ "type": "AriaRole | undefined",
36507
+ "required": false
36508
+ },
36509
+ {
36510
+ "name": "about",
36511
+ "type": "string | undefined",
36512
+ "required": false
36513
+ },
36514
+ {
36515
+ "name": "content",
36516
+ "type": "string | undefined",
36517
+ "required": false
36518
+ },
36519
+ {
36520
+ "name": "datatype",
36521
+ "type": "string | undefined",
36522
+ "required": false
36523
+ },
36524
+ {
36525
+ "name": "inlist",
36526
+ "type": "any",
36527
+ "required": false
36528
+ },
36529
+ {
36530
+ "name": "prefix",
36531
+ "type": "string | undefined",
36532
+ "required": false
36533
+ },
36534
+ {
36535
+ "name": "property",
36536
+ "type": "string | undefined",
36537
+ "required": false
36538
+ },
36539
+ {
36540
+ "name": "rel",
36541
+ "type": "string | undefined",
36542
+ "required": false
36543
+ },
36544
+ {
36545
+ "name": "resource",
36546
+ "type": "string | undefined",
36547
+ "required": false
36548
+ },
36549
+ {
36550
+ "name": "rev",
36551
+ "type": "string | undefined",
36552
+ "required": false
36553
+ },
36554
+ {
36555
+ "name": "typeof",
36556
+ "type": "string | undefined",
36557
+ "required": false
36558
+ },
36559
+ {
36560
+ "name": "vocab",
36561
+ "type": "string | undefined",
36562
+ "required": false
36563
+ },
36564
+ {
36565
+ "name": "autoCorrect",
36566
+ "type": "string | undefined",
36567
+ "required": false
36568
+ },
36569
+ {
36570
+ "name": "autoSave",
36571
+ "type": "string | undefined",
36572
+ "required": false
36573
+ },
36574
+ {
36575
+ "name": "color",
36576
+ "type": "string | undefined",
36577
+ "required": false
36578
+ },
36579
+ {
36580
+ "name": "itemProp",
36581
+ "type": "string | undefined",
36582
+ "required": false
36583
+ },
36584
+ {
36585
+ "name": "itemScope",
36586
+ "type": "boolean | undefined",
36587
+ "required": false
36588
+ },
36589
+ {
36590
+ "name": "itemType",
36591
+ "type": "string | undefined",
36592
+ "required": false
36593
+ },
36594
+ {
36595
+ "name": "itemID",
36596
+ "type": "string | undefined",
36597
+ "required": false
36598
+ },
36599
+ {
36600
+ "name": "itemRef",
36601
+ "type": "string | undefined",
36602
+ "required": false
36603
+ },
36604
+ {
36605
+ "name": "results",
36606
+ "type": "number | undefined",
36607
+ "required": false
36608
+ },
36609
+ {
36610
+ "name": "security",
36611
+ "type": "string | undefined",
36612
+ "required": false
36613
+ },
36614
+ {
36615
+ "name": "unselectable",
36616
+ "type": "\"off\" | \"on\" | undefined",
36617
+ "required": false
36618
+ },
36619
+ {
36620
+ "name": "popover",
36621
+ "type": "\"\" | \"auto\" | \"manual\" | \"hint\" | undefined",
36622
+ "required": false
36623
+ },
36624
+ {
36625
+ "name": "popoverTargetAction",
36626
+ "type": "\"toggle\" | \"show\" | \"hide\" | undefined",
36627
+ "required": false
36628
+ },
36629
+ {
36630
+ "name": "popoverTarget",
36631
+ "type": "string | undefined",
36632
+ "required": false
36633
+ },
36634
+ {
36635
+ "name": "inert",
36636
+ "type": "boolean | undefined",
36637
+ "required": false,
36638
+ "description": "@see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert"
36639
+ },
36640
+ {
36641
+ "name": "inputMode",
36642
+ "type": "\"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined",
36643
+ "required": false,
36644
+ "description": "Hints at the type of data that might be entered by the user while editing the element or its contents"
36645
+ },
36646
+ {
36647
+ "name": "is",
36648
+ "type": "string | undefined",
36649
+ "required": false,
36650
+ "description": "Specify that a standard HTML element should behave like a defined custom built-in element"
36651
+ },
36652
+ {
36653
+ "name": "exportparts",
36654
+ "type": "string | undefined",
36655
+ "required": false,
36656
+ "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts}"
36657
+ },
36658
+ {
36659
+ "name": "part",
36660
+ "type": "string | undefined",
36661
+ "required": false,
36662
+ "description": "@see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part}"
36663
+ }
36664
+ ]
36665
+ }
36666
+ ],
36360
36667
  "examples": [
36361
36668
  {
36362
36669
  "name": "Basic",
@@ -50218,6 +50525,11 @@
50218
50525
  "name": "ColumnResizing",
50219
50526
  "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}"
50220
50527
  },
50528
+ {
50529
+ "name": "ColumnResizingWithOverflowList",
50530
+ "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}",
50531
+ "description": "Resizable column whose cell hosts an OverflowList — resize it to reflow tags."
50532
+ },
50221
50533
  {
50222
50534
  "name": "ColumnResizingWithPinning",
50223
50535
  "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.48.0-rc-feature-shell.1",
3
+ "version": "0.48.0-rc-feature-AS-1033.2",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",