Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,43 @@ describe('calculateFitScale', () => {
})

describe('ResponsiveDesignStage', () => {
it.each([
{ width: 280, height: 400, supportsZoom: true },
{ width: 280, height: 400, supportsZoom: false },
{ width: 350, height: 340, supportsZoom: true },
{ width: 350, height: 340, supportsZoom: false },
])(
'fits an uncapped $width × $height stage with zoom support: $supportsZoom',
({ width, height, supportsZoom }) => {
vi.stubGlobal('CSS', { supports: vi.fn(() => supportsZoom) })
act(() => {
root.render(
createElement(
ResponsiveDesignStage,
{ width, height, maxScale: Number.POSITIVE_INFINITY },
createElement('span', null, 'Preview')
)
)
})

const surface = container.firstElementChild?.firstElementChild
if (!(surface instanceof HTMLElement) || !resizeObserver) {
throw new Error('responsive stage did not mount')
}
const observer = resizeObserver

act(() => observer.deliver(width * 2, height * 1.5))
expect(surface.style.zoom).toBe(supportsZoom ? '1.5' : '1')
expect(surface.style.transform).toBe(supportsZoom ? '' : 'scale(1.5)')
expect(surface.style.opacity).toBe('1')

act(() => observer.deliver(width / 2, height * 2))
expect(surface.style.zoom).toBe(supportsZoom ? '0.5' : '1')
expect(surface.style.transform).toBe(supportsZoom ? '' : 'scale(0.5)')
expect(surface.style.opacity).toBe('1')
}
)

it('hides an already visible surface until a measurable size returns', () => {
act(() => {
root.render(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@

.folderButton {
position: relative;
min-width: 0;
min-height: 0;
container-type: size;
display: block;
width: 100%;
height: 100%;
cursor: pointer;
touch-action: manipulation;
border-radius: 12px;
Expand All @@ -60,11 +60,10 @@
width: 321px;
height: 270px;
left: 50%;
bottom: 12px;
bottom: 0;
margin-left: -160.5px;
perspective: 800px;
transform-origin: bottom center;
scale: min(tan(atan2(100cqw, 350px)), tan(atan2(100cqh, 340px)));
}
.folderBack {
position: absolute;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useState } from 'react'
import { cn } from '@sim/emcn'
import { motion, useReducedMotion } from 'framer-motion'
import { ResponsiveDesignStage } from '@/app/(landing)/components/shared/responsive-design-stage'
import styles from '@/app/(landing)/files/components/feature-graphics/file-library-graphic.module.css'

/**
Expand Down Expand Up @@ -72,53 +73,68 @@ export function InteractiveLibraryFolder({
: { type: 'spring' as const, stiffness: 120, damping: 14 }

return (
<button
type='button'
className={cn(styles.folderButton, className)}
aria-label={`${name}, ${count}`}
aria-pressed={open}
onPointerEnter={(event) => {
if (event.pointerType !== 'touch') setHovered(true)
}}
onPointerLeave={() => setHovered(false)}
onFocus={() => setHovered(true)}
onBlur={() => setHovered(false)}
onClick={() => setOpen((value) => !value)}
onKeyDown={(event) => {
if (event.key === 'Escape') setOpen(false)
}}
>
<span aria-hidden='true' className={styles.folderScene}>
<span className={styles.folderBack} />
{CARDS.map((card, index) => (
<motion.span
key={card.x}
className={styles.folderPaper}
animate={{
x: open ? card.openX : card.x,
y: open ? card.openY : hovered ? card.hoverY : card.y,
rotate: open ? card.openRotate : hovered ? card.hoverRotate : card.rotate,
}}
transition={{ ...transition, delay: reducedMotion ? 0 : (2 - index) * 0.05 }}
>
<span className={styles.paperTitle} />
<span className={styles.paperLines} />
</motion.span>
))}
<motion.span
className={styles.folderFlap}
animate={{ rotateX: open ? -55 : hovered ? -45 : -15 }}
transition={transition}
<div className={cn('relative min-h-0 min-w-0', className)}>
<ResponsiveDesignStage
width={350}
height={340}
maxScale={Number.POSITIVE_INFINITY}
Comment thread
waleedlatif1 marked this conversation as resolved.
className='-translate-y-3 absolute inset-0 items-end overflow-visible [contain:none]'
contentClassName='origin-bottom'
>
<button
type='button'
className={styles.folderButton}
aria-label={`${name}, ${count}`}
aria-pressed={open}
onPointerEnter={(event) => {
if (event.pointerType !== 'touch') setHovered(true)
}}
onPointerLeave={() => setHovered(false)}
onFocus={() => setHovered(true)}
onBlur={() => setHovered(false)}
onClick={() => setOpen((value) => !value)}
onKeyDown={(event) => {
if (event.key === 'Escape') setOpen(false)
}}
>
<svg className='absolute inset-0 size-full' viewBox='0 0 321 241' fill='none'>
<path d={FLAP_PATH} fill='var(--surface-3)' stroke='var(--border)' strokeWidth='2' />
</svg>
<span className={styles.folderLabel}>
<span>{name}</span>
<span>{count}</span>
<span aria-hidden='true' className={styles.folderScene}>
<span className={styles.folderBack} />
{CARDS.map((card, index) => (
<motion.span
key={card.x}
className={styles.folderPaper}
animate={{
x: open ? card.openX : card.x,
y: open ? card.openY : hovered ? card.hoverY : card.y,
rotate: open ? card.openRotate : hovered ? card.hoverRotate : card.rotate,
}}
transition={{ ...transition, delay: reducedMotion ? 0 : (2 - index) * 0.05 }}
>
<span className={styles.paperTitle} />
<span className={styles.paperLines} />
</motion.span>
))}
<motion.span
className={styles.folderFlap}
animate={{ rotateX: open ? -55 : hovered ? -45 : -15 }}
transition={transition}
>
<svg className='absolute inset-0 size-full' viewBox='0 0 321 241' fill='none'>
<path
d={FLAP_PATH}
fill='var(--surface-3)'
stroke='var(--border)'
strokeWidth='2'
/>
</svg>
<span className={styles.folderLabel}>
<span>{name}</span>
<span>{count}</span>
</span>
</motion.span>
</span>
</motion.span>
</span>
</button>
</button>
</ResponsiveDesignStage>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChipTag, cn } from '@sim/emcn'
import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css'
import { ResponsiveDesignStage } from '@/app/(landing)/components/shared/responsive-design-stage'
import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics'
import styles from '@/app/(landing)/workflows/components/feature-graphics/workflow-canvas-graphic.module.css'

Expand Down Expand Up @@ -65,11 +66,14 @@ const EDGE_DRAW_CLASSES = [styles.edgeDraw0, styles.edgeDraw1, styles.edgeDraw2]
export function WorkflowCanvasGraphic() {
return (
<FeatureGraphicShell variant='portrait'>
<div
aria-hidden='true'
className='absolute inset-0 flex items-center justify-center p-3 [container-type:size]'
>
<div className='relative h-[400px] w-[280px] shrink-0 [scale:min(tan(atan2(100cqw,280px)),tan(atan2(100cqh,400px)))]'>
<div aria-hidden='true' className='absolute inset-3'>
<ResponsiveDesignStage
width={LAYOUT.width}
height={LAYOUT.height}
maxScale={Number.POSITIVE_INFINITY}
className='h-full w-full'
contentClassName='relative'
>
<svg
className='absolute inset-0'
fill='none'
Expand Down Expand Up @@ -139,7 +143,7 @@ export function WorkflowCanvasGraphic() {
</span>
</div>
))}
</div>
</ResponsiveDesignStage>
</div>
</FeatureGraphicShell>
)
Expand Down
Loading