UNPKG

@workday/canvas-kit-preview-react

Version:

Canvas Kit Preview is made up of components that have the full design and a11y review, are part of the DS ecosystem and are approved for use in product. The API's could be subject to change, but not without strong communication and migration strategies.

115 lines (114 loc) 4.7 kB
/** @jsxRuntime classic */ /** @jsx jsx */ import * as React from 'react'; import { createComponent, styled } from '@workday/canvas-kit-react/common'; import { jsx, keyframes } from '@emotion/react'; import { colors, depth } from '@workday/canvas-kit-react/tokens'; import { SidePanelContext } from './hooks'; import { SidePanelToggleButton } from './SidePanelToggleButton'; import { px2rem } from '@workday/canvas-kit-styling'; const createKeyframes = (from, to) => { const normalized = { from: typeof from === 'number' ? from + 'px' : from, to: typeof to === 'number' ? to + 'px' : to, }; return keyframes ` from { width: ${normalized.from}; min-width: ${normalized.from}; max-width: ${normalized.from}; } to { width: ${normalized.to}; min-width: ${normalized.to}; max-width: ${normalized.to}; } `; }; const containerVariantStyle = { alternate: { backgroundColor: colors.frenchVanilla100, ...depth[5], }, standard: { backgroundColor: colors.soap100, }, }; const Panel = styled('section')({ overflow: 'hidden', position: 'relative', boxSizing: 'border-box', height: '100%', outline: `${px2rem(1)} solid transparent`, }); export const SidePanel = createComponent('section')({ displayName: 'SidePanel', Component({ children, collapsedWidth = 64, expanded = true, expandedWidth = 320, onAnimationEnd, onAnimationStart, onExpandedChange, onStateTransition, origin = 'left', variant = 'standard', touched, ...elemProps }, ref, Element) { const [state, setState] = React.useState(expanded ? 'expanded' : 'collapsed'); React.useEffect(() => { if (typeof onExpandedChange !== 'undefined') { onExpandedChange(expanded); } }, [expanded, onExpandedChange]); React.useEffect(() => { if (typeof onStateTransition !== 'undefined') { onStateTransition(state); } }, [state, onStateTransition]); const motion = { collapse: createKeyframes(expandedWidth, collapsedWidth), expand: createKeyframes(collapsedWidth, expandedWidth), }; const handleAnimationEnd = (event) => { if (event.currentTarget === event.target) { if (event.animationName === motion.collapse.name) { setState('collapsed'); } if (event.animationName === motion.expand.name) { setState('expanded'); } } if (typeof onAnimationEnd !== 'undefined') { onAnimationEnd(event); } }; const handleAnimationStart = (event) => { if (event.currentTarget === event.target) { if (event.animationName === motion.collapse.name || event.animationName === motion.expand.name) { setState(expanded ? 'expanding' : 'collapsing'); } } if (typeof onAnimationStart !== 'undefined') { onAnimationStart(event); } }; return (jsx(Panel, { ref: ref, as: Element, css: [ { width: expanded ? expandedWidth : collapsedWidth, maxWidth: expanded ? expandedWidth : collapsedWidth, // mounted.current will be false on the first render, thus you won't get an unwanted animation here // Will animate again if you force a re-render (like in Storybook) animation: touched ? `${expanded ? motion.expand : motion.collapse} 200ms ease-out` : undefined, }, containerVariantStyle[variant], ], onAnimationEnd: handleAnimationEnd, onAnimationStart: handleAnimationStart, ...elemProps }, jsx(SidePanelContext.Provider, { value: { state, origin, } }, children))); }, subComponents: { /** * `SidePanel.ToggleButton` is a control that is meant to toggle between `expanded = true` and * `expanded = false` states. It must be used within the `SidePanel` component as a child. Use * in conjunction with `useSidePanel`'s `controlProps`, otherwise it does not come with explicit * `onClick` handlers. * * For accessibility purposes, it must be the first focusable element. We recommend that you * keep it as the first child of `SidePanel` */ ToggleButton: SidePanelToggleButton, }, });