mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-08 05:11:49 +08:00
203 lines
5.1 KiB
TypeScript
203 lines
5.1 KiB
TypeScript
import React, { Fragment, FunctionComponent, memo } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
|
|
import { styled } from '@storybook/theming';
|
|
import { Placeholder, Link as StyledLink } from '@storybook/components';
|
|
import { State } from '@storybook/api';
|
|
import { Location, Link as RouterLink } from '@storybook/router';
|
|
import { TreeState } from './treeview/treeview';
|
|
|
|
import SidebarItem from './SidebarItem';
|
|
import SidebarSearch from './SidebarSearch';
|
|
import SidebarSubheading from './SidebarSubheading';
|
|
|
|
const Search = styled(SidebarSearch)({
|
|
margin: '0 20px 1rem',
|
|
});
|
|
|
|
const Subheading = styled(SidebarSubheading)({
|
|
margin: '0 20px',
|
|
});
|
|
|
|
Subheading.propTypes = {
|
|
className: PropTypes.string,
|
|
};
|
|
|
|
Subheading.defaultProps = {
|
|
className: 'sidebar-subheading',
|
|
};
|
|
|
|
const Section = styled.section({
|
|
'& + section': {
|
|
marginTop: 20,
|
|
},
|
|
'&:last-of-type': {
|
|
marginBottom: 40,
|
|
},
|
|
});
|
|
|
|
const List = styled.div();
|
|
List.displayName = 'List';
|
|
|
|
const plain = {
|
|
color: 'inherit',
|
|
display: 'block',
|
|
textDecoration: 'none',
|
|
userSelect: 'none',
|
|
};
|
|
// @ts-ignore
|
|
const PlainRouterLink = styled(RouterLink)(plain);
|
|
// @ts-ignore
|
|
const PlainLink = styled.a(plain);
|
|
|
|
const Wrapper = styled.div({});
|
|
|
|
export const viewMode = (
|
|
currentViewMode: string | undefined,
|
|
isDocsOnly: boolean,
|
|
parameters: { viewMode?: string } = {}
|
|
) => {
|
|
const { viewMode: paramViewMode } = parameters;
|
|
switch (true) {
|
|
case typeof paramViewMode === 'string':
|
|
return paramViewMode;
|
|
case isDocsOnly:
|
|
return 'docs';
|
|
case currentViewMode === 'settings' || !currentViewMode:
|
|
return 'story';
|
|
default:
|
|
return currentViewMode;
|
|
}
|
|
};
|
|
|
|
const targetId = (childIds?: string[]) =>
|
|
childIds && childIds.find((childId: string) => /.*--.*/.exec(childId));
|
|
|
|
export const Link = ({
|
|
id,
|
|
prefix,
|
|
name,
|
|
children,
|
|
isLeaf,
|
|
isComponent,
|
|
onClick,
|
|
onKeyUp,
|
|
childIds,
|
|
isExpanded,
|
|
parameters,
|
|
}) => {
|
|
return isLeaf || (isComponent && !isExpanded) ? (
|
|
<Location>
|
|
{({ viewMode: currentViewMode }) => (
|
|
<PlainRouterLink
|
|
title={name}
|
|
id={prefix + id}
|
|
to={`/${viewMode(currentViewMode, isLeaf && isComponent, parameters)}/${targetId(
|
|
childIds
|
|
) || id}`}
|
|
onKeyUp={onKeyUp}
|
|
onClick={onClick}
|
|
>
|
|
{children}
|
|
</PlainRouterLink>
|
|
)}
|
|
</Location>
|
|
) : (
|
|
<PlainLink title={name} id={prefix + id} onKeyUp={onKeyUp} onClick={onClick}>
|
|
{children}
|
|
</PlainLink>
|
|
);
|
|
};
|
|
Link.displayName = 'Link';
|
|
Link.propTypes = {
|
|
children: PropTypes.node.isRequired,
|
|
id: PropTypes.string.isRequired,
|
|
name: PropTypes.string.isRequired,
|
|
isLeaf: PropTypes.bool.isRequired,
|
|
prefix: PropTypes.string.isRequired,
|
|
onKeyUp: PropTypes.func.isRequired,
|
|
onClick: PropTypes.func.isRequired,
|
|
childIds: PropTypes.arrayOf(PropTypes.string),
|
|
isExpanded: PropTypes.bool,
|
|
};
|
|
Link.defaultProps = {
|
|
childIds: null,
|
|
isExpanded: false,
|
|
};
|
|
|
|
export interface StoriesProps {
|
|
loading: boolean;
|
|
stories: State['StoriesHash'];
|
|
storyId?: undefined | string;
|
|
className?: undefined | string;
|
|
}
|
|
|
|
const SidebarStories: FunctionComponent<StoriesProps> = memo(
|
|
({ stories, storyId, loading, className, ...rest }) => {
|
|
const list = Object.entries(stories);
|
|
|
|
if (loading) {
|
|
return (
|
|
<Wrapper className={className}>
|
|
<SidebarItem loading />
|
|
<SidebarItem loading />
|
|
<SidebarItem depth={1} loading />
|
|
<SidebarItem depth={1} loading />
|
|
<SidebarItem depth={2} loading />
|
|
<SidebarItem depth={3} loading />
|
|
<SidebarItem depth={3} loading />
|
|
<SidebarItem depth={3} loading />
|
|
<SidebarItem depth={1} loading />
|
|
<SidebarItem depth={1} loading />
|
|
<SidebarItem depth={1} loading />
|
|
<SidebarItem depth={2} loading />
|
|
<SidebarItem depth={2} loading />
|
|
<SidebarItem depth={2} loading />
|
|
<SidebarItem depth={3} loading />
|
|
<SidebarItem loading />
|
|
<SidebarItem loading />
|
|
</Wrapper>
|
|
);
|
|
}
|
|
|
|
if (list.length < 1) {
|
|
return (
|
|
<Wrapper className={className}>
|
|
<Placeholder key="empty">
|
|
<Fragment key="title">No stories found</Fragment>
|
|
<Fragment>
|
|
Learn how to
|
|
<StyledLink href="https://storybook.js.org/basics/writing-stories/" target="_blank">
|
|
write stories
|
|
</StyledLink>
|
|
</Fragment>
|
|
</Placeholder>
|
|
</Wrapper>
|
|
);
|
|
}
|
|
return (
|
|
<Wrapper className={className}>
|
|
<TreeState
|
|
key="treestate"
|
|
dataset={stories}
|
|
prefix="explorer"
|
|
selectedId={storyId}
|
|
filter=""
|
|
List={List}
|
|
Head={SidebarItem}
|
|
Link={Link}
|
|
Leaf={SidebarItem}
|
|
Title={Subheading}
|
|
Section={Section}
|
|
Message={Placeholder}
|
|
// eslint-disable-next-line react/jsx-no-duplicate-props
|
|
Filter={Search}
|
|
{...rest}
|
|
/>
|
|
</Wrapper>
|
|
);
|
|
}
|
|
);
|
|
|
|
export default SidebarStories;
|