Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/open-heads-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/ui': patch
---

Export experimental mosaic components.
4 changes: 2 additions & 2 deletions packages/swingset/src/components/Composition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export interface CompositionPiece {
}

// Mosaic layers, high → low. Drives the order the composition groups render in.
// Plural to match the sidebar group names.
const LAYER_ORDER = ['AIO', 'Panels', 'Sections', 'Blocks', 'Components', 'Primitives'];
// Matches the sidebar group names.
const LAYER_ORDER = ['Organization', 'Blocks', 'Components', 'Primitives'];

function layerRank(layer: string): number {
const i = LAYER_ORDER.indexOf(layer);
Expand Down
12 changes: 4 additions & 8 deletions packages/swingset/src/components/DocsViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ import { ViewSource } from './ViewSource';
// MDX docs keyed by `group` slug → `component` slug. Group-aware so identically-named
// entries (the headless `Dialog` primitive vs. the styled `Dialog` component) stay distinct.
const docModules: Record<string, Record<string, React.ComponentType>> = {
aio: {
organization: {
'organization-profile': dynamic(() => import('../stories/organization-profile.mdx')),
},
panels: {
'organization-profile-general': dynamic(() => import('../stories/organization-profile-general.mdx')),
},
sections: {
'leave-organization': dynamic(() => import('../stories/leave-organization.mdx')),
'delete-organization': dynamic(() => import('../stories/delete-organization.mdx')),
'organization-profile-general-panel': dynamic(() => import('../stories/organization-profile-general-panel.mdx')),
'organization-profile-leave-section': dynamic(() => import('../stories/organization-profile-leave-section.mdx')),
'organization-profile-delete-section': dynamic(() => import('../stories/organization-profile-delete-section.mdx')),
},
blocks: {
destructive: dynamic(() => import('../stories/destructive.mdx')),
Expand Down
11 changes: 7 additions & 4 deletions packages/swingset/src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,19 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<SidebarMenu>
{components.map(({ mod, componentSlug }) => {
const href = `/${groupSlug}/${componentSlug}`;
// Hooks (e.g. `useDataTable`) are called, not rendered — show `useX()` rather
// than JSX `<useX />`. Everything else is a component.
const isHook = /^use[A-Z]/.test(mod.meta.title);
const usage = isHook ? `${mod.meta.title}()` : `<${mod.meta.title} />`;
return (
<SidebarMenuItem key={mod.meta.title}>
<SidebarMenuButton
className='h-auto justify-between py-1 text-xs leading-relaxed'
className='h-auto items-start py-1 text-xs leading-relaxed'
isActive={pathname === href}
render={<Link href={href} />}
>
<span className='truncate'>{mod.meta.label ?? mod.meta.title}</span>
<span className='text-sidebar-foreground/50 shrink-0 font-mono text-[10px] leading-none'>
{`<${mod.meta.title} />`}
<span className='whitespace-normal! break-all font-mono text-[10px] leading-relaxed'>
{usage}
</span>
</SidebarMenuButton>
</SidebarMenuItem>
Expand Down
48 changes: 26 additions & 22 deletions packages/swingset/src/lib/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import {
meta as cardComponentMeta,
} from '../stories/card.component.stories';
import { meta as collapsibleMeta } from '../stories/collapsible.stories';
import {
Default as DeleteOrganizationDefault,
meta as deleteOrganizationMeta,
} from '../stories/delete-organization.stories';
import { Default as DestructiveDefault, meta as destructiveMeta } from '../stories/destructive.stories';
import { Default as DialogDefault, meta as dialogComponentMeta } from '../stories/dialog.component.stories';
import { meta as dialogMeta } from '../stories/dialog.stories';
Expand All @@ -35,19 +31,23 @@ import {
meta as inputMeta,
Sizes as InputSizes,
} from '../stories/input.stories';
import {
Default as LeaveOrganizationDefault,
meta as leaveOrganizationMeta,
} from '../stories/leave-organization.stories';
import { meta as menuMeta } from '../stories/menu.stories';
import {
Default as OrganizationProfileDefault,
meta as organizationProfileMeta,
} from '../stories/organization-profile.stories';
import {
Default as OrganizationProfileGeneralDefault,
meta as organizationProfileGeneralMeta,
} from '../stories/organization-profile-general.stories';
Default as OrganizationProfileDeleteSectionDefault,
meta as organizationProfileDeleteSectionMeta,
} from '../stories/organization-profile-delete-section.stories';
import {
Default as OrganizationProfileGeneralPanelDefault,
meta as organizationProfileGeneralPanelMeta,
} from '../stories/organization-profile-general-panel.stories';
import {
Default as OrganizationProfileLeaveSectionDefault,
meta as organizationProfileLeaveSectionMeta,
} from '../stories/organization-profile-leave-section.stories';
import { meta as popoverMeta } from '../stories/popover.stories';
import { meta as selectMeta } from '../stories/select.stories';
import { Default as TabsComponentDefault, meta as tabsComponentMeta } from '../stories/tabs.component.stories';
Expand All @@ -64,12 +64,18 @@ import { toSlug } from './slug';
import type { StoryModule } from './types';

const destructiveModule: StoryModule = { meta: destructiveMeta, Default: DestructiveDefault };
const leaveOrganizationModule: StoryModule = { meta: leaveOrganizationMeta, Default: LeaveOrganizationDefault };
const deleteOrganizationModule: StoryModule = { meta: deleteOrganizationMeta, Default: DeleteOrganizationDefault };
const organizationProfileLeaveSectionModule: StoryModule = {
meta: organizationProfileLeaveSectionMeta,
Default: OrganizationProfileLeaveSectionDefault,
};
const organizationProfileDeleteSectionModule: StoryModule = {
meta: organizationProfileDeleteSectionMeta,
Default: OrganizationProfileDeleteSectionDefault,
};
const organizationProfileModule: StoryModule = { meta: organizationProfileMeta, Default: OrganizationProfileDefault };
const organizationProfileGeneralModule: StoryModule = {
meta: organizationProfileGeneralMeta,
Default: OrganizationProfileGeneralDefault,
const organizationProfileGeneralPanelModule: StoryModule = {
meta: organizationProfileGeneralPanelMeta,
Default: OrganizationProfileGeneralPanelDefault,
};

const cardComponentModule: StoryModule = { meta: cardComponentMeta, Default: CardDefault, Centered: CardCentered };
Expand Down Expand Up @@ -115,13 +121,11 @@ const tooltipModule: StoryModule = { meta: tooltipMeta };
const useDataTableModule: StoryModule = { meta: useDataTableMeta };

export const registry: StoryModule[] = [
// AIO
// Organization
organizationProfileModule,
// Panels
organizationProfileGeneralModule,
// Sections
leaveOrganizationModule,
deleteOrganizationModule,
organizationProfileGeneralPanelModule,
organizationProfileLeaveSectionModule,
organizationProfileDeleteSectionModule,
// Blocks
destructiveModule,
// Components
Expand Down
30 changes: 0 additions & 30 deletions packages/swingset/src/stories/delete-organization.stories.tsx

This file was deleted.

41 changes: 22 additions & 19 deletions packages/swingset/src/stories/icon.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @jsxImportSource @emotion/react */
import type { MosaicIconRenderer } from '@clerk/ui/mosaic/appearance';
import type { IconProps } from '@clerk/ui/mosaic/components/icon';
import { Icon, iconRecipe } from '@clerk/ui/mosaic/components/icon';
import { iconRegistry } from '@clerk/ui/mosaic/icons/registry';
Expand Down Expand Up @@ -75,26 +74,30 @@ export function Names() {
);
}

// Mosaic's styling (sizing/color) applies to the override just like the built-in glyph, so the
// replacement only needs its viewBox + paths — spread `props` to receive the sizing className and
// `data-cl-slot`. Defined at module scope (not inline in the story) to keep a stable component type.
const CircleGlyph: MosaicIconRenderer = props => (
<svg
{...props}
viewBox='0 0 20 20'
fill='currentColor'
>
<circle
cx={10}
cy={10}
r={6}
/>
</svg>
);

export function Override() {
return (
<MosaicProvider appearance={{ icons: { 'chevron-right': CircleGlyph } }}>
<MosaicProvider
appearance={{
// Overrides are elements now, not render functions: Mosaic injects its sizing className and
// `data-cl-slot` into the element via cloneElement, so the replacement only needs its viewBox
// + paths. Passing an element (vs a function) also lets overrides be supplied from a Server
// Component, since elements serialize across the RSC boundary.
icons: {
'chevron-right': (
<svg
viewBox='0 0 20 20'
fill='currentColor'
>
<circle
cx={10}
cy={10}
r={6}
/>
</svg>
),
},
}}
>
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
<Icon name='chevron-right' />
<Icon name='chevron-left' />
Expand Down
30 changes: 0 additions & 30 deletions packages/swingset/src/stories/leave-organization.stories.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as DeleteOrganizationStories from './delete-organization.stories';
import * as OrganizationProfileDeleteSectionStories from './organization-profile-delete-section.stories';

# Delete Organization
# Organization Profile Delete Section

A section that owns the open/deleting state and wires the `Destructive` block to the delete-organization flow.

<Story
name='Default'
storyModule={DeleteOrganizationStories}
storyModule={OrganizationProfileDeleteSectionStories}
composition={[
{ name: 'Destructive', href: '/blocks/destructive', layer: 'Blocks' },
{ name: 'Button', href: '/components/button', layer: 'Components' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/** @jsxImportSource @emotion/react */
import { useMachine } from '@clerk/ui/mosaic/machine/useMachine';
import { organizationProfileDeleteSectionMachine } from '@clerk/ui/mosaic/organization/organization-profile-delete-section.machine';
import { OrganizationProfileDeleteSectionView } from '@clerk/ui/mosaic/organization/organization-profile-delete-section.view';

import type { StoryMeta } from '@/lib/types';

export const meta: StoryMeta = {
group: 'Organization',
title: 'OrganizationProfileDeleteSection',
source: 'packages/ui/src/mosaic/organization/organization-profile-delete-section.tsx',
};

export function Default() {
const [snapshot, send, actor] = useMachine(organizationProfileDeleteSectionMachine, {
context: {
organizationName: 'Acme Inc',
destroyOrganization: () => new Promise<void>(resolve => setTimeout(resolve, 800)),
},
});

return (
<OrganizationProfileDeleteSectionView
snapshot={snapshot}
send={send}
canSubmit={actor.can({ type: 'CONFIRM' })}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as OrganizationProfileGeneralPanelStories from './organization-profile-general-panel.stories';

# Organization Profile General Panel

The General tab panel of the Organization Profile — composes the organization-level sections shown under "General".

<Story
name='Default'
storyModule={OrganizationProfileGeneralPanelStories}
composition={[
{
name: 'OrganizationProfileLeaveSection',
href: '/organization/organization-profile-leave-section',
layer: 'Organization',
},
{
name: 'OrganizationProfileDeleteSection',
href: '/organization/organization-profile-delete-section',
layer: 'Organization',
},
]}
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/** @jsxImportSource @emotion/react */
import { OrganizationProfileGeneralPanelView } from '@clerk/ui/mosaic/organization/organization-profile-general-panel-view';

import type { StoryMeta } from '@/lib/types';

import { Default as OrganizationProfileDeleteSectionDemo } from './organization-profile-delete-section.stories';
import { Default as OrganizationProfileLeaveSectionDemo } from './organization-profile-leave-section.stories';

export const meta: StoryMeta = {
group: 'Organization',
title: 'OrganizationProfileGeneralPanel',
source: 'packages/ui/src/mosaic/organization/organization-profile-general-panel.tsx',
};

export function Default() {
return (
<OrganizationProfileGeneralPanelView
leaveOrganization={<OrganizationProfileLeaveSectionDemo />}
deleteOrganization={<OrganizationProfileDeleteSectionDemo />}
/>
);
}
14 changes: 0 additions & 14 deletions packages/swingset/src/stories/organization-profile-general.mdx

This file was deleted.

This file was deleted.

Loading
Loading