Skip to content

Commit

Permalink
AB#910 Allow links in the rich-text editor to be updated
Browse files Browse the repository at this point in the history
  • Loading branch information
gjvoosten committed Jan 9, 2024
1 parent 95d2a56 commit e3ed92d
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const MultiTypeAdvancedSelectComponent = ({
objectType,
entityTypes,
value,
valueKey,
isMultiSelect,
filters,
className
Expand All @@ -175,7 +176,9 @@ const MultiTypeAdvancedSelectComponent = ({
const SelectComponent = isMultiSelect
? AdvancedMultiSelect
: AdvancedSingleSelect
const extraSelectProps = isMultiSelect ? {} : { showRemoveButton: false }
const extraSelectProps = isMultiSelect
? {}
: { valueKey, showRemoveButton: false }
const filterDefs =
typeof advancedSelectProps.filterDefs === "function"
? advancedSelectProps.filterDefs(filters[0]?.[entityType])
Expand Down Expand Up @@ -235,6 +238,7 @@ MultiTypeAdvancedSelectComponent.propTypes = {
objectType: PropTypes.string,
entityTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
valueKey: PropTypes.string,
isMultiSelect: PropTypes.bool.isRequired,
filters: PropTypes.array,
className: PropTypes.string
Expand Down
65 changes: 51 additions & 14 deletions client/src/components/editor/LinkSourceAnet.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import { FastField, Form, Formik } from "formik"
import PropTypes from "prop-types"
import React, { useCallback } from "react"
import { Button, Form as FormBS, Modal } from "react-bootstrap"
import { Transforms } from "slate"
import { Editor, Transforms } from "slate"
import { ReactEditor } from "slate-react"
import { ANET_LINK, EXTERNAL_LINK, getEntityInfoFromUrl } from "utils_links"
import * as yup from "yup"

const LinkSourceAnet = ({
editor,
showModal,
setShowModal,
selection,
external
}) => {
const LinkSourceAnet = ({ editor, showModal, setShowModal, external }) => {
const insertAnetLink = useCallback(
node => {
ReactEditor.focus(editor)
if (selection) {
if (editor.selection) {
const { replaceSelection, selectedParentNode } = getParentNodeProps(
editor,
external
)
if (replaceSelection) {
Transforms.removeNodes(editor, { at: selectedParentNode?.[1] })
}
Transforms.insertNodes(editor, node, {
at: { path: selection.focus.path, offset: selection.focus.offset },
at: editor.selection.focus,
select: true
})
} else {
Expand All @@ -32,9 +33,11 @@ const LinkSourceAnet = ({
Transforms.move(editor, { distance: 1 })
setShowModal(false)
},
[editor, selection, setShowModal]
[editor, external, setShowModal]
)

const value = getParentNodeProps(editor, external)?.value

return (
<Modal
centered
Expand All @@ -51,6 +54,8 @@ const LinkSourceAnet = ({
<Modal.Body>
{external ? (
<ExternalLinkForm
url={value?.url}
text={value?.text}
onConfirm={(values, form) => {
const externalLinkNode = createExternalLinkNode(
values.url,
Expand All @@ -62,6 +67,9 @@ const LinkSourceAnet = ({
/>
) : (
<MultiTypeAdvancedSelectComponent
objectType={value?.objectType}
value={value?.object}
valueKey={value?.object && "uuid"}
onConfirm={(value, objectType) => {
const anetLinkNode = createAnetLinkNode(objectType, value.uuid)
insertAnetLink(anetLinkNode)
Expand All @@ -71,21 +79,43 @@ const LinkSourceAnet = ({
</Modal.Body>
</Modal>
)

function getParentNodeProps(editor, external) {
const selectedParentNode =
editor.selection && Editor.parent(editor, editor.selection)
const selectedParent = selectedParentNode?.[0]
let value
if (external && selectedParent?.type === EXTERNAL_LINK) {
value = {
url: selectedParent.url,
text: selectedParent.children?.[0]?.text
}
} else if (!external && selectedParent?.type === ANET_LINK) {
value = {
objectType: selectedParent.entityType,
object: selectedParent.entityUuid
? { uuid: selectedParent.entityUuid }
: null
}
} else {
value = null
}
return { replaceSelection: !!value, selectedParentNode, value }
}
}

LinkSourceAnet.propTypes = {
editor: PropTypes.object.isRequired,
showModal: PropTypes.bool,
setShowModal: PropTypes.func.isRequired,
selection: PropTypes.object,
external: PropTypes.bool
}

LinkSourceAnet.defaultProps = {
external: false
}

const ExternalLinkForm = ({ onConfirm, onCancel }) => {
const ExternalLinkForm = ({ url, text, onConfirm, onCancel }) => {
const yupSchema = yup.object().shape({
url: yup.string().required("Url is required").default(""),
text: yup.string().required("Text is required").default("")
Expand All @@ -94,7 +124,7 @@ const ExternalLinkForm = ({ onConfirm, onCancel }) => {
<Formik
validateOnMount
validationSchema={yupSchema}
initialValues={{ url: "", text: "" }}
initialValues={{ url, text }}
onSubmit={onConfirm}
>
{({ submitForm, isSubmitting, isValid }) => {
Expand Down Expand Up @@ -131,10 +161,17 @@ const ExternalLinkForm = ({ onConfirm, onCancel }) => {
}

ExternalLinkForm.propTypes = {
url: PropTypes.string,
text: PropTypes.string,
onConfirm: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired
}

ExternalLinkForm.defaultProps = {
url: "",
text: ""
}

function createAnetLinkNode(entityType, entityUuid) {
return {
type: ANET_LINK,
Expand Down
19 changes: 9 additions & 10 deletions client/src/components/editor/Toolbar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Icon } from "@blueprintjs/core"
import { Tooltip2 } from "@blueprintjs/popover2"
import PropTypes from "prop-types"
import React, { useRef } from "react"
import React from "react"
import { Editor, Transforms } from "slate"
import { useSlate } from "slate-react"
import { ANET_LINK, EXTERNAL_LINK } from "utils_links"
Expand All @@ -27,7 +27,6 @@ const Toolbar = ({
toolbarRef
}) => {
const editor = useSlate()
const selectionRef = useRef(editor.selection)

return (
<>
Expand Down Expand Up @@ -110,7 +109,6 @@ const Toolbar = ({
text="ANET Link"
showModal={showAnetLinksModal}
setShowModal={setShowAnetLinksModal}
selectionRef={selectionRef}
tooltipText="ANET link (Ctrl + ⇧ + k)"
/>
<EditorToggleButton
Expand All @@ -120,7 +118,6 @@ const Toolbar = ({
icon="link"
showModal={showExternalLinksModal}
setShowModal={setShowExternalLinksModal}
selectionRef={selectionRef}
tooltipText="External link (Ctrl + ⇧ + a)"
/>
<EditorToggleButton
Expand Down Expand Up @@ -150,13 +147,11 @@ const Toolbar = ({
editor={editor}
showModal={showAnetLinksModal}
setShowModal={setShowAnetLinksModal}
selection={selectionRef.current}
/>
<LinkSourceAnet
editor={editor}
showModal={showExternalLinksModal}
setShowModal={setShowExternalLinksModal}
selection={selectionRef.current}
external
/>
</>
Expand Down Expand Up @@ -216,6 +211,12 @@ function isMarkActive(editor, format) {
return marks && marks[format] === true
}

function isModalActive(editor, format, showModal) {
const selectedParent =
editor.selection && Editor.parent(editor, editor.selection)?.[0]
return showModal || selectedParent?.type === format
}

const EditorToggleButton = ({
type,
editor,
Expand All @@ -225,7 +226,6 @@ const EditorToggleButton = ({
tooltipText,
showModal,
setShowModal,
selectionRef,
onClick,
showFullSize,
setShowFullSize
Expand All @@ -242,13 +242,13 @@ const EditorToggleButton = ({
onMouseDown = () => toggleBlock(editor, format)
break
case BUTTON_TYPES.MODAL:
isActive = showModal
isActive = isModalActive(editor, format, showModal)
onMouseDown = () => {
selectionRef.current = editor.selection
setShowModal(true)
}
break
case BUTTON_TYPES.FULLSCREEN:
isActive = showFullSize
onMouseDown = () => setShowFullSize(!showFullSize)
break
default:
Expand Down Expand Up @@ -287,7 +287,6 @@ EditorToggleButton.propTypes = {
tooltipText: PropTypes.string,
showModal: PropTypes.bool,
setShowModal: PropTypes.func,
selectionRef: PropTypes.object,
onClick: PropTypes.func,
showFullSize: PropTypes.bool,
setShowFullSize: PropTypes.func
Expand Down

0 comments on commit e3ed92d

Please sign in to comment.