-
Notifications
You must be signed in to change notification settings - Fork 4k
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
feat(dashboard): Digest liquid helper and popover handler #7439
Merged
Merged
Changes from 61 commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
997c1f8
fix(api): Allow arbitrary variables on the payload namespace
SokratisVidros feaf04c
wup:
scopsy 163ad78
fix: items
scopsy b2111ec
Update email-subject.tsx
scopsy 65733c8
fix: hello world
scopsy 624383a
fix: re order
scopsy 75d2c7d
fix: state
scopsy faa410b
fix: refactor field editor
scopsy 96acb70
j
scopsy 80ed1f3
fix: update other field
scopsy 6ec81ab
fix: refactor
scopsy 25141c8
fix: item
scopsy c32cd7e
fix:
scopsy 2c3e6a9
fix: cursor
scopsy bf9324a
Update field-editor.tsx
scopsy fefc2cc
fix: a
scopsy 96ffc1b
Merge branch 'next' into pills-for-all-inputs
scopsy ed3eaae
Merge branch 'next' into pills-for-all-inputs
scopsy c6be4ab
Merge branch 'next' into pills-for-all-inputs
scopsy ec924ed
fix: open prs
scopsy e717bc8
fix: design
scopsy e56a5c6
fix:
scopsy c83af5c
Update variable-popover.tsx
scopsy 93c1d46
fix: it worked
scopsy 50a25c9
fix: refactor
scopsy 74b7ba4
fix: done
scopsy e78ee36
Merge branch 'next' into pills-for-all-inputs
scopsy b378aac
fix: add some personality
scopsy c0f0c3e
fix: view
scopsy 05842f0
Merge branch 'pills-for-all-inputs' of https://github.com/novuhq/novu…
scopsy 48b448c
fix: popover
scopsy 62c48c2
fixes: asdas
scopsy ecc8a8c
fix: reusability
scopsy fb0901f
fix: refactor
scopsy 95f249b
feat: add comments
scopsy 92f915f
fix: types
scopsy f970981
fix: wip for digest liquid
scopsy f97c433
fix: remove unused
scopsy 44c678a
Merge branch 'next' into pills-for-all-inputs
scopsy 2bd08b0
Update field-editor.tsx
scopsy a20ee80
Merge branch 'next' into pills-for-all-inputs
scopsy 7209172
fix: close on blur
scopsy 64564ef
fix: refactor
scopsy d00d441
fix: working state
scopsy 36da57b
fix: auto complete
scopsy c10c935
fix: liquid
scopsy d355467
fix: pr comments
scopsy a148965
fix: initial values
scopsy 71d6d08
Update variable-pill-widget.ts
scopsy d1d512a
fix: items
scopsy 17164ed
fix: popover
scopsy bda722a
fix: command refactor
scopsy cc3b33b
improve usememo
scopsy a8c1816
fix: revie
scopsy 967be19
fix: minor issues
scopsy dc7d907
Merge branch 'next' into pills-for-all-inputs
scopsy 3601b0d
Merge branch 'pills-for-all-inputs' into digest-liquid-helper
scopsy b76fbfa
fix:
scopsy a88ba03
Update liquid-parser.ts
scopsy 076d365
fix
scopsy 43bbbaa
fix: items
scopsy eea4a0e
Merge branch 'next' into digest-liquid-helper
scopsy a0b3f91
fix: remove
scopsy 81d94b4
fix: review
scopsy a2b8f7a
Merge branch 'next' into digest-liquid-helper
scopsy e656ad4
Merge branch 'next' into digest-liquid-helper
scopsy 4fdab39
Merge branch 'next' into digest-liquid-helper
scopsy 1045e1e
Merge branch 'next' into digest-liquid-helper
scopsy 650a5ca
Update utils.ts
scopsy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
apps/dashboard/src/components/primitives/field-editor/field-editor.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { autocompletion } from '@codemirror/autocomplete'; | ||
import { EditorView } from '@uiw/react-codemirror'; | ||
|
||
import { Editor } from '@/components/primitives/editor'; | ||
import { Popover, PopoverTrigger } from '@/components/primitives/popover'; | ||
import { createAutocompleteSource } from '@/utils/liquid-autocomplete'; | ||
import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; | ||
import { useCallback, useMemo, useRef } from 'react'; | ||
import { useVariables } from './hooks/use-variables'; | ||
import { createVariablePlugin } from './variable-plugin'; | ||
import { variablePillTheme } from './variable-plugin/variable-theme'; | ||
import { VariablePopover } from './variable-popover'; | ||
|
||
type CompletionRange = { | ||
from: number; | ||
to: number; | ||
} | null; | ||
|
||
type FieldEditorProps = { | ||
value: string; | ||
onChange: (value: string) => void; | ||
variables: LiquidVariable[]; | ||
placeholder?: string; | ||
autoFocus?: boolean; | ||
size?: 'default' | 'lg'; | ||
id?: string; | ||
singleLine?: boolean; | ||
indentWithTab?: boolean; | ||
}; | ||
|
||
const baseExtensions = [EditorView.lineWrapping, variablePillTheme]; | ||
|
||
export function FieldEditor({ | ||
value, | ||
onChange, | ||
variables, | ||
placeholder, | ||
autoFocus, | ||
size = 'default', | ||
id, | ||
singleLine, | ||
indentWithTab, | ||
}: FieldEditorProps) { | ||
const viewRef = useRef<EditorView | null>(null); | ||
const lastCompletionRef = useRef<CompletionRange>(null); | ||
|
||
const { selectedVariable, setSelectedVariable, handleVariableSelect, handleVariableUpdate } = useVariables( | ||
viewRef, | ||
onChange | ||
); | ||
|
||
const completionSource = useMemo(() => createAutocompleteSource(variables), [variables]); | ||
|
||
const autocompletionExtension = useMemo( | ||
() => | ||
autocompletion({ | ||
override: [completionSource], | ||
closeOnBlur: true, | ||
defaultKeymap: true, | ||
activateOnTyping: true, | ||
}), | ||
[completionSource] | ||
); | ||
|
||
const variablePlugin = useMemo( | ||
() => | ||
createVariablePlugin({ | ||
viewRef, | ||
lastCompletionRef, | ||
onSelect: handleVariableSelect, | ||
}), | ||
[handleVariableSelect] | ||
); | ||
|
||
const extensions = useMemo( | ||
() => [...baseExtensions, autocompletionExtension, variablePlugin], | ||
[autocompletionExtension, variablePlugin] | ||
); | ||
|
||
const handleOpenChange = useCallback( | ||
(open: boolean) => { | ||
if (!open) { | ||
setTimeout(() => setSelectedVariable(null), 0); | ||
} | ||
}, | ||
[setSelectedVariable] | ||
); | ||
|
||
return ( | ||
<div className="relative"> | ||
<Editor | ||
fontFamily="inherit" | ||
singleLine={singleLine} | ||
indentWithTab={indentWithTab} | ||
size={size} | ||
basicSetup={{ | ||
defaultKeymap: true, | ||
}} | ||
className="flex-1" | ||
autoFocus={autoFocus} | ||
placeholder={placeholder} | ||
id={id} | ||
extensions={extensions} | ||
value={value} | ||
onChange={onChange} | ||
/> | ||
<Popover open={!!selectedVariable} onOpenChange={handleOpenChange}> | ||
<PopoverTrigger asChild> | ||
<div /> | ||
</PopoverTrigger> | ||
{selectedVariable && <VariablePopover variable={selectedVariable.value} onUpdate={handleVariableUpdate} />} | ||
</Popover> | ||
</div> | ||
); | ||
} |
78 changes: 78 additions & 0 deletions
78
apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { EditorView } from '@uiw/react-codemirror'; | ||
import { useCallback, useRef, useState } from 'react'; | ||
|
||
type SelectedVariable = { | ||
value: string; | ||
from: number; | ||
to: number; | ||
} | null; | ||
|
||
/** | ||
* Manages variable selection and updates in the editor. | ||
* | ||
* This hook combines variable selection and update logic: | ||
* 1. Tracks which variable is currently selected | ||
* 2. Prevents recursive updates when variables are being modified | ||
* 3. Handles proper Liquid syntax maintenance | ||
* 4. Manages cursor position and editor state updates | ||
*/ | ||
export function useVariables(viewRef: React.RefObject<EditorView>, onChange: (value: string) => void) { | ||
const [selectedVariable, setSelectedVariable] = useState<SelectedVariable>(null); | ||
const isUpdatingRef = useRef(false); | ||
|
||
const handleVariableSelect = useCallback((value: string, from: number, to: number) => { | ||
if (isUpdatingRef.current) return; | ||
setSelectedVariable({ value, from, to }); | ||
}, []); | ||
|
||
const handleVariableUpdate = useCallback( | ||
(newValue: string) => { | ||
if (!selectedVariable || !viewRef.current || isUpdatingRef.current) return; | ||
|
||
try { | ||
isUpdatingRef.current = true; | ||
const { from, to } = selectedVariable; | ||
const view = viewRef.current; | ||
|
||
// Ensure the new value has proper liquid syntax | ||
const hasLiquidSyntax = newValue.match(/^\{\{.*\}\}$/); | ||
const newVariableText = hasLiquidSyntax ? newValue : `{{${newValue}}}`; | ||
|
||
// Calculate the actual end position including closing brackets | ||
const currentContent = view.state.doc.toString(); | ||
const afterCursor = currentContent.slice(to); | ||
const closingBracketPos = afterCursor.indexOf('}}'); | ||
const actualEnd = closingBracketPos >= 0 ? to + closingBracketPos + 2 : to; | ||
|
||
const changes = { | ||
from, | ||
to: actualEnd, | ||
insert: newVariableText, | ||
}; | ||
|
||
view.dispatch({ | ||
changes, | ||
selection: { anchor: from + newVariableText.length }, | ||
}); | ||
|
||
onChange(view.state.doc.toString()); | ||
|
||
// Update the selected variable with new bounds | ||
setSelectedVariable((prev: SelectedVariable) => | ||
prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null | ||
); | ||
} finally { | ||
isUpdatingRef.current = false; | ||
} | ||
}, | ||
[selectedVariable, onChange, viewRef] | ||
); | ||
|
||
return { | ||
selectedVariable, | ||
setSelectedVariable, | ||
handleVariableSelect, | ||
handleVariableUpdate, | ||
isUpdatingRef, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { FieldEditor } from './field-editor'; |
3 changes: 3 additions & 0 deletions
3
apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const VARIABLE_REGEX = /{{([^{}]+)}}/g; | ||
export const VARIABLE_PILL_CLASS = 'cm-variable-pill'; | ||
export const MODIFIERS_CLASS = 'has-modifiers'; |
33 changes: 33 additions & 0 deletions
33
apps/dashboard/src/components/primitives/field-editor/variable-plugin/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { EditorView, ViewPlugin, Decoration } from '@uiw/react-codemirror'; | ||
import type { PluginState } from './types'; | ||
import { VariablePluginView } from './plugin-view'; | ||
|
||
export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: PluginState) { | ||
return ViewPlugin.fromClass( | ||
class { | ||
private view: VariablePluginView; | ||
|
||
constructor(view: EditorView) { | ||
this.view = new VariablePluginView(view, viewRef, lastCompletionRef, onSelect); | ||
} | ||
|
||
update(update: any) { | ||
this.view.update(update); | ||
} | ||
|
||
get decorations() { | ||
return this.view.decorations; | ||
} | ||
}, | ||
{ | ||
decorations: (v) => v.decorations, | ||
provide: (plugin) => | ||
EditorView.atomicRanges.of((view) => { | ||
return view.plugin(plugin)?.decorations || Decoration.none; | ||
}), | ||
} | ||
); | ||
} | ||
|
||
export * from './types'; | ||
export * from './constants'; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we have 2 version of liquid one in the framework and one in the API, didn't do it in this PR but worth considering consolidating them somehow or exporting from the framework the parser