asma-ui-richeditor 0.1.11-grisha → 0.1.12-grisha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +147 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/rich-input/RichInput.d.ts +13 -0
- package/dist/src/rich-input/RichInput.jsx +129 -0
- package/dist/src/rich-input/components/CustomMenuItem.d.ts +3 -0
- package/dist/src/rich-input/components/CustomMenuItem.jsx +13 -0
- package/dist/src/rich-input/components/FontSizeSelect.d.ts +7 -0
- package/dist/src/rich-input/components/FontSizeSelect.jsx +53 -0
- package/dist/src/rich-input/components/LinkDialog.d.ts +10 -0
- package/dist/src/rich-input/components/LinkDialog.jsx +56 -0
- package/dist/src/rich-input/components/Toolbar.d.ts +13 -0
- package/dist/src/rich-input/components/Toolbar.jsx +202 -0
- package/dist/src/rich-input/helpers/EditorExtensions.d.ts +4 -0
- package/dist/src/rich-input/helpers/EditorExtensions.jsx +25 -0
- package/dist/src/rich-input/hooks/useToggleMenuVisibility.hook.d.ts +6 -0
- package/dist/src/rich-input/hooks/useToggleMenuVisibility.hook.js +12 -0
- package/dist/src/rich-input/interfaces/types.d.ts +26 -0
- package/dist/src/rich-input/interfaces/types.js +1 -0
- package/dist/src/stories/RichInput.stories.d.ts +9 -0
- package/dist/src/stories/RichInput.stories.jsx +33 -0
- package/dist/src/stories/RichInputV2.stories.d.ts +9 -0
- package/dist/src/stories/RichInputV2.stories.jsx +40 -0
- package/dist/tailwind.config.d.ts +265 -0
- package/dist/tailwind.config.js +21 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/vite.config.d.ts +2 -0
- package/dist/vite.config.js +41 -0
- package/package.json +147 -146
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "asma-ui-richeditor",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.1.11-grisha",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/**/*",
|
|
10
|
+
"dist/style.css",
|
|
11
|
+
"tw-configs/**/*"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "asma-team",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"main": "./dist/asma-ui-richeditor.es.js",
|
|
17
|
+
"module": "./dist/asma-ui-richeditor.es.js",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": "./dist/asma-ui-richeditor.es.js",
|
|
21
|
+
"types": "./dist/index.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./dist/style.css": "./dist/style.css",
|
|
24
|
+
"./tw-configs/twConfigs.json": "./tw-configs/twConfigs.json"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"dev": "vite",
|
|
28
|
+
"build": "npx tsc && rm -rf ./dist && vite build",
|
|
29
|
+
"build:win": "rimraf ./dist && npx tsc && vite build",
|
|
30
|
+
"preview": "vite preview",
|
|
31
|
+
"lint": "eslint src/**/*.{js,jsx,ts,tsx}",
|
|
32
|
+
"version": "echo $npm_package_version",
|
|
33
|
+
"lint:fix": "eslint --fix src/**/*.{jsx,ts,tsx}",
|
|
34
|
+
"prettier": "prettier --write src//**/*.{ts,tsx,scss} --config ./.prettierrc",
|
|
35
|
+
"storybook": "storybook dev -p 6006",
|
|
36
|
+
"storybook:no-browser": "storybook dev -p 6006 --no-open",
|
|
37
|
+
"storybook:node18": "export NODE_OPTIONS=--openssl-legacy-provider && storybook dev -p 6006",
|
|
38
|
+
"storybook:node18:win": "set NODE_OPTIONS=--openssl-legacy-provider && storybook dev -p 6006",
|
|
39
|
+
"build-storybook": "export NODE_OPTIONS=--max_old_space_size=6240 && storybook build",
|
|
40
|
+
"test-storybook": "test-storybook",
|
|
41
|
+
"changeset:pre-beta": "changeset pre enter beta",
|
|
42
|
+
"changeset:pre-exit": "changeset pre exit",
|
|
43
|
+
"changeset:version": "changeset version",
|
|
44
|
+
"changeset:publish": "changeset publish",
|
|
45
|
+
"changeset:status": "changeset status"
|
|
46
|
+
},
|
|
47
|
+
"license": "UNLICENSED",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@emoji-mart/data": "^1.2.1",
|
|
50
|
+
"@emoji-mart/react": "^1.1.1",
|
|
51
|
+
"@emotion/react": "^11.11.1",
|
|
52
|
+
"@emotion/styled": "^11.11.0",
|
|
53
|
+
"@floating-ui/react": "^0.27.13",
|
|
54
|
+
"@fontsource/roboto": "^5.0.4",
|
|
55
|
+
"@iconify/react": "^5.0.2",
|
|
56
|
+
"@mui/material": "^5.13.7",
|
|
57
|
+
"@tiptap/core": "^2.11.7",
|
|
58
|
+
"@tiptap/extension-blockquote": "^2.11.7",
|
|
59
|
+
"@tiptap/extension-bold": "^2.11.7",
|
|
60
|
+
"@tiptap/extension-bullet-list": "^2.11.7",
|
|
61
|
+
"@tiptap/extension-color": "^2.11.7",
|
|
62
|
+
"@tiptap/extension-document": "^2.11.7",
|
|
63
|
+
"@tiptap/extension-hard-break": "^2.11.7",
|
|
64
|
+
"@tiptap/extension-heading": "^2.11.7",
|
|
65
|
+
"@tiptap/extension-horizontal-rule": "^2.11.7",
|
|
66
|
+
"@tiptap/extension-italic": "^2.11.7",
|
|
67
|
+
"@tiptap/extension-link": "^2.11.7",
|
|
68
|
+
"@tiptap/extension-list-item": "^2.11.7",
|
|
69
|
+
"@tiptap/extension-ordered-list": "^2.11.7",
|
|
70
|
+
"@tiptap/extension-paragraph": "^2.11.7",
|
|
71
|
+
"@tiptap/extension-placeholder": "^2.11.7",
|
|
72
|
+
"@tiptap/extension-strike": "^2.11.7",
|
|
73
|
+
"@tiptap/extension-text": "^2.11.7",
|
|
74
|
+
"@tiptap/extension-text-style": "^2.11.7",
|
|
75
|
+
"@tiptap/extension-underline": "^2.11.7",
|
|
76
|
+
"@tiptap/react": "^2.11.7",
|
|
77
|
+
"asma-ui-richeditor": "link:../../Users/Ranzi/AppData/Local/pnpm/global/5/node_modules/asma-ui-richeditor",
|
|
78
|
+
"clsx": "^2.1.1",
|
|
79
|
+
"emoji-mart": "^5.6.0",
|
|
80
|
+
"node": "18.17.0",
|
|
81
|
+
"react": "^18.2.0",
|
|
82
|
+
"react-dom": "^18.2.0",
|
|
83
|
+
"tailwind-merge": "^2.5.4",
|
|
84
|
+
"tiptap-extension-font-size": "^1.2.0"
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@changesets/cli": "^2.26.2",
|
|
88
|
+
"@faker-js/faker": "^8.0.2",
|
|
89
|
+
"@rollup/plugin-terser": "^0.4.3",
|
|
90
|
+
"@storybook/addon-a11y": "^7.6.9",
|
|
91
|
+
"@storybook/addon-essentials": "^7.6.9",
|
|
92
|
+
"@storybook/addon-interactions": "^7.6.9",
|
|
93
|
+
"@storybook/addon-links": "^7.6.9",
|
|
94
|
+
"@storybook/addon-mdx-gfm": "^7.6.9",
|
|
95
|
+
"@storybook/addon-styling": "^1.3.7",
|
|
96
|
+
"@storybook/addons": "^7.6.9",
|
|
97
|
+
"@storybook/blocks": "^7.6.9",
|
|
98
|
+
"@storybook/manager-api": "^7.6.9",
|
|
99
|
+
"@storybook/preset-create-react-app": "^7.6.9",
|
|
100
|
+
"@storybook/react": "^7.6.9",
|
|
101
|
+
"@storybook/react-vite": "^7.6.9",
|
|
102
|
+
"@storybook/test-runner": "^0.16.0",
|
|
103
|
+
"@storybook/testing-library": "^0.2.2",
|
|
104
|
+
"@storybook/theming": "^7.6.9",
|
|
105
|
+
"@types/lodash-es": "^4.17.7",
|
|
106
|
+
"@types/node": "^18.0.3",
|
|
107
|
+
"@types/react": "^18.0.37",
|
|
108
|
+
"@types/react-dom": "^18.0.11",
|
|
109
|
+
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
|
110
|
+
"@typescript-eslint/parser": "^5.59.0",
|
|
111
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
112
|
+
"asma-core-ui": "^3.0.147",
|
|
113
|
+
"autoprefixer": "^10.4.14",
|
|
114
|
+
"axe-playwright": "^1.2.3",
|
|
115
|
+
"axios": "^1.6.8",
|
|
116
|
+
"eslint": "^8.38.0",
|
|
117
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
118
|
+
"eslint-plugin-react-refresh": "^0.3.4",
|
|
119
|
+
"eslint-plugin-storybook": "^0.6.15",
|
|
120
|
+
"postcss": "^8.4.24",
|
|
121
|
+
"prop-types": "^15.8.1",
|
|
122
|
+
"rimraf": "^6.0.1",
|
|
123
|
+
"rollup-plugin-typescript2": "^0.35.0",
|
|
124
|
+
"storybook": "^7.6.9",
|
|
125
|
+
"storybook-addon-themes": "^6.1.0",
|
|
126
|
+
"tailwind-scrollbar": "^3.1.0",
|
|
127
|
+
"tailwindcss": "^3.3.2",
|
|
128
|
+
"typescript": "^5.0.2",
|
|
129
|
+
"typescript-plugin-css-modules": "^5.0.1",
|
|
130
|
+
"vite": "^4.3.9",
|
|
131
|
+
"vite-plugin-css-injected-by-js": "^3.5.1",
|
|
132
|
+
"vite-plugin-dts": "^3.1.1",
|
|
133
|
+
"vite-tsconfig-paths": "^4.2.0"
|
|
134
|
+
},
|
|
135
|
+
"peerDependencies": {
|
|
136
|
+
"@emotion/react": "^11.*",
|
|
137
|
+
"@emotion/styled": "^11.*",
|
|
138
|
+
"@mui/material": "^5.*",
|
|
139
|
+
"immer": "^9.*",
|
|
140
|
+
"react": "^18.*",
|
|
141
|
+
"react-dom": "^18.*"
|
|
142
|
+
},
|
|
143
|
+
"resolutions": {
|
|
144
|
+
"@types/node": "18.0.3",
|
|
145
|
+
"node": "18.17.0"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rich-input/RichInput';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rich-input/RichInput';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
import './styles/tiptap.css';
|
|
3
|
+
import type { IRichInput } from './interfaces/types';
|
|
4
|
+
/**
|
|
5
|
+
* ASMA RichInput - A rich text editor component.
|
|
6
|
+
*
|
|
7
|
+
* @param readOnly - Determines the display style in read-only mode.
|
|
8
|
+
* Options:
|
|
9
|
+
* - `'plain'`: Displays the editor with a white background and no borders.
|
|
10
|
+
* - `'outlined'`: Displays the editor with a gray background and visible borders.
|
|
11
|
+
*/
|
|
12
|
+
declare const RichInput: FC<IRichInput>;
|
|
13
|
+
export { RichInput };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { EditorContent, useEditor } from '@tiptap/react';
|
|
4
|
+
import { Toolbar } from './components/Toolbar';
|
|
5
|
+
import './styles/tiptap.css';
|
|
6
|
+
import { StyledFormControl, StyledFormHelperText } from 'asma-core-ui';
|
|
7
|
+
import { Icon } from '@iconify/react';
|
|
8
|
+
import { defaultExtensions, editModeExtensions } from './helpers/EditorExtensions';
|
|
9
|
+
import { LinkDialog } from './components/LinkDialog';
|
|
10
|
+
import Placeholder from '@tiptap/extension-placeholder';
|
|
11
|
+
import data, {} from '@emoji-mart/data';
|
|
12
|
+
import Picker from '@emoji-mart/react';
|
|
13
|
+
/**
|
|
14
|
+
* ASMA RichInput - A rich text editor component.
|
|
15
|
+
*
|
|
16
|
+
* @param readOnly - Determines the display style in read-only mode.
|
|
17
|
+
* Options:
|
|
18
|
+
* - `'plain'`: Displays the editor with a white background and no borders.
|
|
19
|
+
* - `'outlined'`: Displays the editor with a gray background and visible borders.
|
|
20
|
+
*/
|
|
21
|
+
const RichInput = ({ dataTest, attachmentsMenu, id, inputRef, className, editorClassName, disabled, readOnly, error, locale,
|
|
22
|
+
// label, // this in not implemented yet
|
|
23
|
+
title, placeholder, helperText, required, maxScrollableHeight, toolbarDefaultVisible, hideToolbar, noDefaultStyles, ...props }) => {
|
|
24
|
+
const cursor = useRef();
|
|
25
|
+
const [wasTouched, setWasTouched] = useState(false);
|
|
26
|
+
const editor = useEditor({
|
|
27
|
+
...props,
|
|
28
|
+
extensions: [
|
|
29
|
+
Placeholder.configure({ placeholder }),
|
|
30
|
+
...defaultExtensions,
|
|
31
|
+
...editModeExtensions,
|
|
32
|
+
...(props.extensions || []),
|
|
33
|
+
],
|
|
34
|
+
parseOptions: {
|
|
35
|
+
preserveWhitespace: true,
|
|
36
|
+
...props.parseOptions,
|
|
37
|
+
},
|
|
38
|
+
shouldRerenderOnTransaction: props.shouldRerenderOnTransaction || false,
|
|
39
|
+
immediatelyRender: props.immediatelyRender || true,
|
|
40
|
+
editable: props.editable || (!disabled && !readOnly),
|
|
41
|
+
onSelectionUpdate: (updateProps) => {
|
|
42
|
+
props.onSelectionUpdate?.(updateProps);
|
|
43
|
+
cursor.current = updateProps.editor.state.selection.anchor;
|
|
44
|
+
updateProps.editor.commands.focus();
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
//NOTE: the dependency list doesn't accept the props object without creating a max call stack error
|
|
48
|
+
[
|
|
49
|
+
props.extensions,
|
|
50
|
+
props.shouldRerenderOnTransaction,
|
|
51
|
+
props.immediatelyRender,
|
|
52
|
+
props.editable,
|
|
53
|
+
props.content,
|
|
54
|
+
disabled,
|
|
55
|
+
readOnly,
|
|
56
|
+
placeholder,
|
|
57
|
+
]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (cursor.current === undefined)
|
|
60
|
+
return;
|
|
61
|
+
editor?.commands.setTextSelection(cursor.current);
|
|
62
|
+
}, [props.content, editor]);
|
|
63
|
+
const [showToolbar, setShowToolbar] = useState(toolbarDefaultVisible);
|
|
64
|
+
const [linkDialogVisible, setLinkDialogVisible] = useState(false);
|
|
65
|
+
useImperativeHandle(inputRef, () => editor);
|
|
66
|
+
const [focused, setFocused] = useState(false);
|
|
67
|
+
const [emojiPickerVisible, setEmojiPickerVisible] = useState(false);
|
|
68
|
+
const isEmpty = editor?.isEmpty ?? true;
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const handleFocus = () => setFocused(true);
|
|
71
|
+
const handleBlur = () => setFocused(false);
|
|
72
|
+
editor?.on('focus', handleFocus);
|
|
73
|
+
editor?.on('blur', handleBlur);
|
|
74
|
+
return () => {
|
|
75
|
+
editor?.off('focus', handleFocus);
|
|
76
|
+
editor?.off('blur', handleBlur);
|
|
77
|
+
};
|
|
78
|
+
}, [editor]);
|
|
79
|
+
const handleEmojiSelect = (emoji) => {
|
|
80
|
+
if (editor) {
|
|
81
|
+
editor.chain().focus().insertContent(emoji.native).run();
|
|
82
|
+
}
|
|
83
|
+
setEmojiPickerVisible(false);
|
|
84
|
+
};
|
|
85
|
+
const isFieldEmpty = editor?.isEmpty;
|
|
86
|
+
const showError = !readOnly && error && isFieldEmpty;
|
|
87
|
+
if (!editor)
|
|
88
|
+
return null;
|
|
89
|
+
return (<div className='w-full flex justify-between flex-col'>
|
|
90
|
+
<StyledFormControl className={className}>
|
|
91
|
+
{title && <p className='font-semibold text-base text-gray-700 mb-2'>{title}</p>}
|
|
92
|
+
<div className={clsx(!noDefaultStyles && 'rte-wrapper', readOnly === 'outlined' && 'readonly-outlined', readOnly === 'plain' && 'readonly-plain', !readOnly && showError && (focused ? 'error-focused-state' : 'error-state'), !readOnly && !showError && focused && 'focused-state')}>
|
|
93
|
+
<div className='relative'>
|
|
94
|
+
<EditorContent data-test={dataTest} id={id} className={clsx('border-none rounded-none w-full min-h-[80px]', !noDefaultStyles && 'core-ui-rte', !hideToolbar && !disabled && !readOnly && !showToolbar && 'displace-text', !noDefaultStyles && !disabled && !readOnly && 'edit-mode', editorClassName)} editor={editor} onClick={() => editor?.chain().focus().run()} style={showToolbar ? { height: '20px' } : undefined}/>
|
|
95
|
+
|
|
96
|
+
{!hideToolbar && !disabled && !readOnly && (<div className='absolute top-1 right-1 bottom-1 flex flex-col gap-4 items-center z-10'>
|
|
97
|
+
{!showToolbar && (<Icon onClick={() => {
|
|
98
|
+
setShowToolbar(true);
|
|
99
|
+
}} className='cursor-pointer iconButton' icon='material-symbols:format-color-text' color='var(--colors-gray-700)' height={24} width={24}/>)}
|
|
100
|
+
|
|
101
|
+
{attachmentsMenu ?? (
|
|
102
|
+
// <Icon
|
|
103
|
+
// className='iconButton'
|
|
104
|
+
// icon='material-symbols:attach-file'
|
|
105
|
+
// color='var(--colors-gray-700)'
|
|
106
|
+
// height={24}
|
|
107
|
+
// width={24}
|
|
108
|
+
// />
|
|
109
|
+
<div></div>)}
|
|
110
|
+
</div>)}
|
|
111
|
+
</div>
|
|
112
|
+
{!hideToolbar && !disabled && !readOnly && showToolbar && (<Toolbar editor={editor} onClose={() => setShowToolbar(false)} error={error} focused={focused} openLinkDialog={() => setLinkDialogVisible(true)} openEmojiPicker={() => setEmojiPickerVisible(true)} locale={locale}/>)}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{showError && helperText && (<StyledFormHelperText error data-test={`error-${dataTest}`}>
|
|
116
|
+
{helperText}
|
|
117
|
+
</StyledFormHelperText>)}
|
|
118
|
+
</StyledFormControl>
|
|
119
|
+
|
|
120
|
+
<LinkDialog open={linkDialogVisible} setOpen={setLinkDialogVisible} editor={editor} locale={locale}/>
|
|
121
|
+
|
|
122
|
+
{emojiPickerVisible && (<div className='fixed inset-0 bg-black/20 z-[9999] flex items-center justify-center' onClick={() => setEmojiPickerVisible(false)}>
|
|
123
|
+
<div onClick={(e) => e.stopPropagation()}>
|
|
124
|
+
<Picker theme='light' data={data} onEmojiSelect={handleEmojiSelect} locale={locale}/>
|
|
125
|
+
</div>
|
|
126
|
+
</div>)}
|
|
127
|
+
</div>);
|
|
128
|
+
};
|
|
129
|
+
export { RichInput };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StyledMenuItem } from 'asma-core-ui';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export const CustomMenuItem = (props) => {
|
|
4
|
+
return (<StyledMenuItem {...props} sx={{
|
|
5
|
+
'&.MuiMenuItem-root': {
|
|
6
|
+
color: 'var(--colors-delta-700)',
|
|
7
|
+
fontSize: '14px',
|
|
8
|
+
lineHeight: '20px',
|
|
9
|
+
},
|
|
10
|
+
}}>
|
|
11
|
+
{props.children}
|
|
12
|
+
</StyledMenuItem>);
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type TextFieldProps } from 'asma-core-ui';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import '../styles/toolbar.css';
|
|
4
|
+
import type { Editor } from '@tiptap/core';
|
|
5
|
+
export declare const FontSizeSelect: (props: TextFieldProps & {
|
|
6
|
+
editor: Editor;
|
|
7
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Icon } from '@iconify/react';
|
|
2
|
+
import { StyledInputField } from 'asma-core-ui';
|
|
3
|
+
import React, { useLayoutEffect, useState } from 'react';
|
|
4
|
+
import '../styles/toolbar.css';
|
|
5
|
+
const fontSizeMap = {
|
|
6
|
+
small: '10px',
|
|
7
|
+
normal: '16px',
|
|
8
|
+
large: '24px',
|
|
9
|
+
huge: '32px',
|
|
10
|
+
};
|
|
11
|
+
export const FontSizeSelect = (props) => {
|
|
12
|
+
const { editor } = props;
|
|
13
|
+
const [selectedSize, setSelectedSize] = useState('normal');
|
|
14
|
+
const handleFontSizeChange = (event) => {
|
|
15
|
+
const selectedSize = event.target.value;
|
|
16
|
+
editor.chain().focus().setMark('textStyle', { fontSize: fontSizeMap[selectedSize] }).run();
|
|
17
|
+
setSelectedSize(selectedSize);
|
|
18
|
+
};
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
|
+
const { from, to } = editor.state.selection;
|
|
21
|
+
editor.state.doc.nodesBetween(from - 1, to, (node) => {
|
|
22
|
+
if (node.marks) {
|
|
23
|
+
const textStyleMark = node.marks.find((mark) => mark.type.name === 'textStyle');
|
|
24
|
+
const fontSize = textStyleMark?.attrs['fontSize'];
|
|
25
|
+
const currentFontSize = Object.keys(fontSizeMap).find((key) => fontSizeMap[key] === fontSize) || 'normal';
|
|
26
|
+
setSelectedSize(currentFontSize);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}, [editor.state.selection, editor.state.doc]);
|
|
30
|
+
return (<StyledInputField {...props} select size='small' dataTest='font-size-select' onChange={handleFontSizeChange} value={selectedSize} SelectProps={{ IconComponent: () => null }} InputProps={{
|
|
31
|
+
endAdornment: <Icon icon='tabler:caret-up-down-filled' className='chevron-icon'/>,
|
|
32
|
+
}} sx={{
|
|
33
|
+
'.MuiSelect-select': {
|
|
34
|
+
border: 'none',
|
|
35
|
+
padding: 0,
|
|
36
|
+
overflow: 'hidden',
|
|
37
|
+
display: 'flex',
|
|
38
|
+
alignItems: 'center',
|
|
39
|
+
color: 'var(--colors-delta-700)',
|
|
40
|
+
fontSize: '14px',
|
|
41
|
+
lineHeight: '20px',
|
|
42
|
+
},
|
|
43
|
+
'.MuiOutlinedInput-root': {
|
|
44
|
+
height: '32px',
|
|
45
|
+
width: '75px',
|
|
46
|
+
'& .MuiOutlinedInput-notchedOutline': {
|
|
47
|
+
border: 'none',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}}>
|
|
51
|
+
{props.children}
|
|
52
|
+
</StyledInputField>);
|
|
53
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import '../styles/toolbar.css';
|
|
4
|
+
import type { ILocale } from '../interfaces/types';
|
|
5
|
+
export declare const LinkDialog: React.FC<{
|
|
6
|
+
open: boolean;
|
|
7
|
+
setOpen: (value: boolean) => void;
|
|
8
|
+
editor: Editor;
|
|
9
|
+
locale?: ILocale;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { StyledButton, StyledDialog, StyledInputField } from 'asma-core-ui';
|
|
2
|
+
import React, { useLayoutEffect, useMemo, useState } from 'react';
|
|
3
|
+
import styles from './LinkDialog.module.scss';
|
|
4
|
+
import '../styles/toolbar.css';
|
|
5
|
+
export const LinkDialog = ({ open, setOpen, editor, locale }) => {
|
|
6
|
+
const [url, setUrl] = useState('');
|
|
7
|
+
const isNorsk = useMemo(() => locale === 'no', [locale]);
|
|
8
|
+
useLayoutEffect(() => {
|
|
9
|
+
if (open) {
|
|
10
|
+
const { from, to } = editor.state.selection;
|
|
11
|
+
let currentLink = null;
|
|
12
|
+
editor.state.doc.nodesBetween(from - 1, to, (node) => {
|
|
13
|
+
if (node.marks) {
|
|
14
|
+
const linkMark = node.marks.find((mark) => mark.type.name === 'link');
|
|
15
|
+
if (linkMark) {
|
|
16
|
+
currentLink = linkMark.attrs['href'];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (currentLink)
|
|
21
|
+
setUrl(currentLink);
|
|
22
|
+
else
|
|
23
|
+
setUrl('');
|
|
24
|
+
const input = document.querySelector('[data-test="link-input-field"]');
|
|
25
|
+
input?.focus();
|
|
26
|
+
}
|
|
27
|
+
}, [open, editor]);
|
|
28
|
+
const makeLink = () => {
|
|
29
|
+
editor.chain().focus().setLink({ href: url }).run();
|
|
30
|
+
setOpen(false);
|
|
31
|
+
setUrl('');
|
|
32
|
+
};
|
|
33
|
+
return (<StyledDialog open={open} dataTest={'link-modal-rte'} onClose={() => setOpen(false)} className={'z-[99]'} dialogTitle={<div className={'text-gray-700'}>{isNorsk ? 'Legg til lenke' : 'Add link'}</div>}>
|
|
34
|
+
<div className={styles['link-dialog-container']}>
|
|
35
|
+
<div className='link-input-field'>
|
|
36
|
+
<StyledInputField autoFocus multiline className='w-full' dataTest={'link-input-field'} size={'small'} sx={{
|
|
37
|
+
'& .MuiInputBase-root': {
|
|
38
|
+
minHeight: 64,
|
|
39
|
+
alignItems: 'start',
|
|
40
|
+
},
|
|
41
|
+
}} value={url} onChange={(e) => setUrl(e.target.value)}/>
|
|
42
|
+
</div>
|
|
43
|
+
<div className={'flex gap-2 p-0 w-full justify-end'}>
|
|
44
|
+
<StyledButton dataTest={'delete-customer-reject-button'} size={'medium'} variant={'outlined'} onClick={() => {
|
|
45
|
+
setOpen(false);
|
|
46
|
+
setUrl('');
|
|
47
|
+
}}>
|
|
48
|
+
{isNorsk ? 'Avbryt' : 'Cancel'}
|
|
49
|
+
</StyledButton>
|
|
50
|
+
<StyledButton dataTest={'delete-customer-confirm-button'} size={'medium'} variant={'contained'} onClick={makeLink}>
|
|
51
|
+
{isNorsk ? 'Legg til lenke' : 'Add link'}
|
|
52
|
+
</StyledButton>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</StyledDialog>);
|
|
56
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/react';
|
|
2
|
+
import '../styles/toolbar.css';
|
|
3
|
+
import type { ILocale } from '../interfaces/types';
|
|
4
|
+
export declare const Toolbar: ({ editor, onClose, error, focused, openLinkDialog, openEmojiPicker, locale, emojiButtonRef, }: {
|
|
5
|
+
editor: Editor;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
error?: boolean;
|
|
8
|
+
focused: boolean;
|
|
9
|
+
openLinkDialog: () => void;
|
|
10
|
+
openEmojiPicker: () => void;
|
|
11
|
+
locale?: ILocale;
|
|
12
|
+
emojiButtonRef?: React.RefObject<HTMLSpanElement>;
|
|
13
|
+
}) => import("node_modules/@types/react").JSX.Element;
|