diff --git a/frontend/src/component/common/GeneralSelect/GeneralSelect.tsx b/frontend/src/component/common/GeneralSelect/GeneralSelect.tsx index b5844de7f1..b8f7a1a77d 100644 --- a/frontend/src/component/common/GeneralSelect/GeneralSelect.tsx +++ b/frontend/src/component/common/GeneralSelect/GeneralSelect.tsx @@ -33,6 +33,7 @@ export interface IGeneralSelectProps classes?: any; defaultValue?: string; visuallyHideLabel?: boolean; + variant?: 'outlined' | 'filled' | 'standard'; } const StyledFormControl = styled(FormControl)({ @@ -40,6 +41,7 @@ const StyledFormControl = styled(FormControl)({ }); function GeneralSelect({ + variant = 'outlined', name, value, label = '', @@ -61,7 +63,7 @@ function GeneralSelect({ return ( void; + inverted?: boolean; +} + +const StyledSelect = styled(Select)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(0.25, 0), + fontSize: theme.fontSizes.smallerBody, + background: theme.palette.secondary.light, + border: `1px solid ${theme.palette.secondary.border}`, + color: theme.palette.secondary.dark, + fontWeight: theme.typography.fontWeightBold, + fieldset: { + border: 'none', + }, + transition: 'all 0.03s ease', + '&:is(:hover, :focus-within)': { + outline: `1px solid ${theme.palette.primary.main}`, + }, + '&::before,&::after': { + border: 'none', + }, + '.MuiInput-input': { + paddingBlock: theme.spacing(0.25), + }, +})); + +const StyledMenuItem = styled(MenuItem, { + shouldForwardProp: (prop) => prop !== 'separator', +})<{ separator: boolean }>(({ theme, separator }) => + separator + ? { + position: 'relative', + overflow: 'visible', + marginTop: theme.spacing(2), + '&:before': { + content: '""', + display: 'block', + position: 'absolute', + top: theme.spacing(-1), + left: 0, + right: 0, + borderTop: '1px solid', + borderTopColor: theme.palette.divider, + }, + } + : {}, +); + +const StyledValue = styled('span')(({ theme }) => ({ + paddingInline: theme.spacing(1), +})); + +export const ConstraintOperatorSelect = ({ + options, + value, + onChange, + inverted, +}: IConstraintOperatorSelectProps) => { + const selectId = useId(); + const labelId = useId(); + const onSelectChange = (event: SelectChangeEvent) => { + onChange(event.target.value as Operator); + }; + + const renderValue = () => { + return ( + + {formatOperatorDescription(value, inverted)} + + ); + }; + + return ( + + + + Operator + + + + {options.map((operator) => ( + + {formatOperatorDescription(operator, inverted)} + + ))} + + + ); +}; + +const needSeparatorAbove = (options: Operator[], option: Operator): boolean => { + if (option === options[0]) { + return false; + } + + return operatorGroups.some((group) => { + return group[0] === option; + }); +}; + +const operatorGroups = [ + inOperators, + stringOperators, + numOperators, + dateOperators, + semVerOperators, +]; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx index 9209a71f2f..fc45670686 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx @@ -1,4 +1,4 @@ -import { styled } from '@mui/material'; +import { IconButton, styled } from '@mui/material'; import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; import { DateSingleValue } from 'component/common/NewConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/DateSingleValue/DateSingleValue'; import { FreeTextInput } from 'component/common/NewConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput'; @@ -17,8 +17,6 @@ import { STRING_OPERATORS_LEGAL_VALUES, type Input, } from 'component/common/NewConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/useConstraintInput/useConstraintInput'; -import { CaseSensitiveButton } from 'component/common/NewConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/CaseSensitiveButton/CaseSensitiveButton'; -import { ConstraintOperatorSelect } from 'component/common/NewConstraintAccordion/ConstraintOperatorSelect'; import { DATE_AFTER, dateOperators, @@ -38,14 +36,26 @@ import { CURRENT_TIME_CONTEXT_FIELD, operatorsForContext, } from 'utils/operatorsForContext'; +import { ConstraintOperatorSelect } from './ConstraintOperatorSelect'; +import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; +import Delete from '@mui/icons-material/Delete'; const Container = styled('article')(({ theme }) => ({ + '--padding': theme.spacing(2), backgroundColor: theme.palette.background.paper, - padding: theme.spacing(2), borderRadius: theme.shape.borderRadiusLarge, border: `1px solid ${theme.palette.divider}`, })); +const TopRow = styled('div')(({ theme }) => ({ + padding: 'var(--padding)', + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyItems: 'space-between', + borderBottom: `1px dashed ${theme.palette.divider}`, +})); + const resolveLegalValues = ( values: IConstraint['values'], legalValues: IUnleashContextDefinition['legalValues'], @@ -72,6 +82,46 @@ const resolveLegalValues = ( }; }; +const ConstraintDetails = styled('div')(({ theme }) => ({ + display: 'flex', + gap: theme.spacing(1), + flexFlow: 'row nowrap', + width: '100%', + height: 'min-content', +})); + +const InputContainer = styled('div')(({ theme }) => ({ + padding: 'var(--padding)', + paddingTop: 0, +})); + +const StyledSelect = styled(GeneralSelect)(({ theme }) => ({ + fieldset: { border: 'none', borderRadius: 0 }, + ':focus-within fieldset': { borderBottomStyle: 'solid' }, + 'label + &': { + // mui adds a margin top to 'standard' selects with labels + margin: 0, + }, + '&::before': { + border: 'none', + }, +})); + +const StyledButton = styled('button')(({ theme }) => ({ + width: '5ch', + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(0.25, 0), + fontSize: theme.fontSizes.smallerBody, + background: theme.palette.secondary.light, + border: `1px solid ${theme.palette.secondary.border}`, + color: theme.palette.secondary.dark, + fontWeight: theme.typography.fontWeightBold, + transition: 'all 0.03s ease', + '&:is(:hover, :focus-visible)': { + outline: `1px solid ${theme.palette.primary.main}`, + }, +})); + type Props = { localConstraint: IConstraint; setContextName: (contextName: string) => void; @@ -79,8 +129,8 @@ type Props = { setLocalConstraint: React.Dispatch>; action: string; onDelete?: () => void; - setInvertedOperator: () => void; - setCaseInsensitive: () => void; + toggleInvertedOperator: () => void; + toggleCaseSensitivity: () => void; onUndo: () => void; constraintChanges: IConstraint[]; contextDefinition: Pick; @@ -102,8 +152,8 @@ export const EditableConstraint: FC = ({ setOperator, onDelete, onUndo, - setInvertedOperator, - setCaseInsensitive, + toggleInvertedOperator, + toggleCaseSensitivity, input, contextDefinition, constraintValues, @@ -175,7 +225,7 @@ export const EditableConstraint: FC = ({ } }; - const resolveInput = () => { + const Input = () => { switch (input) { case IN_OPERATORS_LEGAL_VALUES: case STRING_OPERATORS_LEGAL_VALUES: @@ -291,31 +341,43 @@ export const EditableConstraint: FC = ({ return ( - - + + + - {/* this is how to style them */} - {/* */} - {showCaseSensitiveButton ? ( - - ) : null} - {resolveInput()} - {/*
    + + {localConstraint.inverted ? 'aint' : 'is'} + + + + + {showCaseSensitiveButton ? ( + + {localConstraint.caseInsensitive ? 'Aa' : 'A/a'} + + ) : null} + {/*
    • = ({ />
    */} + + + + + + + + + + + ); }; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraintWrapper.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraintWrapper.tsx index 44b69b4b57..c4379c81d1 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraintWrapper.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraintWrapper.tsx @@ -191,8 +191,8 @@ export const EditableConstraintWrapper = ({ setContextName={setContextName} setOperator={setOperator} action={action} - setInvertedOperator={setInvertedOperator} - setCaseInsensitive={setCaseInsensitive} + toggleInvertedOperator={setInvertedOperator} + toggleCaseSensitivity={setCaseInsensitive} onDelete={onDelete} onUndo={onUndo} constraintChanges={constraintChanges}