@wallarm-org/design-system 0.30.0-rc-feature-AS-884.5 → 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>;
@@ -2,43 +2,41 @@ 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
4
  import { useTestId } from "../../../utils/testId.js";
5
- import { useOptionalDrawerContentRef } from "../../Drawer/DrawerContext/index.js";
6
5
  import { HStack } from "../../Stack/index.js";
7
6
  import { selectionBulkBarVariants } from "../classes.js";
8
7
  import { useSelectionContext } from "../useSelectionContext.js";
9
8
  import { SelectionBulkBarSummary } from "./SelectionBulkBarSummary.js";
10
9
  const TEST_ID_SLOT = 'bulk-bar';
11
10
  const DEFAULT_ARIA_LABEL = 'Bulk actions';
12
- const SelectionBulkBar = ({ 'aria-label': ariaLabel = DEFAULT_ARIA_LABEL, 'data-testid': testIdProp, children })=>{
11
+ const SelectionBulkBar = ({ 'aria-label': ariaLabel = DEFAULT_ARIA_LABEL, 'data-testid': testIdProp, placement = 'floating', children })=>{
13
12
  const { selectedIds } = useSelectionContext();
14
13
  const fallbackTestId = useTestId(TEST_ID_SLOT);
15
14
  const testId = testIdProp ?? fallbackTestId;
16
- const drawerContentRef = useOptionalDrawerContentRef();
17
- const placement = drawerContentRef ? 'drawer' : 'floating';
18
- return /*#__PURE__*/ jsx(Portal, {
19
- container: drawerContentRef ?? void 0,
20
- children: /*#__PURE__*/ jsx(Presence, {
21
- present: selectedIds.size > 0,
22
- asChild: true,
23
- children: /*#__PURE__*/ jsxs("div", {
24
- role: "toolbar",
25
- "aria-label": ariaLabel,
26
- "data-slot": "selection-bulk-bar",
27
- "data-testid": testId,
28
- className: selectionBulkBarVariants({
29
- placement
30
- }),
31
- children: [
32
- /*#__PURE__*/ jsx(SelectionBulkBarSummary, {}),
33
- /*#__PURE__*/ jsx(HStack, {
34
- gap: 8,
35
- align: "center",
36
- children: children
37
- })
38
- ]
39
- })
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
+ ]
40
34
  })
41
35
  });
36
+ if ('floating' === placement) return /*#__PURE__*/ jsx(Portal, {
37
+ children: bar
38
+ });
39
+ return bar;
42
40
  };
43
41
  SelectionBulkBar.displayName = 'SelectionBulkBar';
44
42
  export { SelectionBulkBar };
@@ -1 +1 @@
1
- export { SelectionBulkBar, type SelectionBulkBarProps } from './SelectionBulkBar';
1
+ export { SelectionBulkBar, type SelectionBulkBarPlacement, type SelectionBulkBarProps, } from './SelectionBulkBar';
@@ -1,4 +1,4 @@
1
1
  export declare const selectionItemVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
2
2
  export declare const selectionBulkBarVariants: (props?: ({
3
- placement?: "drawer" | "floating" | null | undefined;
3
+ placement?: "absolute" | "floating" | null | undefined;
4
4
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
@@ -4,9 +4,12 @@ const selectionItemVariants = cva('flex w-full min-w-0 items-start gap-12');
4
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
5
  variants: {
6
6
  placement: {
7
- drawer: 'absolute bottom-12 left-1/2 -translate-x-1/2 max-w-[calc(100%-24px)]',
7
+ absolute: 'absolute bottom-12 left-1/2 -translate-x-1/2 max-w-[calc(100%-48px)]',
8
8
  floating: 'fixed bottom-32 left-1/2 -translate-x-1/2 max-w-[calc(100vw-32px)]'
9
9
  }
10
+ },
11
+ defaultVariants: {
12
+ placement: 'floating'
10
13
  }
11
14
  });
12
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:30:19.368Z",
3
+ "generatedAt": "2026-04-28T07:02:22.140Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Alert",
@@ -23985,9 +23985,10 @@
23985
23985
  {
23986
23986
  "name": "placement",
23987
23987
  "options": [
23988
- "drawer",
23988
+ "absolute",
23989
23989
  "floating"
23990
- ]
23990
+ ],
23991
+ "defaultValue": "floating"
23991
23992
  }
23992
23993
  ],
23993
23994
  "subComponents": [
@@ -23997,7 +23998,14 @@
23997
23998
  },
23998
23999
  {
23999
24000
  "name": "SelectionBulkBar",
24000
- "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
+ ]
24001
24009
  },
24002
24010
  {
24003
24011
  "name": "SelectionItem",
@@ -24053,7 +24061,7 @@
24053
24061
  },
24054
24062
  {
24055
24063
  "name": "InsideDrawer",
24056
- "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}"
24057
24065
  }
24058
24066
  ]
24059
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.5",
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 };