Skip to content

Commit a5a07ce

Browse files
Merge pull request #755 from devtron-labs/fix/app-details-manifest-layout
feat: add smoothScrollToTop and expose onSearchAction event on CodeEditor
2 parents 92b6081 + 769438b commit a5a07ce

File tree

10 files changed

+114
-64
lines changed

10 files changed

+114
-64
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtron-labs/devtron-fe-common-lib",
3-
"version": "1.14.1-pre-5",
3+
"version": "1.14.1-pre-6",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",

src/Assets/IconV2/ic-key-enter.svg

Lines changed: 3 additions & 0 deletions
Loading

src/Shared/Components/CodeEditor/CodeEditor.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
} from '@uiw/react-codemirror'
3434

3535
import { DEFAULT_JSON_SCHEMA_URI, MODES } from '@Common/Constants'
36-
import { cleanKubeManifest } from '@Common/Helper'
36+
import { cleanKubeManifest, noop } from '@Common/Helper'
3737
import { getUniqueId } from '@Shared/Helpers'
3838
import { AppThemeType, useTheme } from '@Shared/Providers'
3939

@@ -48,7 +48,7 @@ import {
4848
replaceAll,
4949
showReplaceFieldState,
5050
} from './Commands'
51-
import { codeEditorFindReplace, readOnlyTooltip, yamlHighlight } from './Extensions'
51+
import { getCodeEditorFindReplace, readOnlyTooltip, yamlHighlight } from './Extensions'
5252
import { CodeEditorContextProps, CodeEditorProps } from './types'
5353
import { getFoldGutterElement, getLanguageExtension, getValidationSchema, parseValueToCode } from './utils'
5454

@@ -87,6 +87,8 @@ const CodeEditor = <DiffView extends boolean = false>({
8787
onBlur,
8888
onFocus,
8989
autoFocus,
90+
onSearchPanelOpen = noop,
91+
onSearchBarAction = noop,
9092
collapseUnchangedDiffView = false,
9193
...resProps
9294
}: CodeEditorProps<DiffView>) => {
@@ -201,23 +203,33 @@ const CodeEditor = <DiffView extends boolean = false>({
201203
setLhsCode(newLhsValue)
202204
}
203205

206+
const openSearchPanelWrapper: typeof openSearchPanel = (view) => {
207+
onSearchPanelOpen()
208+
return openSearchPanel(view)
209+
}
210+
211+
const openSearchPanelWithReplaceWrapper: typeof openSearchPanelWithReplace = (view) => {
212+
onSearchPanelOpen()
213+
return openSearchPanelWithReplace(view)
214+
}
215+
204216
// EXTENSIONS
205217
const getBaseExtensions = (): Extension[] => [
206218
basicSetup(basicSetupOptions),
207219
themeExtension,
208220
keymap.of([
209221
...vscodeKeymap.filter(({ key }) => key !== 'Mod-Alt-Enter' && key !== 'Mod-Enter' && key !== 'Mod-f'),
210-
...(!disableSearch ? [{ key: 'Mod-f', run: openSearchPanel, scope: 'editor search-panel' }] : []),
222+
...(!disableSearch ? [{ key: 'Mod-f', run: openSearchPanelWrapper, scope: 'editor search-panel' }] : []),
211223
{ key: 'Mod-Enter', run: replaceAll, scope: 'editor search-panel' },
212-
{ key: 'Mod-Alt-f', run: openSearchPanelWithReplace, scope: 'editor search-panel' },
224+
{ key: 'Mod-Alt-f', run: openSearchPanelWithReplaceWrapper, scope: 'editor search-panel' },
213225
{ key: 'Escape', run: blurOnEscape, stopPropagation: true },
214226
]),
215227
indentationMarkers(),
216228
getLanguageExtension(mode, collapseUnchangedDiffView),
217229
foldingCompartment.of(foldConfig),
218230
lintGutter(),
219231
search({
220-
createPanel: codeEditorFindReplace,
232+
createPanel: getCodeEditorFindReplace(onSearchBarAction),
221233
}),
222234
showReplaceFieldState,
223235
...(mode === MODES.YAML ? [yamlHighlight] : []),

src/Shared/Components/CodeEditor/Extensions/findAndReplace.tsx

Lines changed: 62 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import {
5050
REPLACE_SHORTCUT_KEYS,
5151
} from '../CodeEditor.constants'
5252
import { getShowReplaceField, setShowReplaceField } from '../Commands'
53-
import { FindReplaceProps, FindReplaceQuery, FindReplaceToggleButtonProps } from '../types'
53+
import { CodeEditorProps, FindReplaceProps, FindReplaceQuery, FindReplaceToggleButtonProps } from '../types'
5454
import { getFindReplaceToggleButtonIconClass, getUpdatedSearchMatchesCount } from '../utils'
5555

5656
const FindReplaceToggleButton = ({
@@ -85,7 +85,7 @@ const FindReplaceToggleButton = ({
8585
)
8686
}
8787

88-
const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProps) => {
88+
const FindReplace = ({ view, defaultQuery, defaultShowReplace, onSearchBarAction }: FindReplaceProps) => {
8989
// STATES
9090
const [query, setQuery] = useState<SearchQuery>(new SearchQuery({ search: '' }))
9191
const [matchesCount, setMatchesCount] = useState({ count: 0, current: 1 })
@@ -103,6 +103,9 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProp
103103
search = query.search,
104104
wholeWord = query.wholeWord,
105105
}: FindReplaceQuery) => {
106+
// Calling this irrespective of whether the query has changed or not
107+
onSearchBarAction()
108+
106109
const newQuery = new SearchQuery({
107110
caseSensitive,
108111
regexp,
@@ -120,6 +123,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProp
120123

121124
useEffect(() => {
122125
if (!defaultQuery.eq(query)) {
126+
onSearchBarAction()
123127
setMatchesCount(getUpdatedSearchMatchesCount(defaultQuery, view))
124128
setQuery(defaultQuery)
125129
}
@@ -144,13 +148,15 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProp
144148
const onNext = (e?: MouseEvent<HTMLButtonElement>) => {
145149
e?.preventDefault()
146150
e?.stopPropagation()
151+
onSearchBarAction()
147152
findNext(view)
148153
setMatchesCount(getUpdatedSearchMatchesCount(query, view))
149154
}
150155

151156
const onPrevious = (e?: MouseEvent<HTMLButtonElement>) => {
152157
e?.preventDefault()
153158
e?.stopPropagation()
159+
onSearchBarAction()
154160
findPrevious(view)
155161
setMatchesCount(getUpdatedSearchMatchesCount(query, view))
156162
}
@@ -179,6 +185,7 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProp
179185
e.stopPropagation()
180186
if (e.key === 'Enter') {
181187
e.preventDefault()
188+
onSearchBarAction()
182189
replaceNext(view)
183190
}
184191
}
@@ -188,10 +195,12 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProp
188195
}
189196

190197
const onReplaceTextClick = () => {
198+
onSearchBarAction()
191199
replaceNext(view)
192200
}
193201

194202
const onReplaceTextAllClick = () => {
203+
onSearchBarAction()
195204
replaceAll(view)
196205
}
197206

@@ -431,62 +440,65 @@ const FindReplace = ({ view, defaultQuery, defaultShowReplace }: FindReplaceProp
431440
)
432441
}
433442

434-
export const codeEditorFindReplace = (view: EditorView): Panel => {
435-
const dom = document.createElement('div')
443+
export const getCodeEditorFindReplace =
444+
(onSearchBarAction: CodeEditorProps['onSearchBarAction']) =>
445+
(view: EditorView): Panel => {
446+
const dom = document.createElement('div')
436447

437-
const keydown = (e: KeyboardEvent) => {
438-
if (runScopeHandlers(view, e, 'search-panel')) {
439-
e.preventDefault()
440-
e.stopPropagation()
448+
const keydown = (e: KeyboardEvent) => {
449+
if (runScopeHandlers(view, e, 'search-panel')) {
450+
e.preventDefault()
451+
e.stopPropagation()
452+
}
441453
}
442-
}
443454

444-
dom.className =
445-
'code-editor__search mt-8 mb-4 mr-8 ml-auto p-5 bg__secondary dc__border br-4 dc__w-fit-content fs-14'
446-
dom.onkeydown = keydown
447-
448-
const renderFindReplace = () => {
449-
render(
450-
<FindReplace
451-
view={view}
452-
defaultQuery={getSearchQuery(view.state)}
453-
defaultShowReplace={getShowReplaceField(view.state)}
454-
/>,
455-
dom,
456-
)
457-
}
455+
dom.className =
456+
'code-editor__search mt-8 mb-4 mr-8 ml-auto p-5 bg__secondary dc__border br-4 dc__w-fit-content fs-14'
457+
dom.onkeydown = keydown
458+
459+
const renderFindReplace = () => {
460+
render(
461+
<FindReplace
462+
view={view}
463+
defaultQuery={getSearchQuery(view.state)}
464+
defaultShowReplace={getShowReplaceField(view.state)}
465+
onSearchBarAction={onSearchBarAction}
466+
/>,
467+
dom,
468+
)
469+
}
458470

459-
const mount = () => {
460-
requestAnimationFrame(() => {
461-
const findField = document.querySelector('[data-code-editor-find]') as HTMLInputElement
462-
findField?.focus()
463-
findField?.select()
464-
})
465-
}
471+
const mount = () => {
472+
requestAnimationFrame(() => {
473+
const findField = document.querySelector('[data-code-editor-find]') as HTMLInputElement
474+
findField?.focus()
475+
findField?.select()
476+
})
477+
}
466478

467-
const update = ({ transactions, docChanged, state, startState }: ViewUpdate) => {
468-
transactions.forEach((tr) => {
469-
tr.effects.forEach((effect) => {
470-
if (effect.is(setSearchQuery)) {
471-
renderFindReplace()
472-
}
473-
if (effect.is(setShowReplaceField)) {
474-
renderFindReplace()
475-
}
479+
const update = ({ transactions, docChanged, state, startState }: ViewUpdate) => {
480+
transactions.forEach((tr) => {
481+
tr.effects.forEach((effect) => {
482+
if (effect.is(setSearchQuery)) {
483+
renderFindReplace()
484+
}
485+
if (effect.is(setShowReplaceField)) {
486+
renderFindReplace()
487+
}
488+
})
476489
})
477-
})
478490

479-
if (docChanged || state.readOnly !== startState.readOnly) {
480-
renderFindReplace()
491+
if (docChanged || state.readOnly !== startState.readOnly) {
492+
renderFindReplace()
493+
}
481494
}
482-
}
483495

484-
renderFindReplace()
496+
renderFindReplace()
485497

486-
return {
487-
top: true,
488-
dom,
489-
mount,
490-
update,
498+
return {
499+
top: true,
500+
dom,
501+
mount,
502+
update,
503+
}
491504
}
492-
}

src/Shared/Components/CodeEditor/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ export type CodeEditorProps<DiffView extends boolean = false> = {
8484
disableSearch?: boolean
8585
diffView?: DiffView
8686
theme?: AppThemeType
87+
onSearchPanelOpen?: () => void
88+
/**
89+
* This method is triggered when user types something in the search/replace bar or applies a search or replace action.
90+
*/
91+
onSearchBarAction?: () => void
8792
} & CodeEditorPropsBasedOnDiffView<DiffView>
8893

8994
export interface GetCodeEditorHeightReturnType {
@@ -102,7 +107,7 @@ export type FindReplaceQuery = Partial<
102107
Pick<SearchQuery, 'search' | 'wholeWord' | 'regexp' | 'replace' | 'caseSensitive'>
103108
>
104109

105-
export interface FindReplaceProps {
110+
export interface FindReplaceProps extends Pick<CodeEditorProps, 'onSearchBarAction'> {
106111
view: EditorView
107112
/** Default value for Search Query state. */
108113
defaultQuery: SearchQuery

src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Backdrop } from '../Backdrop'
2525
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
2626
import { Confetti } from '../Confetti'
2727
import { CustomInput } from '../CustomInput'
28+
import { Icon } from '../Icon'
2829
import { useConfirmationModalContext } from './ConfirmationModalContext'
2930
import { ConfirmationModalBodyProps, ConfirmationModalProps, ConfirmationModalVariantType } from './types'
3031
import { getConfirmationLabel, getIconFromVariant, getPrimaryButtonStyleFromVariant } from './utils'
@@ -34,7 +35,7 @@ import './confirmationModal.scss'
3435
const ConfirmationModalBody = ({
3536
title,
3637
subtitle,
37-
Icon,
38+
Icon: ButtonIcon,
3839
variant,
3940
buttonConfig,
4041
confirmationConfig,
@@ -53,8 +54,8 @@ const ConfirmationModalBody = ({
5354

5455
const { primaryButtonConfig, secondaryButtonConfig } = buttonConfig
5556

56-
const RenderIcon = Icon ?? getIconFromVariant(variant)
57-
const hideIcon = variant === ConfirmationModalVariantType.custom && !Icon
57+
const RenderIcon = ButtonIcon ?? getIconFromVariant(variant)
58+
const hideIcon = variant === ConfirmationModalVariantType.custom && !ButtonIcon
5859

5960
const disablePrimaryButton: boolean =
6061
('disabled' in primaryButtonConfig && primaryButtonConfig.disabled) ||
@@ -159,7 +160,7 @@ const ConfirmationModalBody = ({
159160
text={primaryButtonConfig.text}
160161
onClick={primaryButtonConfig.onClick as ButtonHTMLAttributes<HTMLButtonElement>['onClick']}
161162
startIcon={primaryButtonConfig.startIcon}
162-
endIcon={primaryButtonConfig.endIcon}
163+
endIcon={primaryButtonConfig.endIcon || <Icon name="ic-key-enter" color={null} />}
163164
/>
164165
)}
165166
</div>

src/Shared/Components/Icon/Icon.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ import { ReactComponent as ICJobColor } from '@IconsV2/ic-job-color.svg'
9898
import { ReactComponent as ICK3s } from '@IconsV2/ic-k3s.svg'
9999
import { ReactComponent as ICK8sJob } from '@IconsV2/ic-k8s-job.svg'
100100
import { ReactComponent as ICKey } from '@IconsV2/ic-key.svg'
101+
import { ReactComponent as ICKeyEnter } from '@IconsV2/ic-key-enter.svg'
101102
import { ReactComponent as ICKind } from '@IconsV2/ic-kind.svg'
102103
import { ReactComponent as ICLaptop } from '@IconsV2/ic-laptop.svg'
103104
import { ReactComponent as ICLdap } from '@IconsV2/ic-ldap.svg'
@@ -260,6 +261,7 @@ export const iconMap = {
260261
'ic-job-color': ICJobColor,
261262
'ic-k3s': ICK3s,
262263
'ic-k8s-job': ICK8sJob,
264+
'ic-key-enter': ICKeyEnter,
263265
'ic-key': ICKey,
264266
'ic-kind': ICKind,
265267
'ic-laptop': ICLaptop,

src/Shared/Helpers.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ReactElement, useEffect, useRef, useState } from 'react'
1919
import { PromptProps } from 'react-router-dom'
2020
import { StrictRJSFSchema } from '@rjsf/utils'
2121
import Tippy from '@tippyjs/react'
22+
import { animate } from 'framer-motion'
2223
import moment from 'moment'
2324
import { nanoid } from 'nanoid'
2425
import { Pair } from 'yaml'
@@ -52,7 +53,7 @@ import {
5253
} from '../Common'
5354
import { getAggregator } from '../Pages'
5455
import { AggregatedNodes, PodMetadatum } from './Components'
55-
import { UNSAVED_CHANGES_PROMPT_MESSAGE } from './constants'
56+
import { CUBIC_BEZIER_CURVE, UNSAVED_CHANGES_PROMPT_MESSAGE } from './constants'
5657
import {
5758
AggregationKeys,
5859
BorderConfigType,
@@ -700,3 +701,16 @@ export const getAppDetailsURL = (appId: number | string, envId?: number | string
700701
}
701702
return baseURL
702703
}
704+
705+
export const smoothScrollToTop = (scrollContainer: HTMLElement, targetPosition: number) => {
706+
const start = scrollContainer.scrollTop
707+
708+
const controls = animate(start, targetPosition, {
709+
ease: CUBIC_BEZIER_CURVE,
710+
onUpdate: (value) => {
711+
scrollContainer.scrollTop = value
712+
},
713+
})
714+
715+
return controls
716+
}

src/Shared/constants.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,3 +580,4 @@ export const DEPLOYMENT_STAGE_TO_NODE_MAP: Readonly<Record<DeploymentStageType,
580580

581581
export const APP_DETAILS_FALLBACK_POLLING_INTERVAL = 30000
582582
export const PROGRESSING_DEPLOYMENT_STATUS_POLLING_INTERVAL = 10000
583+
export const CUBIC_BEZIER_CURVE: [number, number, number, number] = [0.33, 1, 0.68, 1]

0 commit comments

Comments
 (0)