@wallarm-org/design-system 1.28.2-rc-fix-table-window-scroll-root.2 → 1.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.
- package/dist/components/Avatar/Avatar.d.ts +32 -0
- package/dist/components/Avatar/Avatar.figma.d.ts +1 -0
- package/dist/components/Avatar/Avatar.figma.js +27 -0
- package/dist/components/Avatar/Avatar.js +42 -0
- package/dist/components/Avatar/AvatarFallback.d.ts +11 -0
- package/dist/components/Avatar/AvatarFallback.js +25 -0
- package/dist/components/Avatar/AvatarImage.d.ts +9 -0
- package/dist/components/Avatar/AvatarImage.js +18 -0
- package/dist/components/Avatar/AvatarOverlay.d.ts +11 -0
- package/dist/components/Avatar/AvatarOverlay.js +22 -0
- package/dist/components/Avatar/classes.d.ts +6 -0
- package/dist/components/Avatar/classes.js +17 -0
- package/dist/components/Avatar/index.d.ts +4 -0
- package/dist/components/Avatar/index.js +4 -0
- package/dist/components/FileUpload/FileUploadTrigger.d.ts +5 -0
- package/dist/components/FileUpload/FileUploadTrigger.js +18 -5
- package/dist/components/FileUpload/index.d.ts +1 -0
- package/dist/components/FileUpload/index.js +1 -0
- package/dist/components/FileUpload/useFilePreviewUrl.d.ts +6 -0
- package/dist/components/FileUpload/useFilePreviewUrl.js +14 -0
- package/dist/components/FilterDropdown/FilterDropdown.d.ts +58 -0
- package/dist/components/FilterDropdown/FilterDropdown.js +168 -0
- package/dist/components/FilterDropdown/FilterDropdownAllOption.d.ts +14 -0
- package/dist/components/FilterDropdown/FilterDropdownAllOption.js +25 -0
- package/dist/components/FilterDropdown/FilterDropdownClear.d.ts +12 -0
- package/dist/components/FilterDropdown/FilterDropdownClear.js +33 -0
- package/dist/components/FilterDropdown/FilterDropdownContent.d.ts +13 -0
- package/dist/components/FilterDropdown/FilterDropdownContent.js +57 -0
- package/dist/components/FilterDropdown/FilterDropdownContext.d.ts +60 -0
- package/dist/components/FilterDropdown/FilterDropdownContext.js +21 -0
- package/dist/components/FilterDropdown/FilterDropdownEmpty.d.ts +9 -0
- package/dist/components/FilterDropdown/FilterDropdownEmpty.js +21 -0
- package/dist/components/FilterDropdown/FilterDropdownFooter.d.ts +9 -0
- package/dist/components/FilterDropdown/FilterDropdownFooter.js +19 -0
- package/dist/components/FilterDropdown/FilterDropdownFooterClear.d.ts +9 -0
- package/dist/components/FilterDropdown/FilterDropdownFooterClear.js +40 -0
- package/dist/components/FilterDropdown/FilterDropdownGroup.d.ts +5 -0
- package/dist/components/FilterDropdown/FilterDropdownGroup.js +8 -0
- package/dist/components/FilterDropdown/FilterDropdownGroupLabel.d.ts +4 -0
- package/dist/components/FilterDropdown/FilterDropdownGroupLabel.js +8 -0
- package/dist/components/FilterDropdown/FilterDropdownOption.d.ts +15 -0
- package/dist/components/FilterDropdown/FilterDropdownOption.js +32 -0
- package/dist/components/FilterDropdown/FilterDropdownSearch.d.ts +12 -0
- package/dist/components/FilterDropdown/FilterDropdownSearch.js +38 -0
- package/dist/components/FilterDropdown/FilterDropdownSelected.d.ts +16 -0
- package/dist/components/FilterDropdown/FilterDropdownSelected.js +38 -0
- package/dist/components/FilterDropdown/FilterDropdownTrigger.d.ts +16 -0
- package/dist/components/FilterDropdown/FilterDropdownTrigger.js +139 -0
- package/dist/components/FilterDropdown/classes.d.ts +16 -0
- package/dist/components/FilterDropdown/classes.js +43 -0
- package/dist/components/FilterDropdown/index.d.ts +15 -0
- package/dist/components/FilterDropdown/index.js +15 -0
- package/dist/components/FilterDropdown/lib/index.d.ts +1 -0
- package/dist/components/FilterDropdown/lib/index.js +1 -0
- package/dist/components/FilterDropdown/lib/values.d.ts +59 -0
- package/dist/components/FilterDropdown/lib/values.js +58 -0
- package/dist/components/NavRail/NavRailItem.d.ts +1 -1
- package/dist/components/NavRail/NavRailItem.js +9 -12
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/components/Select/SelectContent.d.ts +2 -1
- package/dist/components/Select/SelectContent.js +3 -2
- package/dist/components/Select/SelectEmptyState.d.ts +1 -0
- package/dist/components/Select/SelectEmptyState.js +2 -1
- package/dist/components/Select/SelectFooter.d.ts +7 -2
- package/dist/components/Select/SelectFooter.js +6 -3
- package/dist/components/Select/SelectGroup.d.ts +8 -2
- package/dist/components/Select/SelectGroup.js +3 -2
- package/dist/components/Select/SelectGroupLabel.d.ts +5 -2
- package/dist/components/Select/SelectGroupLabel.js +2 -2
- package/dist/components/Select/SelectOption.js +1 -1
- package/dist/components/Select/SelectOptionHint.d.ts +11 -0
- package/dist/components/Select/SelectOptionHint.js +15 -0
- package/dist/components/Select/SelectPositioner.d.ts +13 -3
- package/dist/components/Select/SelectPositioner.js +2 -1
- package/dist/components/Select/SelectSearchInput.d.ts +5 -2
- package/dist/components/Select/SelectSearchInput.js +11 -1
- package/dist/components/Select/classes.d.ts +3 -0
- package/dist/components/Select/classes.js +13 -0
- package/dist/components/Select/index.d.ts +4 -2
- package/dist/components/Select/index.js +2 -0
- package/dist/components/Table/TableBody/TableBodyVirtualizedCore.js +1 -1
- package/dist/components/Table/TableBody/TableBodyVirtualizedWindow.js +8 -26
- package/dist/components/Table/TableInner/TableInnerWindow.js +2 -7
- package/dist/components/Table/hooks/index.d.ts +0 -1
- package/dist/components/Table/hooks/index.js +0 -1
- package/dist/components/Table/hooks/infiniteScroll/useInfiniteScroll.d.ts +2 -4
- package/dist/components/Table/hooks/infiniteScroll/useInfiniteScroll.js +1 -4
- package/dist/components/Table/hooks/infiniteScroll/usePrependScrollAnchor.d.ts +1 -4
- package/dist/components/Table/hooks/infiniteScroll/usePrependScrollAnchor.js +11 -13
- package/dist/components/Table/hooks/infiniteScroll/useScrollEdge.d.ts +1 -4
- package/dist/components/Table/hooks/infiniteScroll/useScrollEdge.js +18 -6
- package/dist/components/Table/lib/index.d.ts +0 -1
- package/dist/components/Table/lib/index.js +0 -1
- package/dist/components/Table/types.d.ts +1 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/metadata/components.json +16122 -9667
- package/dist/theme/semantic.css +2 -0
- package/dist/utils/getInitials.d.ts +5 -0
- package/dist/utils/getInitials.js +13 -0
- package/package.json +1 -1
- package/dist/components/Table/hooks/useWindowScrollRoot.d.ts +0 -14
- package/dist/components/Table/hooks/useWindowScrollRoot.js +0 -35
- package/dist/components/Table/lib/scrollRoot.d.ts +0 -24
- package/dist/components/Table/lib/scrollRoot.js +0 -22
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, FC, HTMLAttributes, Ref } from 'react';
|
|
2
|
+
import { Avatar as ArkAvatar } from '@ark-ui/react/avatar';
|
|
3
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
4
|
+
import { type TestableProps } from '../../utils/testId';
|
|
5
|
+
import { avatarVariants } from './classes';
|
|
6
|
+
export type AvatarStatusChangeDetails = ArkAvatar.StatusChangeDetails;
|
|
7
|
+
type AvatarButtonAttributes = Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled' | 'type' | 'name' | 'value' | 'form'>;
|
|
8
|
+
interface AvatarBaseProps extends HTMLAttributes<HTMLElement>, VariantProps<typeof avatarVariants>, TestableProps {
|
|
9
|
+
ref?: Ref<HTMLElement>;
|
|
10
|
+
onStatusChange?: (details: AvatarStatusChangeDetails) => void;
|
|
11
|
+
}
|
|
12
|
+
type AvatarRootProps = ({
|
|
13
|
+
/** Root tag. 'button' makes the avatar itself interactive (e.g. inside FileUploadTrigger asChild). */
|
|
14
|
+
as?: 'span';
|
|
15
|
+
/** Render the single child element as the root instead of `as`; takes precedence over it. */
|
|
16
|
+
asChild?: false;
|
|
17
|
+
} & {
|
|
18
|
+
[K in keyof AvatarButtonAttributes]?: never;
|
|
19
|
+
}) | ({
|
|
20
|
+
as: 'button';
|
|
21
|
+
asChild?: false;
|
|
22
|
+
} & AvatarButtonAttributes) | ({
|
|
23
|
+
as?: 'span' | 'button';
|
|
24
|
+
asChild: true;
|
|
25
|
+
} & AvatarButtonAttributes);
|
|
26
|
+
export type AvatarProps = AvatarBaseProps & AvatarRootProps;
|
|
27
|
+
/**
|
|
28
|
+
* A user's photo on a rounded plate, falling back to initials or an icon while the photo loads,
|
|
29
|
+
* when it fails, or when there is none.
|
|
30
|
+
*/
|
|
31
|
+
export declare const Avatar: FC<AvatarProps>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import code_connect from "@figma/code-connect";
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "./index.js";
|
|
4
|
+
const AVATAR_URL = 'https://www.figma.com/design/VKb5gW46uSGw0rqrhZsbXT/WADS-Components?node-id=12336-5504';
|
|
5
|
+
code_connect.connect(Avatar, AVATAR_URL, {
|
|
6
|
+
variant: {
|
|
7
|
+
Photo: 'On'
|
|
8
|
+
},
|
|
9
|
+
example: ()=>/*#__PURE__*/ jsxs(Avatar, {
|
|
10
|
+
children: [
|
|
11
|
+
/*#__PURE__*/ jsx(AvatarImage, {
|
|
12
|
+
src: "/me.png"
|
|
13
|
+
}),
|
|
14
|
+
/*#__PURE__*/ jsx(AvatarFallback, {
|
|
15
|
+
name: "Ada Lovelace"
|
|
16
|
+
})
|
|
17
|
+
]
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
code_connect.connect(Avatar, AVATAR_URL, {
|
|
21
|
+
variant: {
|
|
22
|
+
Photo: 'Off'
|
|
23
|
+
},
|
|
24
|
+
example: ()=>/*#__PURE__*/ jsx(Avatar, {
|
|
25
|
+
children: /*#__PURE__*/ jsx(AvatarFallback, {})
|
|
26
|
+
})
|
|
27
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Avatar } from "@ark-ui/react/avatar";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
import { TestIdProvider } from "../../utils/testId.js";
|
|
5
|
+
import { avatarVariants } from "./classes.js";
|
|
6
|
+
const Avatar_Avatar = ({ ref, id, size, as: Tag = 'span', asChild = false, disabled, type, name, value, form, onStatusChange, className, children, 'data-testid': testId, ...props })=>{
|
|
7
|
+
const role = props.role ?? (props['aria-label'] && !asChild && 'button' !== Tag ? 'img' : void 0);
|
|
8
|
+
const buttonAttributes = asChild || 'button' === Tag ? Object.fromEntries(Object.entries({
|
|
9
|
+
disabled,
|
|
10
|
+
name,
|
|
11
|
+
value,
|
|
12
|
+
form
|
|
13
|
+
}).filter(([, v])=>void 0 !== v)) : {};
|
|
14
|
+
return /*#__PURE__*/ jsx(TestIdProvider, {
|
|
15
|
+
value: testId,
|
|
16
|
+
children: /*#__PURE__*/ jsx(Avatar.Root, {
|
|
17
|
+
"data-slot": "avatar",
|
|
18
|
+
...props,
|
|
19
|
+
ids: id ? {
|
|
20
|
+
root: id
|
|
21
|
+
} : void 0,
|
|
22
|
+
...buttonAttributes,
|
|
23
|
+
ref: ref,
|
|
24
|
+
role: role,
|
|
25
|
+
...asChild && type ? {
|
|
26
|
+
type
|
|
27
|
+
} : {},
|
|
28
|
+
"data-testid": testId,
|
|
29
|
+
onStatusChange: onStatusChange,
|
|
30
|
+
className: cn(avatarVariants({
|
|
31
|
+
size
|
|
32
|
+
}), className),
|
|
33
|
+
asChild: true,
|
|
34
|
+
children: asChild ? children : /*#__PURE__*/ jsx(Tag, {
|
|
35
|
+
type: 'button' === Tag ? type ?? 'button' : void 0,
|
|
36
|
+
children: children
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
Avatar_Avatar.displayName = 'Avatar';
|
|
42
|
+
export { Avatar_Avatar as Avatar };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentType, FC, HTMLAttributes, Ref } from 'react';
|
|
2
|
+
import { type SvgIconProps } from '../../icons';
|
|
3
|
+
export interface AvatarFallbackProps extends HTMLAttributes<HTMLSpanElement> {
|
|
4
|
+
ref?: Ref<HTMLSpanElement>;
|
|
5
|
+
/** Initials are taken from this: first letter of the first and of the last word. */
|
|
6
|
+
name?: string;
|
|
7
|
+
/** Shown when there are no children and `name` gives no initials. */
|
|
8
|
+
icon?: ComponentType<SvgIconProps>;
|
|
9
|
+
}
|
|
10
|
+
/** Shown while the photo loads, when it fails, and when there is none. */
|
|
11
|
+
export declare const AvatarFallback: FC<AvatarFallbackProps>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Avatar } from "@ark-ui/react/avatar";
|
|
3
|
+
import { UserRound } from "../../icons/index.js";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
import { getInitials } from "../../utils/getInitials.js";
|
|
6
|
+
import { useTestId } from "../../utils/testId.js";
|
|
7
|
+
import { avatarFallbackClassNames } from "./classes.js";
|
|
8
|
+
const AvatarFallback = ({ ref, name, icon: Icon = UserRound, className, children, ...props })=>{
|
|
9
|
+
const testId = useTestId('fallback');
|
|
10
|
+
const initials = name ? getInitials(name) : '';
|
|
11
|
+
const showsInitials = null == children && '' !== initials;
|
|
12
|
+
return /*#__PURE__*/ jsx(Avatar.Fallback, {
|
|
13
|
+
"aria-hidden": true,
|
|
14
|
+
...props,
|
|
15
|
+
ref: ref,
|
|
16
|
+
"data-slot": "avatar-fallback",
|
|
17
|
+
"data-testid": testId,
|
|
18
|
+
className: cn(avatarFallbackClassNames, showsInitials && 'text-text-primary', className),
|
|
19
|
+
children: children ?? (initials || /*#__PURE__*/ jsx(Icon, {
|
|
20
|
+
size: "md"
|
|
21
|
+
}))
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
AvatarFallback.displayName = 'AvatarFallback';
|
|
25
|
+
export { AvatarFallback };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FC, ImgHTMLAttributes, Ref } from 'react';
|
|
2
|
+
export interface AvatarImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'id'> {
|
|
3
|
+
ref?: Ref<HTMLImageElement>;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The photo. Always render it — Ark tracks `src` changes on the mounted element — and pass
|
|
7
|
+
* `src={undefined}` for "no photo". Decorative by default (`alt=''`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const AvatarImage: FC<AvatarImageProps>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Avatar } from "@ark-ui/react/avatar";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
import { useTestId } from "../../utils/testId.js";
|
|
5
|
+
import { avatarImageClassNames } from "./classes.js";
|
|
6
|
+
const AvatarImage = ({ ref, alt = '', className, ...props })=>{
|
|
7
|
+
const testId = useTestId('image');
|
|
8
|
+
return /*#__PURE__*/ jsx(Avatar.Image, {
|
|
9
|
+
...props,
|
|
10
|
+
ref: ref,
|
|
11
|
+
alt: alt,
|
|
12
|
+
"data-slot": "avatar-image",
|
|
13
|
+
"data-testid": testId,
|
|
14
|
+
className: cn(avatarImageClassNames, className)
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
AvatarImage.displayName = 'AvatarImage';
|
|
18
|
+
export { AvatarImage };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FC, HTMLAttributes, Ref } from 'react';
|
|
2
|
+
export interface AvatarOverlayProps extends HTMLAttributes<HTMLSpanElement> {
|
|
3
|
+
ref?: Ref<HTMLSpanElement>;
|
|
4
|
+
/** Keep it shown regardless of hover — e.g. a `Loader` while the new photo uploads. */
|
|
5
|
+
visible?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The "change photo" affordance, shown on hover and keyboard focus of an interactive
|
|
9
|
+
* (`as='button'`) avatar. Over a photo it adds a wash; over the fallback it swaps the icon.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AvatarOverlay: FC<AvatarOverlayProps>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { RefreshCcw } from "../../icons/index.js";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
import { useTestId } from "../../utils/testId.js";
|
|
5
|
+
import { avatarOverlayClassNames } from "./classes.js";
|
|
6
|
+
const AvatarOverlay = ({ ref, visible = false, className, children, ...props })=>{
|
|
7
|
+
const testId = useTestId('overlay');
|
|
8
|
+
return /*#__PURE__*/ jsx("span", {
|
|
9
|
+
...props,
|
|
10
|
+
ref: ref,
|
|
11
|
+
"aria-hidden": true,
|
|
12
|
+
"data-slot": "avatar-overlay",
|
|
13
|
+
"data-visible": visible ? '' : void 0,
|
|
14
|
+
"data-testid": testId,
|
|
15
|
+
className: cn(avatarOverlayClassNames, className),
|
|
16
|
+
children: children ?? /*#__PURE__*/ jsx(RefreshCcw, {
|
|
17
|
+
size: "md"
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
AvatarOverlay.displayName = 'AvatarOverlay';
|
|
22
|
+
export { AvatarOverlay };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const avatarVariants: (props?: ({
|
|
2
|
+
size?: "sm" | "xs" | null | undefined;
|
|
3
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
4
|
+
export declare const avatarImageClassNames: string;
|
|
5
|
+
export declare const avatarFallbackClassNames: string;
|
|
6
|
+
export declare const avatarOverlayClassNames: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
const avatarVariants = cva(cn('group/avatar inline-grid shrink-0 place-items-center overflow-hidden align-middle *:col-start-1 *:row-start-1', 'border border-border-primary bg-states-primary-hover text-icon-primary font-sans font-medium', 'branded:border-border-brand branded:bg-states-brand-hover branded:text-icon-brand', 'has-[>img[data-state=visible]]:border-0', 'hover:not-disabled:[&:has(>[data-slot=avatar-overlay])>[data-slot=avatar-fallback]]:invisible', 'focus-visible:[&:has(>[data-slot=avatar-overlay])>[data-slot=avatar-fallback]]:invisible', '[&:has(>[data-slot=avatar-overlay][data-visible])>[data-slot=avatar-fallback]]:invisible', 'enabled:cursor-pointer disabled:cursor-not-allowed', 'focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-focus-primary'), {
|
|
4
|
+
variants: {
|
|
5
|
+
size: {
|
|
6
|
+
xs: 'size-24 rounded-8 text-2xs',
|
|
7
|
+
sm: 'size-32 rounded-12 text-xs'
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
defaultVariants: {
|
|
11
|
+
size: 'sm'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const avatarImageClassNames = cn('size-full object-cover data-[state=hidden]:hidden');
|
|
15
|
+
const avatarFallbackClassNames = cn('inline-flex items-center justify-center select-none data-[state=hidden]:hidden');
|
|
16
|
+
const avatarOverlayClassNames = cn('hidden size-full place-items-center text-icon-primary', 'group-hover/avatar:grid group-focus-visible/avatar:grid group-disabled/avatar:not-data-[visible]:hidden', 'data-[visible]:grid', 'group-has-[>img[data-state=visible]]/avatar:bg-component-avatar-overlay');
|
|
17
|
+
export { avatarFallbackClassNames, avatarImageClassNames, avatarOverlayClassNames, avatarVariants };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Avatar, type AvatarProps, type AvatarStatusChangeDetails } from './Avatar';
|
|
2
|
+
export { AvatarFallback, type AvatarFallbackProps } from './AvatarFallback';
|
|
3
|
+
export { AvatarImage, type AvatarImageProps } from './AvatarImage';
|
|
4
|
+
export { AvatarOverlay, type AvatarOverlayProps } from './AvatarOverlay';
|
|
@@ -4,5 +4,10 @@ export type FileUploadTriggerProps = ButtonProps;
|
|
|
4
4
|
/**
|
|
5
5
|
* "Select file" button — the compact picker for when space is tight. Takes every
|
|
6
6
|
* `Button` prop (defaults: primary / brand / large). Hidden (read-only) and blocked like the Area.
|
|
7
|
+
*
|
|
8
|
+
* `asChild`: no Button is rendered. The single child element (e.g. `<Avatar as='button'>`)
|
|
9
|
+
* becomes the trigger and receives `data-slot`, `data-testid`, `aria-describedby`, `disabled` and the
|
|
10
|
+
* Ark trigger props. Button-only props (`variant`, `color`, `size`, `loading`, `fullWidth`, `as`) are
|
|
11
|
+
* ignored. Pass `disabled` / `aria-describedby` here, not on the child — a child value wins the merge.
|
|
7
12
|
*/
|
|
8
13
|
export declare const FileUploadTrigger: FC<FileUploadTriggerProps>;
|
|
@@ -4,7 +4,7 @@ import { Share } from "../../icons/index.js";
|
|
|
4
4
|
import { useTestId } from "../../utils/testId.js";
|
|
5
5
|
import { Button } from "../Button/index.js";
|
|
6
6
|
import { useRequiredFileUploadRootContext } from "./FileUploadContext.js";
|
|
7
|
-
const FileUploadTrigger = ({ children, disabled, 'data-testid': testIdProp, 'aria-describedby': describedByProp, ...props })=>{
|
|
7
|
+
const FileUploadTrigger = ({ children, disabled, asChild = false, 'data-testid': testIdProp, 'aria-describedby': describedByProp, ...props })=>{
|
|
8
8
|
const ctx = useRequiredFileUploadRootContext('FileUploadTrigger');
|
|
9
9
|
const testId = useTestId('trigger', testIdProp);
|
|
10
10
|
if (ctx.pickerHidden) return null;
|
|
@@ -12,6 +12,22 @@ const FileUploadTrigger = ({ children, disabled, 'data-testid': testIdProp, 'ari
|
|
|
12
12
|
describedByProp,
|
|
13
13
|
ctx.rejections.length ? ctx.errorId : void 0
|
|
14
14
|
].filter(Boolean).join(' ') || void 0;
|
|
15
|
+
const shared = {
|
|
16
|
+
'aria-describedby': describedBy,
|
|
17
|
+
'data-slot': 'file-upload-trigger',
|
|
18
|
+
'data-testid': testId,
|
|
19
|
+
disabled: disabled || ctx.pickerBlocked
|
|
20
|
+
};
|
|
21
|
+
if (asChild) {
|
|
22
|
+
const { variant, color, size, loading, fullWidth, as, ref, ...rest } = props;
|
|
23
|
+
return /*#__PURE__*/ jsx(FileUpload.Trigger, {
|
|
24
|
+
...rest,
|
|
25
|
+
...shared,
|
|
26
|
+
ref: ref,
|
|
27
|
+
asChild: true,
|
|
28
|
+
children: children
|
|
29
|
+
});
|
|
30
|
+
}
|
|
15
31
|
return /*#__PURE__*/ jsx(FileUpload.Trigger, {
|
|
16
32
|
asChild: true,
|
|
17
33
|
children: /*#__PURE__*/ jsx(Button, {
|
|
@@ -19,10 +35,7 @@ const FileUploadTrigger = ({ children, disabled, 'data-testid': testIdProp, 'ari
|
|
|
19
35
|
color: "brand",
|
|
20
36
|
size: "large",
|
|
21
37
|
...props,
|
|
22
|
-
|
|
23
|
-
"data-slot": "file-upload-trigger",
|
|
24
|
-
"data-testid": testId,
|
|
25
|
-
disabled: disabled || ctx.pickerBlocked,
|
|
38
|
+
...shared,
|
|
26
39
|
children: children ?? /*#__PURE__*/ jsxs(Fragment, {
|
|
27
40
|
children: [
|
|
28
41
|
/*#__PURE__*/ jsx(Share, {}),
|
|
@@ -9,3 +9,4 @@ export { FileUploadItemGroup, type FileUploadItemGroupProps } from './FileUpload
|
|
|
9
9
|
export { FileUploadItemReplaceTrigger, type FileUploadItemReplaceTriggerProps, } from './FileUploadItemReplaceTrigger';
|
|
10
10
|
export { FileUploadTrigger, type FileUploadTriggerProps } from './FileUploadTrigger';
|
|
11
11
|
export type { FileUploadErrorCode, FileUploadItemFile, FileUploadRejection } from './types';
|
|
12
|
+
export { useFilePreviewUrl } from './useFilePreviewUrl';
|
|
@@ -8,3 +8,4 @@ export { FileUploadItemDeleteTrigger } from "./FileUploadItemDeleteTrigger.js";
|
|
|
8
8
|
export { FileUploadItemGroup } from "./FileUploadItemGroup.js";
|
|
9
9
|
export { FileUploadItemReplaceTrigger } from "./FileUploadItemReplaceTrigger.js";
|
|
10
10
|
export { FileUploadTrigger } from "./FileUploadTrigger.js";
|
|
11
|
+
export { useFilePreviewUrl } from "./useFilePreviewUrl.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An object URL for previewing a picked file (e.g. in `AvatarImage`), revoked when the file
|
|
3
|
+
* changes or the component unmounts. The effect manages the blob URL's lifetime — an external
|
|
4
|
+
* resource, not derived state.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useFilePreviewUrl: (file?: File | null) => string | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
const useFilePreviewUrl = (file)=>{
|
|
3
|
+
const [url, setUrl] = useState();
|
|
4
|
+
useEffect(()=>{
|
|
5
|
+
if (!file) return void setUrl(void 0);
|
|
6
|
+
const next = URL.createObjectURL(file);
|
|
7
|
+
setUrl(next);
|
|
8
|
+
return ()=>URL.revokeObjectURL(next);
|
|
9
|
+
}, [
|
|
10
|
+
file
|
|
11
|
+
]);
|
|
12
|
+
return url;
|
|
13
|
+
};
|
|
14
|
+
export { useFilePreviewUrl };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type CollectionItem, type ListCollection } from '@ark-ui/react/collection';
|
|
3
|
+
import type { TestableProps } from '../../utils/testId';
|
|
4
|
+
import { type SelectProps } from '../Select/Select';
|
|
5
|
+
import { type UseSelectSearchOptions } from '../Select/useSelectSearch';
|
|
6
|
+
export interface FilterDropdownValueChangeDetails<T extends CollectionItem = CollectionItem> {
|
|
7
|
+
/** Picked values; `[]` means unset («All» in single mode). */
|
|
8
|
+
value: string[];
|
|
9
|
+
items: T[];
|
|
10
|
+
}
|
|
11
|
+
export interface FilterDropdownOpenChangeDetails {
|
|
12
|
+
open: boolean;
|
|
13
|
+
value: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Select props FilterDropdown owns itself: the value model (alias and sentinel values must never
|
|
17
|
+
* reach the consumer), the highlight (reset when the query changes), and form wiring (the hidden
|
|
18
|
+
* select would submit internal values).
|
|
19
|
+
*/
|
|
20
|
+
type FilterDropdownOmittedSelectProps = 'collection' | 'multiple' | 'value' | 'defaultValue' | 'onValueChange' | 'onOpenChange' | 'highlightedValue' | 'defaultHighlightedValue' | 'onHighlightChange' | 'onSelect' | 'deselectable' | 'name' | 'form' | 'loading' | 'children';
|
|
21
|
+
export type FilterDropdownProps<T extends CollectionItem = CollectionItem> = Omit<SelectProps<T>, FilterDropdownOmittedSelectProps> & TestableProps & {
|
|
22
|
+
/** Attribute name: trigger text in multi mode, the trigger's aria-label, «Clear {label}». */
|
|
23
|
+
label: string;
|
|
24
|
+
/**
|
|
25
|
+
* Single mode: trigger text while unset and the default text of `FilterDropdownAllOption`.
|
|
26
|
+
* @default 'All'
|
|
27
|
+
*/
|
|
28
|
+
allLabel?: string;
|
|
29
|
+
/** Items to pick from. `groupBy` is supported. */
|
|
30
|
+
collection: ListCollection<T>;
|
|
31
|
+
/** Checkbox mode: several values, OR-ed, the menu stays open. */
|
|
32
|
+
multiple?: boolean;
|
|
33
|
+
/** Controlled picked values; `[]` = unset. */
|
|
34
|
+
value?: string[];
|
|
35
|
+
defaultValue?: string[];
|
|
36
|
+
onValueChange?: (details: FilterDropdownValueChangeDetails<T>) => void;
|
|
37
|
+
onOpenChange?: (details: FilterDropdownOpenChangeDetails) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Item count from which the search field and (multi) the "Selected" section appear. Counts
|
|
40
|
+
* the collection's own items only.
|
|
41
|
+
* @default 8
|
|
42
|
+
*/
|
|
43
|
+
searchThreshold?: number;
|
|
44
|
+
/** Overrides the default case-insensitive match on the item label. */
|
|
45
|
+
filterFn?: UseSelectSearchOptions<T>['filterFn'];
|
|
46
|
+
/**
|
|
47
|
+
* Multi mode: pin the picked values under «Selected» on top of the list (from
|
|
48
|
+
* `searchThreshold` items, hidden while searching). Render `FilterDropdownSelected` for it.
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
selectedOnTop?: boolean;
|
|
52
|
+
children?: ReactNode;
|
|
53
|
+
};
|
|
54
|
+
export declare function FilterDropdown<T extends CollectionItem>({ label, allLabel, collection, multiple, value: valueProp, defaultValue, onValueChange, open: openProp, defaultOpen, onOpenChange, searchThreshold, filterFn, selectedOnTop, disabled, className, children, 'data-testid': testId, ...props }: FilterDropdownProps<T>): import("react").JSX.Element;
|
|
55
|
+
export declare namespace FilterDropdown {
|
|
56
|
+
var displayName: string;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createListCollection } from "@ark-ui/react/collection";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
import { Select } from "../Select/Select.js";
|
|
6
|
+
import { useSelectSearch } from "../Select/useSelectSearch.js";
|
|
7
|
+
import { FilterDropdownContext } from "./FilterDropdownContext.js";
|
|
8
|
+
import { createAliasItem, createAllItem, isSpecialItem, toConsumerValue, toInternalValue } from "./lib/index.js";
|
|
9
|
+
const DEFAULT_SEARCH_THRESHOLD = 8;
|
|
10
|
+
const EMPTY = [];
|
|
11
|
+
const FilterDropdown = ({ label, allLabel = 'All', collection, multiple = false, value: valueProp, defaultValue, onValueChange, open: openProp, defaultOpen, onOpenChange, searchThreshold = DEFAULT_SEARCH_THRESHOLD, filterFn, selectedOnTop = true, disabled = false, className, children, 'data-testid': testId, ...props })=>{
|
|
12
|
+
const [valueState, setValueState] = useState(defaultValue ?? EMPTY);
|
|
13
|
+
const value = valueProp ?? valueState;
|
|
14
|
+
const [openState, setOpenState] = useState(defaultOpen ?? false);
|
|
15
|
+
const open = openProp ?? openState;
|
|
16
|
+
const baseCollection = collection;
|
|
17
|
+
const searchOptions = useMemo(()=>({
|
|
18
|
+
filterFn: filterFn
|
|
19
|
+
}), [
|
|
20
|
+
filterFn
|
|
21
|
+
]);
|
|
22
|
+
const { searchValue: query, onSearchChange, filteredCollection } = useSelectSearch(baseCollection, searchOptions);
|
|
23
|
+
const [snapshot, setSnapshot] = useState(value);
|
|
24
|
+
const [lockedWidth, setLockedWidth] = useState();
|
|
25
|
+
const [highlightState, setHighlightState] = useState(null);
|
|
26
|
+
const [resetHighlight, setResetHighlight] = useState(false);
|
|
27
|
+
const [prevOpen, setPrevOpen] = useState(open);
|
|
28
|
+
if (open !== prevOpen) {
|
|
29
|
+
setPrevOpen(open);
|
|
30
|
+
onSearchChange('');
|
|
31
|
+
setLockedWidth(void 0);
|
|
32
|
+
setResetHighlight(false);
|
|
33
|
+
if (open) setSnapshot(value);
|
|
34
|
+
}
|
|
35
|
+
const selectedValues = open ? snapshot : value;
|
|
36
|
+
const triggerRef = useRef(null);
|
|
37
|
+
const contentRef = useRef(null);
|
|
38
|
+
const isSearchable = baseCollection.size >= searchThreshold;
|
|
39
|
+
const isSearchActive = isSearchable && query.length > 0;
|
|
40
|
+
const showAll = !multiple && !isSearchActive;
|
|
41
|
+
const showSelected = selectedOnTop && multiple && isSearchable && !isSearchActive && selectedValues.length > 0;
|
|
42
|
+
const allItem = useMemo(()=>createAllItem(allLabel), [
|
|
43
|
+
allLabel
|
|
44
|
+
]);
|
|
45
|
+
const groups = useMemo(()=>filteredCollection.group(), [
|
|
46
|
+
filteredCollection
|
|
47
|
+
]);
|
|
48
|
+
const aliasItems = useMemo(()=>showSelected ? baseCollection.findMany(selectedValues).map((item)=>createAliasItem(item, baseCollection)) : [], [
|
|
49
|
+
showSelected,
|
|
50
|
+
baseCollection,
|
|
51
|
+
selectedValues
|
|
52
|
+
]);
|
|
53
|
+
const arkCollection = useMemo(()=>createListCollection({
|
|
54
|
+
items: [
|
|
55
|
+
...showAll ? [
|
|
56
|
+
allItem
|
|
57
|
+
] : [],
|
|
58
|
+
...aliasItems,
|
|
59
|
+
...groups.flatMap(([, items])=>items)
|
|
60
|
+
],
|
|
61
|
+
itemToValue: (item)=>isSpecialItem(item) ? item.value : baseCollection.getItemValue(item) ?? '',
|
|
62
|
+
itemToString: (item)=>isSpecialItem(item) ? item.label : baseCollection.stringifyItem(item) ?? '',
|
|
63
|
+
isItemDisabled: (item)=>{
|
|
64
|
+
if (!isSpecialItem(item)) return baseCollection.getItemDisabled(item);
|
|
65
|
+
return item.source ? baseCollection.getItemDisabled(item.source) : false;
|
|
66
|
+
}
|
|
67
|
+
}), [
|
|
68
|
+
showAll,
|
|
69
|
+
allItem,
|
|
70
|
+
aliasItems,
|
|
71
|
+
groups,
|
|
72
|
+
baseCollection
|
|
73
|
+
]);
|
|
74
|
+
const aliased = useMemo(()=>new Set(aliasItems.map((item)=>baseCollection.getItemValue(item.source ?? null) ?? '')), [
|
|
75
|
+
aliasItems,
|
|
76
|
+
baseCollection
|
|
77
|
+
]);
|
|
78
|
+
const internalValue = useMemo(()=>toInternalValue(value, {
|
|
79
|
+
multiple,
|
|
80
|
+
aliased,
|
|
81
|
+
hasAll: showAll
|
|
82
|
+
}), [
|
|
83
|
+
value,
|
|
84
|
+
multiple,
|
|
85
|
+
aliased,
|
|
86
|
+
showAll
|
|
87
|
+
]);
|
|
88
|
+
const highlightedValue = resetHighlight ? arkCollection.firstValue : highlightState;
|
|
89
|
+
const commit = (next)=>{
|
|
90
|
+
if (void 0 === valueProp) setValueState(next);
|
|
91
|
+
onValueChange?.({
|
|
92
|
+
value: next,
|
|
93
|
+
items: collection.findMany(next)
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
const clear = ()=>{
|
|
97
|
+
if (disabled || 0 === value.length) return;
|
|
98
|
+
commit([]);
|
|
99
|
+
};
|
|
100
|
+
const handleValueChange = (details)=>{
|
|
101
|
+
commit(toConsumerValue(details.value, {
|
|
102
|
+
multiple,
|
|
103
|
+
previousInternal: internalValue,
|
|
104
|
+
previousValue: value
|
|
105
|
+
}));
|
|
106
|
+
};
|
|
107
|
+
const handleOpenChange = ({ open: nextOpen })=>{
|
|
108
|
+
setOpenState(nextOpen);
|
|
109
|
+
onOpenChange?.({
|
|
110
|
+
open: nextOpen,
|
|
111
|
+
value
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
const handleHighlightChange = (details)=>{
|
|
115
|
+
setHighlightState(details.highlightedValue);
|
|
116
|
+
setResetHighlight(false);
|
|
117
|
+
};
|
|
118
|
+
const setQuery = (next)=>{
|
|
119
|
+
if (!query && next) setLockedWidth(contentRef.current?.offsetWidth || void 0);
|
|
120
|
+
if (query && !next) setSnapshot(value);
|
|
121
|
+
onSearchChange(next);
|
|
122
|
+
setResetHighlight(true);
|
|
123
|
+
};
|
|
124
|
+
const context = {
|
|
125
|
+
label,
|
|
126
|
+
allLabel,
|
|
127
|
+
multiple,
|
|
128
|
+
disabled,
|
|
129
|
+
value,
|
|
130
|
+
collection: baseCollection,
|
|
131
|
+
filteredCollection,
|
|
132
|
+
groups,
|
|
133
|
+
query,
|
|
134
|
+
setQuery,
|
|
135
|
+
isSearchable,
|
|
136
|
+
isSearchActive,
|
|
137
|
+
showAll,
|
|
138
|
+
allItem,
|
|
139
|
+
showSelected,
|
|
140
|
+
aliasItems,
|
|
141
|
+
clear,
|
|
142
|
+
triggerRef,
|
|
143
|
+
contentRef,
|
|
144
|
+
lockedWidth
|
|
145
|
+
};
|
|
146
|
+
return /*#__PURE__*/ jsx(FilterDropdownContext.Provider, {
|
|
147
|
+
value: context,
|
|
148
|
+
children: /*#__PURE__*/ jsx(Select, {
|
|
149
|
+
...props,
|
|
150
|
+
"data-slot": "filter-dropdown",
|
|
151
|
+
"data-testid": testId,
|
|
152
|
+
className: cn('inline-flex max-w-full', className),
|
|
153
|
+
collection: arkCollection,
|
|
154
|
+
multiple: multiple,
|
|
155
|
+
disabled: disabled,
|
|
156
|
+
value: internalValue,
|
|
157
|
+
onValueChange: handleValueChange,
|
|
158
|
+
open: openProp,
|
|
159
|
+
defaultOpen: defaultOpen,
|
|
160
|
+
onOpenChange: handleOpenChange,
|
|
161
|
+
highlightedValue: highlightedValue,
|
|
162
|
+
onHighlightChange: handleHighlightChange,
|
|
163
|
+
children: children
|
|
164
|
+
})
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
FilterDropdown.displayName = 'FilterDropdown';
|
|
168
|
+
export { FilterDropdown };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ComponentProps, FC, ReactNode, Ref } from 'react';
|
|
2
|
+
import { SelectOption } from '../Select/SelectOption';
|
|
3
|
+
export interface FilterDropdownAllOptionProps extends Omit<ComponentProps<typeof SelectOption>, 'item' | 'children'> {
|
|
4
|
+
ref?: Ref<HTMLDivElement>;
|
|
5
|
+
/** @default the root's `allLabel` */
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The «All …» row of single mode: first in the list, picked while the filter is unset, and
|
|
10
|
+
* picking it resets the filter to `[]`. Hidden in multi mode and while a query is active.
|
|
11
|
+
* Always render it in single mode: the row is part of keyboard navigation whenever the menu is
|
|
12
|
+
* open in single mode.
|
|
13
|
+
*/
|
|
14
|
+
export declare const FilterDropdownAllOption: FC<FilterDropdownAllOptionProps>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTestId } from "../../utils/testId.js";
|
|
3
|
+
import { SelectOption } from "../Select/SelectOption.js";
|
|
4
|
+
import { SelectOptionIndicator } from "../Select/SelectOptionIndicator.js";
|
|
5
|
+
import { SelectOptionText } from "../Select/SelectOptionText.js";
|
|
6
|
+
import { useFilterDropdownContext } from "./FilterDropdownContext.js";
|
|
7
|
+
const FilterDropdownAllOption = ({ children, 'data-testid': testIdProp, ...props })=>{
|
|
8
|
+
const { showAll, allItem, allLabel } = useFilterDropdownContext();
|
|
9
|
+
const testId = useTestId('all-option', testIdProp);
|
|
10
|
+
if (!showAll) return null;
|
|
11
|
+
return /*#__PURE__*/ jsxs(SelectOption, {
|
|
12
|
+
...props,
|
|
13
|
+
item: allItem,
|
|
14
|
+
"data-slot": "filter-dropdown-all-option",
|
|
15
|
+
"data-testid": testId,
|
|
16
|
+
children: [
|
|
17
|
+
/*#__PURE__*/ jsx(SelectOptionText, {
|
|
18
|
+
children: children ?? allLabel
|
|
19
|
+
}),
|
|
20
|
+
/*#__PURE__*/ jsx(SelectOptionIndicator, {})
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
FilterDropdownAllOption.displayName = 'FilterDropdownAllOption';
|
|
25
|
+
export { FilterDropdownAllOption };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
|
2
|
+
import { type TestableProps } from '../../utils/testId';
|
|
3
|
+
export interface FilterDropdownClearProps extends ButtonHTMLAttributes<HTMLButtonElement>, TestableProps {
|
|
4
|
+
ref?: Ref<HTMLButtonElement>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The ✕ that clears a multi filter. `FilterDropdownTrigger` renders one on its own; pass a
|
|
8
|
+
* `FilterDropdownClear` as the trigger's child to replace it (for example to add attributes).
|
|
9
|
+
* Renders only in multi mode while something is picked, and hands focus back to the trigger
|
|
10
|
+
* before it disappears.
|
|
11
|
+
*/
|
|
12
|
+
export declare const FilterDropdownClear: FC<FilterDropdownClearProps>;
|