Fix typography feature, scroll top button fix, slugify fix (#93)
* Fix typography feature, scroll top button fix, slugify fix * build fix, some lint * refactoring, lint
This commit is contained in:
parent
b040d5e0c9
commit
47d14b0a5d
16
package-lock.json
generated
16
package-lock.json
generated
|
@ -17,7 +17,8 @@
|
|||
"formidable": "2.1.1",
|
||||
"i18next": "22.4.15",
|
||||
"mailgun.js": "8.2.1",
|
||||
"node-fetch": "3.3.1"
|
||||
"node-fetch": "3.3.1",
|
||||
"typograf": "7.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.21.8",
|
||||
|
@ -19579,6 +19580,14 @@
|
|||
"node": ">=12.20"
|
||||
}
|
||||
},
|
||||
"node_modules/typograf": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/typograf/-/typograf-7.1.0.tgz",
|
||||
"integrity": "sha512-R7Kbb7JKuT96hWHTWQbZrGUchCk98rM2IQ38mi0ye2LoEJRn4o3lSZ8DZxK4ufrszUlrXIvnidIZ0AKA7UHvgg==",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "0.7.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||
|
@ -35067,6 +35076,11 @@
|
|||
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
|
||||
"dev": true
|
||||
},
|
||||
"typograf": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/typograf/-/typograf-7.1.0.tgz",
|
||||
"integrity": "sha512-R7Kbb7JKuT96hWHTWQbZrGUchCk98rM2IQ38mi0ye2LoEJRn4o3lSZ8DZxK4ufrszUlrXIvnidIZ0AKA7UHvgg=="
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||
|
|
|
@ -37,7 +37,8 @@
|
|||
"formidable": "2.1.1",
|
||||
"i18next": "22.4.15",
|
||||
"mailgun.js": "8.2.1",
|
||||
"node-fetch": "3.3.1"
|
||||
"node-fetch": "3.3.1",
|
||||
"typograf": "7.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.21.8",
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
"...subscribing": "...subscribing",
|
||||
"About myself": "About myself",
|
||||
"About the project": "About the project",
|
||||
"Add another image": "Add another image",
|
||||
"Add comment": "Comment",
|
||||
"Add image": "Add image",
|
||||
"Add another image": "Add another image",
|
||||
"Address on Discourse": "Address on Discourse",
|
||||
"All": "All",
|
||||
"All authors": "All authors",
|
||||
|
@ -46,9 +46,6 @@
|
|||
"Create Chat": "Create Chat",
|
||||
"Create Group": "Create a group",
|
||||
"Create account": "Create an account",
|
||||
"Password should be at least 8 characters": "Password should be at least 8 characters",
|
||||
"Password should contain at least one number": "Password should contain at least one number",
|
||||
"Password should contain at least one special character: !@#$%^&*": "Password should contain at least one special character: !@#$%^&*",
|
||||
"Create post": "Create post",
|
||||
"Date of Birth": "Date of Birth",
|
||||
"Delete": "Delete",
|
||||
|
@ -77,6 +74,7 @@
|
|||
"Feed settings": "Feed settings",
|
||||
"Feedback": "Feedback",
|
||||
"Fill email": "Fill email",
|
||||
"Fix typography": "Fix typography",
|
||||
"Follow": "Follow",
|
||||
"Follow the topic": "Follow the topic",
|
||||
"Followers": "Followers",
|
||||
|
@ -146,6 +144,9 @@
|
|||
"Partners": "Partners",
|
||||
"Password": "Password",
|
||||
"Password again": "Password again",
|
||||
"Password should be at least 8 characters": "Password should be at least 8 characters",
|
||||
"Password should contain at least one number": "Password should contain at least one number",
|
||||
"Password should contain at least one special character: !@#$%^&*": "Password should contain at least one special character: !@#$%^&*",
|
||||
"Passwords are not equal": "Passwords are not equal",
|
||||
"Paste Embed code": "Paste Embed code",
|
||||
"Personal": "Personal",
|
||||
|
@ -163,6 +164,7 @@
|
|||
"Profile": "Profile",
|
||||
"Profile settings": "Profile settings",
|
||||
"Publications": "Publications",
|
||||
"Publish Settings": "Publish Settings",
|
||||
"Quit": "Quit",
|
||||
"Quotes": "Quotes",
|
||||
"Reason uknown": "Reason unknown",
|
||||
|
@ -224,6 +226,7 @@
|
|||
"Try to find another way": "Try to find another way",
|
||||
"Unfollow": "Unfollow",
|
||||
"Unfollow the topic": "Unfollow the topic",
|
||||
"Unnamed draft": "Unnamed draft",
|
||||
"Upload": "Upload",
|
||||
"Username": "Username",
|
||||
"Userpic": "Userpic",
|
||||
|
@ -279,7 +282,5 @@
|
|||
"topics": "topics",
|
||||
"user already exist": "user already exists",
|
||||
"view": "view",
|
||||
"zine": "zine",
|
||||
"Unnamed draft": "Unnamed draft",
|
||||
"Publish Settings": "Publish Settings"
|
||||
"zine": "zine"
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
"A short introduction to keep the reader interested": "Небольшое вступление, чтобы заинтересовать читателя",
|
||||
"About myself": "О себе",
|
||||
"About the project": "О проекте",
|
||||
"Add another image": "Добавить другое изображение",
|
||||
"Add comment": "Комментировать",
|
||||
"Add image": "Добавить изображение",
|
||||
"Add another image": "Добавить другое изображение",
|
||||
"Add to bookmarks": "Добавить в закладки",
|
||||
"Address on Discourse": "Адрес на Дискурсе",
|
||||
"All": "Все",
|
||||
|
@ -48,9 +48,6 @@
|
|||
"Create Chat": "Создать чат",
|
||||
"Create Group": "Создать группу",
|
||||
"Create account": "Создать аккаунт",
|
||||
"Password should be at least 8 characters": "Пароль должен быть не менее 8 символов",
|
||||
"Password should contain at least one number": "Пароль должен содержать хотя бы одну цифру",
|
||||
"Password should contain at least one special character: !@#$%^&*": "Пароль должен содержать хотя бы один специальный символ: !@#$%^&*",
|
||||
"Create post": "Создать публикацию",
|
||||
"Date of Birth": "Дата рождения",
|
||||
"Delete": "Удалить",
|
||||
|
@ -80,6 +77,7 @@
|
|||
"Feed settings": "Настройки ленты",
|
||||
"Feedback": "Обратная связь",
|
||||
"Fill email": "Введите почту",
|
||||
"Fix typography": "Исправить типографику",
|
||||
"Follow": "Подписаться",
|
||||
"Follow the topic": "Подписаться на тему",
|
||||
"Followers": "Подписчики",
|
||||
|
@ -153,6 +151,9 @@
|
|||
"Partners": "Партнёры",
|
||||
"Password": "Пароль",
|
||||
"Password again": "Пароль ещё раз",
|
||||
"Password should be at least 8 characters": "Пароль должен быть не менее 8 символов",
|
||||
"Password should contain at least one number": "Пароль должен содержать хотя бы одну цифру",
|
||||
"Password should contain at least one special character: !@#$%^&*": "Пароль должен содержать хотя бы один специальный символ: !@#$%^&*",
|
||||
"Passwords are not equal": "Пароли не совпадают",
|
||||
"Paste Embed code": "Вставьте embed код",
|
||||
"Personal": "Личные",
|
||||
|
@ -174,6 +175,7 @@
|
|||
"Publication settings": "Настройки публикации",
|
||||
"Publications": "Публикации",
|
||||
"Publish": "Опубликовать",
|
||||
"Publish Settings": "Настройки публикации",
|
||||
"Quit": "Выйти",
|
||||
"Quotes": "Цитаты",
|
||||
"Reason uknown": "Причина неизвестна",
|
||||
|
@ -237,6 +239,7 @@
|
|||
"Try to find another way": "Попробуйте найти по-другому",
|
||||
"Unfollow": "Отписаться",
|
||||
"Unfollow the topic": "Отписаться от темы",
|
||||
"Unnamed draft": "Unnamed draft",
|
||||
"Upload": "Загрузить",
|
||||
"Username": "Имя пользователя",
|
||||
"Userpic": "Аватар",
|
||||
|
@ -300,7 +303,5 @@
|
|||
"topics": "темы",
|
||||
"user already exist": "пользователь уже существует",
|
||||
"view": "просмотр",
|
||||
"zine": "журнал",
|
||||
"Publish Settings": "Настройки публикации",
|
||||
"Unnamed draft": "Unnamed draft"
|
||||
"zine": "журнал"
|
||||
}
|
||||
|
|
|
@ -187,12 +187,14 @@ export const Editor = (props: EditorProps) => {
|
|||
]
|
||||
}))
|
||||
|
||||
const html = useEditorHTML(() => editor())
|
||||
|
||||
const {
|
||||
actions: { countWords }
|
||||
actions: { countWords, setEditor }
|
||||
} = useEditorContext()
|
||||
|
||||
setEditor(editor)
|
||||
|
||||
const html = useEditorHTML(() => editor())
|
||||
|
||||
createEffect(() => {
|
||||
props.onChange(html())
|
||||
if (html()) {
|
||||
|
|
|
@ -59,6 +59,13 @@
|
|||
a {
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
color: rgb(255 255 255 / 35%);
|
||||
font-weight: normal !important;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.linkWithIcon {
|
||||
|
@ -73,16 +80,6 @@
|
|||
margin-top: 3em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(255 255 255 / 35%);
|
||||
font-weight: normal !important;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@ import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
|||
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import { router } from '../../../stores/router'
|
||||
import { useEditorHTML } from 'solid-tiptap'
|
||||
import Typograf from 'typograf'
|
||||
|
||||
const typograf = new Typograf({ locale: ['ru', 'en-US'] })
|
||||
|
||||
type Props = {
|
||||
shoutId: number
|
||||
|
@ -18,6 +22,7 @@ export const Panel = (props: Props) => {
|
|||
const {
|
||||
isEditorPanelVisible,
|
||||
wordCounter,
|
||||
editorRef,
|
||||
actions: { toggleEditorPanel, saveShout, publishShout }
|
||||
} = useEditorContext()
|
||||
|
||||
|
@ -47,6 +52,12 @@ export const Panel = (props: Props) => {
|
|||
publishShout()
|
||||
}
|
||||
|
||||
const handleFixTypographyLinkClick = (e) => {
|
||||
e.preventDefault()
|
||||
const html = useEditorHTML(() => editorRef.current())
|
||||
editorRef.current().commands.setContent(typograf.execute(html()))
|
||||
}
|
||||
|
||||
return (
|
||||
<aside
|
||||
ref={(el) => (containerRef.current = el)}
|
||||
|
@ -111,6 +122,11 @@ export const Panel = (props: Props) => {
|
|||
{t('Publication settings')}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a onClick={handleFixTypographyLinkClick} href="#">
|
||||
{t('Fix typography')}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a>{t('Corrections history')}</a>
|
||||
</p>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.ProseMirror {
|
||||
outline: none;
|
||||
min-height: 300px;
|
||||
|
||||
blockquote {
|
||||
@include font-size(1.6rem);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
color: #ccc;
|
||||
}
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
background: #000;
|
||||
content: '';
|
||||
height: 100%;
|
||||
|
|
|
@ -89,14 +89,15 @@
|
|||
}
|
||||
|
||||
h4 {
|
||||
@include font-size(1.2rem);
|
||||
|
||||
color: #9fa1a7;
|
||||
cursor: pointer;
|
||||
@include font-size(1.2rem);
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
content: '+';
|
||||
font-size: 1.6em;
|
||||
line-height: 1;
|
||||
|
@ -107,7 +108,7 @@
|
|||
}
|
||||
|
||||
&.opened {
|
||||
&:after {
|
||||
&::after {
|
||||
right: 0.9rem;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
|
|
@ -44,18 +44,18 @@ export const RegisterForm = () => {
|
|||
}
|
||||
}
|
||||
|
||||
function isValidPassword(password) {
|
||||
function isValidPassword(passwordToCheck) {
|
||||
const minLength = 8
|
||||
const hasNumber = /\d/
|
||||
const hasSpecial = /[!@#$%^&*]/
|
||||
const hasSpecial = /[!#$%&*@^]/
|
||||
|
||||
if (password.length < minLength) {
|
||||
if (passwordToCheck.length < minLength) {
|
||||
return t('Password should be at least 8 characters')
|
||||
}
|
||||
if (!hasNumber.test(password)) {
|
||||
if (!hasNumber.test(passwordToCheck)) {
|
||||
return t('Password should contain at least one number')
|
||||
}
|
||||
if (!hasSpecial.test(password)) {
|
||||
if (!hasSpecial.test(passwordToCheck)) {
|
||||
return t('Password should contain at least one special character: !@#$%^&*')
|
||||
}
|
||||
return null
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
//Grow input
|
||||
// Grow input
|
||||
|
||||
.editSettings,
|
||||
.edit {
|
||||
|
|
|
@ -20,7 +20,8 @@ type EditViewProps = {
|
|||
shout: Shout
|
||||
}
|
||||
|
||||
const scrollTop = () => {
|
||||
const handleScrollTopButtonClick = (e) => {
|
||||
e.preventDefault()
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
|
@ -124,7 +125,7 @@ export const EditView = (props: EditViewProps) => {
|
|||
class={clsx(styles.scrollTopButton, {
|
||||
[styles.visible]: isScrolled()
|
||||
})}
|
||||
onClick={scrollTop}
|
||||
onClick={handleScrollTopButtonClick}
|
||||
>
|
||||
<Icon name="up-button" class={styles.icon} />
|
||||
<span class={styles.scrollTopButtonLabel}>{t('Scroll up')}</span>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
border: none;
|
||||
resize: none;
|
||||
overflow: hidden;
|
||||
|
||||
&::placeholder {
|
||||
color: #858585;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useSnackbar } from './snackbar'
|
|||
import { openPage } from '@nanostores/router'
|
||||
import { router, useRouter } from '../stores/router'
|
||||
import { slugify } from '../utils/slugify'
|
||||
import { Editor } from '@tiptap/core'
|
||||
|
||||
type WordCounter = {
|
||||
characters: number
|
||||
|
@ -30,6 +31,7 @@ type EditorContextType = {
|
|||
wordCounter: Accessor<WordCounter>
|
||||
form: ShoutForm
|
||||
formErrors: Record<keyof ShoutForm, string>
|
||||
editorRef: { current: () => Editor }
|
||||
actions: {
|
||||
saveShout: () => Promise<void>
|
||||
publishShout: () => Promise<void>
|
||||
|
@ -39,6 +41,7 @@ type EditorContextType = {
|
|||
countWords: (value: WordCounter) => void
|
||||
setForm: SetStoreFunction<ShoutForm>
|
||||
setFormErrors: SetStoreFunction<Record<keyof ShoutForm, string>>
|
||||
setEditor: (editor: () => Editor) => void
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +70,8 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
|||
|
||||
const [isEditorPanelVisible, setIsEditorPanelVisible] = createSignal<boolean>(false)
|
||||
|
||||
const editorRef: { current: () => Editor } = { current: null }
|
||||
|
||||
const [form, setForm] = createStore<ShoutForm>(null)
|
||||
const [formErrors, setFormErrors] = createStore<Record<keyof ShoutForm, string>>(null)
|
||||
|
||||
|
@ -198,6 +203,10 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const setEditor = (editor: () => Editor) => {
|
||||
editorRef.current = editor
|
||||
}
|
||||
|
||||
const actions = {
|
||||
saveShout,
|
||||
publishShout,
|
||||
|
@ -206,10 +215,18 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
|||
toggleEditorPanel,
|
||||
countWords,
|
||||
setForm,
|
||||
setFormErrors
|
||||
setFormErrors,
|
||||
setEditor
|
||||
}
|
||||
|
||||
const value: EditorContextType = { actions, form, formErrors, isEditorPanelVisible, wordCounter }
|
||||
const value: EditorContextType = {
|
||||
actions,
|
||||
form,
|
||||
formErrors,
|
||||
editorRef,
|
||||
isEditorPanelVisible,
|
||||
wordCounter
|
||||
}
|
||||
|
||||
return <EditorContext.Provider value={value}>{props.children}</EditorContext.Provider>
|
||||
}
|
||||
|
|
|
@ -211,8 +211,8 @@ h5 {
|
|||
padding-bottom: 0.2rem;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
background: rgb(243, 244, 246);
|
||||
&::before {
|
||||
background: rgb(243 244 246);
|
||||
content: '';
|
||||
height: 100%;
|
||||
left: 0;
|
||||
|
|
|
@ -2,6 +2,6 @@ import { translit } from './ru2en'
|
|||
|
||||
export const slugify = (text) => {
|
||||
return translit(text.toLowerCase())
|
||||
.replaceAll(/[^\da-z]/g, '')
|
||||
.replaceAll(' ', '-')
|
||||
.replaceAll(/[^\da-z]/g, '')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user