From 2bd05c014867db73630e2152c6ac719d96543d92 Mon Sep 17 00:00:00 2001 From: Federico Ruggi <1081051+ruggi@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:51:39 +0200 Subject: [PATCH] Grid advanced modal: visible allowed values outside of shown options for justify/align dropdown (#6585) **Problem:** Acceptable values for the dropdowns in the grid advanced modal for justify/align content should still be visible in the dropdown when set, even if we don't show them in the full dropdown itself by default. **Fix:** 1. Add the missing acceptable values to the type enums in `flex.ts` 2. Restrict which values we show in the dropdown to the ones we had so far (we can tweak this as we go) 3. Add the ability to show an extra dropdown row at the top of the list if its value is allowed for that dropdown and it's not found in the available options https://github.com/user-attachments/assets/9c22d25e-6ac1-4569-945d-e85d22e5fdb2 Fixes #6584 --- .../components/inspector/common/css-utils.ts | 9 ++++ .../controls/advanced-grid-modal.tsx | 22 +++++++-- editor/src/uuiui/radix-components.tsx | 49 ++++++++++++++++++- utopia-api/src/layout/flex.ts | 16 ++++++ 4 files changed, 91 insertions(+), 5 deletions(-) diff --git a/editor/src/components/inspector/common/css-utils.ts b/editor/src/components/inspector/common/css-utils.ts index eb74150ed104..0e982adcfddb 100644 --- a/editor/src/components/inspector/common/css-utils.ts +++ b/editor/src/components/inspector/common/css-utils.ts @@ -4620,6 +4620,11 @@ const flexAlignmentsParser: Parser = isOneOfTheseParser([ FlexAlignment.Center, FlexAlignment.FlexEnd, FlexAlignment.Stretch, + FlexAlignment.Baseline, + FlexAlignment.FirstBaseline, + FlexAlignment.LastBaseline, + FlexAlignment.SafeCenter, + FlexAlignment.UnsafeCenter, ]) const flexJustifyContentParser: Parser = isOneOfTheseParser([ @@ -4629,6 +4634,10 @@ const flexJustifyContentParser: Parser = isOneOfTheseParser( FlexJustifyContent.SpaceAround, FlexJustifyContent.SpaceBetween, FlexJustifyContent.SpaceEvenly, + FlexJustifyContent.Stretch, + FlexJustifyContent.Normal, + FlexJustifyContent.SafeCenter, + FlexJustifyContent.UnsafeCenter, ]) export type CSSPosition = '-webkit-sticky' | 'absolute' | 'fixed' | 'relative' | 'static' | 'sticky' diff --git a/editor/src/components/inspector/controls/advanced-grid-modal.tsx b/editor/src/components/inspector/controls/advanced-grid-modal.tsx index 463dd8ff574e..f42b93c157c9 100644 --- a/editor/src/components/inspector/controls/advanced-grid-modal.tsx +++ b/editor/src/components/inspector/controls/advanced-grid-modal.tsx @@ -14,7 +14,7 @@ import { separatorRadixSelectOption, } from '../../../uuiui/radix-components' import { optionalMap } from '../../../core/shared/optional-utils' -import { FlexAlignment } from 'utopia-api/core' +import { AllFlexAlignments, AllFlexJustifyContents, FlexAlignment } from 'utopia-api/core' import { FlexJustifyContent } from 'utopia-api/core' import { GridAutoColsOrRowsControlInner } from '../grid-auto-cols-or-rows-control' import { Substores, useEditorState, useRefEditorState } from '../../editor/store/store-hook' @@ -82,13 +82,27 @@ export const AdvancedGridModal = React.memo((props: AdvancedGridModalProps) => { const justifyOptions = [ unsetSelectOption, separatorRadixSelectOption(), - ...Object.values(FlexJustifyContent).map(selectOption), + ...[ + FlexJustifyContent.FlexStart, + FlexJustifyContent.Center, + FlexJustifyContent.FlexEnd, + FlexJustifyContent.SpaceAround, + FlexJustifyContent.SpaceBetween, + FlexJustifyContent.SpaceEvenly, + FlexJustifyContent.Stretch, + ].map(selectOption), ] const alignOptions = [ unsetSelectOption, separatorRadixSelectOption(), - ...Object.values(FlexAlignment).map(selectOption), + ...[ + FlexAlignment.Auto, + FlexAlignment.FlexStart, + FlexAlignment.Center, + FlexAlignment.FlexEnd, + FlexAlignment.Stretch, + ].map(selectOption), ] const onSubmitJustifyContent = React.useCallback( @@ -200,6 +214,7 @@ export const AdvancedGridModal = React.memo((props: AdvancedGridModalProps) => { zIndex: 1, }} onOpenChange={toggleJustifyContentDropdown} + allowedValues={AllFlexJustifyContents} /> { zIndex: 1, }} onOpenChange={toggleAlignContentDropdown} + allowedValues={AllFlexAlignments} /> diff --git a/editor/src/uuiui/radix-components.tsx b/editor/src/uuiui/radix-components.tsx index 189d596eac52..77feb64fe1db 100644 --- a/editor/src/uuiui/radix-components.tsx +++ b/editor/src/uuiui/radix-components.tsx @@ -12,6 +12,7 @@ import { when } from '../utils/react-conditionals' import { Icn, type IcnProps } from './icn' import { forceNotNull } from '../core/shared/optional-utils' import { usePropControlledStateV2 } from '../components/inspector/common/inspector-utils' +import { assertNever } from '../core/shared/utils' // Keep this in sync with the radix-components-portal div in index.html. export const RadixComponentsPortalId = 'radix-components-portal' @@ -256,6 +257,29 @@ export function separatorRadixSelectOption(): Separator { export type RadixSelectOption = RegularRadixSelectOption | Separator +function equalRadixSelectOptions( + a: RadixSelectOption | null, + b: RadixSelectOption | null, +): boolean { + if (a == null && b == null) { + return true + } + if (a == null || b == null) { + return false + } + switch (a.type) { + case 'REGULAR': + if (b.type !== 'REGULAR') { + return false + } + return a.value === b.value && a.label === b.label + case 'SEPARATOR': + return b.type === 'SEPARATOR' + default: + assertNever(a) + } +} + function optionLabelToString( option: RegularRadixSelectOption | null, isOpen: boolean, @@ -280,6 +304,7 @@ export const RadixSelect = React.memo( onValueChange?: (value: string) => void contentClassName?: string onOpenChange?: (open: boolean) => void + allowedValues?: string[] }) => { const stopPropagation = React.useCallback((e: React.KeyboardEvent) => { e.stopPropagation() @@ -296,7 +321,27 @@ export const RadixSelect = React.memo( [propsOnOpenChange], ) - const valueLabel = optionLabelToString(props.value ?? null, isOpen, props.value?.value ?? null) + const valueLabel = React.useMemo(() => { + return optionLabelToString(props.value ?? null, isOpen, props.value?.value ?? null) + }, [props.value, isOpen]) + + const options = React.useMemo(() => { + let fullOptions = [...props.options] + + if ( + // the value is not null + props.value != null && + // the value is allowed for this dropdown + props.allowedValues?.some((allowed) => allowed === props.value?.value) && + // the options don't contain the value already + !fullOptions.some((opt) => equalRadixSelectOptions(opt, props.value)) + ) { + // add the given option + separator at the top of the options + fullOptions.unshift(...[props.value, separatorDropdownMenuItem('unknown-dropdown-value')]) + } + + return fullOptions + }, [props.options, props.value, props.allowedValues]) return ( - {props.options.map((option, index) => { + {options.map((option, index) => { if (option.type === 'SEPARATOR') { return ( = [ @@ -206,6 +211,11 @@ export const AllFlexAlignments: Array = [ FlexAlignment.Center, FlexAlignment.FlexEnd, FlexAlignment.Stretch, + FlexAlignment.Baseline, + FlexAlignment.FirstBaseline, + FlexAlignment.LastBaseline, + FlexAlignment.SafeCenter, + FlexAlignment.UnsafeCenter, ] export enum FlexJustifyContent { @@ -216,6 +226,9 @@ export enum FlexJustifyContent { SpaceBetween = 'space-between', SpaceEvenly = 'space-evenly', Stretch = 'stretch', + Normal = 'normal', + SafeCenter = 'safe center', + UnsafeCenter = 'unsafe center', } export const AllFlexJustifyContents: Array = [ @@ -226,6 +239,9 @@ export const AllFlexJustifyContents: Array = [ FlexJustifyContent.SpaceBetween, FlexJustifyContent.SpaceEvenly, FlexJustifyContent.Stretch, + FlexJustifyContent.Normal, + FlexJustifyContent.SafeCenter, + FlexJustifyContent.UnsafeCenter, ] export enum FlexDirection {