@wallarm-org/design-system 0.30.0-rc-feature-AS-884.4 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { type FC, type ReactNode, type Ref } from 'react';
1
+ import type { FC, ReactNode, Ref } from 'react';
2
2
  export interface DrawerContentProps {
3
3
  children: ReactNode;
4
4
  asChild?: boolean;
@@ -1,31 +1,22 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback, useRef } from "react";
3
2
  import { Dialog } from "@ark-ui/react/dialog";
4
3
  import { cn } from "../../utils/cn.js";
5
4
  import { useTestId } from "../../utils/testId.js";
6
5
  import { drawerContentVariants } from "./classes.js";
7
- import { DrawerContentContext, useDrawerContext } from "./DrawerContext/index.js";
6
+ import { useDrawerContext } from "./DrawerContext/index.js";
8
7
  import { DrawerOverlay } from "./DrawerOverlay.js";
9
8
  import { DrawerPortal } from "./DrawerPortal.js";
10
9
  import { DrawerPositioner } from "./DrawerPositioner.js";
11
10
  const DrawerContent = ({ children, asChild, ref })=>{
12
11
  const testId = useTestId('content');
13
12
  const { width, isResizing, overlay } = useDrawerContext();
14
- const contentRef = useRef(null);
15
- const setRef = useCallback((node)=>{
16
- contentRef.current = node;
17
- if ('function' == typeof ref) ref(node);
18
- else if (ref) ref.current = node;
19
- }, [
20
- ref
21
- ]);
22
13
  return /*#__PURE__*/ jsxs(DrawerPortal, {
23
14
  children: [
24
15
  overlay && /*#__PURE__*/ jsx(DrawerOverlay, {}),
25
16
  /*#__PURE__*/ jsx(DrawerPositioner, {
26
17
  isResizing: isResizing,
27
18
  children: /*#__PURE__*/ jsx(Dialog.Content, {
28
- ref: setRef,
19
+ ref: ref,
29
20
  "data-testid": testId,
30
21
  className: cn(drawerContentVariants({
31
22
  isResizing
@@ -34,10 +25,7 @@ const DrawerContent = ({ children, asChild, ref })=>{
34
25
  width
35
26
  },
36
27
  asChild: asChild,
37
- children: /*#__PURE__*/ jsx(DrawerContentContext.Provider, {
38
- value: contentRef,
39
- children: children
40
- })
28
+ children: children
41
29
  })
42
30
  })
43
31
  ]
@@ -1,4 +1,3 @@
1
- export { DrawerContentContext, useOptionalDrawerContentRef } from './DrawerContentContext';
2
1
  export { DrawerContext } from './DrawerContext';
3
2
  export { DrawerProvider } from './DrawerProvider';
4
3
  export type { DrawerContextValue } from './types';
@@ -1,5 +1,4 @@
1
- import { DrawerContentContext, useOptionalDrawerContentRef } from "./DrawerContentContext.js";
2
1
  import { DrawerContext } from "./DrawerContext.js";
3
2
  import { DrawerProvider } from "./DrawerProvider.js";
4
3
  import { useDrawerContext } from "./useDrawerContext.js";
5
- export { DrawerContentContext, DrawerContext, DrawerProvider, useDrawerContext, useOptionalDrawerContentRef };
4
+ export { DrawerContext, DrawerProvider, useDrawerContext };
@@ -70,7 +70,7 @@ const Selection = ({ items, getItemId, value, onChange, className, 'data-testid'
70
70
  children: /*#__PURE__*/ jsx("div", {
71
71
  "data-slot": "selection",
72
72
  "data-testid": testId,
73
- className: cn('outline-none', className),
73
+ className: cn('relative outline-none', className),
74
74
  children: children
75
75
  })
76
76
  })
@@ -1,8 +1,22 @@
1
1
  import type { FC, ReactNode } from 'react';
2
+ export type SelectionBulkBarPlacement = 'floating' | 'absolute';
2
3
  export interface SelectionBulkBarProps {
3
4
  /** Override the toolbar accessible name. Defaults to "Bulk actions". */
4
5
  'aria-label'?: string;
5
6
  'data-testid'?: string;
7
+ /**
8
+ * How the bar is positioned.
9
+ *
10
+ * - `'floating'` (default): portaled to `document.body` and pinned to the
11
+ * bottom of the viewport with `position: fixed`. Use for page-level
12
+ * selection lists.
13
+ * - `'absolute'`: rendered in place with `position: absolute`. Anchors to
14
+ * the nearest positioned ancestor — by default the enclosing `Selection`
15
+ * root (which is `position: relative`), so the bar pins to the bottom of
16
+ * the selection area. Use this inside a Drawer or any other bounded
17
+ * container.
18
+ */
19
+ placement?: SelectionBulkBarPlacement;
6
20
  children?: ReactNode;
7
21
  }
8
22
  export declare const SelectionBulkBar: FC<SelectionBulkBarProps>;
@@ -1,40 +1,42 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Portal } from "@ark-ui/react/portal";
3
3
  import { Presence } from "@ark-ui/react/presence";
4
- import { cn } from "../../../utils/cn.js";
5
4
  import { useTestId } from "../../../utils/testId.js";
6
- import { useOptionalDrawerContentRef } from "../../Drawer/DrawerContext/index.js";
7
5
  import { HStack } from "../../Stack/index.js";
6
+ import { selectionBulkBarVariants } from "../classes.js";
8
7
  import { useSelectionContext } from "../useSelectionContext.js";
9
8
  import { SelectionBulkBarSummary } from "./SelectionBulkBarSummary.js";
10
- const SelectionBulkBar = ({ 'aria-label': ariaLabel = 'Bulk actions', 'data-testid': testIdProp, children })=>{
9
+ const TEST_ID_SLOT = 'bulk-bar';
10
+ const DEFAULT_ARIA_LABEL = 'Bulk actions';
11
+ const SelectionBulkBar = ({ 'aria-label': ariaLabel = DEFAULT_ARIA_LABEL, 'data-testid': testIdProp, placement = 'floating', children })=>{
11
12
  const { selectedIds } = useSelectionContext();
12
- const fallbackTestId = useTestId('bulk-bar');
13
+ const fallbackTestId = useTestId(TEST_ID_SLOT);
13
14
  const testId = testIdProp ?? fallbackTestId;
14
- const drawerContentRef = useOptionalDrawerContentRef();
15
- const isInDrawer = !!drawerContentRef;
16
- return /*#__PURE__*/ jsx(Portal, {
17
- container: drawerContentRef ?? void 0,
18
- children: /*#__PURE__*/ jsx(Presence, {
19
- present: selectedIds.size > 0,
20
- asChild: true,
21
- children: /*#__PURE__*/ jsxs("div", {
22
- role: "toolbar",
23
- "aria-label": ariaLabel,
24
- "data-slot": "selection-bulk-bar",
25
- "data-testid": testId,
26
- className: cn('z-[200] flex flex-nowrap items-center gap-8', isInDrawer ? 'absolute inset-x-12 bottom-12 justify-between' : 'fixed bottom-32 left-1/2 -translate-x-1/2 w-fit max-w-[calc(100vw-32px)]', 'bg-component-toast-bg rounded-16 shadow-lg', 'pl-12 pr-8 py-8', '[&_button]:shrink-0 [&_button]:whitespace-nowrap', 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom data-[state=open]:duration-300', 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom data-[state=closed]:duration-150'),
27
- children: [
28
- /*#__PURE__*/ jsx(SelectionBulkBarSummary, {}),
29
- /*#__PURE__*/ jsx(HStack, {
30
- gap: 8,
31
- align: "center",
32
- children: children
33
- })
34
- ]
35
- })
15
+ const bar = /*#__PURE__*/ jsx(Presence, {
16
+ present: selectedIds.size > 0,
17
+ asChild: true,
18
+ children: /*#__PURE__*/ jsxs("div", {
19
+ role: "toolbar",
20
+ "aria-label": ariaLabel,
21
+ "data-slot": "selection-bulk-bar",
22
+ "data-testid": testId,
23
+ className: selectionBulkBarVariants({
24
+ placement
25
+ }),
26
+ children: [
27
+ /*#__PURE__*/ jsx(SelectionBulkBarSummary, {}),
28
+ /*#__PURE__*/ jsx(HStack, {
29
+ gap: 8,
30
+ align: "center",
31
+ children: children
32
+ })
33
+ ]
36
34
  })
37
35
  });
36
+ if ('floating' === placement) return /*#__PURE__*/ jsx(Portal, {
37
+ children: bar
38
+ });
39
+ return bar;
38
40
  };
39
41
  SelectionBulkBar.displayName = 'SelectionBulkBar';
40
42
  export { SelectionBulkBar };
@@ -1 +1 @@
1
- export { SelectionBulkBar, type SelectionBulkBarProps } from './SelectionBulkBar';
1
+ export { SelectionBulkBar, type SelectionBulkBarPlacement, type SelectionBulkBarProps, } from './SelectionBulkBar';
@@ -1 +1,4 @@
1
1
  export declare const selectionItemVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
2
+ export declare const selectionBulkBarVariants: (props?: ({
3
+ placement?: "absolute" | "floating" | null | undefined;
4
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
@@ -1,3 +1,15 @@
1
1
  import { cva } from "class-variance-authority";
2
+ import { cn } from "../../utils/cn.js";
2
3
  const selectionItemVariants = cva('flex w-full min-w-0 items-start gap-12');
3
- export { selectionItemVariants };
4
+ const selectionBulkBarVariants = cva(cn('z-[200] flex w-fit flex-nowrap items-center gap-16', 'bg-component-toast-bg rounded-16 shadow-lg', 'pl-12 pr-8 py-8', '[&_button]:shrink-0 [&_button]:whitespace-nowrap', 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom data-[state=open]:duration-300', 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom data-[state=closed]:duration-150'), {
5
+ variants: {
6
+ placement: {
7
+ absolute: 'absolute bottom-12 left-1/2 -translate-x-1/2 max-w-[calc(100%-48px)]',
8
+ floating: 'fixed bottom-32 left-1/2 -translate-x-1/2 max-w-[calc(100vw-32px)]'
9
+ }
10
+ },
11
+ defaultVariants: {
12
+ placement: 'floating'
13
+ }
14
+ });
15
+ export { selectionBulkBarVariants, selectionItemVariants };
@@ -1,4 +1,4 @@
1
1
  export { Selection, type SelectionProps } from './Selection';
2
2
  export { SelectionAll, type SelectionAllProps } from './SelectionAll';
3
- export { SelectionBulkBar, type SelectionBulkBarProps, } from './SelectionBulkBar';
3
+ export { SelectionBulkBar, type SelectionBulkBarPlacement, type SelectionBulkBarProps, } from './SelectionBulkBar';
4
4
  export { SelectionItem, type SelectionItemProps } from './SelectionItem';
package/dist/index.d.ts CHANGED
@@ -44,7 +44,7 @@ export { ScrollArea, ScrollAreaContent, type ScrollAreaContentProps, ScrollAreaC
44
44
  export { SegmentedControl, SegmentedControlButton, type SegmentedControlButtonProps, SegmentedControlItem, type SegmentedControlItemProps, type SegmentedControlProps, SegmentedControlSeparator, type SegmentedControlSeparatorProps, } from './components/SegmentedControl';
45
45
  export { SegmentedTabs, SegmentedTabsButton, SegmentedTabsContent, SegmentedTabsList, SegmentedTabsSeparator, SegmentedTabsTrigger, SegmentedTabsTriggerButton, } from './components/SegmentedTabs';
46
46
  export { Select, SelectButton, SelectClearTrigger, SelectContent, SelectFooter, SelectGroup, SelectGroupLabel, SelectHeader, SelectInput, SelectOption, SelectOptionDescription, SelectOptionIndicator, SelectOptionText, SelectPositioner, SelectSearchInput, SelectSeparator, } from './components/Select';
47
- export { Selection, SelectionAll, type SelectionAllProps, SelectionBulkBar, type SelectionBulkBarProps, SelectionItem, type SelectionItemProps, type SelectionProps, } from './components/Selection';
47
+ export { Selection, SelectionAll, type SelectionAllProps, SelectionBulkBar, type SelectionBulkBarPlacement, type SelectionBulkBarProps, SelectionItem, type SelectionItemProps, type SelectionProps, } from './components/Selection';
48
48
  export { Separator, type SeparatorProps } from './components/Separator';
49
49
  export { Skeleton, type SkeletonProps } from './components/Skeleton';
50
50
  export { SplitButton, type SplitButtonProps } from './components/SplitButton';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.29.2",
3
- "generatedAt": "2026-04-27T21:07:40.981Z",
3
+ "generatedAt": "2026-04-28T07:02:22.140Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Alert",
@@ -23981,7 +23981,16 @@
23981
23981
  "description": "Controlled selection state — array of selected item ids."
23982
23982
  }
23983
23983
  ],
23984
- "variants": [],
23984
+ "variants": [
23985
+ {
23986
+ "name": "placement",
23987
+ "options": [
23988
+ "absolute",
23989
+ "floating"
23990
+ ],
23991
+ "defaultValue": "floating"
23992
+ }
23993
+ ],
23985
23994
  "subComponents": [
23986
23995
  {
23987
23996
  "name": "SelectionAll",
@@ -23989,7 +23998,14 @@
23989
23998
  },
23990
23999
  {
23991
24000
  "name": "SelectionBulkBar",
23992
- "props": []
24001
+ "props": [
24002
+ {
24003
+ "name": "placement",
24004
+ "type": "SelectionBulkBarPlacement | undefined",
24005
+ "required": false,
24006
+ "description": "How the bar is positioned.\n\n- `'floating'` (default): portaled to `document.body` and pinned to the\n bottom of the viewport with `position: fixed`. Use for page-level\n selection lists.\n- `'absolute'`: rendered in place with `position: absolute`. Anchors to\n the nearest positioned ancestor — by default the enclosing `Selection`\n root (which is `position: relative`), so the bar pins to the bottom of\n the selection area. Use this inside a Drawer or any other bounded\n container."
24007
+ }
24008
+ ]
23993
24009
  },
23994
24010
  {
23995
24011
  "name": "SelectionItem",
@@ -24045,7 +24061,7 @@
24045
24061
  },
24046
24062
  {
24047
24063
  "name": "InsideDrawer",
24048
- "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Drawer width={720}>\n <DrawerTrigger asChild>\n <Button>Open drawer with selection</Button>\n </DrawerTrigger>\n <DrawerContent>\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Clusters</DrawerTitle>\n </DrawerHeader>\n <DrawerBody>\n <Selection items={clusters} getItemId={c => c.id} value={selected} onChange={setSelected}>\n <VStack gap={16}>\n <HStack gap={8} align='center'>\n <SelectionAll />\n <Text size='sm' color='secondary'>\n Select all\n </Text>\n </HStack>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n\n <SelectionBulkBar>\n <Button\n variant='secondary'\n color='neutral-alt'\n size='large'\n onClick={() => alert(`Duplicate ${selected.length}`)}\n >\n <Copy /> Duplicate\n </Button>\n <Button\n variant='secondary'\n color='neutral-alt'\n size='large'\n onClick={() => alert(`Move ${selected.length}`)}\n >\n <Folder /> Move\n </Button>\n <Button\n variant='secondary'\n color='destructive'\n size='large'\n onClick={() => alert(`Delete ${selected.length}`)}\n >\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n </DrawerBody>\n </DrawerContent>\n </Drawer>\n );\n}"
24064
+ "code": "() => {\n const [selected, setSelected] = useState<string[]>([]);\n\n return (\n <Drawer width={720}>\n <DrawerTrigger asChild>\n <Button>Open drawer with selection</Button>\n </DrawerTrigger>\n <DrawerContent>\n <Selection\n items={clusters}\n getItemId={c => c.id}\n value={selected}\n onChange={setSelected}\n className='flex min-h-0 flex-1 flex-col'\n >\n <DrawerResizeHandle />\n <DrawerHeader>\n <DrawerTitle>Clusters</DrawerTitle>\n </DrawerHeader>\n <DrawerBody>\n <VStack gap={16}>\n <HStack gap={8} align='center'>\n <SelectionAll />\n <Text size='sm' color='secondary'>\n Select all\n </Text>\n </HStack>\n\n <VStack gap={12}>\n {clusters.map(c => (\n <SelectionItem key={c.id} itemId={c.id}>\n <ClusterCard cluster={c} />\n </SelectionItem>\n ))}\n </VStack>\n </VStack>\n </DrawerBody>\n\n <SelectionBulkBar placement='absolute'>\n <Button\n variant='secondary'\n color='neutral-alt'\n size='large'\n onClick={() => alert(`Duplicate ${selected.length}`)}\n >\n <Copy /> Duplicate\n </Button>\n <Button\n variant='secondary'\n color='neutral-alt'\n size='large'\n onClick={() => alert(`Move ${selected.length}`)}\n >\n <Folder /> Move\n </Button>\n <Button\n variant='secondary'\n color='destructive'\n size='large'\n onClick={() => alert(`Delete ${selected.length}`)}\n >\n <Trash2 /> Delete\n </Button>\n </SelectionBulkBar>\n </Selection>\n </DrawerContent>\n </Drawer>\n );\n}"
24049
24065
  }
24050
24066
  ]
24051
24067
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.30.0-rc-feature-AS-884.4",
3
+ "version": "0.30.0",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,11 +0,0 @@
1
- import { type RefObject } from 'react';
2
- /**
3
- * Holds a ref to the rendered DrawerContent root element.
4
- *
5
- * Descendants that need to portal/anchor against the drawer panel itself
6
- * (e.g. SelectionBulkBar) should read this — not the broader DrawerContext,
7
- * which is also active around DrawerTrigger and other ancestors that are
8
- * outside the panel.
9
- */
10
- export declare const DrawerContentContext: import("react").Context<RefObject<HTMLElement | null> | null>;
11
- export declare const useOptionalDrawerContentRef: () => RefObject<HTMLElement | null> | null;
@@ -1,4 +0,0 @@
1
- import { createContext, useContext } from "react";
2
- const DrawerContentContext = createContext(null);
3
- const useOptionalDrawerContentRef = ()=>useContext(DrawerContentContext);
4
- export { DrawerContentContext, useOptionalDrawerContentRef };