From 4ab5deb62104224ca1f02c12ef44ce0bf9a0ea01 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Fri, 14 Jan 2022 15:35:45 +0100 Subject: [PATCH 1/3] editor: improve extension points typings + reexport some more slate types --- .../richText/blockEditor/BlockEditor.tsx | 4 ++-- .../blockEditor/editor/initBlockEditor.ts | 2 +- .../richText/editorFactory/createEditor.ts | 24 +++++++++---------- .../plugins/element/lists/withLists.ts | 2 +- .../bindingFacade/richText/slate-reexport.ts | 3 +++ .../bindingFacade/richText/slate-types.ts | 7 +++++- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/admin/src/components/bindingFacade/richText/blockEditor/BlockEditor.tsx b/packages/admin/src/components/bindingFacade/richText/blockEditor/BlockEditor.tsx index 60661809f2..812a537769 100644 --- a/packages/admin/src/components/bindingFacade/richText/blockEditor/BlockEditor.tsx +++ b/packages/admin/src/components/bindingFacade/richText/blockEditor/BlockEditor.tsx @@ -34,7 +34,7 @@ import { ToolbarButtonSpec, } from '../toolbars' import { BlockHoveringToolbarContents, BlockHoveringToolbarContentsProps } from './BlockHoveringToolbarContents' -import { initBlockEditor } from './editor' +import { EditorWithBlocks, initBlockEditor } from './editor' import type { EmbedHandler } from './embed' import type { FieldBackedElement } from './FieldBackedElement' import { useCreateElementReference } from './references' @@ -46,7 +46,7 @@ import { useBlockEditorState } from './state/useBlockEditorState' import { ContentOutlet, ContentOutletProps, useEditorReferenceBlocks } from './templating' import { useReferentiallyStableCallback } from './useReferentiallyStableCallback' -export interface BlockEditorProps extends SugaredRelativeEntityList, CreateEditorPublicOptions { +export interface BlockEditorProps extends SugaredRelativeEntityList, CreateEditorPublicOptions { label: string contentField: SugaredFieldProps['field'] sortableBy: SugaredFieldProps['field'] diff --git a/packages/admin/src/components/bindingFacade/richText/blockEditor/editor/initBlockEditor.ts b/packages/admin/src/components/bindingFacade/richText/blockEditor/editor/initBlockEditor.ts index 558a9a3bee..95a6ea7bb0 100644 --- a/packages/admin/src/components/bindingFacade/richText/blockEditor/editor/initBlockEditor.ts +++ b/packages/admin/src/components/bindingFacade/richText/blockEditor/editor/initBlockEditor.ts @@ -20,7 +20,7 @@ export interface CreateEditorOptions ReferenceElementOptions, OverrideInsertDataOptions, OverrideInsertElementWithReferenceOptions, - CreateEditorPublicOptions {} + CreateEditorPublicOptions {} export const initBlockEditor = ({ editor, ...options }: CreateEditorOptions & { editor: Editor }) => { if (options.plugins && options.plugins.indexOf(paragraphElementType) === -1) { diff --git a/packages/admin/src/components/bindingFacade/richText/editorFactory/createEditor.ts b/packages/admin/src/components/bindingFacade/richText/editorFactory/createEditor.ts index 904e7bcc32..5ae2f20093 100644 --- a/packages/admin/src/components/bindingFacade/richText/editorFactory/createEditor.ts +++ b/packages/admin/src/components/bindingFacade/richText/editorFactory/createEditor.ts @@ -18,10 +18,10 @@ import { } from '../plugins' import type { BuiltinEditorPlugins } from './BuiltinEditorPlugins' import { defaultEditorPluginPreset } from './presets' -import { Editor as SlateEditor } from 'slate' +import { Editor, Editor as SlateEditor } from 'slate' const pluginAugmenters: { - [pluginName in BuiltinEditorPlugins]: (editor: SlateEditor) => SlateEditor + [pluginName in BuiltinEditorPlugins]: (editor: E) => E } = { anchor: withAnchors, paragraph: withParagraphs, @@ -40,15 +40,15 @@ const pluginAugmenters: { underline: withUnderline, } -export interface CreateEditorPublicOptions { +export interface CreateEditorPublicOptions { plugins?: BuiltinEditorPlugins[] - augmentEditor?: (baseEditor: SlateEditor) => SlateEditor - augmentEditorBuiltins?: (editor: SlateEditor) => SlateEditor + augmentEditor?: (baseEditor: Editor) => Editor | void + augmentEditorBuiltins?: (editor: E) => E | void } -export interface CreateEditorOptions extends CreateEditorPublicOptions { +export interface CreateEditorOptions extends CreateEditorPublicOptions { defaultElementType: string - addEditorBuiltins: (augmentedBaseEditor: SlateEditor) => SlateEditor + addEditorBuiltins: (augmentedBaseEditor: SlateEditor) => E | void } export const createEditor = ({ @@ -63,19 +63,19 @@ export const createEditor = ({ } -export const initializeEditor = ({ +export const initializeEditor = ({ editor, plugins = defaultEditorPluginPreset, augmentEditorBuiltins = identityFunction, addEditorBuiltins, augmentEditor = identityFunction, -}: Omit & {editor: SlateEditor}) => { +}: Omit, 'defaultElementType'> & {editor: Editor}): E => { for (const plugin of new Set(plugins)) { editor = pluginAugmenters[plugin](editor) } - const withAugmentedBase = augmentEditor(editor) - const withBuiltins = addEditorBuiltins(withAugmentedBase) + const withAugmentedBase = augmentEditor(editor) ?? editor + const withBuiltins = addEditorBuiltins(withAugmentedBase) ?? (withAugmentedBase as E) - return augmentEditorBuiltins(withBuiltins) + return augmentEditorBuiltins(withBuiltins) ?? withBuiltins } diff --git a/packages/admin/src/components/bindingFacade/richText/plugins/element/lists/withLists.ts b/packages/admin/src/components/bindingFacade/richText/plugins/element/lists/withLists.ts index a65ff108e6..d59782400a 100644 --- a/packages/admin/src/components/bindingFacade/richText/plugins/element/lists/withLists.ts +++ b/packages/admin/src/components/bindingFacade/richText/plugins/element/lists/withLists.ts @@ -15,7 +15,7 @@ import { unorderedListElementPlugin } from './UnorderedListElement' import { isListElement } from './ListElement' import { listHtmlDeserializerFactory } from './ListHtmlDeserializer' -export const withLists = (editor: E): Editor => { +export const withLists = (editor: E): E => { const { insertBreak, onKeyDown, diff --git a/packages/admin/src/components/bindingFacade/richText/slate-reexport.ts b/packages/admin/src/components/bindingFacade/richText/slate-reexport.ts index f1c397628a..6ea5ed92fc 100644 --- a/packages/admin/src/components/bindingFacade/richText/slate-reexport.ts +++ b/packages/admin/src/components/bindingFacade/richText/slate-reexport.ts @@ -1,3 +1,6 @@ import { Editor } from 'slate' +import { useSlateStatic } from 'slate-react' + export { Transforms as EditorTransforms } from 'slate' export const EditorUtils = Editor +export const useEditor = useSlateStatic diff --git a/packages/admin/src/components/bindingFacade/richText/slate-types.ts b/packages/admin/src/components/bindingFacade/richText/slate-types.ts index 5fd7ce5609..f5e950197d 100644 --- a/packages/admin/src/components/bindingFacade/richText/slate-types.ts +++ b/packages/admin/src/components/bindingFacade/richText/slate-types.ts @@ -1,4 +1,4 @@ -import { BaseEditor, Descendant } from 'slate' +import { BaseEditor, Descendant, Selection, Point, Range, Ancestor, Path } from 'slate' import { EditorWithEssentials } from './baseEditor' import { ReactEditor, RenderElementProps } from 'slate-react' import { HistoryEditor } from 'slate-history' @@ -17,6 +17,11 @@ export type EditorText = { } export type EditorDescendant = Descendant +export type EditorAncestor = Ancestor +export type EditorSelection = Selection +export type EditorRange = Range +export type EditorPath = Path +export type EditorPoint = Point export type EditorRenderElementProps = RenderElementProps declare module 'slate' { From 3de5383495f551758cc1551ddce35a13713e6032 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Fri, 14 Jan 2022 17:12:35 +0100 Subject: [PATCH 2/3] editor: add default inline reference handler --- .../toolbars/HoveringToolbarContents.tsx | 23 ++++++++++++++++++- .../richText/toolbars/ToolbarButtonSpec.ts | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/admin/src/components/bindingFacade/richText/toolbars/HoveringToolbarContents.tsx b/packages/admin/src/components/bindingFacade/richText/toolbars/HoveringToolbarContents.tsx index c38c87dafc..515ae10594 100644 --- a/packages/admin/src/components/bindingFacade/richText/toolbars/HoveringToolbarContents.tsx +++ b/packages/admin/src/components/bindingFacade/richText/toolbars/HoveringToolbarContents.tsx @@ -5,6 +5,8 @@ import { Transforms } from 'slate' import { useSlate } from 'slate-react' import type { EditorWithBlocks } from '../blockEditor' import type { ToolbarButtonSpec } from './ToolbarButtonSpec' +import { EditorTransforms } from '../slate-reexport' +import { referenceElementType } from '../blockEditor' export interface HoveringToolbarContentsProps { buttons: ToolbarButtonSpec[] | ToolbarButtonSpec[][] @@ -69,7 +71,26 @@ export const HoveringToolbarContents = memo(({ buttons: rawButtons }: HoveringTo referenceId={reference.id} editor={editor} selection={selection} - onSuccess={() => props.resolve(undefined)} + onSuccess={({ createElement } = {}) => { + if (createElement !== undefined) { + if (!selection) { + return + } + EditorTransforms.select(editor, selection) + EditorTransforms.wrapNodes( + editor, + { + type: referenceElementType, + children: [{ text: '' }], + referenceId: reference.id, + ...createElement, + }, + { split: true }, + ) + EditorTransforms.collapse(editor, { edge: 'end' }) + } + props.resolve(undefined) + }} onCancel={() => props.reject()} /> diff --git a/packages/admin/src/components/bindingFacade/richText/toolbars/ToolbarButtonSpec.ts b/packages/admin/src/components/bindingFacade/richText/toolbars/ToolbarButtonSpec.ts index d41c1d4a93..5cfeb8678b 100644 --- a/packages/admin/src/components/bindingFacade/richText/toolbars/ToolbarButtonSpec.ts +++ b/packages/admin/src/components/bindingFacade/richText/toolbars/ToolbarButtonSpec.ts @@ -20,7 +20,7 @@ export interface InitializeReferenceContentProps { referenceId: string editor: EditorWithBlocks selection: SlateRange | null - onSuccess: () => void + onSuccess: (options?: { createElement?: Partial }) => void onCancel: () => void } From d92266489fb7d24926a831f10a22c3b04f6032bb Mon Sep 17 00:00:00 2001 From: David Matejka Date: Fri, 14 Jan 2022 17:13:41 +0100 Subject: [PATCH 3/3] sandbox: add "custom links" to editor --- .../migrations/2022-01-17-164045-links.json | 273 ++++++++++++++++++ packages/admin-sandbox/api/model/Content.ts | 5 +- packages/admin-sandbox/api/model/Link.ts | 10 + packages/admin-sandbox/api/model/Url.ts | 13 + packages/admin-sandbox/api/model/index.ts | 2 + .../src/components/AnchorInsertHandler.ts | 18 ++ .../src/components/ContentField.tsx | 114 ++------ .../src/components/customLinks.tsx | 75 +++++ .../src/components/editorButton.sass | 16 + 9 files changed, 441 insertions(+), 85 deletions(-) create mode 100644 packages/admin-sandbox/api/migrations/2022-01-17-164045-links.json create mode 100644 packages/admin-sandbox/api/model/Link.ts create mode 100644 packages/admin-sandbox/api/model/Url.ts create mode 100644 packages/admin-sandbox/src/components/AnchorInsertHandler.ts create mode 100644 packages/admin-sandbox/src/components/customLinks.tsx create mode 100644 packages/admin-sandbox/src/components/editorButton.sass diff --git a/packages/admin-sandbox/api/migrations/2022-01-17-164045-links.json b/packages/admin-sandbox/api/migrations/2022-01-17-164045-links.json new file mode 100644 index 0000000000..97b843e419 --- /dev/null +++ b/packages/admin-sandbox/api/migrations/2022-01-17-164045-links.json @@ -0,0 +1,273 @@ +{ + "formatVersion": 3, + "modifications": [ + { + "modification": "removeField", + "entityName": "ContentReference", + "fieldName": "url" + }, + { + "modification": "createEnum", + "enumName": "LinkType", + "values": [ + "internal", + "external" + ] + }, + { + "modification": "createEnum", + "enumName": "UrlType", + "values": [ + "article", + "category", + "tag" + ] + }, + { + "modification": "createEntity", + "entity": { + "name": "Link", + "primary": "id", + "primaryColumn": "id", + "unique": {}, + "fields": { + "id": { + "name": "id", + "columnName": "id", + "nullable": false, + "type": "Uuid", + "columnType": "uuid" + } + }, + "tableName": "link" + } + }, + { + "modification": "createEntity", + "entity": { + "name": "Url", + "primary": "id", + "primaryColumn": "id", + "unique": {}, + "fields": { + "id": { + "name": "id", + "columnName": "id", + "nullable": false, + "type": "Uuid", + "columnType": "uuid" + } + }, + "tableName": "url" + } + }, + { + "modification": "createColumn", + "entityName": "Link", + "field": { + "name": "type", + "columnName": "type", + "nullable": false, + "type": "Enum", + "columnType": "LinkType" + } + }, + { + "modification": "createColumn", + "entityName": "Link", + "field": { + "name": "externalLink", + "columnName": "external_link", + "nullable": true, + "type": "String", + "columnType": "text" + } + }, + { + "modification": "createColumn", + "entityName": "Url", + "field": { + "name": "url", + "columnName": "url", + "nullable": false, + "type": "String", + "columnType": "text" + } + }, + { + "modification": "createColumn", + "entityName": "Url", + "field": { + "name": "type", + "columnName": "type", + "nullable": false, + "type": "Enum", + "columnType": "UrlType" + } + }, + { + "modification": "createRelation", + "entityName": "ContentReference", + "owningSide": { + "name": "link", + "nullable": true, + "type": "ManyHasOne", + "target": "Link", + "joiningColumn": { + "columnName": "link_id", + "onDelete": "restrict" + } + } + }, + { + "modification": "createRelation", + "entityName": "Link", + "owningSide": { + "name": "internalLink", + "nullable": true, + "type": "ManyHasOne", + "target": "Url", + "joiningColumn": { + "columnName": "internal_link_id", + "onDelete": "set null" + } + } + }, + { + "modification": "createRelation", + "entityName": "Url", + "owningSide": { + "name": "article", + "nullable": true, + "type": "OneHasOne", + "target": "Article", + "joiningColumn": { + "columnName": "article_id", + "onDelete": "cascade" + } + } + }, + { + "modification": "createRelation", + "entityName": "Url", + "owningSide": { + "name": "category", + "nullable": true, + "type": "OneHasOne", + "target": "Category", + "joiningColumn": { + "columnName": "category_id", + "onDelete": "cascade" + } + } + }, + { + "modification": "createRelation", + "entityName": "Url", + "owningSide": { + "name": "tag", + "nullable": true, + "type": "OneHasOne", + "target": "Tag", + "joiningColumn": { + "columnName": "tag_id", + "onDelete": "cascade" + } + } + }, + { + "modification": "createUniqueConstraint", + "entityName": "Url", + "unique": { + "fields": [ + "url" + ], + "name": "unique_Url_url_a758c1" + } + }, + { + "modification": "patchAclSchema", + "patch": [ + { + "op": "add", + "path": "/roles/admin/entities/Link", + "value": { + "predicates": {}, + "operations": { + "create": { + "id": true, + "type": true, + "internalLink": true, + "externalLink": true + }, + "read": { + "id": true, + "type": true, + "internalLink": true, + "externalLink": true + }, + "update": { + "id": true, + "type": true, + "internalLink": true, + "externalLink": true + }, + "delete": true, + "customPrimary": true + } + } + }, + { + "op": "add", + "path": "/roles/admin/entities/Url", + "value": { + "predicates": {}, + "operations": { + "create": { + "id": true, + "url": true, + "type": true, + "article": true, + "category": true, + "tag": true + }, + "read": { + "id": true, + "url": true, + "type": true, + "article": true, + "category": true, + "tag": true + }, + "update": { + "id": true, + "url": true, + "type": true, + "article": true, + "category": true, + "tag": true + }, + "delete": true, + "customPrimary": true + } + } + }, + { + "op": "add", + "path": "/roles/admin/entities/ContentReference/operations/create/link", + "value": true + }, + { + "op": "add", + "path": "/roles/admin/entities/ContentReference/operations/update/link", + "value": true + }, + { + "op": "add", + "path": "/roles/admin/entities/ContentReference/operations/read/link", + "value": true + } + ] + } + ] +} diff --git a/packages/admin-sandbox/api/model/Content.ts b/packages/admin-sandbox/api/model/Content.ts index 48d609de79..2b8be7be00 100644 --- a/packages/admin-sandbox/api/model/Content.ts +++ b/packages/admin-sandbox/api/model/Content.ts @@ -1,5 +1,6 @@ import { SchemaDefinition as d } from '@contember/schema-definition' import { BasicImage } from './Files' +import { Link } from './Link' export class Content { blocks: d.OneHasManyDefinition = d.oneHasMany(ContentBlock, 'content').orderBy('order') @@ -15,7 +16,7 @@ export class ContentBlock { export const ContentReferenceType = d.createEnum( 'image', // image 'quote', // primaryText, secondaryText - 'link', // url + 'link', // link ) export class ContentReference { @@ -24,5 +25,5 @@ export class ContentReference { primaryText = d.stringColumn() secondaryText = d.stringColumn() image = d.manyHasOne(BasicImage) - url = d.stringColumn() + link = d.manyHasOne(Link) } diff --git a/packages/admin-sandbox/api/model/Link.ts b/packages/admin-sandbox/api/model/Link.ts new file mode 100644 index 0000000000..9ee7c5109f --- /dev/null +++ b/packages/admin-sandbox/api/model/Link.ts @@ -0,0 +1,10 @@ +import { SchemaDefinition as d } from '@contember/schema-definition' +import { Url } from './Url' + +export const LinkType = d.createEnum('internal', 'external') + +export class Link { + type = d.enumColumn(LinkType).notNull() + internalLink = d.manyHasOne(Url).setNullOnDelete() + externalLink = d.stringColumn() +} diff --git a/packages/admin-sandbox/api/model/Url.ts b/packages/admin-sandbox/api/model/Url.ts new file mode 100644 index 0000000000..622565051e --- /dev/null +++ b/packages/admin-sandbox/api/model/Url.ts @@ -0,0 +1,13 @@ +import { SchemaDefinition as d } from '@contember/schema-definition' +import { Article, Category, Tag } from './Article' + +export const UrlType = d.createEnum('article', 'category', 'tag') + +@d.Unique('url') +export class Url { + url = d.stringColumn().notNull() + type = d.enumColumn(UrlType).notNull() + article = d.oneHasOne(Article).cascadeOnDelete() + category = d.oneHasOne(Category).cascadeOnDelete() + tag = d.oneHasOne(Tag).cascadeOnDelete() +} diff --git a/packages/admin-sandbox/api/model/index.ts b/packages/admin-sandbox/api/model/index.ts index 7c37659cd5..72aca7ff97 100644 --- a/packages/admin-sandbox/api/model/index.ts +++ b/packages/admin-sandbox/api/model/index.ts @@ -2,7 +2,9 @@ export * from './Article' export * from './Content' export * from './Homepage' export * from './Files' +export * from './Link' export * from './One' export * from './InputShowcase' export * from './Locale' export * from './UploadShowcase' +export * from './Url' diff --git a/packages/admin-sandbox/src/components/AnchorInsertHandler.ts b/packages/admin-sandbox/src/components/AnchorInsertHandler.ts new file mode 100644 index 0000000000..70dd41a114 --- /dev/null +++ b/packages/admin-sandbox/src/components/AnchorInsertHandler.ts @@ -0,0 +1,18 @@ +import { EditorTransforms, EditorWithBlocks, EntityAccessor } from '@contember/admin' + +export const withAnchorsAsReference = (editor: EditorWithBlocks, { elementType, referenceType = elementType, updateReference }: { + elementType: string, + updateReference: (url: string, getAccessor: () => EntityAccessor) => void + referenceType?: string, +}) => { + const { normalizeNode } = editor + editor.normalizeNode = ([element, path]) => { + if ('type' in element && element.type === 'anchor' && 'href' in element && typeof element.href === 'string') { + const referenceId = editor.createElementReference(path, referenceType, getAccessor => { + updateReference(element.href as string, getAccessor) + }).id + return EditorTransforms.setNodes(editor, { referenceId, href: null, type: elementType }, { at: path }) + } + normalizeNode([element, path]) + } +} diff --git a/packages/admin-sandbox/src/components/ContentField.tsx b/packages/admin-sandbox/src/components/ContentField.tsx index 4f165ef0e6..1a6cdcc2d6 100644 --- a/packages/admin-sandbox/src/components/ContentField.tsx +++ b/packages/admin-sandbox/src/components/ContentField.tsx @@ -2,62 +2,36 @@ import { Block, BlockEditor, BlockEditorProps, - Button, Component, - EditorRenderElementProps, - EditorTransforms, - EditorWithBlocks, horizontalRuleToolbarButton, - ImageUploadField, isElementWithReference, + ImageUploadField, paragraphNumberedToolbarButton, paragraphToolbarButton, - RichEditor, Scheme, scrollTargetToolbarButton, + RichEditor, + Scheme, + scrollTargetToolbarButton, tableToolbarButton, TextField, - useEntity, } from '@contember/admin' +import { withAnchorsAsReference } from './AnchorInsertHandler' +import * as React from 'react' +import { InsertLink, LinkElement } from './customLinks' + const RB = RichEditor.buttons export const fullEditorInlineButtons: BlockEditorProps['inlineButtons'] = [ [RB.bold, RB.italic, RB.underline, RB.anchor], [RB.headingOne, RB.headingTwo, RB.headingThree, RB.headingFour, RB.unorderedList, RB.orderedList], [RB.strikeThrough, RB.code], - [{ - label: 'Link', - blueprintIcon: 'link', - discriminateBy: 'link', - referenceContent: Component(({ onSuccess, selection, editor, referenceId }) => { - return <> - - - - - }), - }], + [ + { + discriminateBy: 'link', + referenceContent: InsertLink, + label: 'Insert link', + title: 'Insert link', + blueprintIcon: 'link', + }, + ], ] export interface ContentFieldProps { @@ -65,53 +39,27 @@ export interface ContentFieldProps { toolbarScheme?: Scheme } -const LinkElement = ({ attributes, children, element }: EditorRenderElementProps) => { - const ref = useEntity() - if (!isElementWithReference(element)) { - return {children} // stub - } - return ( - <> - - {children} - ({ref.getField('url').value}, {ref.id}) - - - ) -} export const ContentField = Component( ({ field, toolbarScheme }) => ( { + augmentEditorBuiltins={editor => { + + withAnchorsAsReference( + editor, + { + elementType: 'link', + updateReference: (url, getAccessor) => { + getAccessor().getField('link.type').updateValue('external') + getAccessor().getField('link.externalLink').updateValue(url) + }, + }, + ) + editor.registerElement({ type: 'link', - render: LinkElement, isInline: true, - normalizeNode: ({ element, path }) => { - if ('href' in element) { - const referenceId = (editor as EditorWithBlocks).createElementReference(path, 'link', getAccessor => ( - getAccessor().getField('url').updateValue(element.href) - )).id - EditorTransforms.setNodes(editor, { referenceId, href: null }, { at: path }) - } - }, - }) - editor.htmlDeserializer.registerPlugin({ - processInlinePaste: ({ element, next, cumulativeTextAttrs }) => { - if (element.tagName === 'A' && element.getAttribute('href')) { - const href = element.getAttribute('href') - - const node = { - type: 'link', - children: next(element.childNodes, cumulativeTextAttrs), - href, - } - return [node] - } - return null - }, + render: LinkElement, }) - return editor }} leadingFieldBackedElements={[ { diff --git a/packages/admin-sandbox/src/components/customLinks.tsx b/packages/admin-sandbox/src/components/customLinks.tsx new file mode 100644 index 0000000000..f11518f471 --- /dev/null +++ b/packages/admin-sandbox/src/components/customLinks.tsx @@ -0,0 +1,75 @@ +import { + Block, + Box, + Button, + Component, + DiscriminatedBlocks, + Dropdown, + EditorRenderElementProps, + EditorTransforms, + HasOne, + Icon, + InitializeReferenceContentProps, + SelectField, + TextField, + useEditor, +} from '@contember/admin' +import * as React from 'react' +import './editorButton.sass' + +export const LinkTarget = Component(() => ( + + + + + + + + + + + ), +) + +export const InsertLink = Component( + ({ onSuccess, onCancel }) => ( + <> + +
+ + +
+ + ), + () => , +) + + +export const LinkElement = (props: EditorRenderElementProps) => { + const editor = useEditor() + return ( + + {props.children} + + ( + + )} + > + + + + + + + + ) +} diff --git a/packages/admin-sandbox/src/components/editorButton.sass b/packages/admin-sandbox/src/components/editorButton.sass new file mode 100644 index 0000000000..2eefc0e168 --- /dev/null +++ b/packages/admin-sandbox/src/components/editorButton.sass @@ -0,0 +1,16 @@ +.editorButton + border-radius: 50% + width: 1.8em + height: 1.8em + background: white + border: 1px solid #e2e2ea + margin-left: .3em + font-size: .7rem + cursor: pointer + display: inline-flex + align-items: center + justify-content: center + + &:focus + outline: none + background: #f2f2f2