Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@
"lint": "next lint"
},
"dependencies": {
"@heroicons/react": "^2.0.1",
"@emotion/css": "^11.10.0",
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@hookform/resolvers": "^2.9.7",
"@mui/icons-material": "^5.10.6",
"@mui/material": "^5.10.6",
"draft-js": "^0.11.7",
"next": "12.2.5",
"qrcode.react": "^3.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-draft-wysiwyg": "^1.15.0",
"react-file-base64": "^1.0.3",
"react-hook-form": "^7.34.2",
"react-icons": "^4.4.0",
"react-icons-kit": "^2.0.0",
"save-svg-as-png": "^1.4.17",
"slate": "^0.82.1",
"slate-history": "^0.66.0",
"slate-react": "^0.82.2",
"yup": "^0.32.11"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions src/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const Layout = (props: any) => {
</div>
{props.children}
</div>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<Footer />
</main>
</div>
Expand Down
333 changes: 333 additions & 0 deletions src/Layout/Main/VacancyForm.tsx

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/Layout/Navigation/ListButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ const ListButton = () => {
<ButtonNav text="Social Media" />
</a>
</Link>
<Link href="/qr-code-for-vacancy">
<a>
<ButtonNav text="Vacancy" />
</a>
</Link>
</div>
);
};
Expand Down
228 changes: 228 additions & 0 deletions src/components/Wysiwig/RichTextEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import isHotkey from 'is-hotkey'
import { useCallback, useMemo } from 'react'
import { createEditor, Descendant, Editor, Element as SlateElement, Transforms } from 'slate'
import { withHistory } from 'slate-history'
import { Editable, Slate, useSlate, withReact } from 'slate-react'

// import { Button, Icon, Toolbar } from "./slateComponent";
import { Button, Icon, Toolbar } from './SlateComponent'

const HOTKEYS = {
'mod+b': 'bold',
'mod+i': 'italic',
'mod+u': 'underline',
'mod+`': 'code',
}

const LIST_TYPES = ['numbered-list', 'bulleted-list']
const TEXT_ALIGN_TYPES = ['left', 'center', 'right', 'justify']

export const RichTextEditor = ({ onChange, value }) => {
const renderElement = useCallback((props) => <Element {...props} />, [])
const renderLeaf = useCallback((props) => <Leaf {...props} />, [])
const editor = useMemo(() => withHistory(withReact(createEditor())), [])

return (
<Slate
editor={editor}
value={
value || [
{
type: 'paragraph',
children: [{ text: 'Add your job requirements / skills', italic: true }],
},
]
}
onChange={onChange}>
<Toolbar>
<MarkButton format='bold' icon='format_bold' />
<MarkButton format='italic' icon='format_italic' />
<MarkButton format='underline' icon='format_underlined' />
<MarkButton format='code' icon='code' />
<BlockButton format='heading-one' icon='looks_one' />
<BlockButton format='heading-two' icon='looks_two' />
<BlockButton format='block-quote' icon='format_quote' />
<BlockButton format='numbered-list' icon='format_list_numbered' />
<BlockButton format='bulleted-list' icon='format_list_bulleted' />
<BlockButton format='left' icon='format_align_left' />
<BlockButton format='center' icon='format_align_center' />
<BlockButton format='right' icon='format_align_right' />
<BlockButton format='justify' icon='format_align_justify' />
</Toolbar>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
placeholder='Enter some rich text…'
spellCheck
autoFocus
onKeyDown={(event) => {
for (const hotkey in HOTKEYS) {
if (isHotkey(hotkey, event as any)) {
event.preventDefault()
const mark = HOTKEYS[hotkey]
toggleMark(editor, mark)
}
}
}}
/>
</Slate>
)
}

const toggleBlock = (editor, format) => {
const isActive = isBlockActive(editor, format, TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type')
const isList = LIST_TYPES.includes(format)

Transforms.unwrapNodes(editor, {
match: (n) => !Editor.isEditor(n) && SlateElement.isElement(n) && LIST_TYPES.includes(n.type) && !TEXT_ALIGN_TYPES.includes(format),
split: true,
})
let newProperties: Partial<SlateElement>
if (TEXT_ALIGN_TYPES.includes(format)) {
newProperties = {
align: isActive ? undefined : format,
}
} else {
newProperties = {
type: isActive ? 'paragraph' : isList ? 'list-item' : format,
}
}
Transforms.setNodes<SlateElement>(editor, newProperties)

if (!isActive && isList) {
const block = { type: format, children: [] }
Transforms.wrapNodes(editor, block)
}
}

const toggleMark = (editor, format) => {
const isActive = isMarkActive(editor, format)

if (isActive) {
Editor.removeMark(editor, format)
} else {
Editor.addMark(editor, format, true)
}
}

const isBlockActive = (editor, format, blockType = 'type') => {
const { selection } = editor
if (!selection) return false

const [match] = Array.from(
Editor.nodes(editor, {
at: Editor.unhangRange(editor, selection),
match: (n) => !Editor.isEditor(n) && SlateElement.isElement(n) && n[blockType] === format,
})
)

return !!match
}

const isMarkActive = (editor, format) => {
const marks = Editor.marks(editor)
return marks ? marks[format] === true : false
}

const Element = ({ attributes, children, element }) => {
const style = { textAlign: element.align }
switch (element.type) {
case 'block-quote':
return (
<blockquote style={style} {...attributes}>
{children}
</blockquote>
)
case 'bulleted-list':
return (
<ul style={style} {...attributes}>
{children}
</ul>
)
case 'heading-one':
return (
<h1 style={style} {...attributes}>
{children}
</h1>
)
case 'heading-two':
return (
<h2 style={style} {...attributes}>
{children}
</h2>
)
case 'list-item':
return (
<li style={style} {...attributes}>
{children}
</li>
)
case 'numbered-list':
return (
<ol style={style} {...attributes}>
{children}
</ol>
)
default:
return (
<p style={style} {...attributes}>
{children}
</p>
)
}
}

const Leaf = ({ attributes, children, leaf }) => {
if (leaf.bold) {
children = <strong>{children}</strong>
}

if (leaf.code) {
children = <code>{children}</code>
}

if (leaf.italic) {
children = <em>{children}</em>
}

if (leaf.underline) {
children = <u>{children}</u>
}

return <span {...attributes}>{children}</span>
}

const BlockButton = ({ format, icon }) => {
const editor = useSlate()
return (
<Button
active={isBlockActive(editor, format, TEXT_ALIGN_TYPES.includes(format) ? 'align' : 'type')}
onMouseDown={(event) => {
event.preventDefault()
toggleBlock(editor, format)
}}>
<Icon>{icon}</Icon>
</Button>
)
}

const MarkButton = ({ format, icon }) => {
const editor = useSlate()
return (
<Button
active={isMarkActive(editor, format)}
onMouseDown={(event) => {
event.preventDefault()
toggleMark(editor, format)
}}>
<Icon>{icon}</Icon>
</Button>
)
}

const initialValue: Descendant[] = [
{
type: 'paragraph',
children: [{ text: 'Add your job requirements / skills', italic: true }],
},
]
Loading