123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- /*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- import styled from '@emotion/styled';
- import classNames from 'classnames';
- import { useIntl } from 'react-intl';
- import tw from 'twin.macro';
- import { themeBorder, themeColor, themeContrast, themeShadow } from '../helpers/theme';
- import { LightLabel } from './Text';
- import { RecommendedIcon } from './icons/RecommendedIcon';
- import { RadioButtonStyled } from './input/RadioButton';
-
- export interface SelectionCardProps {
- children?: React.ReactNode;
- className?: string;
- disabled?: boolean;
- onClick?: VoidFunction;
- recommended?: boolean;
- recommendedReason?: string;
- selected?: boolean;
- title: string;
- titleInfo?: React.ReactNode;
- vertical?: boolean;
- }
-
- export function SelectionCard(props: SelectionCardProps) {
- const {
- children,
- className,
- disabled,
- onClick,
- recommended,
- recommendedReason,
- selected = false,
- title,
- titleInfo,
- vertical = false,
- } = props;
- const isActionable = Boolean(onClick);
-
- const intl = useIntl();
-
- return (
- <StyledButton
- aria-checked={selected}
- aria-disabled={disabled}
- className={classNames(
- 'js-radio-card',
- {
- 'card-actionable': isActionable && !disabled,
- 'card-vertical': vertical,
- disabled,
- selected,
- },
- className,
- )}
- onClick={isActionable && !disabled && !selected ? onClick : undefined}
- role={isActionable ? 'radio' : 'presentation'}
- tabIndex={disabled ? -1 : 0}
- type="button"
- >
- <StyledContent>
- {isActionable && (
- <div className="sw-items-start sw-mt-1/2 sw-mr-2">
- <RadioButtonStyled
- as="i"
- className={classNames({ 'is-checked': selected, 'is-disabled': disabled })}
- />
- </div>
- )}
- <div>
- <StyledLabel>
- {title}
- <LightLabel>{titleInfo}</LightLabel>
- </StyledLabel>
- <StyledBody>{children}</StyledBody>
- </div>
- </StyledContent>
- {recommended && (
- <StyledRecommended>
- <StyledRecommendedIcon className="sw-mr-1" />
- <span className="sw-align-middle">
- <strong>{intl.formatMessage({ id: 'recommended' })}</strong> {recommendedReason}
- </span>
- </StyledRecommended>
- )}
- </StyledButton>
- );
- }
-
- const StyledButton = styled.button`
- ${tw`sw-relative sw-flex sw-flex-col`}
- ${tw`sw-rounded-2`}
- ${tw`sw-box-border`}
-
- background-color: ${themeColor('backgroundSecondary')};
- border: ${themeBorder('default', 'selectionCardBorder')};
- color: inherit;
-
- &:focus {
- outline: none;
- border: ${themeBorder('default', 'selectionCardBorderHover')};
- box-shadow: ${themeShadow('sm')};
- }
-
- &.card-vertical {
- ${tw`sw-w-full`}
- min-height: auto;
- }
-
- &.card-actionable {
- ${tw`sw-cursor-pointer`}
-
- &:hover {
- border: ${themeBorder('default', 'selectionCardBorderHover')};
- box-shadow: ${themeShadow('sm')};
- }
-
- &.selected {
- border: ${themeBorder('default', 'selectionCardBorderSelected')};
- }
- }
-
- &.disabled {
- ${tw`sw-cursor-not-allowed`}
-
- background-color: ${themeColor('selectionCardDisabled')};
- color: ${themeColor('selectionCardDisabledText')};
- border: ${themeBorder('default', 'selectionCardBorderDisabled')};
- }
- `;
-
- const StyledContent = styled.div`
- ${tw`sw-my-4 sw-mx-3`}
- ${tw`sw-flex sw-grow`}
- ${tw`sw-text-left`}
- `;
-
- const StyledRecommended = styled.div`
- ${tw`sw-body-sm`}
- ${tw`sw-py-2 sw-px-4`}
- ${tw`sw-box-border`}
- ${tw`sw-rounded-b-2`}
-
- color: ${themeContrast('infoBackground')};
- background-color: ${themeColor('infoBackground')};
- `;
-
- const StyledRecommendedIcon = styled(RecommendedIcon)`
- color: ${themeColor('iconInfo')};
- ${tw`sw-align-middle`}
- `;
-
- const StyledLabel = styled.label`
- ${tw`sw-flex`}
- ${tw`sw-mb-3 sw-gap-2`}
- ${tw`sw-body-sm-highlight`}
-
- color: ${themeColor('selectionCardHeader')};
- cursor: inherit;
-
- .disabled & {
- color: ${themeContrast('selectionCardDisabled')};
- }
- `;
-
- const StyledBody = styled.div`
- ${tw`sw-flex sw-grow`}
- ${tw`sw-flex-col sw-justify-between`}
- `;
|