Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions smart-frontend/app/src/components/uikit/CheckAll/CheckAll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useMemo } from 'react';
import Checkbox from '@uikit/Checkbox/Checkbox';

export interface CheckAllProps<T> {
allList: T[];
selectedValues: T[] | null;
onChange: (value: T[]) => void;
label?: string;
className?: string;
disabled?: boolean;
}

const CheckAll = <T,>({ label, allList, selectedValues, onChange, className, disabled }: CheckAllProps<T>) => {
const isAllChecked = useMemo(() => {
if (!selectedValues?.length) return false;

return allList.length === selectedValues.length;
}, [allList, selectedValues]);

const handlerAllChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(event.target.checked ? allList.map((item) => item) : []);
};

return (
<Checkbox
label={label}
className={className}
checked={isAllChecked}
onChange={handlerAllChanged}
disabled={disabled}
/>
);
};

export default CheckAll;
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
:global {
body.theme-dark {
--checkbox-border: var(--color-grayUsual);
--checkbox-label-color: var(--checkbox-border);

--checkbox-checked-border: var(--color-greenSaturated);
--checkbox-checked-label-color: var(--color-offWhite);

// hover
--checkbox-hover-border: var(--color-greenSaturated);
--checkbox-hover-label-color: var(--color-offWhite);

// disabled
--checkbox-disabled-border: var(--color-grayReadingOnly);
--checkbox-disabled-label-color: var(--color-grayReadingOnly);

// readonly
--checkbox-readonly-border: var(--color-grayReadingOnly);
--checkbox-readonly-label-color: var(--color-grayReadingOnly);
}

body.theme-light {
--checkbox-border: var(--color-ADCM);
--checkbox-label-color: var(--color-ADCM);

--checkbox-checked-border: var(--color-greenLogo);
--checkbox-checked-label-color: var(--color-grayDarker);

// hover
--checkbox-hover-border: var(--color-greenLogo);
--checkbox-hover-label-color: var(--color-dark1);

// disabled
--checkbox-disabled-border: var(--color-lightStrokeDark);
--checkbox-disabled-label-color: var(--color-lightStrokeDark);

// readonly
--checkbox-readonly-border: var(--color-lightStrokeDark);
--checkbox-readonly-label-color: var(--color-lightStrokeDark);
}
}

.checkbox {
display: inline-flex;
align-items: center;
cursor: pointer;
position: relative;

&__input {
width: 20px;
height: 20px;
opacity: 0;
position: absolute;
cursor: inherit;
}

&__square {
width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--checkbox-border);
flex-shrink: 0;
color: var(--checkbox-mark-color, var(--checkbox-border));
border-radius: 3px;
transition:
border-color 250ms,
color 250ms;
}

&__label {
color: var(--checkbox-label-color);
margin-inline-start: 12px;
transition: color 250ms;
}

&__input:not(:checked):not(.checkbox_disabled) ~ &__square {
--checkbox-mark-color: transparent !important;
}

&:not(&_error) &__input:not(:disabled):checked ~ &__square {
--checkbox-border: var(--checkbox-checked-border);
}

&:not(:hover) &__input:not(:disabled):checked ~ &__label {
--checkbox-label-color: var(--checkbox-checked-label-color);
}

&:hover {
--checkbox-border: var(--checkbox-hover-border);
--checkbox-label-color: var(--checkbox-hover-label-color);
}

// &.checkbox_error {

// }

&.checkbox_disabled {
--checkbox-border: var(--checkbox-disabled-border);
--checkbox-label-color: var(--checkbox-disabled-label-color);

cursor: not-allowed !important;
}

&.checkbox_readonly {
--checkbox-border: var(--checkbox-readonly-border);
--checkbox-label-color: var(--checkbox-readonly-label-color);
cursor: auto !important;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Meta, StoryObj } from '@storybook/react';
import Checkbox from './Checkbox';

type Story = StoryObj<typeof Checkbox>;
export default {
title: 'uikit/Checkbox',
component: Checkbox,
argTypes: {
disabled: {
description: 'Disabled',
defaultValue: false,
},
required: {
description: 'Required',
defaultValue: false,
control: 'boolean',
},
},
} as Meta<typeof Checkbox>;

export const Checkboxes: Story = {
args: {
disabled: false,
label: 'Label text',
},
render: (args) => {
return <Checkbox {...args} />;
},
};
47 changes: 47 additions & 0 deletions smart-frontend/app/src/components/uikit/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { InputHTMLAttributes, ReactNode } from 'react';
import { forwardRef } from 'react';
import cn from 'classnames';
import s from './Checkbox.module.scss';
import Icon from '@uikit/Icon/Icon';

interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
label?: ReactNode;
readOnly?: boolean;
hasError?: boolean;
}
const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
const { label, checked = false, disabled = false, className, readOnly = false, hasError = false, ...rest } = props;

const checkboxClasses = cn(
s.checkbox,
{
[s.checkbox_disabled]: disabled,
// technically, we can set readonly and disabled. It's strange but if this case then ignore readonly
[s.checkbox_readonly]: readOnly && !disabled,
[s.checkbox_error]: hasError,
},
className,
);

return (
<label className={checkboxClasses}>
<input
className={s.checkbox__input}
checked={rest.onChange ? checked : undefined}
defaultChecked={rest.onChange ? undefined : checked}
ref={ref}
disabled={disabled || readOnly}
{...rest}
type="checkbox"
/>
<div className={s.checkbox__square}>
<Icon name="check" className={s.checkbox__mark} size={10} />
</div>
{label && <span className={s.checkbox__label}>{label}</span>}
</label>
);
});

Checkbox.displayName = 'Checkbox';

export default Checkbox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

type WrapOrEmptyProps<T = object> = React.PropsWithChildren<T> & {
Component: React.FC<T>;
isWrap: boolean;
};

const ConditionalWrapper = <T,>({ Component, isWrap, children, ...props }: WrapOrEmptyProps<T>) => {
if (isWrap) {
return <Component {...(props as T)}>{children}</Component>;
}
return <>{children}</>;
};

export default ConditionalWrapper;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions smart-frontend/app/src/components/uikit/Icon/sprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const allowIconsNames = [
//
'chevron',
'close',
'check',
'eye',
'eye-closed',
'logout',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useRef } from 'react';
import cn from 'classnames';
import type { InputProps } from '@uikit/Input/Input';
import Input from '@uikit/Input/Input';
import { useForwardRef } from '@hooks/useForwardRef';
import { createChangeEvent } from '@utils/handlerUtils';
import IconButton from '@uikit/IconButton/IconButton';

const SearchInput = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {
const localRef = useRef<HTMLInputElement>(null);
const reference = useForwardRef(ref, localRef);

const handleIconClick = () => {
if (props.value && localRef.current) {
const event = createChangeEvent(localRef.current);
event.target.value = '';
props.onChange?.(event);
}
};

return (
<Input
{...props}
className={cn(className, 'search-input')}
ref={reference}
endAdornment={
<IconButton icon={props.value ? 'close' : 'search'} onClick={handleIconClick} size={12} variant="secondary" />
}
size="medium"
/>
);
});

SearchInput.displayName = 'SearchInput';
export default SearchInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.commonSelectField {
cursor: pointer;


&:global(:not(.is-active)) button > svg {
transform: rotate(-90deg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import type { InputProps } from '@uikit/Input/Input';
import Input from '@uikit/Input/Input';
import IconButton from '@uikit/IconButton/IconButton';
import cn from 'classnames';
import s from './CommonSelectField.module.scss';

type CommonSelectFieldProps = Omit<InputProps, 'endAdornment' | 'startAdornment' | 'readOnly' | 'onClick'> & {
onClick: () => void;
onClear: () => void;
isOpen: boolean;
};

const CommonSelectField = React.forwardRef<HTMLInputElement, CommonSelectFieldProps>(
({ className, onClick, onClear, isOpen, hasError, disabled, ...props }, ref) => {
const classes = cn(className, s.commonSelectField, { 'is-active': isOpen });

const handleClick = () => {
onClick?.();
};

return (
<>
<Input
//
{...props}
className={classes}
endAdornment={
!disabled && (
<>
<IconButton icon="chevron" onClick={handleClick} size={12} variant="secondary" />
</>
)
}
readOnly={true}
onClick={handleClick}
ref={ref}
hasError={hasError}
disabled={disabled}
/>
</>
);
},
);
export default CommonSelectField;

CommonSelectField.displayName = 'CommonSelectField';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.commonSelectNoResult {
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: var(--color-grayUsual);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import s from './CommonSelectNoResult.module.scss';

const CommonSelectNoResult: React.FC = () => (
<div className={s.commonSelectNoResult} data-test="no-options">
No results found
</div>
);
export default CommonSelectNoResult;
Loading