@devalok/shilp-sutra 0.53.0 → 0.54.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/AGENTS.md +7 -1
- package/BREAKING.json +29 -0
- package/MIGRATION.md +55 -0
- package/dist/composed/avatar-group.d.ts +11 -9
- package/dist/composed/avatar-group.d.ts.map +1 -1
- package/dist/composed/avatar-group.js +108 -140
- package/dist/composed/avatar-group.js.map +1 -1
- package/dist/composed/bulk-action-bar.d.ts +19 -11
- package/dist/composed/bulk-action-bar.d.ts.map +1 -1
- package/dist/composed/bulk-action-bar.js +81 -110
- package/dist/composed/bulk-action-bar.js.map +1 -1
- package/dist/composed/error-boundary.d.ts +15 -3
- package/dist/composed/error-boundary.d.ts.map +1 -1
- package/dist/composed/error-boundary.js +43 -26
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/loading-skeleton.d.ts +2 -0
- package/dist/composed/loading-skeleton.d.ts.map +1 -1
- package/dist/composed/loading-skeleton.js +79 -56
- package/dist/composed/loading-skeleton.js.map +1 -1
- package/dist/composed/master-detail.d.ts +21 -8
- package/dist/composed/master-detail.d.ts.map +1 -1
- package/dist/composed/master-detail.js +91 -77
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/page-skeletons.d.ts +6 -0
- package/dist/composed/page-skeletons.d.ts.map +1 -1
- package/dist/composed/page-skeletons.js +93 -71
- package/dist/composed/page-skeletons.js.map +1 -1
- package/dist/shell/index.d.ts +5 -3
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +2 -3
- package/dist/shell/notification-preferences.d.ts.map +1 -1
- package/dist/shell/notification-preferences.js +2 -0
- package/dist/shell/notification-preferences.js.map +1 -1
- package/dist/ui/combobox.d.ts +1 -1
- package/dist/ui/combobox.d.ts.map +1 -1
- package/dist/ui/combobox.js +133 -128
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/file-upload.d.ts.map +1 -1
- package/dist/ui/file-upload.js +124 -138
- package/dist/ui/file-upload.js.map +1 -1
- package/dist/ui/form.d.ts +7 -0
- package/dist/ui/form.d.ts.map +1 -1
- package/dist/ui/form.js +4 -2
- package/dist/ui/form.js.map +1 -1
- package/dist/ui/icon-button.d.ts +2 -1
- package/dist/ui/icon-button.d.ts.map +1 -1
- package/dist/ui/icon-button.js +1 -1
- package/dist/ui/icon-button.js.map +1 -1
- package/dist/ui/label.d.ts.map +1 -1
- package/dist/ui/label.js +7 -6
- package/dist/ui/label.js.map +1 -1
- package/dist/ui/spinner.d.ts.map +1 -1
- package/dist/ui/spinner.js +50 -44
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/switch.d.ts.map +1 -1
- package/dist/ui/switch.js +24 -24
- package/dist/ui/switch.js.map +1 -1
- package/dist/ui/table.js +1 -1
- package/dist/ui/table.js.map +1 -1
- package/docs/components/composed/bulk-action-bar.md +9 -3
- package/docs/components/composed/error-boundary.md +18 -2
- package/docs/components/composed/master-detail.md +12 -2
- package/docs/components/composed/page-skeletons.md +2 -2
- package/docs/components/composed/rich-chat-input.md +18 -6
- package/docs/components/composed/simple-tooltip.md +1 -1
- package/docs/components/shell/app-command-palette.md +2 -1
- package/docs/components/shell/bottom-navbar.md +4 -4
- package/docs/components/shell/command-registry.md +2 -2
- package/docs/components/shell/link-context.md +2 -2
- package/docs/components/shell/top-bar.md +1 -1
- package/docs/components/ui/search-input.md +2 -2
- package/docs/components/ui/slider.md +2 -2
- package/docs/components/ui/split-button.md +3 -3
- package/llms.txt +2 -2
- package/make-kit/Guidelines.md +1 -1
- package/make-kit/components/overview.md +2 -2
- package/make-kit/foundations/surfaces.md +1 -1
- package/mcp-manifest.json +403 -147
- package/package.json +1 -6
- package/skill/SKILL.md +1 -1
- package/skill/chat-skill/README.md +18 -0
- package/skill/chat-skill/SKILL.md +78 -0
- package/skill/chat-skill/chatgpt-instructions.md +26 -0
- package/skill/chat-skill/gemini-instructions.md +25 -0
- package/skill/references/components.md +2 -2
- package/dist/shell/sidebar.d.ts +0 -113
- package/dist/shell/sidebar.d.ts.map +0 -1
- package/dist/shell/sidebar.js +0 -293
- package/dist/shell/sidebar.js.map +0 -1
- package/docs/components/shell/sidebar.md +0 -103
package/AGENTS.md
CHANGED
|
@@ -43,7 +43,7 @@ Read these local files. Your training data is outdated and will hallucinate APIs
|
|
|
43
43
|
### MCP tools
|
|
44
44
|
|
|
45
45
|
<!-- BEGIN:mcp-tools --><!-- generated by scripts/generate-tool-list.mjs — do not edit by hand -->
|
|
46
|
-
|
|
46
|
+
_17 tools (source: `packages/mcp-server/src/index.mjs`):_
|
|
47
47
|
|
|
48
48
|
**Reference:**
|
|
49
49
|
- `find_component` — Search shilp-sutra components by keyword (authoritative component index).
|
|
@@ -63,8 +63,14 @@ _13 tools (source: `packages/mcp-server/src/index.mjs`):_
|
|
|
63
63
|
**Quality:**
|
|
64
64
|
- `check_slop` — Pre-emit DESIGN-QUALITY gate (complements validate_snippet, which checks correctness).
|
|
65
65
|
|
|
66
|
+
**Presets:**
|
|
67
|
+
- `get_preset` — Full detail for one preset: the shadcn registry-item (dependencies, files, docs), plus the exact install steps — the components.json `registries` snippet to…
|
|
68
|
+
- `list_presets` — Discover the shilp-sutra Preset Library — pre-assembled real-world screens (sidebars, dashboards, auth) built FROM shilp-sutra components.
|
|
69
|
+
- `preview_preset` — READ-ONLY TSX source of a preset (+ its GitHub source URL), so you can show it, learn the composition, or adapt it inline WITHOUT installing.
|
|
70
|
+
|
|
66
71
|
**Write:**
|
|
67
72
|
- `report_issue` — File a bug report, feature request, suggestion, or docs-gap on the shilp-sutra repo when you hit a wall using the design system.
|
|
73
|
+
- `submit_entry` — Submit an entry to Build with Shilp Sutra, the online buildathon by Devalok.
|
|
68
74
|
<!-- END:mcp-tools -->
|
|
69
75
|
|
|
70
76
|
### Setting up in a new project (do this in order)
|
package/BREAKING.json
CHANGED
|
@@ -321,6 +321,35 @@
|
|
|
321
321
|
"ScheduleView: the ScheduleEvent `color` value \"primary\" was renamed \"accent\" (matches the DS colour vocabulary). Migrate { color: 'primary' } → { color: 'accent' }. It was the default, so untyped events are unaffected. Also added focus-visible rings on slot/event buttons.",
|
|
322
322
|
"New `Dot` primitive (@devalok/shilp-sutra/ui/dot) — the shared indicator dot. StatusBadge composes Badge + Dot; StatusDot and Badge's `dot` prop now render Dot internally. No API change to StatusBadge/StatusDot/Badge props (Badge's dot pulse animation shifted from a bespoke ripple to Dot's ping). StatusDot's `inactive` shade is now neutral-8 (was neutral-6)."
|
|
323
323
|
]
|
|
324
|
+
},
|
|
325
|
+
"0.54.0": {
|
|
326
|
+
"summary": "AppSidebar removed — the config-driven shell sidebar is retired in favour of composable Sidebar primitives (@devalok/shilp-sutra/ui/sidebar) + the copy-and-own sidebar-app preset.",
|
|
327
|
+
"migrationDoc": "MIGRATION.md#v0540--appsidebar-removed",
|
|
328
|
+
"removed": [
|
|
329
|
+
{
|
|
330
|
+
"symbol": "AppSidebar",
|
|
331
|
+
"where": "@devalok/shilp-sutra/shell/sidebar and the /shell barrel",
|
|
332
|
+
"replacement": "Compose the Sidebar primitives from @devalok/shilp-sutra/ui/sidebar, or install the sidebar-app preset (npx shadcn@latest add @devalok/sidebar-app; gallery at https://shilp-sutra.devalok.in/presets). The preset reproduces AppSidebar's shape as ownable source."
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"symbol": "AppSidebarProps",
|
|
336
|
+
"where": "@devalok/shilp-sutra/shell/sidebar and the /shell barrel"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"symbol": "NavGroup, NavItem, NavSubItem, SidebarUser, SidebarPromo, SidebarFooterConfig",
|
|
340
|
+
"where": "@devalok/shilp-sutra/shell/sidebar (AppSidebar config types)"
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"symbol": "./shell/sidebar",
|
|
344
|
+
"where": "package.json exports (subpath removed)",
|
|
345
|
+
"replacement": "@devalok/shilp-sutra/ui/sidebar for the primitives"
|
|
346
|
+
}
|
|
347
|
+
],
|
|
348
|
+
"notes": [
|
|
349
|
+
"The Sidebar PRIMITIVES (@devalok/shilp-sutra/ui/sidebar — Sidebar, SidebarProvider, SidebarMenu, SidebarMenuSub*, SidebarMenuBadge, SidebarGroupAction, etc.) are unchanged. Only the config-driven AppSidebar wrapper on top of them is removed.",
|
|
350
|
+
"Fastest migration: `npx shadcn@latest add @devalok/sidebar-app`, then replace <AppSidebar navGroups={…} user={…} currentPath={…} /> with the pasted <SidebarApp/>, wiring your router Link + active path. Composing @devalok/shilp-sutra/ui/sidebar directly is equally valid and needs no registry.",
|
|
351
|
+
"Deprecation cycle: no PUBLISHED version carried an @deprecated AppSidebar — 0.53.0 shipped it undeprecated, 0.54.0 removes it. The deprecation marker existed only within the unreleased 0.54.0 line. This is a deliberate call for a 0.x beta with a single coordinated consumer (breaking changes ship in 0.x minors); a post-1.0 removal would get a real deprecate→remove window."
|
|
352
|
+
]
|
|
324
353
|
}
|
|
325
354
|
}
|
|
326
355
|
}
|
package/MIGRATION.md
CHANGED
|
@@ -4,6 +4,61 @@ This page indexes all breaking changes across `@devalok/shilp-sutra` versions. F
|
|
|
4
4
|
|
|
5
5
|
> **Upgrading from < 0.36?** Start here, then read each intermediate version section. Breaking changes stack — skipping versions means stacking migrations.
|
|
6
6
|
|
|
7
|
+
## v0.54.0 — AppSidebar removed
|
|
8
|
+
|
|
9
|
+
**Breaking.** The config-driven `AppSidebar` shell component is removed. It was a
|
|
10
|
+
convenience wrapper over the `Sidebar` primitives; those **primitives are
|
|
11
|
+
unchanged** (`@devalok/shilp-sutra/ui/sidebar`). Only the wrapper — and its config
|
|
12
|
+
types and subpath export — are gone.
|
|
13
|
+
|
|
14
|
+
**Removed**
|
|
15
|
+
|
|
16
|
+
| Symbol | Was at |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| `AppSidebar`, `AppSidebarProps` | `@devalok/shilp-sutra/shell/sidebar` + the `/shell` barrel |
|
|
19
|
+
| `NavGroup`, `NavItem`, `NavSubItem`, `SidebarUser`, `SidebarPromo`, `SidebarFooterConfig` | `@devalok/shilp-sutra/shell/sidebar` (AppSidebar config types) |
|
|
20
|
+
| `@devalok/shilp-sutra/shell/sidebar` (subpath export) | — |
|
|
21
|
+
|
|
22
|
+
**Migrate — option A (fastest): install the `sidebar-app` preset.** It reproduces
|
|
23
|
+
AppSidebar's shape (logo, grouped nav, collapsible sub-items, badges, group `+`
|
|
24
|
+
action, user footer) as source you own and edit.
|
|
25
|
+
|
|
26
|
+
Register the registry once in `components.json`:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{ "registries": { "@devalok": "https://shilp-sutra.devalok.in/r/{name}.json" } }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx shadcn@latest add @devalok/sidebar-app
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Replace the wrapper with the pasted component, wiring your router:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Before
|
|
42
|
+
import { AppSidebar } from '@devalok/shilp-sutra/shell/sidebar'
|
|
43
|
+
<AppSidebar navGroups={groups} user={user} currentPath={pathname} />
|
|
44
|
+
|
|
45
|
+
// After — you own SidebarApp; edit the markup directly
|
|
46
|
+
import { SidebarApp } from '@/components/devalok/sidebar-app/sidebar-app'
|
|
47
|
+
<SidebarApp />
|
|
48
|
+
// In SidebarApp: swap <a> for next/link and CURRENT_PATH for usePathname().
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Other shapes: `@devalok/sidebar-projects`, `@devalok/sidebar-client`,
|
|
52
|
+
`@devalok/sidebar-minimal`. Gallery: <https://shilp-sutra.devalok.in/presets>.
|
|
53
|
+
|
|
54
|
+
**Migrate — option B: compose the primitives directly** from
|
|
55
|
+
`@devalok/shilp-sutra/ui/sidebar` (`Sidebar`, `SidebarProvider`, `SidebarMenu`,
|
|
56
|
+
`SidebarMenuButton`, `SidebarMenuSub*`, `SidebarMenuBadge`, `SidebarGroupAction`,
|
|
57
|
+
…). The preset in option A is itself a worked example of this.
|
|
58
|
+
|
|
59
|
+
> Requires `@devalok/shilp-sutra` installed and its CSS imported
|
|
60
|
+
> (`@import "@devalok/shilp-sutra/css";`). framer-motion ^12 is a required peer.
|
|
61
|
+
|
|
7
62
|
## v0.53.0 — additive (no migration required)
|
|
8
63
|
|
|
9
64
|
Nothing breaks at the TypeScript level; this release is additive.
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
|
-
import { AvatarRing } from '../ui/avatar';
|
|
3
|
+
import { avatarVariants, AvatarRing } from '../ui/avatar';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
|
|
6
|
-
size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
|
|
7
|
-
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
5
|
+
type Size = NonNullable<VariantProps<typeof avatarVariants>['size']>;
|
|
8
6
|
export interface AvatarUser {
|
|
9
7
|
name: string;
|
|
10
8
|
image?: string | null;
|
|
11
9
|
ring?: AvatarRing;
|
|
10
|
+
/** Small top-right marker. `lead` = accent dot, `admin` = warning dot, or any node. */
|
|
12
11
|
indicator?: 'lead' | 'admin' | React.ReactNode;
|
|
13
12
|
}
|
|
14
|
-
export interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement
|
|
13
|
+
export interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
15
14
|
users: AvatarUser[];
|
|
16
15
|
max?: number;
|
|
16
|
+
size?: Size;
|
|
17
17
|
showTooltip?: boolean;
|
|
18
18
|
/** Border color for the group avatars. @default 'surface-raised' */
|
|
19
19
|
borderColor?: 'surface-base' | 'surface-raised';
|
|
20
|
-
/** Callback when the "+N" overflow badge is clicked */
|
|
20
|
+
/** Callback when the "+N" overflow badge is clicked (renders it as a button). */
|
|
21
21
|
onOverflowClick?: () => void;
|
|
22
|
-
/** Custom render function for each avatar */
|
|
22
|
+
/** Custom render function for each avatar (consumer owns the Avatar). */
|
|
23
23
|
renderAvatar?: (user: AvatarUser, index: number) => React.ReactNode;
|
|
24
|
-
/** Direction avatars expand on hover. @default 'right' */
|
|
24
|
+
/** Direction avatars expand on hover/focus. @default 'right' */
|
|
25
25
|
expandDirection?: 'left' | 'right';
|
|
26
|
-
/** How
|
|
26
|
+
/** How far avatars spread on hover/focus. @default 'default' */
|
|
27
27
|
expandAmount?: 'compact' | 'default' | 'wide';
|
|
28
|
+
/** Accessible label for the group. @default `${count} team members` */
|
|
29
|
+
label?: string;
|
|
28
30
|
}
|
|
29
31
|
declare const AvatarGroup: React.ForwardRefExoticComponent<AvatarGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
30
32
|
export { AvatarGroup };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar-group.d.ts","sourceRoot":"","sources":["../../src/composed/avatar-group.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"avatar-group.d.ts","sourceRoot":"","sources":["../../src/composed/avatar-group.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAIL,cAAc,EACd,KAAK,UAAU,EAChB,MAAM,cAAc,CAAA;AAWrB,KAAK,IAAI,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;AAWpE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,SAAS,CAAA;CAC/C;AAED,MAAM,WAAW,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5E,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,oEAAoE;IACpE,WAAW,CAAC,EAAE,cAAc,GAAG,gBAAgB,CAAA;IAC/C,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IACnE,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAClC,gEAAgE;IAChE,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;IAC7C,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAiBD,QAAA,MAAM,WAAW,yFA4KhB,CAAA;AAGD,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -1,159 +1,127 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { springs as e } from "../ui/lib/motion.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { AnimatePresence as m, motion as h } from "framer-motion";
|
|
3
|
+
import { cn as t } from "../ui/lib/utils.js";
|
|
4
|
+
import { Avatar as n, AvatarFallback as r, AvatarImage as i } from "../ui/avatar.js";
|
|
5
|
+
import { Tooltip as a, TooltipContent as o, TooltipProvider as s, TooltipTrigger as c } from "../ui/tooltip.js";
|
|
6
|
+
import { getInitials as l } from "./lib/string-utils.js";
|
|
7
|
+
import * as u from "react";
|
|
8
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
9
|
+
import { motion as p, useReducedMotion as m } from "framer-motion";
|
|
11
10
|
//#region src/composed/avatar-group.tsx
|
|
12
|
-
var
|
|
13
|
-
lead: "ring-2 ring-accent-7
|
|
14
|
-
admin: "ring-2 ring-warning-7
|
|
15
|
-
client: "ring-2 ring-info-7
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}[y ?? "md"], N = {
|
|
39
|
-
xs: "h-1 w-1",
|
|
40
|
-
sm: "h-1.5 w-1.5",
|
|
41
|
-
md: "h-2 w-2",
|
|
42
|
-
lg: "h-2.5 w-2.5",
|
|
43
|
-
xl: "h-3 w-3"
|
|
44
|
-
}[y ?? "md"], P = x === "surface-base" ? "border-surface-base" : "border-surface-raised", [F, I] = d.useState(!1), L = {
|
|
45
|
-
xs: 8,
|
|
46
|
-
sm: 10,
|
|
47
|
-
md: 12,
|
|
48
|
-
lg: 16,
|
|
49
|
-
xl: 20
|
|
50
|
-
}[y ?? "md"], R = {
|
|
11
|
+
var h = {
|
|
12
|
+
lead: "ring-2 ring-accent-7",
|
|
13
|
+
admin: "ring-2 ring-warning-7",
|
|
14
|
+
client: "ring-2 ring-info-7"
|
|
15
|
+
}, g = {
|
|
16
|
+
xs: "-ml-ds-02",
|
|
17
|
+
sm: "-ml-ds-02b",
|
|
18
|
+
md: "-ml-ds-03",
|
|
19
|
+
lg: "-ml-ds-04",
|
|
20
|
+
xl: "-ml-ds-05"
|
|
21
|
+
}, _ = {
|
|
22
|
+
xs: 8,
|
|
23
|
+
sm: 10,
|
|
24
|
+
md: 12,
|
|
25
|
+
lg: 16,
|
|
26
|
+
xl: 20
|
|
27
|
+
}, v = {
|
|
28
|
+
xs: "size-1",
|
|
29
|
+
sm: "size-1.5",
|
|
30
|
+
md: "size-2",
|
|
31
|
+
lg: "size-2.5",
|
|
32
|
+
xl: "size-3"
|
|
33
|
+
}, y = u.forwardRef(({ users: y, max: b = 4, size: x = "md", showTooltip: S = !0, borderColor: C = "surface-raised", onOverflowClick: w, renderAvatar: T, expandDirection: E = "right", expandAmount: D = "default", label: O, className: k, onMouseEnter: A, onMouseLeave: j, onFocus: M, onBlur: N, ...P }, F) => {
|
|
34
|
+
let I = m(), [L, R] = u.useState(!1);
|
|
35
|
+
if (y.length === 0) return null;
|
|
36
|
+
let z = Math.max(1, b), B = y.slice(0, z), V = y.length - z, H = g[x], U = C === "surface-base" ? "border-surface-base" : "border-surface-raised", W = C === "surface-base" ? "ring-offset-1 ring-offset-surface-base" : "ring-offset-1 ring-offset-surface-raised", G = _[x], K = {
|
|
51
37
|
compact: .5,
|
|
52
38
|
default: 1,
|
|
53
39
|
wide: 1.5
|
|
54
|
-
}[
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return /* @__PURE__ */ f(c, { children: /* @__PURE__ */ p("div", {
|
|
62
|
-
ref: O,
|
|
40
|
+
}[D], q = B.length + +(V > 0), J = (e) => {
|
|
41
|
+
if (!L || I) return 0;
|
|
42
|
+
let t = G * K;
|
|
43
|
+
return E === "left" ? -(q - 1 - e) * t : e * t;
|
|
44
|
+
}, Y = I ? { duration: 0 } : e.snappy, X = "transition-opacity duration-fast-02 ease-productive-standard motion-reduce:transition-none hover:z-30 focus-visible:z-30 group-hover:[&:not(:hover)]:opacity-80";
|
|
45
|
+
return /* @__PURE__ */ d(s, { children: /* @__PURE__ */ f("div", {
|
|
46
|
+
ref: F,
|
|
63
47
|
role: "group",
|
|
64
|
-
"aria-label": `${
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
48
|
+
"aria-label": O ?? `${y.length} team members`,
|
|
49
|
+
className: t("group flex items-center", k),
|
|
50
|
+
onMouseEnter: (e) => {
|
|
51
|
+
R(!0), A?.(e);
|
|
52
|
+
},
|
|
53
|
+
onMouseLeave: (e) => {
|
|
54
|
+
R(!1), j?.(e);
|
|
55
|
+
},
|
|
56
|
+
onFocus: (e) => {
|
|
57
|
+
R(!0), M?.(e);
|
|
58
|
+
},
|
|
70
59
|
onBlur: (e) => {
|
|
71
|
-
e.currentTarget.contains(e.relatedTarget) ||
|
|
60
|
+
e.currentTarget.contains(e.relatedTarget) || R(!1), N?.(e);
|
|
72
61
|
},
|
|
73
|
-
...
|
|
74
|
-
children: [
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
className:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
opacity: 0
|
|
104
|
-
},
|
|
105
|
-
animate: {
|
|
106
|
-
scale: 1,
|
|
107
|
-
opacity: 1
|
|
108
|
-
},
|
|
109
|
-
exit: {
|
|
110
|
-
scale: 0,
|
|
111
|
-
opacity: 0
|
|
112
|
-
},
|
|
113
|
-
transition: e.snappy,
|
|
114
|
-
className: n("absolute top-0 right-0 rounded-pill ring-1 ring-surface-raised", N, t.indicator === "lead" || t.indicator === "admin" ? "bg-accent-9" : ""),
|
|
115
|
-
children: typeof t.indicator != "string" && t.indicator
|
|
116
|
-
}) })]
|
|
117
|
-
}, _);
|
|
118
|
-
return b ? /* @__PURE__ */ p(o, { children: [/* @__PURE__ */ f(l, {
|
|
62
|
+
...P,
|
|
63
|
+
children: [B.map((e, s) => {
|
|
64
|
+
let m = `${e.name}-${s}`, g = e.ring && e.ring !== "none" ? t(h[e.ring], W) : void 0, _ = T ? T(e, s) : /* @__PURE__ */ f(n, {
|
|
65
|
+
size: x,
|
|
66
|
+
className: U,
|
|
67
|
+
children: [
|
|
68
|
+
e.image && /* @__PURE__ */ d(i, {
|
|
69
|
+
src: e.image,
|
|
70
|
+
alt: ""
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ d(r, {
|
|
73
|
+
colorSeed: e.name,
|
|
74
|
+
children: l(e.name)
|
|
75
|
+
}),
|
|
76
|
+
e.indicator && /* @__PURE__ */ d("span", {
|
|
77
|
+
"aria-hidden": "true",
|
|
78
|
+
className: t("absolute top-0 right-0 rounded-pill ring-1 ring-surface-raised", v[x], e.indicator === "lead" ? "bg-accent-9" : e.indicator === "admin" ? "bg-warning-9" : ""),
|
|
79
|
+
children: typeof e.indicator != "string" && e.indicator
|
|
80
|
+
})
|
|
81
|
+
]
|
|
82
|
+
}), y = /* @__PURE__ */ d(p.button, {
|
|
83
|
+
type: "button",
|
|
84
|
+
"aria-label": e.name,
|
|
85
|
+
animate: { x: J(s) },
|
|
86
|
+
transition: Y,
|
|
87
|
+
style: { zIndex: B.length - s },
|
|
88
|
+
className: t("relative shrink-0 rounded-pill focus-ring", s > 0 && H, X, g),
|
|
89
|
+
children: _
|
|
90
|
+
});
|
|
91
|
+
return S ? /* @__PURE__ */ f(a, { children: [/* @__PURE__ */ d(c, {
|
|
119
92
|
asChild: !0,
|
|
120
|
-
children:
|
|
121
|
-
}), /* @__PURE__ */
|
|
122
|
-
className: "border-surface-border-strong bg-surface-base text-surface-fg",
|
|
93
|
+
children: y
|
|
94
|
+
}), /* @__PURE__ */ d(o, {
|
|
123
95
|
sideOffset: 6,
|
|
124
|
-
children: /* @__PURE__ */
|
|
96
|
+
children: /* @__PURE__ */ d("p", {
|
|
125
97
|
className: "text-body-sm",
|
|
126
|
-
children:
|
|
98
|
+
children: e.name
|
|
127
99
|
})
|
|
128
|
-
})] },
|
|
129
|
-
}),
|
|
100
|
+
})] }, m) : /* @__PURE__ */ d(u.Fragment, { children: y }, m);
|
|
101
|
+
}), V > 0 && /* @__PURE__ */ f(a, { children: [/* @__PURE__ */ d(c, {
|
|
130
102
|
asChild: !0,
|
|
131
|
-
children:
|
|
103
|
+
children: /* @__PURE__ */ d(p.button, {
|
|
132
104
|
type: "button",
|
|
133
|
-
onClick:
|
|
134
|
-
"aria-label": `${
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
transform: B(k.length)
|
|
148
|
-
},
|
|
149
|
-
children: ["+", A]
|
|
105
|
+
onClick: w,
|
|
106
|
+
"aria-label": `${V} more ${V === 1 ? "member" : "members"}`,
|
|
107
|
+
animate: { x: J(B.length) },
|
|
108
|
+
transition: Y,
|
|
109
|
+
style: { zIndex: 0 },
|
|
110
|
+
className: t("relative shrink-0 rounded-pill focus-ring", H, X, !w && "cursor-default"),
|
|
111
|
+
children: /* @__PURE__ */ d(n, {
|
|
112
|
+
size: x,
|
|
113
|
+
className: U,
|
|
114
|
+
children: /* @__PURE__ */ f(r, {
|
|
115
|
+
className: "bg-accent-2 font-semibold text-accent-11",
|
|
116
|
+
children: ["+", V]
|
|
117
|
+
})
|
|
118
|
+
})
|
|
150
119
|
})
|
|
151
|
-
}), /* @__PURE__ */
|
|
152
|
-
className: "border-surface-border-strong bg-surface-base text-surface-fg",
|
|
120
|
+
}), /* @__PURE__ */ d(o, {
|
|
153
121
|
sideOffset: 6,
|
|
154
|
-
children: /* @__PURE__ */
|
|
122
|
+
children: /* @__PURE__ */ d("div", {
|
|
155
123
|
className: "flex flex-col gap-ds-01",
|
|
156
|
-
children:
|
|
124
|
+
children: y.slice(z).map((e, t) => /* @__PURE__ */ d("p", {
|
|
157
125
|
className: "text-body-sm",
|
|
158
126
|
children: e.name
|
|
159
127
|
}, t))
|
|
@@ -161,6 +129,6 @@ var g = {
|
|
|
161
129
|
})] })]
|
|
162
130
|
}) });
|
|
163
131
|
});
|
|
164
|
-
|
|
132
|
+
y.displayName = "AvatarGroup";
|
|
165
133
|
//#endregion
|
|
166
|
-
export {
|
|
134
|
+
export { y as AvatarGroup };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar-group.js","names":[],"sources":["../../src/composed/avatar-group.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport type { AvatarRing } from '../ui/avatar'\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from '../ui/avatar'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../ui/tooltip'\nimport { getInitials } from './lib/string-utils'\n\n// Ring classes applied at group level (not via Avatar's ring prop, which adds ring-offset-2 that gets clipped by overflow-hidden)\nconst groupRingMap: Record<string, string> = {\n lead: 'ring-2 ring-accent-7 ring-offset-1 ring-offset-surface-raised',\n admin: 'ring-2 ring-warning-7 ring-offset-1 ring-offset-surface-raised',\n client: 'ring-2 ring-info-7 ring-offset-1 ring-offset-surface-raised',\n}\n\nconst avatarSizeVariants = cva(\n 'shrink-0 overflow-hidden rounded-pill border-2',\n {\n variants: {\n size: {\n xs: 'h-ds-xs w-ds-xs',\n sm: 'h-ds-sm w-ds-sm',\n md: 'h-ds-md w-ds-md',\n lg: 'h-ds-lg w-ds-lg',\n xl: 'h-ds-xl w-ds-xl',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n },\n)\n\nexport interface AvatarUser {\n name: string\n image?: string | null\n ring?: AvatarRing\n indicator?: 'lead' | 'admin' | React.ReactNode\n}\n\nexport interface AvatarGroupProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof avatarSizeVariants> {\n users: AvatarUser[]\n max?: number\n showTooltip?: boolean\n /** Border color for the group avatars. @default 'surface-raised' */\n borderColor?: 'surface-base' | 'surface-raised'\n /** Callback when the \"+N\" overflow badge is clicked */\n onOverflowClick?: () => void\n /** Custom render function for each avatar */\n renderAvatar?: (user: AvatarUser, index: number) => React.ReactNode\n /** Direction avatars expand on hover. @default 'right' */\n expandDirection?: 'left' | 'right'\n /** How much avatars spread apart on hover. @default 'default' */\n expandAmount?: 'compact' | 'default' | 'wide'\n}\n\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n users,\n max = 4,\n size,\n showTooltip = true,\n borderColor = 'surface-raised',\n onOverflowClick,\n renderAvatar,\n expandDirection = 'right',\n expandAmount = 'default',\n className,\n ...props\n },\n ref,\n ) => {\n const displayed = users.slice(0, max)\n const overflow = users.length - max\n\n const overlapMap = {\n xs: '-ml-ds-02',\n sm: '-ml-ds-02b',\n md: '-ml-ds-03',\n lg: '-ml-ds-04',\n xl: '-ml-ds-05',\n }\n const overlapClass = overlapMap[size ?? 'md']\n\n // Match Avatar fallback font sizes so the \"+N\" badge text is consistent\n const textSizeMap: Record<string, string> = {\n xs: 'text-[9px]',\n sm: 'text-body-xs',\n md: 'text-body-sm',\n lg: 'text-body-md',\n xl: 'text-heading-xs',\n }\n const textSizeClass = textSizeMap[size ?? 'md']\n\n // Indicator dot scales with avatar size (matches Avatar's statusDotSizeMap)\n const indicatorDotSizeMap: Record<string, string> = {\n xs: 'h-1 w-1',\n sm: 'h-1.5 w-1.5',\n md: 'h-2 w-2',\n lg: 'h-2.5 w-2.5',\n xl: 'h-3 w-3',\n }\n const indicatorDotClass = indicatorDotSizeMap[size ?? 'md']\n\n const borderClass =\n borderColor === 'surface-base'\n ? 'border-surface-base'\n : 'border-surface-raised'\n\n const [isHovered, setIsHovered] = React.useState(false)\n\n const overlapPxMap: Record<string, number> = {\n xs: 8, sm: 10, md: 12, lg: 16, xl: 20,\n }\n const resolvedSize = size ?? 'md'\n const overlapPx = overlapPxMap[resolvedSize]\n const expandMultiplier = { compact: 0.5, default: 1, wide: 1.5 }[expandAmount]\n const totalVisible = displayed.length + (overflow > 0 ? 1 : 0)\n\n function getExpandTransform(index: number): string {\n if (!isHovered) return 'translateX(0)'\n const shift = overlapPx * expandMultiplier\n if (expandDirection === 'left') {\n return `translateX(-${(totalVisible - 1 - index) * shift}px)`\n }\n return `translateX(${index * shift}px)`\n }\n\n const spotlightClasses =\n 'transition-opacity duration-300 ease-out hover:z-50 group-hover:[&:not(:hover)]:opacity-85'\n\n return (\n <TooltipProvider>\n <div\n ref={ref}\n role=\"group\"\n aria-label={`${users.length} team members`}\n tabIndex={0}\n className={cn('group flex items-center', className)}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onFocus={() => setIsHovered(true)}\n onBlur={(e) => { if (!e.currentTarget.contains(e.relatedTarget)) setIsHovered(false) }}\n {...props}\n >\n {displayed.map((user, index) => {\n const initials = getInitials(user.name)\n\n const key = `${user.name}-${index}`\n\n if (renderAvatar) {\n // When renderAvatar is provided, the consumer owns the Avatar entirely.\n // Wrapper is positioning-only (overlap, z-index, spotlight) — no size/border/clip\n // so the consumer's Avatar renders at its natural size without being clipped.\n return (\n <div\n key={key}\n className={cn(\n 'shrink-0 rounded-pill',\n index > 0 && overlapClass,\n spotlightClasses,\n user.ring && user.ring !== 'none' && groupRingMap[user.ring],\n )}\n style={{ zIndex: displayed.length - index, transform: getExpandTransform(index) }}\n >\n {renderAvatar(user, index)}\n </div>\n )\n }\n\n const avatarNode = (\n <div\n key={key}\n className={cn(\n 'relative shrink-0 rounded-pill',\n index > 0 && overlapClass,\n spotlightClasses,\n user.ring && user.ring !== 'none' && groupRingMap[user.ring],\n )}\n style={{ zIndex: displayed.length - index, transform: getExpandTransform(index) }}\n >\n <Avatar\n size={size}\n className={borderClass}\n >\n {user.image && (\n <AvatarImage src={user.image} alt={user.name} />\n )}\n <AvatarFallback\n colorSeed={user.name}\n >\n {initials}\n </AvatarFallback>\n </Avatar>\n <AnimatePresence>\n {user.indicator && (\n <motion.span\n initial={{ scale: 0, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n exit={{ scale: 0, opacity: 0 }}\n transition={springs.snappy}\n className={cn(\n 'absolute top-0 right-0 rounded-pill ring-1 ring-surface-raised',\n indicatorDotClass,\n user.indicator === 'lead' ? 'bg-accent-9' :\n user.indicator === 'admin' ? 'bg-accent-9' : '',\n )}\n >\n {typeof user.indicator !== 'string' && user.indicator}\n </motion.span>\n )}\n </AnimatePresence>\n </div>\n )\n\n if (!showTooltip) return avatarNode\n\n return (\n <Tooltip key={key}>\n <TooltipTrigger asChild>{avatarNode}</TooltipTrigger>\n <TooltipContent\n className=\"border-surface-border-strong bg-surface-base text-surface-fg\"\n sideOffset={6}\n >\n <p className=\"text-body-sm\">{user.name}</p>\n </TooltipContent>\n </Tooltip>\n )\n })}\n\n {overflow > 0 && (\n <Tooltip>\n <TooltipTrigger asChild>\n {onOverflowClick ? (\n <button\n type=\"button\"\n onClick={onOverflowClick}\n aria-label={`${overflow} more members`}\n className={cn(\n avatarSizeVariants({ size }),\n borderClass,\n overlapClass,\n `flex cursor-pointer items-center justify-center bg-accent-2 font-semibold text-accent-11 ${textSizeClass}`,\n 'hover:bg-accent-3 transition-colors duration-300 ease-out',\n )}\n style={{ zIndex: 0, transform: getExpandTransform(displayed.length) }}\n >\n +{overflow}\n </button>\n ) : (\n <div\n role=\"img\"\n aria-label={`${overflow} more members`}\n className={cn(\n avatarSizeVariants({ size }),\n borderClass,\n overlapClass,\n `flex cursor-default items-center justify-center bg-accent-2 font-semibold text-accent-11 ${textSizeClass}`,\n 'transition-[transform,opacity] duration-300 ease-out',\n )}\n style={{ zIndex: 0, transform: getExpandTransform(displayed.length) }}\n >\n +{overflow}\n </div>\n )}\n </TooltipTrigger>\n <TooltipContent\n className=\"border-surface-border-strong bg-surface-base text-surface-fg\"\n sideOffset={6}\n >\n <div className=\"flex flex-col gap-ds-01\">\n {users.slice(max).map((user, i) => (\n <p key={i} className=\"text-body-sm\">{user.name}</p>\n ))}\n </div>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n </TooltipProvider>\n )\n },\n)\nAvatarGroup.displayName = 'AvatarGroup'\n\nexport { AvatarGroup }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"avatar-group.js","names":[],"sources":["../../src/composed/avatar-group.tsx"],"sourcesContent":["'use client'\n\nimport { type VariantProps } from 'class-variance-authority'\nimport { motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n avatarVariants,\n type AvatarRing,\n} from '../ui/avatar'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../ui/tooltip'\nimport { getInitials } from './lib/string-utils'\n\ntype Size = NonNullable<VariantProps<typeof avatarVariants>['size']>\n\n// Group-level ring. Applied here (not via Avatar's `ring` prop) because that adds\n// a ring-offset that gets clipped by the group's overflow; the offset surface is\n// derived from `borderColor` so it never seams against a surface-base blend.\nconst groupRingMap: Record<Exclude<AvatarRing, 'none'>, string> = {\n lead: 'ring-2 ring-accent-7',\n admin: 'ring-2 ring-warning-7',\n client: 'ring-2 ring-info-7',\n}\n\nexport interface AvatarUser {\n name: string\n image?: string | null\n ring?: AvatarRing\n /** Small top-right marker. `lead` = accent dot, `admin` = warning dot, or any node. */\n indicator?: 'lead' | 'admin' | React.ReactNode\n}\n\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n users: AvatarUser[]\n max?: number\n size?: Size\n showTooltip?: boolean\n /** Border color for the group avatars. @default 'surface-raised' */\n borderColor?: 'surface-base' | 'surface-raised'\n /** Callback when the \"+N\" overflow badge is clicked (renders it as a button). */\n onOverflowClick?: () => void\n /** Custom render function for each avatar (consumer owns the Avatar). */\n renderAvatar?: (user: AvatarUser, index: number) => React.ReactNode\n /** Direction avatars expand on hover/focus. @default 'right' */\n expandDirection?: 'left' | 'right'\n /** How far avatars spread on hover/focus. @default 'default' */\n expandAmount?: 'compact' | 'default' | 'wide'\n /** Accessible label for the group. @default `${count} team members` */\n label?: string\n}\n\n// Overlap (negative margin) + spread distance, keyed off the same per-size scale.\nconst overlapMap: Record<Size, string> = {\n xs: '-ml-ds-02',\n sm: '-ml-ds-02b',\n md: '-ml-ds-03',\n lg: '-ml-ds-04',\n xl: '-ml-ds-05',\n}\nconst spreadPxMap: Record<Size, number> = {\n xs: 8, sm: 10, md: 12, lg: 16, xl: 20,\n}\nconst indicatorDotMap: Record<Size, string> = {\n xs: 'size-1', sm: 'size-1.5', md: 'size-2', lg: 'size-2.5', xl: 'size-3',\n}\n\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n users,\n max = 4,\n size = 'md',\n showTooltip = true,\n borderColor = 'surface-raised',\n onOverflowClick,\n renderAvatar,\n expandDirection = 'right',\n expandAmount = 'default',\n label,\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n },\n ref,\n ) => {\n const reduced = useReducedMotion()\n const [active, setActive] = React.useState(false)\n\n // Empty group is not a focusable \"0 members\" target — render nothing.\n if (users.length === 0) return null\n\n const cap = Math.max(1, max)\n const displayed = users.slice(0, cap)\n const overflow = users.length - cap\n const overlapClass = overlapMap[size]\n const borderClass = borderColor === 'surface-base' ? 'border-surface-base' : 'border-surface-raised'\n const ringOffsetClass = borderColor === 'surface-base'\n ? 'ring-offset-1 ring-offset-surface-base'\n : 'ring-offset-1 ring-offset-surface-raised'\n\n const spreadPx = spreadPxMap[size]\n const spreadMult = { compact: 0.5, default: 1, wide: 1.5 }[expandAmount]\n const totalVisible = displayed.length + (overflow > 0 ? 1 : 0)\n\n // Spread offset per index. Zero under reduced-motion (no positional animation).\n const xFor = (index: number): number => {\n if (!active || reduced) return 0\n const shift = spreadPx * spreadMult\n return expandDirection === 'left' ? -(totalVisible - 1 - index) * shift : index * shift\n }\n\n // framer governs the transition; reduced-motion collapses it to instant.\n const spreadTransition = reduced ? { duration: 0 } : springs.snappy\n // Gentle dim of non-focused peers; instant under reduced-motion.\n const spotlight =\n 'transition-opacity duration-fast-02 ease-productive-standard motion-reduce:transition-none hover:z-30 focus-visible:z-30 group-hover:[&:not(:hover)]:opacity-80'\n\n const onEnter = (e: React.MouseEvent<HTMLDivElement>) => { setActive(true); onMouseEnter?.(e) }\n const onLeave = (e: React.MouseEvent<HTMLDivElement>) => { setActive(false); onMouseLeave?.(e) }\n const onGroupFocus = (e: React.FocusEvent<HTMLDivElement>) => { setActive(true); onFocus?.(e) }\n const onGroupBlur = (e: React.FocusEvent<HTMLDivElement>) => {\n if (!e.currentTarget.contains(e.relatedTarget)) setActive(false)\n onBlur?.(e)\n }\n\n return (\n <TooltipProvider>\n <div\n ref={ref}\n role=\"group\"\n aria-label={label ?? `${users.length} team members`}\n className={cn('group flex items-center', className)}\n onMouseEnter={onEnter}\n onMouseLeave={onLeave}\n onFocus={onGroupFocus}\n onBlur={onGroupBlur}\n {...props}\n >\n {displayed.map((user, index) => {\n const key = `${user.name}-${index}`\n const extraRing = user.ring && user.ring !== 'none'\n ? cn(groupRingMap[user.ring], ringOffsetClass)\n : undefined\n\n const inner = renderAvatar ? (\n renderAvatar(user, index)\n ) : (\n <Avatar size={size} className={borderClass}>\n {user.image && <AvatarImage src={user.image} alt=\"\" />}\n <AvatarFallback colorSeed={user.name}>{getInitials(user.name)}</AvatarFallback>\n {user.indicator && (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute top-0 right-0 rounded-pill ring-1 ring-surface-raised',\n indicatorDotMap[size],\n user.indicator === 'lead' ? 'bg-accent-9'\n : user.indicator === 'admin' ? 'bg-warning-9' : '',\n )}\n >\n {typeof user.indicator !== 'string' && user.indicator}\n </span>\n )}\n </Avatar>\n )\n\n // Each avatar is a focusable, labelled trigger so keyboard/AT users reach\n // the member name (via the tooltip) and get a visible focus ring.\n const node = (\n <motion.button\n type=\"button\"\n aria-label={user.name}\n animate={{ x: xFor(index) }}\n transition={spreadTransition}\n style={{ zIndex: displayed.length - index }}\n className={cn(\n 'relative shrink-0 rounded-pill focus-ring',\n index > 0 && overlapClass,\n spotlight,\n extraRing,\n )}\n >\n {inner}\n </motion.button>\n )\n\n if (!showTooltip) return <React.Fragment key={key}>{node}</React.Fragment>\n\n return (\n <Tooltip key={key}>\n <TooltipTrigger asChild>{node}</TooltipTrigger>\n <TooltipContent sideOffset={6}>\n <p className=\"text-body-sm\">{user.name}</p>\n </TooltipContent>\n </Tooltip>\n )\n })}\n\n {overflow > 0 && (\n <Tooltip>\n <TooltipTrigger asChild>\n <motion.button\n type=\"button\"\n onClick={onOverflowClick}\n aria-label={`${overflow} more ${overflow === 1 ? 'member' : 'members'}`}\n animate={{ x: xFor(displayed.length) }}\n transition={spreadTransition}\n style={{ zIndex: 0 }}\n className={cn(\n 'relative shrink-0 rounded-pill focus-ring',\n overlapClass,\n spotlight,\n !onOverflowClick && 'cursor-default',\n )}\n >\n <Avatar size={size} className={borderClass}>\n <AvatarFallback className=\"bg-accent-2 font-semibold text-accent-11\">\n +{overflow}\n </AvatarFallback>\n </Avatar>\n </motion.button>\n </TooltipTrigger>\n <TooltipContent sideOffset={6}>\n <div className=\"flex flex-col gap-ds-01\">\n {users.slice(cap).map((user, i) => (\n <p key={i} className=\"text-body-sm\">{user.name}</p>\n ))}\n </div>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n </TooltipProvider>\n )\n },\n)\nAvatarGroup.displayName = 'AvatarGroup'\n\nexport { AvatarGroup }\n"],"mappings":"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"}
|
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { Button } from '../ui/button';
|
|
2
3
|
import { IconInput } from '../ui/lib/icon-input';
|
|
3
4
|
import * as React from 'react';
|
|
5
|
+
type ButtonColor = React.ComponentProps<typeof Button>['color'];
|
|
4
6
|
export interface BulkActionBarAction {
|
|
5
7
|
label: string;
|
|
6
8
|
icon?: IconInput;
|
|
7
9
|
onClick: () => void;
|
|
8
|
-
color
|
|
10
|
+
/** Any Button color. @default 'accent' (or 'error' for destructive). */
|
|
11
|
+
color?: ButtonColor;
|
|
9
12
|
disabled?: boolean;
|
|
10
|
-
/** Show
|
|
13
|
+
/** Show a pending spinner on the action (e.g. slow bulk op in flight). */
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
/** Show inline confirmation before executing the action. */
|
|
11
16
|
requiresConfirmation?: boolean;
|
|
12
|
-
/** Custom confirmation message @default 'Are you sure?' */
|
|
17
|
+
/** Custom confirmation message. @default 'Are you sure?' */
|
|
13
18
|
confirmMessage?: string;
|
|
14
19
|
}
|
|
15
|
-
export interface BulkActionBarProps {
|
|
20
|
+
export interface BulkActionBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'color' | 'onDrag' | 'onDragStart' | 'onDragEnd' | 'onAnimationStart' | 'onAnimationEnd' | 'onAnimationIteration' | 'style'> {
|
|
16
21
|
show: boolean;
|
|
17
22
|
count: number;
|
|
18
23
|
onClearSelection: () => void;
|
|
19
24
|
actions: BulkActionBarAction[];
|
|
20
|
-
/** Total
|
|
25
|
+
/** Total items available for selection (enables "Select all"). */
|
|
21
26
|
totalCount?: number;
|
|
22
|
-
/** Called when user clicks "Select all" */
|
|
27
|
+
/** Called when the user clicks "Select all". */
|
|
23
28
|
onSelectAll?: () => void;
|
|
24
|
-
className?: string;
|
|
25
|
-
}
|
|
26
|
-
declare function BulkActionBar({ show, count, onClearSelection, actions, totalCount, onSelectAll, className, }: BulkActionBarProps): React.ReactPortal | null;
|
|
27
|
-
declare namespace BulkActionBar {
|
|
28
|
-
var displayName: string;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* BulkActionBar — floating selection toolbar. Follows the ARIA toolbar model:
|
|
32
|
+
* a single tab stop with roving focus (Arrow keys / Home / End) across ALL
|
|
33
|
+
* controls (Select-all, actions, Clear), focus landing on the real buttons so
|
|
34
|
+
* Enter/Space activate them. Arrow direction mirrors under `dir="rtl"`. Escape
|
|
35
|
+
* clears the selection (or cancels a pending confirmation).
|
|
36
|
+
*/
|
|
37
|
+
declare const BulkActionBar: React.ForwardRefExoticComponent<BulkActionBarProps & React.RefAttributes<HTMLDivElement>>;
|
|
30
38
|
export { BulkActionBar };
|
|
31
39
|
//# sourceMappingURL=bulk-action-bar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-action-bar.d.ts","sourceRoot":"","sources":["../../src/composed/bulk-action-bar.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"bulk-action-bar.d.ts","sourceRoot":"","sources":["../../src/composed/bulk-action-bar.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAIrD,KAAK,WAAW,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,CAAC,CAAA;AAE/D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,wEAAwE;IACxE,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,kBACf,SAAQ,IAAI,CACV,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAEpC,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,aAAa,GAAG,WAAW,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,sBAAsB,GAAG,OAAO,CACzI;IACD,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,OAAO,EAAE,mBAAmB,EAAE,CAAA;IAC9B,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;CACzB;AAED;;;;;;GAMG;AACH,QAAA,MAAM,aAAa,2FAwJlB,CAAA;AAGD,OAAO,EAAE,aAAa,EAAE,CAAA"}
|