Skip to content

feat: CodeEditor - Autocomplete UI and FilterSelectPicker - selection on close #791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 26, 2025
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"yaml": "^2.4.1"
},
"dependencies": {
"@codemirror/autocomplete": "6.18.6",
"@codemirror/lang-json": "6.0.1",
"@codemirror/lang-yaml": "6.1.2",
"@codemirror/language": "6.10.8",
Expand Down
22 changes: 12 additions & 10 deletions src/Common/SegmentedControl/Segment.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactElement, useMemo } from 'react'
import { motion } from 'framer-motion'
import { AnimatePresence, motion } from 'framer-motion'

import { Tooltip } from '@Common/Tooltip'
import { Icon } from '@Shared/Components'
Expand Down Expand Up @@ -38,19 +38,21 @@ const Segment = ({ segment, isSelected, name, onChange, fullWidth, size, disable
disabled={disabled}
/>

<motion.label
<label
htmlFor={inputId}
layout
className={`pointer m-0 dc__position-rel flex ${!fullWidth ? 'left' : ''} dc__gap-4 br-4 segmented-control__segment segmented-control__segment--${size} ${isSelected ? 'fw-6 segmented-control__segment--selected' : 'fw-4'} ${segment.isError ? 'cr-5' : 'cn-9'} ${disabled ? 'cursor-not-allowed' : ''} ${COMPONENT_SIZE_TO_SEGMENT_CLASS_MAP[size]}`}
key={inputId}
aria-label={ariaLabel}
>
{isSelected && (
<motion.div
layoutId={`active-segment-control-${name}`}
className="dc__position-abs active-mask dc__top-0 dc__left-0 dc__right-0 dc__bottom-0 bg__primary br-4"
/>
)}
<AnimatePresence>
{isSelected && (
<motion.div
layoutId={`active-segment-control-${name}`}
className="dc__position-abs active-mask dc__top-0 dc__left-0 dc__right-0 dc__bottom-0 bg__primary br-4"
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
)}
</AnimatePresence>

{(isError || icon) && (
<span className={`flex ${COMPONENT_SIZE_TO_ICON_CLASS_MAP[size]}`}>
Expand All @@ -69,7 +71,7 @@ const Segment = ({ segment, isSelected, name, onChange, fullWidth, size, disable
</span>
)}
{label && <span>{label}</span>}
</motion.label>
</label>
</div>
</ConditionalWrap>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,12 @@ export const IO_VARIABLES_VALUE_COLUMN_DATE_OPTIONS: SelectPickerOptionType<stri
description: 'ISO8601 with nanoseconds',
},
]

export const VALUE_COLUMN_DROPDOWN_LABEL = {
CHOICES: 'Choices',
SUPPORTED_DATE_FORMATS: 'Supported date formats',
SYSTEM_VARIABLES: 'System variables',
PRE_BUILD_STAGE: 'From Pre-build Stage',
POST_BUILD_STAGE: 'From Post-build Stage',
PREVIOUS_STEPS: 'From Previous Steps',
} as const
3 changes: 3 additions & 0 deletions src/Shared/Components/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { useEffect, useMemo, useRef, useState } from 'react'
import { autocompletion } from '@codemirror/autocomplete'
import { foldGutter } from '@codemirror/language'
import { lintGutter } from '@codemirror/lint'
import { search } from '@codemirror/search'
Expand Down Expand Up @@ -193,6 +194,7 @@ const CodeEditor = <DiffView extends boolean = false>({
drawSelection: true,
highlightActiveLineGutter: true,
tabSize,
autocompletion: false,
}

const handleOnChange: ReactCodeMirrorProps['onChange'] = (newValue) => {
Expand All @@ -216,6 +218,7 @@ const CodeEditor = <DiffView extends boolean = false>({
// EXTENSIONS
const getBaseExtensions = (): Extension[] => [
basicSetup(basicSetupOptions),
autocompletion({ closeOnBlur: false }),
themeExtension,
keymap.of([
...vscodeKeymap.filter(({ key }) => key !== 'Mod-Alt-Enter' && key !== 'Mod-Enter' && key !== 'Mod-f'),
Expand Down
66 changes: 65 additions & 1 deletion src/Shared/Components/CodeEditor/codeEditor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,54 @@
}

.cm-tooltip-autocomplete {
background-color: var(--bg-primary);
padding: 2px;
background-color: var(--bg-menu-primary);
border: 1px solid var(--border-primary-translucent);

& > ul > li {
display: flex;
gap: 6px;
padding: 2px 4px;
color: var(--N900);
border-radius: 2px;
font-size: 13px;
line-height: 20px;
}

& > ul li[aria-selected] {
color: var(--N900);
background-color: var(--bg-hover);
}

.cm-completionDetail {
margin-left: 0;
}

.cm-completionIcon.cm-completionIcon-property {
box-sizing: border-box;
height: 14px;
width: 14px;
padding-right: 0;
font-size: 100%;
vertical-align: middle;
opacity: 1;

&::after {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='rgb(29, 39, 48)' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M12 22.015a.75.75 0 0 1-.368-.097l-8.25-4.64A.75.75 0 0 1 3 16.623V7.376a.75.75 0 0 1 .382-.654l8.25-4.64a.75.75 0 0 1 .736 0l8.25 4.64a.75.75 0 0 1 .382.654v9.248a.75.75 0 0 1-.382.654l-8.25 4.64a.75.75 0 0 1-.368.097Zm0 0L12.089 12m8.808-5.004L12.089 12m0 0L3.104 6.995' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
}
}
}

.cm-tooltip .cm-completionInfo {
padding: 3px 7px;
background-color: var(--bg-menu-primary);
color: var(--N700);
border: 1px solid var(--border-primary-translucent);
border-radius: 0;
font-size: 13px;
line-height: 1.5;
max-height: 240px;
overflow-y: auto;
}

.cm-diagnostic-error {
Expand Down Expand Up @@ -277,6 +324,23 @@
}
}

// THEME SPECIFIC STYLES
.component-specific-theme__dark {
.cm-editor {
.cm-tooltip-autocomplete .cm-completionIcon.cm-completionIcon-property::after {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='rgb(228, 229, 230)' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M12 22.015a.75.75 0 0 1-.368-.097l-8.25-4.64A.75.75 0 0 1 3 16.623V7.376a.75.75 0 0 1 .382-.654l8.25-4.64a.75.75 0 0 1 .736 0l8.25 4.64a.75.75 0 0 1 .382.654v9.248a.75.75 0 0 1-.382.654l-8.25 4.64a.75.75 0 0 1-.368.097Zm0 0L12.089 12m8.808-5.004L12.089 12m0 0L3.104 6.995' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
}

.cm-lint-marker-error {
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><g transform="translate(2.667 2.667)"><rect width="26.667" height="26.667" class="fill-color" fill="%23FF5D60" rx="13.333"/><path fill="%23FFF" fill-rule="nonzero" d="M8.62 8.62c.52-.521 1.364-.521 1.885 0l2.828 2.827 2.829-2.828a1.334 1.334 0 0 1 1.777-.097l.108.097c.521.521.521 1.365 0 1.886l-2.828 2.828 2.828 2.829c.486.486.519 1.254.098 1.777l-.098.108c-.52.521-1.365.521-1.885 0l-2.83-2.828-2.827 2.828a1.334 1.334 0 0 1-1.777.098l-.109-.098a1.333 1.333 0 0 1 0-1.885l2.828-2.83-2.828-2.827a1.334 1.334 0 0 1-.097-1.777z"/></g></g></svg>');
}

.cm-lint-marker-warning {
content: url('data:image/svg+xml,<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.702 3.74857L2.45423 17.9978C2.32226 18.2259 2.25266 18.4846 2.25244 18.748C2.25222 19.0115 2.32139 19.2703 2.45299 19.4986C2.58459 19.7268 2.77397 19.9163 3.00209 20.0481C3.2302 20.1799 3.489 20.2493 3.75245 20.2493H20.248C20.5114 20.2493 20.7702 20.1799 20.9984 20.0481C21.2265 19.9163 21.4159 19.7268 21.5474 19.4986C21.679 19.2703 21.7482 19.0115 21.748 18.748C21.7478 18.4846 21.6782 18.2258 21.5462 17.9978L13.2984 3.74857C13.1667 3.52093 12.9774 3.33193 12.7495 3.20055C12.5216 3.06916 12.2632 3 12.0002 3C11.7372 3 11.4788 3.06916 11.2509 3.20055C11.0231 3.33193 10.8338 3.52093 10.702 3.74857Z" fill="%23F4BA63"/><path d="M13 9.75C13 9.19772 12.5523 8.75 12 8.75C11.4477 8.75 11 9.19772 11 9.75V13.5C11 14.0523 11.4477 14.5 12 14.5C12.5523 14.5 13 14.0523 13 13.5V9.75Z" fill="%23000A14"/><path d="M13.125 16.875C13.125 17.4963 12.6213 18 12 18C11.3787 18 10.875 17.4963 10.875 16.875C10.875 16.2537 11.3787 15.75 12 15.75C12.6213 15.75 13.125 16.2537 13.125 16.875Z" fill="%23000A14"/></svg>');
}
}
}

// MINIMAP STYLES
.code-editor__mini-map {
user-select: none;
Expand Down
7 changes: 1 addition & 6 deletions src/Shared/Components/SelectPicker/FilterSelectPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,6 @@ const FilterSelectPicker = ({
setSelectedOptions(structuredClone(selectedOptionsToUpdate) as SelectPickerOptionType[])
}

const handleMenuClose = () => {
closeMenu()
setSelectedOptions(structuredClone(appliedFilterOptions ?? []))
}

const handleApplyClick: ButtonProps['onClick'] = (e) => {
handleApplyFilter(selectedOptions)
resetTriggerAutoClickTimestamp()
Expand Down Expand Up @@ -113,7 +108,7 @@ const FilterSelectPicker = ({
isMulti
menuIsOpen={isMenuOpen}
onMenuOpen={openMenu}
onMenuClose={handleMenuClose}
onMenuClose={handleApplyClick as () => void}
onChange={handleSelectOnChange}
menuListFooterConfig={{
type: 'button',
Expand Down
Loading