TODO: Slider

DONE: Segmented buttons
This commit is contained in:
2024-02-12 20:11:33 +04:00
committed by doryan
parent ff00e19aeb
commit fef77fb0f9
9 changed files with 175 additions and 62 deletions

View File

@@ -0,0 +1,74 @@
'use client';
import { string } from 'prop-types';
import React, { forwardRef, useState } from 'react';
import { IconWrapper } from '../../icon/icon-wrapper';
import { SegmentedButton } from './segmented-buttons.types';
import { ButtonLayout } from '../button-layout/button-layout';
import { ButtonLayoutProps } from '../button-layout/button-layout.types';
export const SegmentButton = forwardRef<
HTMLButtonElement,
ButtonLayoutProps & SegmentedButton
>(
(
{
icon,
toggled = false,
iconPlace = 'left',
centralRipple = false,
...props
},
ref,
) => {
const {
fillIcon,
grade,
svgSize,
iconSize,
opticalSize,
type,
weight,
} = props;
const [selectedState, setSelectedState] = useState<boolean>(false);
const classes =
`m3-button-segment${selectedState ? ' selected' : ''} ${props.className ?? ''}`.trimEnd();
const _icon = selectedState && icon;
return (
<ButtonLayout
{...props}
centralRipple={centralRipple}
className={classes}
onClick={() => {
if (toggled) {
setSelectedState(state => !state);
}
props.onClick?.apply(this, props.onClick.arguments);
}}
ref={ref}
>
<IconWrapper
fillIcon={fillIcon}
grade={grade}
icon={_icon}
iconPlace={iconPlace}
iconSize={iconSize}
opticalSize={opticalSize}
svgSize={svgSize}
type={type}
weight={weight}
>
<span className={'label-large'}>{props.children}</span>
</IconWrapper>
<span className={'m3 m3-button-segment-state-layer'} />
</ButtonLayout>
);
},
);
SegmentButton.propTypes = {
children: string,
};

View File

@@ -1,51 +1,33 @@
import React, { forwardRef } from 'react';
import {
SegmentedButton,
SegmentedButtonsProps,
} from './segmented-buttons.types';
import { string } from 'prop-types';
import { ButtonLayout } from '../../components';
import { ButtonLayoutProps } from '../button-layout/button-layout.types';
import { IconWrapper } from '../../icon/icon-wrapper';
'use client';
export const SegmentButton = forwardRef<
HTMLButtonElement,
ButtonLayoutProps & SegmentedButton
>(({ centralRipple = false, iconPlace = 'left', icon, ...props }, ref) => {
const classes = `m3-button-segment ${props.className ?? ''}`.trimEnd();
return (
<ButtonLayout
{...props}
centralRipple={centralRipple}
className={classes}
ref={ref}
>
<IconWrapper icon={icon} iconPlace={iconPlace}>
<span className={'label-large'}>{props.children}</span>
</IconWrapper>
<span className={'m3 m3-button-segment-state-layer'} />
</ButtonLayout>
);
});
SegmentButton.propTypes = {
children: string,
};
import { SegmentButton } from './segment-button';
import { SegmentedButtonsProps } from './segmented-buttons.types';
import React, { cloneElement, forwardRef, ReactElement } from 'react';
export const SegmentedButtons = forwardRef<
HTMLDivElement,
SegmentedButtonsProps
>(({ children, ...props }, ref) => {
>(({ toggled = false, children, ...props }, ref) => {
if (children.length <= 1) {
throw 'You must build segmented button with 2 or more buttton';
throw 'You must build segmented button with 2 or more button';
}
const SegmentedButtons: Array<ReactElement> = children.map(
(Button: ReactElement, index: number) => {
return cloneElement(<SegmentButton />, {
...Button.props,
toggled: toggled,
key: index,
});
},
);
return (
<div
className={`m3 m3-segmented-buttons ${props.className ?? ''}`.trimEnd()}
ref={ref}
>
{children}
{SegmentedButtons}
</div>
);
});

View File

@@ -1,12 +1,15 @@
import { HTMLAttributes, ReactElement } from 'react';
import { IconWrapperProps } from '../../icon/icon.types';
import { IconProps, IconWrapperProps } from '../../icon/icon.types';
export type SegmentedButton = IconWrapperProps & {
icon?: string;
toggled?: boolean;
centralRipple?: boolean;
children?: string | ReactElement<IconProps>;
};
export interface SegmentedButtons {
toggled?: boolean;
children?: ReactElement<HTMLButtonElement>[];
}

View File

@@ -16,7 +16,7 @@ const Ripple = ({
const rippleDomainContext = useContext(rippleAreaContext);
useLayoutEffect(() => {
if (endLifetime !== null && !rippleDomainContext) {
if (endLifetime && !rippleDomainContext) {
setClasses('m3 ripple');
setTimeout(() => endLifetime(rippleKey), lifetime);
}