From cab09e59b979173cba0d23a216b3883ea38f91df Mon Sep 17 00:00:00 2001 From: "DESKTOP-C9V2M01\\Zeeri" Date: Thu, 27 Oct 2022 08:56:26 -0700 Subject: [PATCH] Switched to using Verbum (https://github.com/ozanyurtsever/verbum) which builds on Lexical to create a rich text editor; Isolated the rich text editor into a component so it can be easily swapped out; Added a higher resolution background for the student login page; Still a lot of bugs in the workshop list view. --- .meteor/packages | 4 +- imports/api/index.js | 1 + imports/api/workshops.js | 2 +- imports/ui/App.jsx | 4 +- imports/ui/components/RichText/Editor.jsx | 122 ++++++++++++ imports/ui/components/RichText/Nodes.js | 65 +++++++ .../{Workshops.jsx => WorkshopList.jsx} | 62 ++++-- package.json | 8 +- public/images/student2.svg | 182 ++++++++++++++++++ tsconfig.json | 7 +- 10 files changed, 433 insertions(+), 24 deletions(-) create mode 100644 imports/ui/components/RichText/Editor.jsx create mode 100644 imports/ui/components/RichText/Nodes.js rename imports/ui/pages/Student/{Workshops.jsx => WorkshopList.jsx} (63%) create mode 100644 public/images/student2.svg diff --git a/.meteor/packages b/.meteor/packages index 40d59bd..8990821 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -14,7 +14,7 @@ standard-minifier-css@1.8.1 # CSS minifier run for production mode standard-minifier-js@2.8.0 # JS minifier run for production mode es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers ecmascript@0.16.2 # Enable ECMAScript2015+ syntax in app code -typescript@4.5.4 # Enable TypeScript syntax in .ts and .tsx modules +typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server@0.5.0 # Server-side component of the `meteor shell` command static-html@1.3.2 # Define static page content in .html files @@ -26,4 +26,4 @@ service-configuration@1.3.0 google-config-ui@1.0.3 # Adds the UI for logging in via Google alanning:roles # Adds roles to the user -msavin:mongol # Free version of MeteorToys - Provides access to the client side MongoDB for debugging. (Ctrl-M to activate :: https://atmospherejs.com/msavin/mongol) \ No newline at end of file +msavin:mongol # Free version of MeteorToys - Provides access to the client side MongoDB for debugging. (Ctrl-M to activate :: https://atmospherejs.com/msavin/mongol) diff --git a/imports/api/index.js b/imports/api/index.js index 5c32ba9..30eb81a 100644 --- a/imports/api/index.js +++ b/imports/api/index.js @@ -7,5 +7,6 @@ import "./sites.js"; import "./asset-types.js"; import "./assets.js"; import "./asset-assignment-history.js"; +import "./workshops.js"; // console.log("Finished setting up server side models."); \ No newline at end of file diff --git a/imports/api/workshops.js b/imports/api/workshops.js index 74c57c0..c5e6d73 100644 --- a/imports/api/workshops.js +++ b/imports/api/workshops.js @@ -29,7 +29,7 @@ Meteor.methods({ let signupSheet = []; check(name, String); - check(description, String); + check(description, Match.Maybe(String)); // Match a positive integer or undefined/null. check(signupLimit, Match.Where((x) => { check(x, Match.Maybe(Match.Integer)); diff --git a/imports/ui/App.jsx b/imports/ui/App.jsx index 9acd569..5d7b3b5 100644 --- a/imports/ui/App.jsx +++ b/imports/ui/App.jsx @@ -14,7 +14,7 @@ import Users from './pages/Users' import Admin from './pages/Admin' import Home from './pages/Home' import {StudentPage} from './pages/Student/StudentPage' -import {Workshops} from './pages/Student/Workshops' +import {WorkshopList} from './pages/Student/WorkshopList' const appTheme = createTheme({ components: { @@ -92,7 +92,7 @@ export const App = (props) => { }/> - {user && } + {user && } }/> { + return ( + // + // + // } placeholder={
Sample...
}/> + // {props.onChange && } + // + // + // + //
+ + + + + + + + + + + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/imports/ui/components/RichText/Nodes.js b/imports/ui/components/RichText/Nodes.js new file mode 100644 index 0000000..b709d81 --- /dev/null +++ b/imports/ui/components/RichText/Nodes.js @@ -0,0 +1,65 @@ +/* +import {CodeHighlightNode, CodeNode} from '@lexical/code'; +import {HashtagNode} from '@lexical/hashtag'; +import {AutoLinkNode, LinkNode} from '@lexical/link'; +import {ListItemNode, ListNode} from '@lexical/list'; +import {MarkNode} from '@lexical/mark'; +import {OverflowNode} from '@lexical/overflow'; +import {HorizontalRuleNode} from '@lexical/react/LexicalHorizontalRuleNode'; +import {HeadingNode, QuoteNode} from '@lexical/rich-text'; +import {TableCellNode, TableNode, TableRowNode} from '@lexical/table'; + +// import {CollapsibleContainerNode} from '../plugins/CollapsiblePlugin/CollapsibleContainerNode'; +// import {CollapsibleContentNode} from '../plugins/CollapsiblePlugin/CollapsibleContentNode'; +// import {CollapsibleTitleNode} from '../plugins/CollapsiblePlugin/CollapsibleTitleNode'; +// import {AutocompleteNode} from './AutocompleteNode'; +// import {EmojiNode} from './EmojiNode'; +// import {EquationNode} from './EquationNode'; +// import {ExcalidrawNode} from './ExcalidrawNode'; +// import {FigmaNode} from './FigmaNode'; +// import {ImageNode} from './ImageNode'; +// import {KeywordNode} from './KeywordNode'; +// import {MentionNode} from './MentionNode'; +// import {PollNode} from './PollNode'; +// import {StickyNode} from './StickyNode'; +// import {TableNode as NewTableNode} from './TableNode'; +// import {TweetNode} from './TweetNode'; +// import {YouTubeNode} from './YouTubeNode'; + +const Nodes = [ + HeadingNode, + ListNode, + ListItemNode, + QuoteNode, + CodeNode, + TableNode, + TableCellNode, + TableRowNode, + HashtagNode, + CodeHighlightNode, + AutoLinkNode, + LinkNode, + OverflowNode, + HorizontalRuleNode, + MarkNode, + // NewTableNode, + // PollNode, + // StickyNode, + // ImageNode, + // MentionNode, + // EmojiNode, + // ExcalidrawNode, + // EquationNode, + // AutocompleteNode, + // KeywordNode, + // TweetNode, + // YouTubeNode, + // FigmaNode, + // CollapsibleContainerNode, + // CollapsibleContentNode, + // CollapsibleTitleNode, +]; + +export default Nodes; + + */ \ No newline at end of file diff --git a/imports/ui/pages/Student/Workshops.jsx b/imports/ui/pages/Student/WorkshopList.jsx similarity index 63% rename from imports/ui/pages/Student/Workshops.jsx rename to imports/ui/pages/Student/WorkshopList.jsx index 3ac8c8b..c115aca 100644 --- a/imports/ui/pages/Student/Workshops.jsx +++ b/imports/ui/pages/Student/WorkshopList.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import {Meteor} from "meteor/meteor"; import {Roles} from 'meteor/alanning:roles'; import { useTracker } from 'meteor/react-meteor-data'; @@ -16,11 +16,23 @@ import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; -import {Editor} from 'react-draft-wysiwyg' -import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' import {Students} from "/imports/api/students"; import {Staff} from "/imports/api/staff"; import {conditions} from "/imports/api/assets"; +import {Workshops} from "/imports/api/workshops"; + +import {$getRoot, $getSelection} from 'lexical'; +import {LexicalComposer} from '@lexical/react/LexicalComposer'; +import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin'; +import {ContentEditable} from '@lexical/react/LexicalContentEditable'; +import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin'; +import {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin'; +import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; +import {RichTextPlugin} from "@lexical/react/LexicalRichTextPlugin"; +import {LinkPlugin} from "@lexical/react/LexicalLinkPlugin"; +import {MarkdownShortcutPlugin} from "@lexical/react/LexicalMarkdownShortcutPlugin"; +import {AutoLinkNode, LinkNode} from '@lexical/link'; +import {Editor} from "/imports/ui/components/RichText/Editor"; const cssTwoColumnContainer = { display: 'grid', @@ -29,7 +41,7 @@ const cssTwoColumnContainer = { rowGap: '0.4rem', } -export const Workshops = () => { +export const WorkshopList = () => { Meteor.subscribe('students'); Meteor.subscribe('staff'); Meteor.subscribe('workshops'); @@ -58,46 +70,66 @@ export const Workshops = () => { backgroundColor: selectedWorkshop === item ? '#EECFA6' : 'white' } } + + const [openWorkshopEditor, setOpenWorkshopEditor] = useState(false) + const [editedWorkshop, setEditedWorkshop] = useState({}) + const [editedName, setEditedName] = useState("") + const [editedDescription, setEditedDescription] = useState("") + const [editedSignupLimit, setEditedSignupLimit] = useState("") const newWorkshop = () => { if(isAdmin) { + setEditedDescription("") setEditedWorkshop({}) + setEditedDescription("") + setEditedName("") + setEditedSignupLimit("") setOpenWorkshopEditor(true) } } const editWorkshop = () => { if(isAdmin && selectedWorkshop) { setEditedWorkshop({...selectedWorkshop}) + setEditedDescription(selectedWorkshop.description ? selectedWorkshop.description : "") + setEditedName(selectedWorkshop.name ? selectedWorkshop.name : "") + setEditedSignupLimit(selectedWorkshop.signupLimit ? selectedWorkshop.signupLimit : "") setOpenWorkshopEditor(true) } } - - const [openWorkshopEditor, setOpenWorkshopEditor] = useState(false) - const [editedWorkshop, setEditedWorkshop] = useState(false) const workshopEditorClosed = (save) => { const completeHandler = (err, result) => { if(err) console.error(err) else { setOpenWorkshopEditor(false) - setEditedWorkshop(null) } } if(save) { - if(editedWorkshop._id) Meteor.call('workshops.update', editedWorkshop._id, editedWorkshop.name, editedWorkshop.description, editedWorkshop.signupLimit, completeHandler) - else Meteor.call('workshops.add', editedWorkshop.name, editedWorkshop.description, editedWorkshop.signupLimit, completeHandler) + if(editedWorkshop._id) Meteor.call('workshops.update', editedWorkshop._id, editedName, editedDescription, editedSignupLimit, completeHandler) + else Meteor.call('workshops.add', editedName, editedDescription, editedSignupLimit, completeHandler) } else completeHandler() } + const onLexicalComposerError = (err) => { + console.error(err) + } + const lexicalComposerTheme = { + + } + const lexicalComposerChanged = (e) => { + console.log(e) + } + const descriptionEditorNodes = [LinkNode] + return ( <> Workshop Editor - {editedWorkshop.name = e.target.value; setEditedWorkshop(editedWorkshop)}}/> - {selectedWorkshop.description = e.target.value; setEditedWorkshop(editedWorkshop)}}/> - {editedWorkshop.signupLimit = e.target.value; setEditedWorkshop(editedWorkshop)}}/> + {setEditedName(e.target.value)}}/> + + {setEditedSignupLimit(e.target.value)}}/> @@ -121,10 +153,10 @@ export const Workshops = () => { {/* {selectedWorkshop.description = ""}}/>*/} - {`${selectedWorkshop.description}`} + {selectedWorkshop && `${selectedWorkshop.description}`} - {selectedWorkshop.signupSheet.map((next, i) => { + {selectedWorkshop && selectedWorkshop.signupSheet.map((next, i) => { return ( diff --git a/package.json b/package.json index 9b56887..1778fec 100644 --- a/package.json +++ b/package.json @@ -10,25 +10,29 @@ "@babel/runtime": "^7.16.7", "@emotion/react": "^11.10.0", "@emotion/styled": "^11.10.0", + "@lexical/headless": "^0.5.0", + "@lexical/link": "^0.5.0", + "@lexical/react": "^0.5.0", "@mui/icons-material": "^5.10.2", "@mui/material": "^5.10.2", "bcrypt": "^5.0.1", "classnames": "^2.2.6", "csv-parse": "^5.3.0", "dayjs": "^1.11.3", - "draft-js": "^0.11.7", "html5-qrcode": "^2.2.0", "jquery": "^3.6.0", + "lexical": "^0.5.0", "lodash": "^4.17.15", "meteor-node-stubs": "^1.0.0", "moment": "^2.29.2", "mongodb": "^4.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-draft-wysiwyg": "^1.15.0", "react-router-dom": "^6.3.0", + "typescript": "^4.8.4", "umbrellajs": "^3.3.1", "underscore": "^1.13.2", + "verbum": "^0.4.0", "winston": "^3.7.2", "winston-daily-rotate-file": "^4.6.1", "ws": "^8.4.2" diff --git a/public/images/student2.svg b/public/images/student2.svg new file mode 100644 index 0000000..567df6c --- /dev/null +++ b/public/images/student2.svg @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tsconfig.json b/tsconfig.json index c406978..ebb707e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { /* Basic Options */ - "target": "es2018", + "target": "es2019", "module": "esNext", "lib": ["esnext", "dom"], "allowJs": true, @@ -25,7 +25,7 @@ "baseUrl": ".", "paths": { /* Support absolute /imports/* with a leading '/' */ - "/*": ["*"] + "/*": ["*"], }, "moduleResolution": "node", "resolveJsonModule": true, @@ -33,6 +33,9 @@ "esModuleInterop": true, "preserveSymlinks": true }, + "include": [ + "./imports/**" + ], "exclude": [ "./.meteor/**", "./packages/**"