@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
245 lines • 9.91 kB
JavaScript
import { Shade, createComponent } from '@furystack/shades';
import { cssVariableTheme } from '../services/css-variable-theme.js';
import { paletteMainColors } from '../services/palette-css-vars.js';
/**
* A single item in a Timeline.
* Renders a dot, a connector tail, and the item's content (children).
*/
export const TimelineItem = Shade({
customElementName: 'shade-timeline-item',
css: {
display: 'flex',
position: 'relative',
minHeight: '64px',
'& .timeline-label': {
flex: '1',
textAlign: 'right',
paddingRight: cssVariableTheme.spacing.md,
paddingTop: cssVariableTheme.spacing.xs,
fontFamily: cssVariableTheme.typography.fontFamily,
fontSize: cssVariableTheme.typography.fontSize.sm,
lineHeight: cssVariableTheme.typography.lineHeight.normal,
color: cssVariableTheme.text.secondary,
boxSizing: 'border-box',
},
'& .timeline-dot-column': {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flexShrink: '0',
width: '24px',
paddingTop: cssVariableTheme.spacing.xs,
},
'& .timeline-dot-line': {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: '0',
},
'& .timeline-dot-line::before': {
content: '"\\200b"',
fontSize: cssVariableTheme.typography.fontSize.sm,
lineHeight: cssVariableTheme.typography.lineHeight.normal,
fontFamily: cssVariableTheme.typography.fontFamily,
},
'& .timeline-dot': {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '12px',
height: '12px',
borderRadius: cssVariableTheme.shape.borderRadius.full,
backgroundColor: 'var(--timeline-dot-color)',
flexShrink: '0',
zIndex: '1',
},
'& .timeline-dot[data-custom]': {
width: 'auto',
height: 'auto',
backgroundColor: 'transparent',
color: 'var(--timeline-dot-color)',
fontSize: '20px',
lineHeight: '1',
},
'& .timeline-tail': {
flex: '1',
width: '2px',
backgroundColor: cssVariableTheme.divider,
marginTop: cssVariableTheme.spacing.xs,
},
'& .timeline-tail[data-pending]': {
borderLeft: `2px dashed ${cssVariableTheme.divider}`,
backgroundColor: 'transparent',
width: '0',
},
'& .timeline-content': {
flex: '1',
paddingTop: cssVariableTheme.spacing.xs,
paddingLeft: cssVariableTheme.spacing.md,
paddingBottom: cssVariableTheme.spacing.lg,
fontFamily: cssVariableTheme.typography.fontFamily,
fontSize: cssVariableTheme.typography.fontSize.sm,
lineHeight: cssVariableTheme.typography.lineHeight.normal,
color: cssVariableTheme.text.primary,
boxSizing: 'border-box',
},
'&:last-of-type .timeline-tail': {
display: 'none',
},
},
render: ({ props, children, useHostProps }) => {
const { color = 'primary', dot, label, style } = props;
const colors = paletteMainColors[color];
useHostProps({
style: {
'--timeline-dot-color': colors.main,
...style,
},
});
const isPending = props['data-pending'] !== undefined;
return (createComponent(createComponent, null,
createComponent("div", { className: "timeline-label" }, label),
createComponent("div", { className: "timeline-dot-column" },
createComponent("div", { className: "timeline-dot-line" },
createComponent("div", { className: "timeline-dot", ...(dot ? { 'data-custom': '' } : {}) }, dot ?? null)),
createComponent("div", { className: "timeline-tail", ...(isPending ? { 'data-pending': '' } : {}) })),
createComponent("div", { className: "timeline-content" }, children)));
},
});
/**
* Timeline displays a list of events in chronological order.
* Supports left, right, and alternate layout modes with optional pending state.
*/
export const Timeline = Shade({
customElementName: 'shade-timeline',
css: {
display: 'flex',
flexDirection: 'column',
padding: `${cssVariableTheme.spacing.md} 0`,
margin: '0',
listStyle: 'none',
fontFamily: cssVariableTheme.typography.fontFamily,
'&[data-mode="right"] > shade-timeline-item .timeline-label': {
display: 'block',
textAlign: 'left',
paddingRight: '0',
paddingLeft: cssVariableTheme.spacing.md,
order: '3',
},
'&[data-mode="right"] > shade-timeline-item .timeline-content': {
textAlign: 'right',
paddingLeft: '0',
paddingRight: cssVariableTheme.spacing.md,
order: '1',
},
'&[data-mode="right"] > shade-timeline-item .timeline-dot-column': {
order: '2',
},
'&[data-mode="alternate"] > shade-timeline-item .timeline-label': {
display: 'block',
},
'&[data-mode="alternate"] > shade-timeline-item:nth-of-type(even) .timeline-label': {
textAlign: 'left',
paddingRight: '0',
paddingLeft: cssVariableTheme.spacing.md,
order: '3',
},
'&[data-mode="alternate"] > shade-timeline-item:nth-of-type(even) .timeline-content': {
textAlign: 'right',
paddingLeft: '0',
paddingRight: cssVariableTheme.spacing.md,
order: '1',
},
'&[data-mode="alternate"] > shade-timeline-item:nth-of-type(even) .timeline-dot-column': {
order: '2',
},
'&[data-orientation="horizontal"]': {
flexDirection: 'row',
},
'&[data-orientation="horizontal"] > shade-timeline-item': {
flexDirection: 'column',
minHeight: 'auto',
flex: '1',
},
'&[data-orientation="horizontal"] > shade-timeline-item .timeline-label': {
flex: 'none',
textAlign: 'center',
paddingRight: '0',
paddingBottom: cssVariableTheme.spacing.xs,
},
'&[data-orientation="horizontal"] > shade-timeline-item .timeline-dot-column': {
flexDirection: 'row',
width: '100%',
paddingTop: '0',
},
'&[data-orientation="horizontal"] > shade-timeline-item .timeline-tail': {
height: '2px',
width: 'auto',
flex: '1',
marginTop: '0',
marginLeft: cssVariableTheme.spacing.xs,
},
'&[data-orientation="horizontal"] > shade-timeline-item .timeline-tail[data-pending]': {
borderLeft: 'none',
borderTop: `2px dashed ${cssVariableTheme.divider}`,
height: '0',
width: 'auto',
},
'&[data-orientation="horizontal"] > shade-timeline-item .timeline-content': {
flex: 'none',
paddingLeft: '0',
paddingTop: cssVariableTheme.spacing.sm,
paddingBottom: '0',
textAlign: 'center',
},
'&[data-orientation="horizontal"][data-mode="right"] > shade-timeline-item .timeline-label': {
order: '3',
paddingBottom: '0',
paddingLeft: '0',
paddingTop: cssVariableTheme.spacing.sm,
textAlign: 'center',
},
'&[data-orientation="horizontal"][data-mode="right"] > shade-timeline-item .timeline-content': {
order: '1',
paddingTop: '0',
paddingRight: '0',
paddingBottom: cssVariableTheme.spacing.xs,
textAlign: 'center',
},
'&[data-orientation="horizontal"][data-mode="right"] > shade-timeline-item .timeline-dot-column': {
order: '2',
},
'&[data-orientation="horizontal"][data-mode="alternate"] > shade-timeline-item:nth-of-type(even) .timeline-label': {
order: '3',
paddingRight: '0',
paddingLeft: '0',
paddingBottom: '0',
paddingTop: cssVariableTheme.spacing.sm,
textAlign: 'center',
},
'&[data-orientation="horizontal"][data-mode="alternate"] > shade-timeline-item:nth-of-type(even) .timeline-content': {
order: '1',
paddingLeft: '0',
paddingRight: '0',
paddingTop: '0',
paddingBottom: cssVariableTheme.spacing.xs,
textAlign: 'center',
},
'&[data-orientation="horizontal"][data-mode="alternate"] > shade-timeline-item:nth-of-type(even) .timeline-dot-column': {
order: '2',
},
},
render: ({ props, children, useHostProps }) => {
const { mode = 'left', orientation = 'vertical', pending, style } = props;
useHostProps({
'data-mode': mode,
...(orientation === 'horizontal' ? { 'data-orientation': 'horizontal' } : {}),
...(style ? { style: style } : {}),
});
const pendingItem = pending ? (createComponent(TimelineItem, { color: "info", dot: createComponent("span", { style: { fontSize: cssVariableTheme.typography.fontSize.md } }, "\u23F3"), "data-pending": "" }, pending === true ? 'Loading...' : pending)) : null;
return (createComponent(createComponent, null,
children,
pendingItem));
},
});
//# sourceMappingURL=timeline.js.map