Skip to content

Commit

Permalink
feat: sliding selector
Browse files Browse the repository at this point in the history
  • Loading branch information
lee-chase committed Jun 26, 2024
1 parent e411923 commit 8c8245f
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,40 @@ export const IconOnly = (args) => (
</ContentSwitcher>
);

export const IconOnlySlide = (args) => (
<ContentSwitcher onChange={() => {}} {...args} iconOnlySlide>
<IconSwitch name="one" text="Table of Contents">
<TableOfContents />
</IconSwitch>
<IconSwitch name="two" text="Workspace Test">
<Workspace />
</IconSwitch>
<IconSwitch name="three" text="View Mode">
<ViewMode_2 />
</IconSwitch>
</ContentSwitcher>
);

export const IconOnlySlide5 = (args) => (
<ContentSwitcher onChange={() => {}} {...args} iconOnlySlide>
<IconSwitch name="one" text="Table of Contents">
<TableOfContents />
</IconSwitch>
<IconSwitch name="two" text="Workspace Test">
<Workspace />
</IconSwitch>
<IconSwitch name="three" text="View Mode">
<ViewMode_2 />
</IconSwitch>
<IconSwitch name="four" text="View Mode">
<ViewMode_2 />
</IconSwitch>
<IconSwitch name="five" text="View Mode">
<ViewMode_2 />
</IconSwitch>
</ContentSwitcher>
);

export const IconOnlyWithLayer = (args) => (
<WithLayer>
<ContentSwitcher onChange={() => {}} {...args}>
Expand Down
46 changes: 37 additions & 9 deletions packages/react/src/components/ContentSwitcher/ContentSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface ContentSwitcherProps
*/
className?: string;

/**
* changes the animation when icon only from pop up to sideways slide
*/
iconOnlySlide?: boolean;

/**
* `true` to use the light version.
*
Expand Down Expand Up @@ -66,6 +71,7 @@ export interface ContentSwitcherProps

interface ContentSwitcherState {
selectedIndex?: number;
prevSelectedIndex?: number;
}

export default class ContentSwitcher extends React.Component<
Expand All @@ -81,6 +87,7 @@ export default class ContentSwitcher extends React.Component<

state = {
selectedIndex: undefined,
prevSelectedIndex: undefined,
};

static propTypes = {
Expand Down Expand Up @@ -128,12 +135,11 @@ export default class ContentSwitcher extends React.Component<
static contextType = PrefixContext;

static getDerivedStateFromProps({ selectedIndex = 0 }, state) {
const { prevSelectedIndex } = state;
return prevSelectedIndex === selectedIndex
return selectedIndex === state.prevPropSelectedIndex
? null
: {
selectedIndex,
prevSelectedIndex: selectedIndex,
prevPropSelectedIndex: selectedIndex,
};
}

Expand Down Expand Up @@ -162,6 +168,7 @@ export default class ContentSwitcher extends React.Component<
} else {
this.setState(
{
prevSelectedIndex: selectedIndex,
selectedIndex: nextIndex,
},
() => {
Expand All @@ -182,11 +189,14 @@ export default class ContentSwitcher extends React.Component<
);
}
} else if (selectedIndex !== index) {
this.setState({ selectedIndex: index }, () => {
const switchRef = this._switchRefs[index];
switchRef && switchRef.focus();
this.props.onChange(data);
});
this.setState(
{ selectedIndex: index, prevSelectedIndex: selectedIndex },
() => {
const switchRef = this._switchRefs[index];
switchRef && switchRef.focus();
this.props.onChange(data);
}
);
}
};

Expand All @@ -195,6 +205,7 @@ export default class ContentSwitcher extends React.Component<
const {
children,
className,
iconOnlySlide,
light,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
selectedIndex = 0,
Expand All @@ -215,15 +226,31 @@ export default class ContentSwitcher extends React.Component<
[`${prefix}--content-switcher--${size}`]: size, // TODO: V12 - Remove this class
[`${prefix}--layout--size-${size}`]: size,
[`${prefix}--content-switcher--icon-only`]: isIconOnly,
[`${prefix}--content-switcher--icon-only-slide`]: iconOnlySlide,
});

const style = {
// '--duration': `${
// 250 +
// 83.33 *
// Math.abs(
// (this.state?.selectedIndex ?? 0) -
// (this.state?.prevSelectedIndex ?? 0)
// )
// }ms`,
'--prev-selected-index': this.state?.prevSelectedIndex ?? 0,
'--selected-index': this.state?.selectedIndex ?? 0,
};

return (
<LayoutConstraint
size={{ default: 'md', min: 'sm', max: 'lg' }}
{...other}
className={classes}
role="tablist"
onChange={undefined}>
onChange={undefined}
// eslint-disable-next-line react/forbid-component-props
style={style}>
{children &&
React.Children.toArray(children).map((child, index) =>
React.cloneElement(child as ReactElement, {
Expand All @@ -236,6 +263,7 @@ export default class ContentSwitcher extends React.Component<
selected: index === this.state.selectedIndex,
ref: this.handleItemRef(index),
size,
iconOnlySlide,
})
)}
</LayoutConstraint>
Expand Down
16 changes: 16 additions & 0 deletions packages/react/src/components/Switch/IconSwitch.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const IconSwitch = React.forwardRef(function Switch(props, tabRef) {
className,
disabled,
enterDelayMs,
iconOnlySlide,
index,
leaveDelayMs = 0,
name,
Expand Down Expand Up @@ -75,6 +76,8 @@ const IconSwitch = React.forwardRef(function Switch(props, tabRef) {
size,
};

const styleIndex = { '--index': index };

return (
<IconButton
label={text}
Expand All @@ -89,9 +92,17 @@ const IconSwitch = React.forwardRef(function Switch(props, tabRef) {
aria-selected={selected}
aria-label={text}
wrapperClasses={iconButtonClasses}
{...styleIndex}
{...other}
{...commonProps}>
{children}
{iconOnlySlide ? (
<div
className={`${prefix}--content-switcher-btn__selection-indicator`}
aria-hidden="true">
{children}
</div>
) : null}
</IconButton>
);
});
Expand Down Expand Up @@ -133,6 +144,11 @@ IconSwitch.propTypes = {
*/
enterDelayMs: PropTypes.number,

/**
* changes the animation when icon only from pop up to sideways slide
*/
iconOnlySlide: PropTypes.bool,

/**
* The index of your Switch in your ContentSwitcher that is used for event handlers.
* Reserved for usage in ContentSwitcher
Expand Down
120 changes: 110 additions & 10 deletions packages/styles/scss/components/content-switcher/_content-switcher.scss
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@
}
}

.#{$prefix}--content-switcher.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-btn::after {
display: none;
}

.#{$prefix}--content-switcher:not(.#{$prefix}--content-switcher--icon-only)
.#{$prefix}--content-switcher-btn {
align-items: center;
Expand Down Expand Up @@ -138,11 +143,17 @@
}
}

.#{$prefix}--content-switcher
.#{$prefix}--content-switcher:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected,
.#{$prefix}--content-switcher
.#{$prefix}--content-switcher:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected:first-child,
.#{$prefix}--content-switcher
.#{$prefix}--content-switcher:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected:last-child {
border: 0;
}
Expand Down Expand Up @@ -210,7 +221,10 @@
fill: $icon-primary;
}

.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected {
.#{$prefix}--content-switcher:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected {
z-index: 3;
background-color: $layer-selected-inverse;
color: $text-inverse;
Expand All @@ -225,7 +239,9 @@
}
}

.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected
.#{$prefix}--content-switcher:not(
.#{$prefix}--content-switcher--icon-only-slide
).#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected
.#{$prefix}--content-switcher__icon {
fill: $icon-inverse;
}
Expand All @@ -249,10 +265,14 @@
border-start-start-radius: convert.to-rem(4px);
}

.#{$prefix}--content-switcher--icon-only
.#{$prefix}--content-switcher--icon-only:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-popover__wrapper:first-child
.#{$prefix}--content-switcher--selected[disabled],
.#{$prefix}--content-switcher--icon-only
.#{$prefix}--content-switcher--icon-only:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-popover__wrapper:last-child
.#{$prefix}--content-switcher--selected[disabled] {
border-color: $layer-selected-disabled;
Expand All @@ -266,10 +286,14 @@
border-start-end-radius: convert.to-rem(4px);
}

.#{$prefix}--content-switcher--icon-only
.#{$prefix}--content-switcher--icon-only:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-popover__wrapper:last-child
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected,
.#{$prefix}--content-switcher--icon-only
.#{$prefix}--content-switcher--icon-only:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-popover__wrapper:first-child
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected {
border-color: $background;
Expand All @@ -292,7 +316,9 @@
fill: $icon-primary;
}

.#{$prefix}--content-switcher--icon-only
.#{$prefix}--content-switcher--icon-only:not(
.#{$prefix}--content-switcher--icon-only-slide
)
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher--selected
svg {
z-index: 1;
Expand Down Expand Up @@ -354,4 +380,78 @@
.#{$prefix}--content-switcher-btn[disabled]:hover::before {
background-color: $border-subtle;
}

.#{$prefix}--content-switcher--icon-only-slide {
--selected-index: 0;
--index: 0;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-btn
.#{$prefix}--content-switcher-btn__selection-indicator {
position: absolute;
display: flex;
justify-content: center;
inset: 0;
padding-block-start: $spacing-04;
pointer-events: none;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher-btn
.#{$prefix}--content-switcher-btn__selection-indicator {
background-color: $layer-selected-inverse;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-btn.#{$prefix}--content-switcher-btn
.#{$prefix}--content-switcher-btn__selection-indicator
svg {
fill: $icon-inverse;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-btn__selection-indicator {
z-index: 10;
opacity: 0;
transform: translateX(calc((var(--selected-index) - var(--index)) * 100%));
transition-duration: calc(
250ms + 83.33ms *
(
max(var(--prev-selected-index), var(--selected-index)) -
min(var(--prev-selected-index), var(--selected-index))
)
);
// transition-duration: var(--duration, 250ms);
transition-property: transform, opacity;
transition-timing-function: motion(standard, productive);
// transition-delay: 0 !important;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-btn {
overflow: visible;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-popover--selected
.#{$prefix}--content-switcher-btn__selection-indicator {
opacity: 1;
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-popover--selected:first-child {
.#{$prefix}--content-switcher-btn__selection-indicator {
border-end-start-radius: convert.to-rem(4px);
border-start-start-radius: convert.to-rem(4px);
}
}

.#{$prefix}--content-switcher--icon-only-slide
.#{$prefix}--content-switcher-popover--selected:last-child {
.#{$prefix}--content-switcher-btn__selection-indicator {
border-end-end-radius: convert.to-rem(4px);
border-start-end-radius: convert.to-rem(4px);
}
}
}

0 comments on commit 8c8245f

Please sign in to comment.