diff --git a/demo/collab/SelectionPlugin.ts b/demo/collab/SelectionPlugin.ts index 20203f37..713d07f4 100644 --- a/demo/collab/SelectionPlugin.ts +++ b/demo/collab/SelectionPlugin.ts @@ -132,14 +132,11 @@ const getStateForNewUserSelection = ( ) ); - const newClientIDs = oldState.clientIDs.add(selectionChange.clientID); - if (selectionChange.selection) { const decorations = getDecosForSelection( selectionChange.userName, selectionChange.clientID, - selectionChange.selection, - newClientIDs + selectionChange.selection ); newDecSet = newDecSet.add(doc, decorations); } @@ -148,25 +145,18 @@ const getStateForNewUserSelection = ( ...oldState, decorations: newDecSet, selections: newSels, - clientIDs: newClientIDs, }; }; const getDecosForSelection = ( userName: string, clientID: ClientID, - { head, from, to, empty }: Selection, - clientIDs: Set + { head, from, to, empty }: Selection ): Decoration[] => { - const clientIDIndex = Array.from(clientIDs).indexOf(clientID); - if (clientIDIndex === -1) { - return []; - } - - const cursorColor = selectColor(clientIDIndex); + const cursorColor = selectColor(parseInt(clientID)); const cursorDeco = getCursorDeco(head, clientID, userName, cursorColor); - const selectionColor = selectColor(clientIDIndex, true); + const selectionColor = selectColor(parseInt(clientID), true); const selectionDeco = empty ? undefined : getSelectionDeco(from, to, clientID, selectionColor); @@ -220,7 +210,7 @@ const notEmpty = ( return value !== null && value !== undefined; }; -const selectColor = (index: number, isBackground = false) => { +export const selectColor = (index: number, isBackground = false) => { const hue = index * 137.508; // Use golden angle approximation return `hsl(${hue},50%,${isBackground ? 90 : 50}%)`; }; diff --git a/demo/index.html b/demo/index.html index 2853ae3b..c06d3052 100644 --- a/demo/index.html +++ b/demo/index.html @@ -11,7 +11,7 @@ -
+
diff --git a/demo/index.ts b/demo/index.tsx similarity index 65% rename from demo/index.ts rename to demo/index.tsx index 69aaadb6..04e68f4e 100644 --- a/demo/index.ts +++ b/demo/index.tsx @@ -1,14 +1,21 @@ +import { css } from "@emotion/react"; +import { Button } from "@guardian/src-button"; +import { headline } from "@guardian/src-foundations/typography"; import { FocusStyleManager } from "@guardian/src-foundations/utils"; +import { Column, Columns, Stack } from "@guardian/src-layout"; +import { Option, Select } from "@guardian/src-select"; import { UserTelemetryEventSender } from "@guardian/user-telemetry-client"; import omit from "lodash/omit"; import { collab } from "prosemirror-collab"; -import applyDevTools from "prosemirror-dev-tools"; +import prosemirrorDevTools from "prosemirror-dev-tools"; import { exampleSetup } from "prosemirror-example-setup"; import type { MarkSpec, Node } from "prosemirror-model"; import { Schema } from "prosemirror-model"; import { schema as basicSchema, marks } from "prosemirror-schema-basic"; import { EditorState } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; +import { DecorationSet, EditorView } from "prosemirror-view"; +import React, { useEffect, useRef, useState } from "react"; +import { render } from "react-dom"; import { buildElementPlugin, undefinedDropdownValue } from "../src"; import { keyTakeawaysElement } from "../src/elements/alt-style/AltStyleElementForm"; import { createCalloutElement } from "../src/elements/callout/Callout"; @@ -43,7 +50,10 @@ import { testWidgetDecorationPlugin, } from "../src/plugin/helpers/test"; import { CollabServer, EditorConnection } from "./collab/CollabServer"; -import { createSelectionCollabPlugin } from "./collab/SelectionPlugin"; +import { + createSelectionCollabPlugin, + selectColor, +} from "./collab/SelectionPlugin"; import { getImageFromMediaPayload, onCropCartoon, @@ -266,12 +276,6 @@ const schema = new Schema({ const { serializer, parser } = createParsers(schema); -const editorsContainer = document.querySelector("#editor-container"); -const btnContainer = document.getElementById("button-container"); -if (!editorsContainer || !btnContainer) { - throw new Error("No #editor element present in DOM"); -} - const get = () => { const state = window.localStorage.getItem("pm"); return state @@ -290,13 +294,13 @@ let editorNo = 0; let firstCollabPlugin: ReturnType | undefined; let firstEditor: EditorView | undefined; -const createEditor = (server: CollabServer) => { +const createEditor = (editorContainer: HTMLElement) => { // Add the editor nodes to the DOM const isFirstEditor = !firstEditor; const editorElement = document.createElement("div"); editorElement.id = `editor-${editorNo}`; editorElement.classList.add("Editor"); - editorsContainer.appendChild(editorElement); + editorContainer.appendChild(editorElement); const contentElement = document.getElementById(`content-${editorNo}`); if (contentElement?.parentElement) { @@ -305,6 +309,7 @@ const createEditor = (server: CollabServer) => { // Create the editor const clientID = editorNo.toString(); + editorElement.style.border = `2px solid ${selectColor(parseInt(clientID))}`; const currentVersion = firstEditor && firstCollabPlugin ? (firstCollabPlugin.getState(firstEditor.state) as number) @@ -380,125 +385,6 @@ const createEditor = (server: CollabServer) => { firstEditor = view; } - const createElementButton = ( - buttonText: string, - elementName: keyof typeof elements, - values: Record - ) => { - const elementButton = document.createElement("button"); - elementButton.innerHTML = `Add ${buttonText}`; - elementButton.id = elementName; - elementButton.addEventListener("click", () => { - insertElement({ elementName, values })(view.state, view.dispatch); - }); - btnContainer.appendChild(elementButton); - }; - - const buttonData = [ - { - label: "Campaign Callout List", - name: campaignCalloutListElementName, - values: sampleCampaignCalloutList, - }, - { label: "Embed", name: embedElementName, values: sampleEmbed }, - { label: "Callout", name: embedElementName, values: sampleCallout }, - { label: "Demo image", name: demoImageElementName, values: sampleImage }, - { label: "Rich-link", name: richlinkElementName, values: sampleRichLink }, - { label: "Video", name: videoElementName, values: sampleVideo }, - { label: "Audio", name: audioElementName, values: sampleAudio }, - { label: "Map", name: mapElementName, values: sampleMap }, - { label: "Document", name: documentElementName, values: sampleDocument }, - { label: "Table", name: tableElementName, values: sampleTable }, - { - label: "Membership", - name: membershipElementName, - values: sampleMembership, - }, - { - label: "Interactive", - name: interactiveElementName, - values: sampleInteractive, - }, - { label: "Pullquote", name: pullquoteElementName, values: samplePullquote }, - { label: "Code", name: codeElementName, values: sampleCode }, - { label: "Form", name: formElementName, values: sampleForm }, - { label: "Vine", name: vineElementName, values: sampleVine }, - { label: "Tweet", name: tweetElementName, values: sampleTweet }, - { label: "Recipe", name: recipeElementName, values: sampleRecipe }, - { label: "Recipe atom", name: contentAtomName, values: sampleContentAtom }, - { - label: "Interactive atom", - name: contentAtomName, - values: sampleInteractiveAtom, - }, - { label: "Comment", name: commentElementName, values: sampleComment }, - { - label: "Alt Style", - name: altStyleElementName, - values: sampleAltStylesElement, - }, - { label: "Repeater", name: repeaterElementName, values: sampleRepeater }, - { label: "Nested", name: nestedElementName, values: sampleNested }, - ] as const; - - buttonData.map(({ label, name, values }) => - createElementButton(label, name, values) - ); - - const imageElementButton = document.createElement("button"); - imageElementButton.innerHTML = "Add Image"; - imageElementButton.id = imageElementName; - imageElementButton.addEventListener("click", () => { - const setMedia = (mediaPayload: MediaPayload) => { - const { - mediaId, - mediaApiUri, - assets, - suppliersReference, - caption, - photographer, - source, - } = mediaPayload; - insertElement({ - elementName: imageElementName, - values: { - caption, - photographer, - source, - mainImage: { assets, suppliersReference, mediaId, mediaApiUri }, - }, - })(view.state, view.dispatch); - }; - onCropImage(setMedia); - }); - btnContainer.appendChild(imageElementButton); - - const cartoonElementButton = document.createElement("button"); - cartoonElementButton.innerHTML = "Add Cartoon"; - cartoonElementButton.id = cartoonElementName; - cartoonElementButton.addEventListener("click", () => { - const setMedia = (mediaPayload: MediaPayload) => { - const { photographer, caption, source } = mediaPayload; - - const imageToInsert = getImageFromMediaPayload(mediaPayload); - - // TODO: handle this error - if (!imageToInsert) return; - - insertElement({ - elementName: cartoonElementName, - values: { - largeImages: [imageToInsert], - photographer, - source, - caption, - }, - })(view.state, view.dispatch); - }; - onCropImage(setMedia); - }); - btnContainer.appendChild(cartoonElementButton); - // Add a button allowing you to toggle the image role fields const toggleImageFields = document.createElement("button"); toggleImageFields.innerHTML = "Randomise image role options"; @@ -507,7 +393,6 @@ const createEditor = (server: CollabServer) => { [...additionalRoleOptions].splice(Math.floor(Math.random() * 3), 2) ); }); - btnContainer.appendChild(toggleImageFields); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- for dev use if (enableExpensiveFeatures) { @@ -530,20 +415,234 @@ const createEditor = (server: CollabServer) => { }; const server = new CollabServer(); -firstEditor = createEditor(server); -const doc = firstEditor.state.doc; -// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- for dev use -if (enableExpensiveFeatures) { - server.init(doc); -} +const buttonData: Array<{ + label: string; + name: string; + values?: Record; + callback?: (view: EditorView) => void; +}> = [ + { + label: "Campaign Callout List", + name: campaignCalloutListElementName, + values: sampleCampaignCalloutList, + }, + { label: "Embed", name: embedElementName, values: sampleEmbed }, + { label: "Callout", name: embedElementName, values: sampleCallout }, + { label: "Demo image", name: demoImageElementName, values: sampleImage }, + { + label: "Image", + name: imageElementName, + callback: (view: EditorView) => { + const setMedia = (mediaPayload: MediaPayload) => { + const { + mediaId, + mediaApiUri, + assets, + suppliersReference, + caption, + photographer, + source, + } = mediaPayload; + insertElement({ + elementName: imageElementName, + values: { + caption, + photographer, + source, + mainImage: { assets, suppliersReference, mediaId, mediaApiUri }, + }, + })(view.state, view.dispatch); + }; + onCropImage(setMedia); + }, + }, + { + label: "Cartoon", + name: cartoonElementName, + callback: (view: EditorView) => { + const setMedia = (mediaPayload: MediaPayload) => { + const { photographer, caption, source } = mediaPayload; + + const imageToInsert = getImageFromMediaPayload(mediaPayload); + + // TODO: handle this error + if (!imageToInsert) return; + + insertElement({ + elementName: cartoonElementName, + values: { + largeImages: [imageToInsert], + photographer, + source, + caption, + }, + })(view.state, view.dispatch); + }; + onCropImage(setMedia); + }, + }, + { label: "Rich-link", name: richlinkElementName, values: sampleRichLink }, + { label: "Video", name: videoElementName, values: sampleVideo }, + { label: "Audio", name: audioElementName, values: sampleAudio }, + { label: "Map", name: mapElementName, values: sampleMap }, + { label: "Document", name: documentElementName, values: sampleDocument }, + { label: "Table", name: tableElementName, values: sampleTable }, + { + label: "Membership", + name: membershipElementName, + values: sampleMembership, + }, + { + label: "Interactive", + name: interactiveElementName, + values: sampleInteractive, + }, + { label: "Pullquote", name: pullquoteElementName, values: samplePullquote }, + { label: "Code", name: codeElementName, values: sampleCode }, + { label: "Form", name: formElementName, values: sampleForm }, + { label: "Vine", name: vineElementName, values: sampleVine }, + { label: "Tweet", name: tweetElementName, values: sampleTweet }, + { label: "Recipe", name: recipeElementName, values: sampleRecipe }, + { label: "Recipe atom", name: contentAtomName, values: sampleContentAtom }, + { + label: "Interactive atom", + name: contentAtomName, + values: sampleInteractiveAtom, + }, + { label: "Comment", name: commentElementName, values: sampleComment }, + { + label: "Alt Style", + name: altStyleElementName, + values: sampleAltStylesElement, + }, + { label: "Repeater", name: repeaterElementName, values: sampleRepeater }, + { label: "Nested", name: nestedElementName, values: sampleNested }, +]; + +const App = () => { + const editorContainerRef = useRef(null); + + useEffect(() => { + if (!editorContainerRef.current) { + return; + } + firstEditor = createEditor(editorContainerRef.current); + const doc = firstEditor.state.doc; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- for dev use + if (enableExpensiveFeatures) { + server.init(doc); + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- for dev use + if (enableExpensiveFeatures) { + prosemirrorDevTools(firstEditor); + } + + window.PM_ELEMENTS = { + view: firstEditor, + insertElement: insertElement, + docToHtml: () => + firstEditor ? docToHtml(serializer, firstEditor.state.doc) : "", + htmlToDoc: (html: string) => { + const node = htmlToDoc(parser, html); + firstEditor?.updateState( + EditorState.create({ + doc: node, + plugins: firstEditor.state.plugins, + }) + ); + }, + }; + }, [editorContainerRef]); + + const [elementType, setElementType] = useState(buttonData[0].label); + + const addElement = () => { + const elementData = buttonData.find((e) => e.label === elementType); + if (!elementData) { + return; + } + const { values, callback, name: elementName } = elementData; -// Add more editors -const addEditorButton = document.createElement("button"); -addEditorButton.innerHTML = "Add another editor"; -addEditorButton.id = "add-editor"; -addEditorButton.addEventListener("click", () => createEditor(server)); -btnContainer.appendChild(addEditorButton); + if (!firstEditor) { + return; + } + + if (values) { + insertElement({ elementName, values } as any)( + firstEditor.state, + firstEditor.dispatch + ); + } + + if (callback) { + callback(firstEditor); + } + }; + + return ( + <> +

+ prosemirror-elements sandbox +

+ + + + + + + + + +
+
+
+ + ); +}; + +const appContainer = document.getElementById("app"); + +render(, appContainer); // Handy debugging tools. We assign a few things to window for our integration tests, // and to facilitate debugging. @@ -552,24 +651,3 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-empty-interface -- necessary to extend the Window object interface Window extends WindowType {} } - -// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- for dev use -if (enableExpensiveFeatures) { - applyDevTools(firstEditor); -} - -window.PM_ELEMENTS = { - view: firstEditor, - insertElement: insertElement, - docToHtml: () => - firstEditor ? docToHtml(serializer, firstEditor.state.doc) : "", - htmlToDoc: (html: string) => { - const node = htmlToDoc(parser, html); - firstEditor?.updateState( - EditorState.create({ - doc: node, - plugins: firstEditor.state.plugins, - }) - ); - }, -}; diff --git a/demo/style.css b/demo/style.css index e640e670..35e1cc28 100644 --- a/demo/style.css +++ b/demo/style.css @@ -1,9 +1,10 @@ html, body { - font-family: 'TE31 Text Egyptian'; + font-family: "TE31 Text Egyptian"; width: 100%; height: 100%; margin: 0; + overflow: hidden; } html * { @@ -17,7 +18,7 @@ h4, h5, h6 { font-weight: 400; - font-family: 'DE5 Display Egyptian SemiBold' + font-family: "DE5 Display Egyptian SemiBold"; } .TestDecoration { @@ -27,8 +28,8 @@ h6 { @font-face { font-family: "Guardian Agate Sans"; src: url("fonts/GuardianAgateSans1Web-Regular.woff2") format("woff2"), - url("fonts/GuardianAgateSans1Web-Regular.woff") format("woff"), - url("fonts/GuardianAgateSans1Web-Regular.ttf") format("truetype"); + url("fonts/GuardianAgateSans1Web-Regular.woff") format("woff"), + url("fonts/GuardianAgateSans1Web-Regular.ttf") format("truetype"); font-weight: 400; font-style: normal; font-stretch: normal; @@ -64,6 +65,25 @@ h6 { font-stretch: normal; } +#app { + margin: 0 10px 10px 10px; + height: 100%; + width: 100%; + overflow-y: scroll; + overflow-x: hidden; +} + +.ProseMirror-menubar { + font-family: "GuardianTextSans", "Guardian Text Sans Web", "Helvetica Neue", + "Helvetica", "Arial", "Lucida Grande", sans-serif; + display: flex; +} + +.ProseMirror-menuitem { + display: flex; + align-items: center; +} + .modal { position: fixed; left: 0; @@ -97,7 +117,7 @@ h6 { .modal__dismiss { float: right; color: #333333; - background-color: #DCDCDC; + background-color: #dcdcdc; } #content-container { @@ -113,7 +133,8 @@ h6 { min-height: 0; } -#editor-container, #content-data { +#editor-container, +#content-data { margin: 10px; overflow-y: scroll; } @@ -178,9 +199,15 @@ h6 { position: relative; } -.ProseMirror-hideselection *::selection { background: transparent; } -.ProseMirror-hideselection *::-moz-selection { background: transparent; } -.ProseMirror-hideselection { caret-color: transparent; } +.ProseMirror-hideselection *::selection { + background: transparent; +} +.ProseMirror-hideselection *::-moz-selection { + background: transparent; +} +.ProseMirror-hideselection { + caret-color: transparent; +} .ProseMirror-selectednode { outline: 2px solid #8cf; @@ -196,7 +223,9 @@ li.ProseMirror-selectednode:after { content: ""; position: absolute; left: -32px; - right: -2px; top: -2px; bottom: -2px; + right: -2px; + top: -2px; + bottom: -2px; border: 2px solid #8cf; pointer-events: none; } @@ -223,17 +252,13 @@ img.ProseMirror-separator { white-space: pre; } -.ProseMirror-menuitem { - margin-right: 3px; - display: inline-block; -} - .ProseMirror-menuseparator { border-right: 1px solid #ddd; margin-right: 3px; } -.ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { +.ProseMirror-menu-dropdown, +.ProseMirror-menu-dropdown-menu { font-size: 90%; white-space: nowrap; } @@ -256,13 +281,14 @@ img.ProseMirror-separator { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid currentColor; - opacity: .6; + opacity: 0.6; position: absolute; right: 4px; top: calc(50% - 2px); } -.ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { +.ProseMirror-menu-dropdown-menu, +.ProseMirror-menu-submenu { position: absolute; background: white; color: #666; @@ -294,7 +320,7 @@ img.ProseMirror-separator { border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 4px solid currentColor; - opacity: .6; + opacity: 0.6; position: absolute; right: 4px; top: calc(50% - 4px); @@ -313,10 +339,11 @@ img.ProseMirror-separator { } .ProseMirror-menu-disabled { - opacity: .3; + opacity: 0.3; } -.ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { +.ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, +.ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { display: block; } @@ -327,7 +354,9 @@ img.ProseMirror-separator { min-height: 1em; color: #666; padding: 1px 6px; - top: 0; left: 0; right: 0; + top: 0; + left: 0; + right: 0; border-bottom: 1px solid silver; background: white; z-index: 10; @@ -338,7 +367,7 @@ img.ProseMirror-separator { .ProseMirror-icon { display: inline-block; - line-height: .8; + line-height: 0.8; vertical-align: -2px; /* Compensate for padding */ padding: 2px 8px; cursor: pointer; @@ -397,14 +426,16 @@ img.ProseMirror-separator { line-height: 2px; } -.ProseMirror ul, .ProseMirror ol { +.ProseMirror ul, +.ProseMirror ol { padding-left: 30px; } .ProseMirror blockquote { padding-left: 1em; border-left: 3px solid #eee; - margin-left: 0; margin-right: 0; + margin-left: 0; + margin-right: 0; } .ProseMirror-example-setup-style img { @@ -418,7 +449,7 @@ img.ProseMirror-separator { position: fixed; border-radius: 3px; z-index: 11; - box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2); + box-shadow: -0.5px 2px 5px rgba(0, 0, 0, 0.2); } .ProseMirror-prompt h5 { @@ -441,9 +472,12 @@ img.ProseMirror-separator { .ProseMirror-prompt-close { position: absolute; - left: 2px; top: 1px; + left: 2px; + top: 1px; color: #666; - border: none; background: transparent; padding: 0; + border: none; + background: transparent; + padding: 0; } .ProseMirror-prompt-close:after { @@ -464,7 +498,8 @@ img.ProseMirror-separator { margin-top: 5px; display: none; } -#editor, .editor { +#editor, +.editor { background: white; color: black; background-clip: padding-box; @@ -490,7 +525,9 @@ img.ProseMirror-separator { outline: none; } -.ProseMirror p { margin-bottom: 1em } +.ProseMirror p { + margin-bottom: 1em; +} .ProseMirror-menubar-wrapper { width: 100%; @@ -515,23 +552,18 @@ img.ProseMirror-separator { font-weight: bold; white-space: nowrap; color: white; + font-family: "Guardian Agate Sans"; } - /* Editor styles */ - -.ProseMirror-menuitem { - margin-right: 3px; - display: inline-block; -} - .ProseMirror-menuseparator { border-right: 1px solid #ddd; margin-right: 3px; } -.ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { +.ProseMirror-menu-dropdown, +.ProseMirror-menu-dropdown-menu { font-size: 90%; white-space: nowrap; } @@ -554,13 +586,14 @@ img.ProseMirror-separator { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid currentColor; - opacity: .6; + opacity: 0.6; position: absolute; right: 4px; top: calc(50% - 2px); } -.ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { +.ProseMirror-menu-dropdown-menu, +.ProseMirror-menu-submenu { position: absolute; background: white; color: #666; @@ -592,7 +625,7 @@ img.ProseMirror-separator { border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 4px solid currentColor; - opacity: .6; + opacity: 0.6; position: absolute; right: 4px; top: calc(50% - 4px); @@ -611,10 +644,11 @@ img.ProseMirror-separator { } .ProseMirror-menu-disabled { - opacity: .3; + opacity: 0.3; } -.ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { +.ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, +.ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { display: block; } @@ -625,7 +659,9 @@ img.ProseMirror-separator { min-height: 1em; color: #666; padding: 1px 6px; - top: 0; left: 0; right: 0; + top: 0; + left: 0; + right: 0; border-bottom: 1px solid silver; background: white; z-index: 10; @@ -636,7 +672,7 @@ img.ProseMirror-separator { .ProseMirror-icon { display: inline-block; - line-height: .8; + line-height: 0.8; vertical-align: -2px; /* Compensate for padding */ padding: 2px 8px; cursor: pointer; @@ -703,7 +739,7 @@ img.ProseMirror-separator { position: fixed; border-radius: 3px; z-index: 11; - box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2); + box-shadow: -0.5px 2px 5px rgba(0, 0, 0, 0.2); } .ProseMirror-prompt h5 { @@ -726,9 +762,12 @@ img.ProseMirror-separator { .ProseMirror-prompt-close { position: absolute; - left: 2px; top: 1px; + left: 2px; + top: 1px; color: #666; - border: none; background: transparent; padding: 0; + border: none; + background: transparent; + padding: 0; } .ProseMirror-prompt-close:after { @@ -766,9 +805,9 @@ img.ProseMirror-separator { /* Match Composer styling */ .table { - background: #F6F6F6; + background: #f6f6f6; border-collapse: collapse; - border-top: 2px solid #00ADEE; + border-top: 2px solid #00adee; width: 100%; } .table th { @@ -778,13 +817,13 @@ img.ProseMirror-separator { vertical-align: top; } .table tbody td { - border-top: 1px solid #EDEDED; + border-top: 1px solid #ededed; padding: 7px; } .table .table-row--highlight { - background: #F6F6F6; + background: #f6f6f6; font-weight: bold; } .table .table-row--divider td { - border-top: 1px solid #2B2B29; + border-top: 1px solid #2b2b29; } diff --git a/webpack.config.js b/webpack.config.js index 711c400f..b4b64053 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,7 +21,7 @@ export default { path: path.resolve(dirName, "dist"), }, devtool: "inline-source-map", - entry: "./demo/index.ts", + entry: "./demo/index.tsx", mode: "development", devServer: { static: {