diff --git a/packages/web/app/foundry.config.ts b/packages/web/app/foundry.config.ts index b78920a53c3..8977cc4ecf8 100644 --- a/packages/web/app/foundry.config.ts +++ b/packages/web/app/foundry.config.ts @@ -47,7 +47,7 @@ export default defineConfig({ { label: 'Primitives', children: [ - { label: 'Accordion' }, + { label: 'Accordion', children: [{ label: 'Component Examples' }] }, { label: 'Avatar', children: [{ label: 'Component Examples' }] }, { label: 'Badge', children: [{ label: 'Component Examples' }] }, { label: 'StatusDot' }, diff --git a/packages/web/app/package.json b/packages/web/app/package.json index a1d7138c13a..cfe11b897ed 100644 --- a/packages/web/app/package.json +++ b/packages/web/app/package.json @@ -33,7 +33,6 @@ "@hookform/resolvers": "5.9.1", "@monaco-editor/react": "4.8.0-rc.2", "@pierre/diffs": "1.3.5", - "@radix-ui/react-accordion": "1.2.2", "@radix-ui/react-icons": "1.3.2", "@radix-ui/react-label": "2.1.1", "@radix-ui/react-slot": "1.1.1", diff --git a/packages/web/app/src/components/base/accordion/accordion.call-sites.preview.tsx b/packages/web/app/src/components/base/accordion/accordion.call-sites.preview.tsx new file mode 100644 index 00000000000..db9c80f6029 --- /dev/null +++ b/packages/web/app/src/components/base/accordion/accordion.call-sites.preview.tsx @@ -0,0 +1,644 @@ +import { useState } from 'react'; +import { FolderIcon, FolderOpenIcon } from 'lucide-react'; +import { createPreview, type NavPath } from 'react-foundry'; +import { Badge } from '@/components/base/badge/badge'; +import { Card } from '@/components/base/card/card'; +import { Menu } from '@/components/base/floating/menu/menu'; +import { Select } from '@/components/base/floating/select/select'; +import { CallSite, InventoryList } from '@/components/inventory/shared'; +import { PermissionTable } from '@/components/organization/permission-table'; +import { PulseIcon } from '@/components/ui/icon'; +import { + CheckIcon, + DotsHorizontalIcon, + ExclamationTriangleIcon, + PlusIcon, +} from '@radix-ui/react-icons'; +import { Accordion } from './accordion'; + +export const nav: NavPath = 'Base/Primitives/Accordion/Component Examples'; + +/** + * Every accordion in the app, transcribed as it ships on base Accordion. Nine mounts in eight + * files. The pages around them run queries and mutations, so the content inside each item is stood + * in where it would need one: the schema highlighter is a `
`, the change details a paragraph,
+ * the approval badge a Badge.
+ *
+ * History: until round 7 six of these were on `ui/accordion` (Radix, shadcn parts) and three on
+ * `v2/accordion` (Radix, compound parts, always collapsible), one site mixing the two. A single
+ * service on the target page was an accordion locked open with its trigger disabled; it is a Card
+ * now. Both old components and `@radix-ui/react-accordion` are deleted.
+ */
+
+const ENTRIES = [
+  {
+    source: 'components/organization/members/permission-selector.tsx:93',
+    origin: 'base',
+    what: 'Permission groups on the role form: multiple open, controlled, panels kept mounted for the dependency jump, "N selected" trailing',
+    coveredBy: 'Permission selector',
+  },
+  {
+    source: 'components/organization/members/selected-permission-overview.tsx:146',
+    origin: 'base',
+    what: 'A role\'s granted permissions: one item, open by default when anything is granted, "N allowed" trailing',
+    coveredBy: 'Permission overview',
+  },
+  {
+    source: 'components/organization/settings/access-tokens/permission-detail-view.tsx:45',
+    origin: 'base',
+    what: "An access token's permissions per level, the same shape plus the resources it was granted on",
+    coveredBy: 'Permission overview',
+  },
+  {
+    source: 'components/target/history/errors-and-changes.tsx:227',
+    origin: 'base',
+    what: 'One accordion per schema change on the check, version and proposal pages: a rich label, details below',
+    coveredBy: 'Schema changes',
+  },
+  {
+    source: 'components/target/proposals/change-detail.tsx:20',
+    origin: 'base',
+    what: 'A proposal change: dimmed message and an icon in the label',
+    coveredBy: 'Proposal change',
+  },
+  {
+    source: 'components/target/settings/registry-access-token.tsx:231',
+    origin: 'base',
+    what: "The registry token form's permission section, plain and open by default",
+    coveredBy: 'Registry token',
+  },
+  {
+    source: 'pages/target.tsx:67',
+    origin: 'base',
+    what: 'The services of a federation target, boxed, a two-line header and the SDL inside',
+    coveredBy: 'Target services',
+  },
+  {
+    source: 'pages/target.tsx:86',
+    origin: 'base',
+    what: 'A single service: a Card with the same header and SDL, since there is nothing to collapse',
+    coveredBy: 'Target services',
+  },
+  {
+    source: 'lib/hooks/laboratory/use-operation-collections-plugin.tsx:301',
+    origin: 'base',
+    what: 'Laboratory collections: plain, no chevron, folder icons in the label, a menu through action, e2e hooks on item and trigger',
+    coveredBy: 'Laboratory collections',
+  },
+] as const;
+
+export const Inventory = createPreview({
+  label: 'Inventory',
+  render: () => (
+    
+          Nine mounts, all on base Accordion. Three of the four variants ship: list on the
+          permission views and the change rows, boxed on the target services, plain on the alert
+          form's disclosure, the registry token section and the collections sidebar.
+        
+      }
+      entries={ENTRIES}
+    />
+  ),
+});
+
+// ---------------------------------------------------------------------------
+// components/organization/members/permission-selector.tsx:93
+// ---------------------------------------------------------------------------
+
+const PERMISSION_GROUPS = [
+  {
+    title: 'Organization',
+    permissions: [
+      {
+        id: 'organization:describe',
+        title: 'Describe organization',
+        description: 'Fetch information about the organization.',
+      },
+      {
+        id: 'member:modify',
+        title: 'Modify members',
+        description: 'Invite, remove and assign roles.',
+      },
+    ],
+  },
+  {
+    title: 'Project',
+    permissions: [
+      {
+        id: 'project:describe',
+        title: 'Describe project',
+        description: 'Fetch information about projects.',
+      },
+      { id: 'project:delete', title: 'Delete project', description: 'Delete projects.' },
+    ],
+  },
+  {
+    title: 'Schema Registry',
+    permissions: [
+      { id: 'schemaCheck:create', title: 'Check schema', description: 'Run schema checks.' },
+      {
+        id: 'schemaVersion:publish',
+        title: 'Publish schema',
+        description: 'Publish schema versions.',
+      },
+      {
+        id: 'schemaCheck:approve',
+        title: 'Approve failed check',
+        description: 'Approve failed schema checks.',
+      },
+    ],
+  },
+];
+
+function PermissionSelector() {
+  const [openAccordions, setOpenAccordions] = useState([] as Array);
+  const [selected, setSelected] = useState(new Set(['organization:describe', 'project:describe']));
+  return (
+     {
+        const count = group.permissions.filter(p => selected.has(p.id)).length;
+        return {
+          value: group.title,
+          label: group.title,
+          trailing: count > 0 ? {count} selected : undefined,
+          content: (
+            
+ {group.permissions.map(permission => ( +
+
+
{permission.title}
+
{permission.description}
+
+ +
+ ), + }, + ]} + /> +
+ + ), +}); + +// --------------------------------------------------------------------------- +// pages/target.tsx:67 and :86 +// --------------------------------------------------------------------------- + +const SERVICES = [ + { + id: 's1', + service: 'users', + url: 'https://users.storefront.local/graphql', + source: + 'type Query {\n me: User\n user(id: ID!): User\n}\n\ntype User @key(fields: "id") {\n id: ID!\n fullName: String!\n}', + }, + { + id: 's2', + service: 'products', + url: 'https://products.storefront.local/graphql', + source: + 'type Query {\n products(first: Int = 20): [Product!]!\n}\n\ntype Product @key(fields: "id") {\n id: ID!\n name: String!\n}', + }, +]; + +function serviceHeader(schema: (typeof SERVICES)[number]) { + return ( +
+
{schema.service}
+
{schema.url}
+
+ ); +} + +/** GraphQLHighlight is a Monaco editor; a code block stands in for it. */ +function serviceSchema(schema: (typeof SERVICES)[number]) { + return ( +
+
{schema.source}
+
+ ); +} + +export const TargetServicesPreview = createPreview({ + label: 'Target services', + render: () => ( +
+ +
+ ({ + value: schema.id, + label: serviceHeader(schema), + content: serviceSchema(schema), + }))} + /> +
+
+ +
+ + {serviceHeader(SERVICES[0])} + {serviceSchema(SERVICES[0])} + +
+
+
+ ), +}); + +// --------------------------------------------------------------------------- +// lib/hooks/laboratory/use-operation-collections-plugin.tsx:301 +// --------------------------------------------------------------------------- + +const COLLECTIONS = [ + { + id: 'c1', + name: 'Onboarding', + operations: ['GetViewer', 'ListProjects', 'CreateTarget'], + }, + { id: 'c2', name: 'Regression', operations: ['PublishSchema'] }, + { id: 'c3', name: 'Scratch', operations: [] }, +]; + +function Collections() { + const [value, setValue] = useState(['c1']); + return ( + ({ + value: collection.id, + label: ( + + {value.includes(collection.id) ? ( + + ) : ( + + )} + {collection.name} + + ), + attrs: { 'data-cy': 'collection-item' }, + triggerAttrs: { 'data-cy': 'collection-item-trigger' }, + action: ( + + + + } + sections={[ + [{ label: 'Add operation', trailingIcon: PlusIcon, onClick: () => {} }], + [ + { label: 'Edit', onClick: () => {} }, + { label: 'Delete', variant: 'destructiveAction', onClick: () => {} }, + ], + ]} + /> + ), + content: ( +
+ {collection.operations.length ? ( + collection.operations.map(name => ( + + )) + ) : ( + + )} +
+ ), + }))} + attrs={{ id: 'laboratory-collections' }} + /> + ); +} + +export const LaboratoryCollectionsPreview = createPreview({ + label: 'Laboratory collections', + render: () => ( + +
+ +
+
+ ), +}); diff --git a/packages/web/app/src/components/base/accordion/accordion.preview.tsx b/packages/web/app/src/components/base/accordion/accordion.preview.tsx index 4ac13112b89..f6efd3d030a 100644 --- a/packages/web/app/src/components/base/accordion/accordion.preview.tsx +++ b/packages/web/app/src/components/base/accordion/accordion.preview.tsx @@ -1,71 +1,279 @@ -import { createPreview, type NavPath } from 'react-foundry'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './accordion'; +import { useState, type ReactNode } from 'react'; +import { FolderIcon, FolderOpenIcon, MoreHorizontal, Plus } from 'lucide-react'; +import { controlsFor, createPreview, type NavPath } from 'react-foundry'; +import { Badge } from '../badge/badge'; +import { Menu } from '../floating/menu/menu'; +import { Accordion } from './accordion'; export const nav: NavPath = 'Base/Primitives/Accordion'; +/** + * Items in, an accordion out, on Base UI. One trigger look: medium text, the chevron at the end, + * no underline on hover. Each example below is drawn at the call site it will ship at, so the + * Component Examples beside it, which still render the old components, can be compared like for + * like. + */ + +function Copy({ children }: { children: ReactNode }) { + return

{children}

; +} + +function Labelled(props: { label: string; children: ReactNode }) { + return ( +
+ {props.label} + {props.children} +
+ ); +} + +const CHANGES = [ + { + value: 'breaking', + label: 'Breaking changes', + body: 'Field User.email was removed from type User.', + }, + { + value: 'dangerous', + label: 'Dangerous changes', + body: 'Enum value PENDING was added to Status.', + }, + { value: 'safe', label: 'Safe changes', body: 'Description on type Query has changed.' }, +]; + export const Default = createPreview(() => (
- - - - -

- Field User.email was removed from type User. -

-
-
- - - -

- Enum value PENDING was added to Status. -

-
-
- - - -

Description on type Query has changed.

-
-
-
+ ({ + value: change.value, + label: change.label, + content: {change.body}, + }))} + />
)); +/** `defaultValue` opens items on mount; `multiple` lets more than one stay open. */ export const DefaultOpen = createPreview(() => ( -
- - - - -

Driven by the `defaultValue` prop.

-
-
- - - -

Not listed in `defaultValue`.

-
-
-
+
+ + ({ + value: change.value, + label: change.label, + content: {change.body}, + }))} + /> + + + ({ + value: change.value, + label: change.label, + content: {change.body}, + }))} + /> +
)); -export const AccentVariant = createPreview(() => ( +const PERMISSION_GROUPS = [ + { value: 'Organization', selected: 2, total: 4 }, + { value: 'Project', selected: 0, total: 3 }, + { value: 'Schema Registry', selected: 3, total: 3 }, +]; + +/** + * members/permission-selector.tsx: the open set held by the form, a count at the trigger's far + * end. `trailing` sits before the chevron. + */ +export const Controlled = createPreview(() => { + const [open, setOpen] = useState([]); + return ( +
+ ({ + value: group.value, + label: group.value, + trailing: group.selected ? ( + {group.selected} selected + ) : undefined, + content: {group.total} permissions in this group., + }))} + /> +

Open: {open.join(', ') || 'none'}

+
+ ); +}); + +const SERVICES = [ + { id: 's1', service: 'users', url: 'https://users.storefront.local/graphql' }, + { id: 's2', service: 'products', url: 'https://products.storefront.local/graphql' }, +]; + +/** pages/target.tsx: `boxed`, each service a card with a two-line header and its SDL inside. */ +export const Boxed = createPreview(() => ( +
+ ({ + value: schema.id, + label: ( +
+
{schema.service}
+
{schema.url}
+
+ ), + content: ( +
+            {'type Query {\n  me: User\n}'}
+          
+ ), + }))} + /> +
+)); + +/** + * alerts/alert-form.tsx, Advanced settings: `plain`, so no hairline under a lone disclosure; + * small, the chevron at the start, and the accent tone to call it out. + */ +export const SmallAccentDisclosure = createPreview(() => ( +
+ On filter, hold minutes., + }, + ]} + /> +
+)); + +const COLLECTIONS = [ + { id: 'c1', name: 'Onboarding', operations: ['GetViewer', 'ListProjects'] }, + { id: 'c2', name: 'Regression', operations: ['PublishSchema'] }, +]; + +/** + * laboratory collections: no chevron, folder icons in the label that swap on open, and a menu + * beside the trigger through `action`, which stays outside the trigger button. + */ +export const WithAction = createPreview(() => { + const [open, setOpen] = useState(['c1']); + return ( +
+ ({ + value: collection.id, + label: ( + + {open.includes(collection.id) ? ( + + ) : ( + + )} + {collection.name} + + ), + action: ( + + + + } + sections={[ + [{ label: 'Add operation', trailingIcon: Plus, onClick: () => {} }], + [ + { label: 'Edit', onClick: () => {} }, + { label: 'Delete', variant: 'destructiveAction', onClick: () => {} }, + ], + ]} + /> + ), + content: ( +
    + {collection.operations.map(name => ( +
  • {name}
  • + ))} +
+ ), + attrs: { 'data-cy': 'collection-item' }, + }))} + /> +
+ ); +}); + +/** A disabled item keeps its place and its label, and cannot open. */ +export const Disabled = createPreview(() => (
- - - - -

Used where the section needs emphasis.

-
-
- - - -

The standard neutral treatment.

-
-
-
+ None. }, + { + value: 'b', + label: ( + + Breaking changes + + + ), + content: Two, both approved., + disabled: true, + }, + ]} + />
)); + +export const Playground = createPreview({ + controls: controlsFor(Accordion, { + variant: { type: 'radio', options: ['list', 'boxed', 'plain'], default: 'list' }, + size: { type: 'radio', options: ['default', 'sm'], default: 'default' }, + chevron: { type: 'radio', options: ['end', 'start', 'none'], default: 'end' }, + tone: { type: 'radio', options: ['default', 'accent'], default: 'default' }, + multiple: { type: 'boolean', default: false }, + keepMounted: { type: 'boolean', default: false }, + }), + render: v => ( +
+ ({ + value: change.value, + label: change.label, + trailing: index === 0 ? 2 : undefined, + content: {change.body}, + }))} + /> +
+ ), +}); diff --git a/packages/web/app/src/components/base/accordion/accordion.spec.tsx b/packages/web/app/src/components/base/accordion/accordion.spec.tsx new file mode 100644 index 00000000000..615856354bb --- /dev/null +++ b/packages/web/app/src/components/base/accordion/accordion.spec.tsx @@ -0,0 +1,68 @@ +// @vitest-environment jsdom +import { fireEvent, render, screen } from '@testing-library/react'; +import { Accordion } from './accordion'; + +const ITEMS = [ + { value: 'a', label: 'Breaking', content:

Breaking panel

, attrs: { 'data-cy': 'row-a' } }, + { value: 'b', label: 'Safe', content:

Safe panel

}, + { value: 'c', label: 'Locked', content:

Locked panel

, disabled: true }, +]; + +describe('Accordion', () => { + it('opens one item at a time by default, and reports the open set', () => { + const onValueChange = vi.fn(); + render(); + expect(screen.queryByText('Breaking panel')).toBeNull(); + + fireEvent.click(screen.getByRole('button', { name: 'Breaking' })); + expect(screen.getByText('Breaking panel')).toBeTruthy(); + expect(onValueChange).toHaveBeenLastCalledWith(['a']); + + fireEvent.click(screen.getByRole('button', { name: 'Safe' })); + expect(screen.queryByText('Breaking panel')).toBeNull(); + expect(screen.getByText('Safe panel')).toBeTruthy(); + expect(onValueChange).toHaveBeenLastCalledWith(['b']); + }); + + it('keeps several open with multiple, and follows a controlled value', () => { + const { rerender } = render( + {}} />, + ); + expect(screen.getByText('Breaking panel')).toBeTruthy(); + expect(screen.getByText('Safe panel')).toBeTruthy(); + + rerender( {}} />); + expect(screen.queryByText('Breaking panel')).toBeNull(); + }); + + it('puts trailing inside the trigger and action beside it, and lands attrs on item and trigger', () => { + render( + 3 selected, + action: , + triggerAttrs: { 'data-cy': 'row-a-trigger' }, + }, + ]} + />, + ); + const trigger = screen.getByRole('button', { name: /Breaking/ }); + expect(trigger.textContent).toContain('3 selected'); + expect(trigger.getAttribute('data-cy')).toBe('row-a-trigger'); + const more = screen.getByRole('button', { name: 'More' }); + expect(trigger.contains(more)).toBe(false); + expect(document.querySelector('[data-cy="row-a"]')!.contains(more)).toBe(true); + }); + + it('will not open a disabled item', () => { + render(); + const locked = screen.getByRole('button', { name: 'Locked' }); + expect(locked.hasAttribute('disabled') || locked.getAttribute('aria-disabled') === 'true').toBe( + true, + ); + fireEvent.click(locked); + expect(screen.queryByText('Locked panel')).toBeNull(); + }); +}); diff --git a/packages/web/app/src/components/base/accordion/accordion.tsx b/packages/web/app/src/components/base/accordion/accordion.tsx index 27dfe0d69d8..9dc3e8fc5cb 100644 --- a/packages/web/app/src/components/base/accordion/accordion.tsx +++ b/packages/web/app/src/components/base/accordion/accordion.tsx @@ -1,51 +1,194 @@ import { type ReactNode } from 'react'; +import { cva } from 'class-variance-authority'; import { ChevronDown } from 'lucide-react'; +import { cn } from '@/lib/utils'; import { Accordion as BaseAccordion } from '@base-ui/react/accordion'; +import { focusRingQuiet } from '../shared-styles'; -export function Accordion({ - children, - defaultValue, -}: { - children: ReactNode; - defaultValue?: number[]; -}) { - return {children}; -} +export type AccordionItem = { + value: string; + /** Text, or a two-line header, badges, icons. */ + label: ReactNode; + /** At the far end of the trigger, before the chevron: a count, a status. */ + trailing?: ReactNode; + /** Beside the trigger, outside it: a menu button. A button cannot nest in a button. */ + action?: ReactNode; + content: ReactNode; + disabled?: boolean; + /** Test hooks and the like, landed on the item. */ + attrs?: Record; + /** Test hooks on the trigger button itself. */ + triggerAttrs?: Record; +}; -export function AccordionItem({ children, value }: { children: ReactNode; value: number }) { - return {children}; -} +export type AccordionVariant = 'list' | 'boxed' | 'plain'; +export type AccordionSize = 'default' | 'sm'; +export type AccordionChevron = 'end' | 'start' | 'none'; +export type AccordionTone = 'default' | 'accent'; -export function AccordionTrigger({ - label, - variant = 'default', -}: { - label: string; - variant?: 'default' | 'accent'; -}) { - const colorClass = - variant === 'accent' - ? 'text-accent hover:text-accent/80' - : 'text-neutral-12 hover:text-neutral-11'; +type AccordionProps = { + items: readonly AccordionItem[]; + value?: string[]; + defaultValue?: string[]; + onValueChange?: (value: string[]) => void; + /** More than one item open at once. */ + multiple?: boolean; + /** + * `list` draws a hairline under each item, so rows read as a list even one at a time; `boxed` + * gives each item a card border, spaced apart; `plain` draws nothing, for a lone disclosure. + */ + variant?: AccordionVariant; + size?: AccordionSize; + /** Where the chevron sits. `start` for a disclosure under a form; `none` when the label carries its own icon. */ + chevron?: AccordionChevron; + /** `accent` calls the trigger out: a disclosure that invites, like Advanced settings, rather than a section. */ + tone?: AccordionTone; + /** Closed panels stay in the DOM. */ + keepMounted?: boolean; + attrs?: Record; +}; - const chevronColor = variant === 'accent' ? 'text-accent' : 'text-neutral-8'; +const rootVariants = cva('', { + variants: { + variant: { + list: '', + boxed: 'flex flex-col gap-4', + plain: '', + }, + }, +}); - return ( - svg]:rotate-0`} - > - - {label} - - ); -} +const itemVariants = cva('', { + variants: { + variant: { + list: 'border-neutral-5 border-b', + boxed: 'border-neutral-5 rounded-md border', + plain: '', + }, + }, +}); -export function AccordionContent({ children }: { children: ReactNode }) { +const headerRowVariants = cva('flex items-center', { + variants: { + variant: { + list: '', + boxed: 'px-4', + plain: '', + }, + }, +}); + +const triggerVariants = cva( + cn( + 'group flex w-full items-center gap-2 text-left font-medium transition-colors', + focusRingQuiet, + 'focus-visible:-outline-offset-2', + 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + ), + { + variants: { + size: { + default: 'py-3 text-sm', + sm: 'py-2 text-xs', + }, + tone: { + default: 'text-neutral-12 hover:text-neutral-11', + accent: 'text-accent hover:text-accent/80', + }, + }, + }, +); + +const chevronVariants = cva('shrink-0 transition-transform duration-200', { + variants: { + size: { + default: 'size-4', + sm: 'size-3', + }, + tone: { + default: 'text-neutral-8', + accent: 'text-accent', + }, + chevron: { + end: 'ml-auto group-data-[panel-open]:rotate-180', + start: '-rotate-90 group-data-[panel-open]:rotate-0', + none: 'hidden', + }, + }, +}); + +// Base UI measures the panel into a variable, which is what lets the height animate. +const panelVariants = cva( + 'h-[var(--accordion-panel-height)] overflow-hidden text-sm transition-[height] duration-200 ease-out data-[ending-style]:h-0 data-[starting-style]:h-0', +); + +const panelInnerVariants = cva('', { + variants: { + variant: { + list: '', + boxed: 'px-4', + plain: '', + }, + size: { + default: 'pb-4', + sm: 'pb-2', + }, + }, +}); + +export function Accordion({ + items, + value, + defaultValue, + onValueChange, + multiple = false, + variant = 'list', + size = 'default', + chevron = 'end', + tone = 'default', + keepMounted, + attrs, +}: AccordionProps) { + const chevronIcon = ; return ( - -
{children}
-
+ onValueChange?.(next.map(String))} + multiple={multiple} + keepMounted={keepMounted} + className={rootVariants({ variant })} + {...attrs} + > + {items.map(item => ( + +
+ + + {chevron === 'start' ? chevronIcon : null} + {item.label} + {item.trailing != null ? ( + {item.trailing} + ) : null} + {chevron === 'end' ? chevronIcon : null} + + + {item.action != null ?
{item.action}
: null} +
+ +
{item.content}
+
+
+ ))} +
); } diff --git a/packages/web/app/src/components/organization/members/permission-selector.tsx b/packages/web/app/src/components/organization/members/permission-selector.tsx index 78c0df43606..ff7c012aa96 100644 --- a/packages/web/app/src/components/organization/members/permission-selector.tsx +++ b/packages/web/app/src/components/organization/members/permission-selector.tsx @@ -1,14 +1,9 @@ import { useMemo, useRef, useState } from 'react'; import { InfoIcon, TriangleAlert } from 'lucide-react'; +import { Accordion } from '@/components/base/accordion/accordion'; import { Popover } from '@/components/base/floating/popover/popover'; import { Select } from '@/components/base/floating/select/select'; import type { OnSurface } from '@/components/base/shared-styles'; -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from '@/components/ui/accordion'; import { Button } from '@/components/ui/button'; import { FragmentType, graphql, useFragment } from '@/gql'; import { cn } from '@/lib/utils'; @@ -96,184 +91,173 @@ export function PermissionSelector(props: PermissionSelectorProps) { return ( setOpenAccordions(values)} - > - {groups.map(group => { - return ( - - - {group.title}{' '} - - {group.selectedPermissionCount > 0 && ( - - {group.selectedPermissionCount} selected - - )} - - - - {group.permissions.map(permission => { - const needsDependency = - !!permission.dependsOnId && - !props.selectedPermissionIds.has(permission.dependsOnId); + onValueChange={setOpenAccordions} + items={groups.map(group => ({ + value: group.title, + label: group.title, + trailing: + group.selectedPermissionCount > 0 ? ( + {group.selectedPermissionCount} selected + ) : undefined, + content: ( +
+ {group.permissions.map(permission => { + const needsDependency = + !!permission.dependsOnId && + !props.selectedPermissionIds.has(permission.dependsOnId); - return ( -
+ return ( +
+
{ + if (ref) { + permissionRefs.current.set(permission.id, ref); + } + }} + >
{ - if (ref) { - permissionRefs.current.set(permission.id, ref); - } - }} > -
-
{permission.title}
-
{permission.description}
+
{permission.title}
+
{permission.description}
+
+ {permission.isAssignableByViewer === false ? ( +
+ + + + } + openOnHover + content={ +

+ Your membership has insufficient authority for assigning this + permission. +

+ } + /> +
+ ) : permission.warning && props.selectedPermissionIds.has(permission.id) ? ( +
+ + + + } + openOnHover + content={

{permission.warning}

} + />
- {permission.isAssignableByViewer === false ? ( + ) : ( + !!permission.dependsOnId && + permissionToGroupTitleMapping.has(permission.dependsOnId) && (
+ } openOnHover content={

- Your membership has insufficient authority for assigning this - permission. + This permission depends on another permission.{' '} +

} />
- ) : permission.warning && props.selectedPermissionIds.has(permission.id) ? ( -
- - - - } - openOnHover - content={ -

{permission.warning}

- } - /> -
- ) : ( - !!permission.dependsOnId && - permissionToGroupTitleMapping.has(permission.dependsOnId) && ( -
- - - - } - openOnHover - content={ -

- This permission depends on another permission.{' '} - -

- } - /> -
- ) - )} - { + const dependents = dependencyGraph.get(permission.id) ?? []; + if (value === 'allow') { + props.onSelectedPermissionsChange( + new Set([...props.selectedPermissionIds, permission.id]), + ); + } else if (value === 'not-selected') { + const selectedPermissionIds = new Set(props.selectedPermissionIds); + selectedPermissionIds.delete(permission.id); + for (const dependent of dependents) { + selectedPermissionIds.delete(dependent); + } + props.onSelectedPermissionsChange(selectedPermissionIds); + } + setFocusedPermission(null); + }} + width="sm" + />
- ); - })} - - - ); - })} - + {focusedPermission === permission.id && ( +
+ )} +
+ ); + })} +
+ ), + }))} + /> ); } diff --git a/packages/web/app/src/components/organization/members/selected-permission-overview.tsx b/packages/web/app/src/components/organization/members/selected-permission-overview.tsx index c9e1e7c7b4f..f93b315fcdb 100644 --- a/packages/web/app/src/components/organization/members/selected-permission-overview.tsx +++ b/packages/web/app/src/components/organization/members/selected-permission-overview.tsx @@ -1,11 +1,6 @@ import { useMemo } from 'react'; +import { Accordion } from '@/components/base/accordion/accordion'; import { PermissionTable } from '@/components/organization/permission-table'; -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from '@/components/ui/accordion'; import { FragmentType, graphql, useFragment } from '@/gql'; import { PermissionLevelType } from '@/gql/graphql'; import { ResultOf } from '@graphql-typed-document-node/core'; @@ -149,43 +144,43 @@ function PermissionLevelGroup(props: { return ( 0 && props.isExpanded ? props.title : undefined} - collapsible - > - - - {props.title} - {totalAllowedCount} allowed - - - {filteredGroups.map(group => - props.showOnlyAllowedPermissions && group.totalAllowedCount === 0 ? null : ( -
- { - const granted = - props.activePermissionIds.has(permission.id) || permission.isReadOnly; - if (props.showOnlyAllowedPermissions && !granted) { - return []; - } - return [ - { - id: permission.id, - title: permission.title, - granted, - warning: permission.warning, - }, - ]; - })} - /> -
- ), - )} - {props.additionalContent} -
-
-
+ defaultValue={totalAllowedCount > 0 && props.isExpanded ? [props.title] : undefined} + items={[ + { + value: props.title, + label: props.title, + trailing: {totalAllowedCount} allowed, + content: ( +
+ {filteredGroups.map(group => + props.showOnlyAllowedPermissions && group.totalAllowedCount === 0 ? null : ( +
+ { + const granted = + props.activePermissionIds.has(permission.id) || permission.isReadOnly; + if (props.showOnlyAllowedPermissions && !granted) { + return []; + } + return [ + { + id: permission.id, + title: permission.title, + granted, + warning: permission.warning, + }, + ]; + })} + /> +
+ ), + )} + {props.additionalContent} +
+ ), + }, + ]} + /> ); } diff --git a/packages/web/app/src/components/organization/settings/access-tokens/permission-detail-view.tsx b/packages/web/app/src/components/organization/settings/access-tokens/permission-detail-view.tsx index 34337037476..a954eb1e320 100644 --- a/packages/web/app/src/components/organization/settings/access-tokens/permission-detail-view.tsx +++ b/packages/web/app/src/components/organization/settings/access-tokens/permission-detail-view.tsx @@ -1,6 +1,6 @@ +import { Accordion } from '@/components/base/accordion/accordion'; import { Badge } from '@/components/base/badge/badge'; import { PermissionTable } from '@/components/organization/permission-table'; -import * as Accordion from '@/components/ui/accordion'; import { FragmentType, graphql, useFragment } from '@/gql'; import { permissionLevelToResourceName } from './shared-helpers'; @@ -42,52 +42,52 @@ export function PermissionDetailView(props: { ); return ( - 0 ? group.title : undefined} - collapsible - > - - - {group.title} - {totalAllowedCount} allowed - - - {group.resolvedPermissionGroups.map(group => ( -
- ({ - id: permission.permission.id, - title: permission.permission.title, - granted: permission.isGranted, - warning: permission.permission.warning, - }))} - /> + 0 ? [group.title] : undefined} + items={[ + { + value: group.title, + label: group.title, + trailing: {totalAllowedCount} allowed, + content: ( +
+ {group.resolvedPermissionGroups.map(group => ( +
+ ({ + id: permission.permission.id, + title: permission.permission.title, + granted: permission.isGranted, + warning: permission.permission.warning, + }))} + /> +
+ ))} +
+ {group.resolvedResourceIds == null ? ( +

+ Not granted on any {permissionLevelToResourceName(group.level).slice(0, -1)}. +

+ ) : ( + <> +

+ Granted on {permissionLevelToResourceName(group.level)}: +

+
    + {group.resolvedResourceIds.map(id => ( +
  • + +
  • + ))} +
+ + )} +
- ))} -
- {group.resolvedResourceIds == null ? ( -

- Not granted on any {permissionLevelToResourceName(group.level).slice(0, -1)}. -

- ) : ( - <> -

- Granted on {permissionLevelToResourceName(group.level)}: -

-
    - {group.resolvedResourceIds.map(id => ( -
  • - -
  • - ))} -
- - )} -
- - - + ), + }, + ]} + /> ); } diff --git a/packages/web/app/src/components/target/alerts/alert-form.tsx b/packages/web/app/src/components/target/alerts/alert-form.tsx index f6eb7fc719c..2b3e31ef884 100644 --- a/packages/web/app/src/components/target/alerts/alert-form.tsx +++ b/packages/web/app/src/components/target/alerts/alert-form.tsx @@ -4,12 +4,7 @@ import { Plus, X } from 'lucide-react'; import { useFieldArray, useForm } from 'react-hook-form'; import { useMutation, useQuery } from 'urql'; import { z } from 'zod'; -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from '@/components/base/accordion/accordion'; +import { Accordion } from '@/components/base/accordion/accordion'; import { Button } from '@/components/base/button/button'; import { Card } from '@/components/base/card/card'; import { Select } from '@/components/base/floating/select/select'; @@ -870,77 +865,87 @@ export function AlertForm(props: AlertFormProps) { clipToCurrentWindow /> - - - - -
- { - const isLoading = savedFiltersQuery.fetching; - const savedFilterOptions = [ - { - value: '', - label: isLoading - ? 'Loading filters...' - : 'No filter (all operations)', - }, - ...(savedFiltersQuery.data?.target?.savedFilters?.edges?.map(edge => ({ - value: edge.node.id, - label: edge.node.name, - })) ?? []), - ]; - - return ( + + { + const isLoading = savedFiltersQuery.fetching; + const savedFilterOptions = [ + { + value: '', + label: isLoading + ? 'Loading filters...' + : 'No filter (all operations)', + }, + ...(savedFiltersQuery.data?.target?.savedFilters?.edges?.map( + edge => ({ + value: edge.node.id, + label: edge.node.name, + }), + ) ?? []), + ]; + + return ( + + + + 10} - onSurface="raised" - /> + - + + Wait for the condition to exist for{' '} + + {field.value || '0'} + {' '} + minutes before firing. Helps prevent false alarms from brief + spikes. Leave at 0 to fire as soon as the condition holds for + two consecutive evaluations (recommended for alert ranges + greater than 1 day). + + } + /> - ); - }} - /> - ( - - - - - - - Wait for the condition to exist for{' '} - - {field.value || '0'} - {' '} - minutes before firing. Helps prevent false alarms from brief - spikes. Leave at 0 to fire as soon as the condition holds for two - consecutive evaluations (recommended for alert ranges greater than - 1 day). - - } - /> - - )} - /> -
-
-
-
+ )} + /> +
+ ), + }, + ]} + />
diff --git a/packages/web/app/src/components/target/history/errors-and-changes.tsx b/packages/web/app/src/components/target/history/errors-and-changes.tsx index 27ddeb47715..969448fb983 100644 --- a/packages/web/app/src/components/target/history/errors-and-changes.tsx +++ b/packages/web/app/src/components/target/history/errors-and-changes.tsx @@ -3,6 +3,7 @@ import { clsx } from 'clsx'; import { format } from 'date-fns'; import { BoxIcon, CheckIcon } from 'lucide-react'; import reactStringReplace from 'react-string-replace'; +import { Accordion } from '@/components/base/accordion/accordion'; import { DataTable } from '@/components/base/data-table/data-table'; import { DataTableCell } from '@/components/base/data-table/data-table-cell'; import { Popover } from '@/components/base/floating/popover/popover'; @@ -10,13 +11,6 @@ import { Tooltip } from '@/components/base/floating/tooltip/tooltip'; import { ScrollArea } from '@/components/base/scroll-area/scroll-area'; import { Label, Label as LegacyLabel } from '@/components/common'; import { CompositionErrorsPopover } from '@/components/target/history/composition-errors-popover'; -import { - Accordion, - AccordionContent, - AccordionHeader, - AccordionItem, - AccordionTrigger, -} from '@/components/ui/accordion'; import { Button } from '@/components/ui/button'; import { Heading } from '@/components/ui/heading'; import { PulseIcon } from '@/components/ui/icon'; @@ -230,10 +224,11 @@ function ChangeItem( ); return ( - - - - +
- - - - {change.approval && ( - - )} - {'usageStatistics' in change && change.usageStatistics && metadata ? ( -
-

- Affected Operations (based on usage) -

-
- - Top 10 operations and clients affected by this change based on usage data. - - {metadata && ( - - See{' '} - {metadata.settings.targets.map((target, index, arr) => ( - <> - {!target.target ? ( - - ) : ( - - {target.slug} - - )} - {index === arr.length - 1 - ? null - : index === arr.length - 2 - ? ' and ' - : ', '} - - ))}{' '} - target insights for live usage data. - - )} -
- - {'affectedAppDeployments' in change && - change.affectedAppDeployments?.edges?.length ? ( -
- + {change.approval && ( + + )} + {'usageStatistics' in change && change.usageStatistics && metadata ? ( +
+

+ Affected Operations (based on usage) +

+
+ + Top 10 operations and clients affected by this change based on usage data. + + {metadata && ( + + See{' '} + {metadata.settings.targets.map((target, index, arr) => ( + <> + {!target.target ? ( + + ) : ( + + {target.slug} + + )} + {index === arr.length - 1 + ? null + : index === arr.length - 2 + ? ' and ' + : ', '} + + ))}{' '} + target insights for live usage data. + + )} +
+ + {'affectedAppDeployments' in change && + change.affectedAppDeployments?.edges?.length ? ( +
+ +
+ ) : null}
- ) : null} + ) : 'affectedAppDeployments' in change && + change.affectedAppDeployments?.edges?.length ? ( + + ) : ( + <> + {change.severityReason ?? + `No details available for this ${ + change.severityLevel === SeverityLevelType.Breaking ? 'breaking ' : '' + }change.`} + + )}
- ) : 'affectedAppDeployments' in change && change.affectedAppDeployments?.edges?.length ? ( - - ) : ( - <> - {change.severityReason ?? - `No details available for this ${ - change.severityLevel === SeverityLevelType.Breaking ? 'breaking ' : '' - }change.`} - - )} - - - + ), + }, + ]} + /> ); } diff --git a/packages/web/app/src/components/target/proposals/change-detail.tsx b/packages/web/app/src/components/target/proposals/change-detail.tsx index fefb63431c7..dde72ca3ead 100644 --- a/packages/web/app/src/components/target/proposals/change-detail.tsx +++ b/packages/web/app/src/components/target/proposals/change-detail.tsx @@ -1,13 +1,7 @@ import { ReactNode } from 'react'; +import { Accordion } from '@/components/base/accordion/accordion'; import { Popover } from '@/components/base/floating/popover/popover'; -import { - AccordionContent, - AccordionHeader, - AccordionItem, - AccordionTrigger, -} from '@/components/ui/accordion'; import { Button } from '@/components/ui/button'; -import { Accordion } from '@/components/v2'; import { MergeStatus } from '@/pages/target-proposal-details'; import type { Change } from '@graphql-inspector/core'; import { ComponentNoneIcon, ExclamationTriangleIcon, InfoCircledIcon } from '@radix-ui/react-icons'; @@ -19,21 +13,20 @@ export function ProposalChangeDetail(props: { icon?: ReactNode; }) { return ( - - - - -
+
{labelize(props.change.message)}
{props.icon}
-
-
- - {props.error?.message ?? <>No details available for this change.} - -
-
+ ), + content: props.error?.message ?? 'No details available for this change.', + }, + ]} + /> ); } diff --git a/packages/web/app/src/components/target/settings/registry-access-token.tsx b/packages/web/app/src/components/target/settings/registry-access-token.tsx index 73dfd56828d..9b52b411f56 100644 --- a/packages/web/app/src/components/target/settings/registry-access-token.tsx +++ b/packages/web/app/src/components/target/settings/registry-access-token.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useForm, UseFormReturn } from 'react-hook-form'; import { useMutation } from 'urql'; import { z } from 'zod'; +import { Accordion } from '@/components/base/accordion/accordion'; import { Input } from '@/components/base/input/input'; import { Dialog } from '@/components/base/overlays/dialog/dialog'; import { useToast } from '@/components/base/toast/toast'; @@ -10,7 +11,6 @@ import { Button } from '@/components/ui/button'; import { Callout } from '@/components/ui/callout'; import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form'; import { InputCopy } from '@/components/ui/input-copy'; -import { Accordion } from '@/components/v2/accordion'; import { graphql } from '@/gql'; import { TargetAccessScope } from '@/gql/graphql'; import { RegistryAccessScope } from '@/lib/access/common'; @@ -228,31 +228,36 @@ export function GenerateTokenContent(props: { )} /> - - - Registry & Usage - - true} - onChange={value => { - if (value === 'no-access') { - props.setSelectedScope('no-access'); - return; - } - props.setSelectedScope(value); - }} - possibleScope={Object.values(RegistryAccessScope.mapping)} - initialScope={props.selectedScope} - selectedScope={props.selectedScope} - /> - - - + true} + onChange={value => { + if (value === 'no-access') { + props.setSelectedScope('no-access'); + return; + } + props.setSelectedScope(value); + }} + possibleScope={Object.values(RegistryAccessScope.mapping)} + initialScope={props.selectedScope} + selectedScope={props.selectedScope} + /> + ), + }, + ]} + />
- } - sections={[ - [ - { - label: 'Add operation', - trailingIcon: PlusIcon, - onClick: () => void addOperation(collection.id), - disabled: createOperationState.fetching, - attrs: { 'data-cy': 'add-operation-to-collection' }, - }, - ], - [ - { - label: 'Edit', - onClick: () => { - setCollectionId(collection.id); - toggleCollectionModal(); - }, - attrs: { 'data-cy': 'edit-collection' }, - }, - { - label: 'Delete', - variant: 'destructiveAction', - onClick: () => { - setCollectionId(collection.id); - toggleDeleteCollectionModalOpen(); - }, - attrs: { 'data-cy': 'delete-collection' }, - }, - ], - ]} - /> + const renderedCollections = collections.map(collection => ({ + value: collection.id, + label: ( + + {accordionValue.includes(collection.id) ? ( + + ) : ( + )} - - + {collection.name} + + ), + attrs: { 'data-cy': 'collection-item' }, + triggerAttrs: { 'data-cy': 'collection-item-trigger' }, + action: shouldShowMenu ? ( + + + + } + sections={[ + [ + { + label: 'Add operation', + trailingIcon: PlusIcon, + onClick: () => void addOperation(collection.id), + disabled: createOperationState.fetching, + attrs: { 'data-cy': 'add-operation-to-collection' }, + }, + ], + [ + { + label: 'Edit', + onClick: () => { + setCollectionId(collection.id); + toggleCollectionModal(); + }, + attrs: { 'data-cy': 'edit-collection' }, + }, + { + label: 'Delete', + variant: 'destructiveAction', + onClick: () => { + setCollectionId(collection.id); + toggleDeleteCollectionModalOpen(); + }, + attrs: { 'data-cy': 'delete-collection' }, + }, + ], + ]} + /> + ) : undefined, + content: ( +
{collection.operations.edges.length ? ( collection.operations.edges.map(({ node }) => (
@@ -433,9 +434,9 @@ export function Content() { Add Operation )} - - - )); +
+ ), + })); const target = query.data?.target; @@ -476,13 +477,14 @@ export function Content() {
) : collections.length ? ( - {renderedCollections} - + items={renderedCollections} + attrs={{ id: COLLECTIONS_ID }} + /> ) : (
diff --git a/packages/web/app/src/pages/target.tsx b/packages/web/app/src/pages/target.tsx index 8be88327e65..460e7e05573 100644 --- a/packages/web/app/src/pages/target.tsx +++ b/packages/web/app/src/pages/target.tsx @@ -1,6 +1,8 @@ import { ReactElement } from 'react'; import { XIcon } from 'lucide-react'; import { useQuery } from 'urql'; +import { Accordion } from '@/components/base/accordion/accordion'; +import { Card } from '@/components/base/card/card'; import { Select } from '@/components/base/floating/select/select'; import { Page, TargetLayout } from '@/components/layouts/target'; import { Button } from '@/components/ui/button'; @@ -8,7 +10,6 @@ import { EmptyList, noSchema, NoSchemaVersion } from '@/components/ui/empty-list import { Meta } from '@/components/ui/meta'; import { Subtitle, Title } from '@/components/ui/page'; import { QueryError } from '@/components/ui/query-error'; -import { Accordion } from '@/components/v2/accordion'; import { GraphQLBlock, GraphQLHighlight } from '@/components/v2/graphql-block'; import { DocumentType, FragmentType, graphql, useFragment } from '@/gql'; import { ProjectType } from '@/gql/graphql'; @@ -34,23 +35,23 @@ function isCompositeSchema( return schema.__typename === 'CompositeSchema'; } -function SchemaBlock({ schema }: { schema: CompositeSchema }) { +/** The service's name, an anchor for deep links, and its URL. */ +function serviceHeader(schema: CompositeSchema) { return ( - - -
-
- {schema.service ?? 'SDL'} -
- {schema.url ?
{schema.url}
: null} -
-
- -
- -
-
-
+
+
+ {schema.service ?? 'SDL'} +
+ {schema.url ?
{schema.url}
: null} +
+ ); +} + +function serviceSchema(schema: CompositeSchema) { + return ( +
+ +
); } @@ -71,11 +72,14 @@ function Schemas(props: { schemas?: readonly CompositeSchema[]; schema?: SingleS if (props.schemas.length > 1) { return ( - - {props.schemas.map(schema => ( - - ))} - + ({ + value: schema.id, + label: serviceHeader(schema), + content: serviceSchema(schema), + }))} + /> ); } @@ -90,10 +94,12 @@ function Schemas(props: { schemas?: readonly CompositeSchema[]; schema?: SingleS ); } + // One service has nothing to collapse, so it is a card, not an accordion locked open. return ( - - - + + {serviceHeader(schema)} + {serviceSchema(schema)} + ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b1cd19b3a6..5337725a7f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1769,7 +1769,7 @@ importers: version: 1.1.0(pino@10.3.0) '@graphql-hive/plugin-opentelemetry': specifier: 1.4.35 - version: 1.4.35(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0)(ws@8.21.0) + version: 1.4.35(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0) '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 @@ -2139,9 +2139,6 @@ importers: '@pierre/diffs': specifier: 1.3.5 version: 1.3.5(@shikijs/themes@3.23.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-accordion': - specifier: 1.2.2 - version: 1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-icons': specifier: 1.3.2 version: 1.3.2(react@18.3.1) @@ -7453,9 +7450,6 @@ packages: '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} - '@radix-ui/primitive@1.1.1': - resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} - '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} @@ -7485,19 +7479,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-accordion@1.2.2': - resolution: {integrity: sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-alert-dialog@1.1.15': resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} peerDependencies: @@ -7576,32 +7557,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.2': - resolution: {integrity: sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-collection@1.1.1': - resolution: {integrity: sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-collection@1.1.7': resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: @@ -7646,15 +7601,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-context@1.1.1': - resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-context@1.1.2': resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: @@ -7677,15 +7623,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.0': - resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-direction@1.1.1': resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: @@ -7774,15 +7711,6 @@ packages: peerDependencies: react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc - '@radix-ui/react-id@1.1.0': - resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: @@ -7935,19 +7863,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.2': - resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-presence@1.1.5': resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: @@ -8222,15 +8137,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-use-callback-ref@1.1.0': - resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-use-callback-ref@1.1.1': resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: @@ -8240,15 +8146,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-controllable-state@1.1.0': - resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-use-controllable-state@1.2.2': resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: @@ -8285,15 +8182,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.0': - resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-use-layout-effect@1.1.1': resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: @@ -21901,6 +21789,57 @@ snapshots: - pino - winston + '@graphql-hive/gateway-runtime@2.9.10(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0)': + dependencies: + '@envelop/core': 5.5.1 + '@envelop/disable-introspection': 9.0.0(@envelop/core@5.5.1)(graphql@16.14.2) + '@envelop/generic-auth': 11.0.0(@envelop/core@5.5.1)(graphql@16.14.2) + '@envelop/instrumentation': 1.0.0 + '@graphql-hive/core': 0.21.1(graphql@16.14.2)(pino@10.3.0) + '@graphql-hive/logger': 1.1.1(pino@10.3.0) + '@graphql-hive/pubsub': 2.1.1(ioredis@5.10.1) + '@graphql-hive/signal': 2.0.0 + '@graphql-hive/yoga': 0.48.2(graphql-yoga@5.22.0(graphql@16.14.2))(graphql@16.14.2)(pino@10.3.0) + '@graphql-mesh/cross-helpers': 0.4.14(graphql@16.14.2) + '@graphql-mesh/fusion-runtime': 1.10.10(@types/node@25.5.0)(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0) + '@graphql-mesh/hmac-upstream-signature': 2.0.12(graphql@16.14.2)(ioredis@5.10.1) + '@graphql-mesh/plugin-response-cache': 0.104.43(graphql@16.14.2)(ioredis@5.10.1) + '@graphql-mesh/transport-common': 1.0.17(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0) + '@graphql-mesh/types': 0.104.28(graphql@16.14.2)(ioredis@5.10.1) + '@graphql-mesh/utils': 0.104.36(graphql@16.14.2)(ioredis@5.10.1) + '@graphql-tools/batch-delegate': 10.0.26(graphql@16.14.2) + '@graphql-tools/delegate': 12.0.19(graphql@16.14.2) + '@graphql-tools/executor-common': 1.0.6(graphql@16.14.2) + '@graphql-tools/executor-http': 3.3.0(@types/node@25.5.0)(graphql@16.14.2) + '@graphql-tools/federation': 4.4.8(@types/node@25.5.0)(graphql@16.14.2) + '@graphql-tools/stitch': 10.2.1(graphql@16.14.2) + '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/wrap': 11.1.18(graphql@16.14.2) + '@graphql-yoga/plugin-apollo-usage-report': 0.16.0(@envelop/core@5.5.1)(graphql-yoga@5.22.0(graphql@16.14.2))(graphql@16.14.2) + '@graphql-yoga/plugin-csrf-prevention': 3.16.2(graphql-yoga@5.22.0(graphql@16.14.2)) + '@graphql-yoga/plugin-defer-stream': 3.16.2(graphql-yoga@5.22.0(graphql@16.14.2))(graphql@16.14.2) + '@graphql-yoga/plugin-persisted-operations': 3.16.2(graphql-yoga@5.22.0(graphql@16.14.2))(graphql@16.14.2) + '@types/node': 25.5.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + '@whatwg-node/server': 0.11.0 + '@whatwg-node/server-plugin-cookies': 1.0.7(@whatwg-node/server@0.11.0) + graphql: 16.14.2 + graphql-ws: 6.0.6(graphql@16.14.2)(ws@8.21.0) + graphql-yoga: 5.22.0(graphql@16.14.2) + tslib: 2.8.1 + transitivePeerDependencies: + - '@fastify/websocket' + - '@logtape/logtape' + - '@nats-io/nats-core' + - crossws + - ioredis + - pino + - uWebSockets.js + - winston + - ws + '@graphql-hive/gateway-runtime@2.9.10(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0)(ws@8.21.0)': dependencies: '@envelop/core': 5.5.1 @@ -21952,6 +21891,57 @@ snapshots: - winston - ws + '@graphql-hive/gateway-runtime@2.9.10(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0)': + dependencies: + '@envelop/core': 5.5.1 + '@envelop/disable-introspection': 9.0.0(@envelop/core@5.5.1)(graphql@16.9.0) + '@envelop/generic-auth': 11.0.0(@envelop/core@5.5.1)(graphql@16.9.0) + '@envelop/instrumentation': 1.0.0 + '@graphql-hive/core': 0.21.1(graphql@16.9.0)(pino@10.3.0) + '@graphql-hive/logger': 1.1.1(pino@10.3.0) + '@graphql-hive/pubsub': 2.1.1(ioredis@5.10.1) + '@graphql-hive/signal': 2.0.0 + '@graphql-hive/yoga': 0.48.2(graphql-yoga@5.22.0(graphql@16.9.0))(graphql@16.9.0)(pino@10.3.0) + '@graphql-mesh/cross-helpers': 0.4.14(graphql@16.9.0) + '@graphql-mesh/fusion-runtime': 1.10.10(@types/node@25.5.0)(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0) + '@graphql-mesh/hmac-upstream-signature': 2.0.12(graphql@16.9.0)(ioredis@5.10.1) + '@graphql-mesh/plugin-response-cache': 0.104.43(graphql@16.9.0)(ioredis@5.10.1) + '@graphql-mesh/transport-common': 1.0.17(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0) + '@graphql-mesh/types': 0.104.28(graphql@16.9.0)(ioredis@5.10.1) + '@graphql-mesh/utils': 0.104.36(graphql@16.9.0)(ioredis@5.10.1) + '@graphql-tools/batch-delegate': 10.0.26(graphql@16.9.0) + '@graphql-tools/delegate': 12.0.19(graphql@16.9.0) + '@graphql-tools/executor-common': 1.0.6(graphql@16.9.0) + '@graphql-tools/executor-http': 3.3.0(@types/node@25.5.0)(graphql@16.9.0) + '@graphql-tools/federation': 4.4.8(@types/node@25.5.0)(graphql@16.9.0) + '@graphql-tools/stitch': 10.2.1(graphql@16.9.0) + '@graphql-tools/utils': 11.1.0(graphql@16.9.0) + '@graphql-tools/wrap': 11.1.18(graphql@16.9.0) + '@graphql-yoga/plugin-apollo-usage-report': 0.16.0(@envelop/core@5.5.1)(graphql-yoga@5.22.0(graphql@16.9.0))(graphql@16.9.0) + '@graphql-yoga/plugin-csrf-prevention': 3.16.2(graphql-yoga@5.22.0(graphql@16.9.0)) + '@graphql-yoga/plugin-defer-stream': 3.16.2(graphql-yoga@5.22.0(graphql@16.9.0))(graphql@16.9.0) + '@graphql-yoga/plugin-persisted-operations': 3.16.2(graphql-yoga@5.22.0(graphql@16.9.0))(graphql@16.9.0) + '@types/node': 25.5.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + '@whatwg-node/server': 0.11.0 + '@whatwg-node/server-plugin-cookies': 1.0.7(@whatwg-node/server@0.11.0) + graphql: 16.9.0 + graphql-ws: 6.0.6(graphql@16.9.0)(ws@8.21.0) + graphql-yoga: 5.22.0(graphql@16.9.0) + tslib: 2.8.1 + transitivePeerDependencies: + - '@fastify/websocket' + - '@logtape/logtape' + - '@nats-io/nats-core' + - crossws + - ioredis + - pino + - uWebSockets.js + - winston + - ws + '@graphql-hive/gateway-runtime@2.9.10(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0)(ws@8.21.3)': dependencies: '@envelop/core': 5.5.1 @@ -22143,6 +22133,44 @@ snapshots: - pino - winston + '@graphql-hive/plugin-opentelemetry@1.4.35(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0)': + dependencies: + '@graphql-hive/core': 0.21.1(graphql@16.14.2)(pino@10.3.0) + '@graphql-hive/gateway-runtime': 2.9.10(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0) + '@graphql-hive/logger': 1.1.1(pino@10.3.0) + '@graphql-mesh/cross-helpers': 0.4.14(graphql@16.14.2) + '@graphql-mesh/transport-common': 1.0.17(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0) + '@graphql-mesh/types': 0.104.28(graphql@16.14.2)(ioredis@5.10.1) + '@graphql-mesh/utils': 0.104.36(graphql@16.14.2)(ioredis@5.10.1) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/auto-instrumentations-node': 0.77.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)) + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-grpc': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-node': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.14.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@fastify/websocket' + - '@logtape/logtape' + - '@nats-io/nats-core' + - crossws + - ioredis + - pino + - supports-color + - uWebSockets.js + - winston + - ws + '@graphql-hive/plugin-opentelemetry@1.4.35(graphql@16.14.2)(ioredis@5.10.1)(pino@10.3.0)(ws@8.21.0)': dependencies: '@graphql-hive/core': 0.21.1(graphql@16.14.2)(pino@10.3.0) @@ -22184,7 +22212,7 @@ snapshots: '@graphql-hive/plugin-opentelemetry@1.4.35(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0)': dependencies: '@graphql-hive/core': 0.21.1(graphql@16.9.0)(pino@10.3.0) - '@graphql-hive/gateway-runtime': 2.9.10(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0)(ws@8.21.3) + '@graphql-hive/gateway-runtime': 2.9.10(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0) '@graphql-hive/logger': 1.1.1(pino@10.3.0) '@graphql-mesh/cross-helpers': 0.4.14(graphql@16.9.0) '@graphql-mesh/transport-common': 1.0.17(graphql@16.9.0)(ioredis@5.10.1)(pino@10.3.0) @@ -27544,8 +27572,6 @@ snapshots: '@radix-ui/number@1.1.1': {} - '@radix-ui/primitive@1.1.1': {} - '@radix-ui/primitive@1.1.3': {} '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -27574,23 +27600,6 @@ snapshots: '@types/react': 18.3.18 '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-accordion@1.2.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collapsible': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-collection': 1.1.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -27668,34 +27677,6 @@ snapshots: '@types/react': 18.3.18 '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-collapsible@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - - '@radix-ui/react-collection@1.1.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.18)(react@18.3.1) @@ -27734,12 +27715,6 @@ snapshots: '@types/react': 18.3.18 '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-context@1.1.1(@types/react@18.3.18)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.18 - '@radix-ui/react-context@1.1.2(@types/react@18.3.18)(react@18.3.1)': dependencies: react: 18.3.1 @@ -27768,12 +27743,6 @@ snapshots: '@types/react': 18.3.18 '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-direction@1.1.0(@types/react@18.3.18)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.18 - '@radix-ui/react-direction@1.1.1(@types/react@18.3.18)(react@18.3.1)': dependencies: react: 18.3.1 @@ -27860,13 +27829,6 @@ snapshots: dependencies: react: 18.3.1 - '@radix-ui/react-id@1.1.0(@types/react@18.3.18)(react@18.3.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.18 - '@radix-ui/react-id@1.1.1(@types/react@18.3.18)(react@18.3.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.18)(react@18.3.1) @@ -28054,16 +28016,6 @@ snapshots: '@types/react': 18.3.18 '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-presence@1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.18)(react@18.3.1) @@ -28362,25 +28314,12 @@ snapshots: '@types/react': 18.3.18 '@types/react-dom': 18.3.5(@types/react@18.3.18) - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.18)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.18 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.18)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 18.3.18 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.18)(react@18.3.1)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.18 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.18)(react@18.3.1)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.18)(react@18.3.1) @@ -28410,12 +28349,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.18 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.18)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.18 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.18)(react@18.3.1)': dependencies: react: 18.3.1