Repository navigation
ENG-2258 Create a discourse relation from canvas and associate it with the selected node type pair #1494
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
trangdoan982
merged 7 commits into
eng-2257-add-an-existing-discourse-relation-from-relation-menu
from
eng-2258-create-a-discourse-relation-from-canvas-and-associate-it
Oct 2, 2026
Merged
ENG-2258 Create a discourse relation from canvas and associate it with the selected node type pair #1494
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
71f189b
ENG-2258 Create a discourse relation from canvas and associate it wit…
trangdoan982 a9665b1
ENG-2258 Add unit tests for relation type creation and validation
trangdoan982 ac2f414
ENG-2258 Share the relation type props and completeness check, reject…
trangdoan982 9804c98
ENG-2258 Remove only the created relation type when its save fails
trangdoan982 8b467b7
ENG-2258 Drop a restating comment
trangdoan982 4992cb5
ENG-2258 Trim relation type fields before validating and saving
trangdoan982 6a2e3a4
ENG-2258 Validate against current relation types and keep the dialog …
trangdoan982 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 hidden or 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
154 changes: 154 additions & 0 deletions
154
apps/obsidian/src/components/canvas/CreateRelationTypeModal.tsx
This file contains hidden or 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,154 @@ | ||
| import { App, Modal } from "obsidian"; | ||
| import { StrictMode, useState } from "react"; | ||
| import { createRoot, Root } from "react-dom/client"; | ||
| import { ColorPicker } from "~/components/RelationshipTypeSettings"; | ||
| import type { DiscourseRelationType } from "~/types"; | ||
| import { | ||
| DEFAULT_TLDRAW_COLOR, | ||
| type TldrawColorName, | ||
| } from "~/utils/tldrawColors"; | ||
| import { getRelationTypeErrors } from "~/utils/typeUtils"; | ||
|
|
||
| type RelationTypeFields = { | ||
| label: string; | ||
| complement: string; | ||
| color: TldrawColorName; | ||
| }; | ||
|
|
||
| type CreateRelationTypeProps = { | ||
| // Read at submit: another canvas may add a type while the dialog is open | ||
| getRelationTypes: () => DiscourseRelationType[]; | ||
| onSubmit: (fields: RelationTypeFields) => Promise<void>; | ||
| onClose: () => void; | ||
| }; | ||
|
|
||
| const CreateRelationTypeForm = ({ | ||
| getRelationTypes, | ||
| onSubmit, | ||
| onClose, | ||
| }: CreateRelationTypeProps) => { | ||
| const [label, setLabel] = useState(""); | ||
| const [complement, setComplement] = useState(""); | ||
| const [color, setColor] = useState<TldrawColorName>(DEFAULT_TLDRAW_COLOR); | ||
| const [error, setError] = useState(""); | ||
| const [isSubmitting, setIsSubmitting] = useState(false); | ||
|
|
||
| const handleSubmit = async () => { | ||
| // Trimmed so "supports " can't pass as a second "supports" | ||
| const fields = { | ||
| label: label.trim(), | ||
| complement: complement.trim(), | ||
| color, | ||
| }; | ||
| const candidate = { id: "new", ...fields, created: 0, modified: 0 }; | ||
| const relationTypes = getRelationTypes(); | ||
| const nextError = !fields.label | ||
| ? "Label is required" | ||
| : !fields.complement | ||
| ? "Complement is required" | ||
| : (getRelationTypeErrors([...relationTypes, candidate])[ | ||
| relationTypes.length | ||
| ] ?? ""); | ||
| setError(nextError); | ||
| if (nextError) return; | ||
|
|
||
| setIsSubmitting(true); | ||
| try { | ||
| await onSubmit(fields); | ||
|
trangdoan982 marked this conversation as resolved.
|
||
| } catch { | ||
| setError("Couldn't save the relation type. Try again."); | ||
| setIsSubmitting(false); | ||
| return; | ||
| } | ||
| onClose(); | ||
| }; | ||
|
|
||
| const inputClassName = `w-full ${error ? "input-error" : ""}`; | ||
|
|
||
| return ( | ||
| <form | ||
| className="flex flex-col gap-3" | ||
| onSubmit={(e) => { | ||
| e.preventDefault(); | ||
| void handleSubmit(); | ||
| }} | ||
| > | ||
| <input | ||
| type="text" | ||
| className={inputClassName} | ||
| placeholder="Label (e.g., supports)" | ||
| value={label} | ||
| onChange={(e) => setLabel(e.target.value)} | ||
| autoFocus | ||
| /> | ||
| <input | ||
| type="text" | ||
| className={inputClassName} | ||
| placeholder="Complement (e.g., is supported by)" | ||
| value={complement} | ||
| onChange={(e) => setComplement(e.target.value)} | ||
| /> | ||
| <ColorPicker value={color} onChange={setColor} /> | ||
| {error && <div className="text-error text-xs">{error}</div>} | ||
| <div className="modal-button-container mt-2 flex justify-end gap-2"> | ||
| <button | ||
| type="button" | ||
| className="mod-normal" | ||
| onClick={onClose} | ||
| disabled={isSubmitting} | ||
| > | ||
| Cancel | ||
| </button> | ||
| <button type="submit" className="mod-cta" disabled={isSubmitting}> | ||
| Continue | ||
| </button> | ||
| </div> | ||
| </form> | ||
| ); | ||
| }; | ||
|
|
||
| export class CreateRelationTypeModal extends Modal { | ||
| private root: Root | null = null; | ||
| private props: CreateRelationTypeProps; | ||
| private isSaving = false; | ||
|
|
||
| constructor(app: App, props: CreateRelationTypeProps) { | ||
| super(app); | ||
| this.props = props; | ||
| } | ||
|
|
||
| // Escape and the backdrop call close(); a pending save must finish first | ||
| close() { | ||
| if (!this.isSaving) super.close(); | ||
| } | ||
|
|
||
| onOpen() { | ||
| this.setTitle("Add relation type"); | ||
| // The color list is absolutely positioned and extends past this short form | ||
| this.modalEl.addClass("overflow-visible"); | ||
| this.root = createRoot(this.contentEl); | ||
| this.root.render( | ||
| <StrictMode> | ||
| <CreateRelationTypeForm | ||
| getRelationTypes={this.props.getRelationTypes} | ||
| onSubmit={async (fields) => { | ||
| this.isSaving = true; | ||
| try { | ||
| await this.props.onSubmit(fields); | ||
| } finally { | ||
| this.isSaving = false; | ||
| } | ||
| }} | ||
| onClose={() => this.close()} | ||
| /> | ||
| </StrictMode>, | ||
| ); | ||
| } | ||
|
|
||
| onClose() { | ||
| this.root?.unmount(); | ||
| this.root = null; | ||
| this.contentEl.empty(); | ||
| this.props.onClose(); | ||
| } | ||
| } | ||
This file contains hidden or 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
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.
Uh oh!
There was an error while loading. Please reload this page.