@pallasoft/ps-input-components 1.23.0 → 1.24.0
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/components/ps-portable-text-editor.d.ts +2 -2
- package/dist/components/ps-portable-text-editor.js +4 -29
- package/dist/components/ps-portable-text-link-dialog.d.ts +1 -1
- package/dist/components/ps-portable-text-link-dialog.js +4 -26
- package/dist/components/ps-portable-text-toolbar.d.ts +1 -1
- package/dist/components/ps-portable-text-toolbar.js +12 -52
- package/dist/constants/ps-portable-text-editor-config.d.ts +0 -55
- package/dist/constants/ps-portable-text-editor-config.js +0 -40
- package/dist/constants/ps-portable-text-editor-renderers.js +8 -8
- package/dist/esm/components/ps-portable-text-editor.js +5 -30
- package/dist/esm/components/ps-portable-text-link-dialog.js +4 -26
- package/dist/esm/components/ps-portable-text-toolbar.js +13 -53
- package/dist/esm/constants/ps-portable-text-editor-config.js +0 -40
- package/dist/esm/constants/ps-portable-text-editor-renderers.js +8 -8
- package/dist/esm/index.js +1 -1
- package/dist/esm/portable-text-core/components/ps-portable-text-core.js +31 -0
- package/dist/esm/portable-text-core/constants/ps-portable-text-core-config.js +42 -0
- package/dist/esm/portable-text-core/hooks/use-ps-portable-text-link-form.js +32 -0
- package/dist/esm/portable-text-core/hooks/use-ps-portable-text-toolbar.js +54 -0
- package/dist/esm/portable-text-core/index.js +6 -0
- package/dist/esm/{services → portable-text-core/services}/ps-portable-text-editor-service.js +6 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/portable-text-core/components/ps-portable-text-core.d.ts +17 -0
- package/dist/portable-text-core/components/ps-portable-text-core.js +35 -0
- package/dist/portable-text-core/constants/ps-portable-text-core-config.d.ts +57 -0
- package/dist/portable-text-core/constants/ps-portable-text-core-config.js +45 -0
- package/dist/portable-text-core/hooks/use-ps-portable-text-link-form.d.ts +13 -0
- package/dist/portable-text-core/hooks/use-ps-portable-text-link-form.js +36 -0
- package/dist/portable-text-core/hooks/use-ps-portable-text-toolbar.d.ts +18 -0
- package/dist/portable-text-core/hooks/use-ps-portable-text-toolbar.js +58 -0
- package/dist/portable-text-core/index.d.ts +7 -0
- package/dist/portable-text-core/index.js +22 -0
- package/dist/{services → portable-text-core/services}/ps-portable-text-editor-service.js +6 -6
- package/package.json +10 -1
- /package/dist/esm/{interfaces → portable-text-core/interfaces}/ps-portable-text-editor-labels.js +0 -0
- /package/dist/{interfaces → portable-text-core/interfaces}/ps-portable-text-editor-labels.d.ts +0 -0
- /package/dist/{interfaces → portable-text-core/interfaces}/ps-portable-text-editor-labels.js +0 -0
- /package/dist/{services → portable-text-core/services}/ps-portable-text-editor-service.d.ts +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { PsPortableTextEditorLabels } from '../interfaces/ps-portable-text-editor-labels';
|
|
1
|
+
import type { PortableTextBlock } from '@portabletext/editor';
|
|
2
|
+
import type { PsPortableTextEditorLabels } from '../portable-text-core/interfaces/ps-portable-text-editor-labels';
|
|
3
3
|
export interface PsPortableTextEditorProps {
|
|
4
4
|
className?: string;
|
|
5
5
|
disabled?: boolean;
|
|
@@ -2,37 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsPortableTextEditor = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const react_1 = require("react");
|
|
6
5
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
-
const editor_1 = require("@portabletext/editor");
|
|
8
|
-
const plugins_1 = require("@portabletext/editor/plugins");
|
|
9
6
|
const ps_portable_text_editor_config_1 = require("../constants/ps-portable-text-editor-config");
|
|
10
7
|
const ps_portable_text_editor_renderers_1 = require("../constants/ps-portable-text-editor-renderers");
|
|
11
|
-
const
|
|
8
|
+
const ps_portable_text_core_1 = require("../portable-text-core/components/ps-portable-text-core");
|
|
9
|
+
const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
|
|
10
|
+
const ps_portable_text_editor_service_1 = require("../portable-text-core/services/ps-portable-text-editor-service");
|
|
12
11
|
const ps_portable_text_toolbar_1 = require("./ps-portable-text-toolbar");
|
|
13
|
-
const PsPortableTextEditor = ({ className, disabled = false, labels =
|
|
14
|
-
const [isMounted, setIsMounted] = (0, react_1.useState)(false);
|
|
15
|
-
const [version, setVersion] = (0, react_1.useState)(0);
|
|
16
|
-
const lastValue = (0, react_1.useRef)(value);
|
|
17
|
-
(0, react_1.useEffect)(() => {
|
|
18
|
-
setIsMounted(true);
|
|
19
|
-
}, []);
|
|
20
|
-
(0, react_1.useEffect)(() => {
|
|
21
|
-
if (ps_portable_text_editor_service_1.PsPortableTextEditorService.isSame(value, lastValue.current))
|
|
22
|
-
return;
|
|
23
|
-
lastValue.current = value;
|
|
24
|
-
setVersion((current) => current + 1);
|
|
25
|
-
}, [value]);
|
|
26
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.root, className), children: isMounted ? ((0, jsx_runtime_1.jsxs)(editor_1.EditorProvider, { initialConfig: {
|
|
27
|
-
initialValue: value,
|
|
28
|
-
readOnly: disabled,
|
|
29
|
-
schemaDefinition: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.schemaDefinition,
|
|
30
|
-
}, children: [(0, jsx_runtime_1.jsx)(plugins_1.EventListenerPlugin, { on: (event) => {
|
|
31
|
-
if (event.type !== 'mutation')
|
|
32
|
-
return;
|
|
33
|
-
const next = Array.isArray(event.value) ? event.value : [];
|
|
34
|
-
lastValue.current = next;
|
|
35
|
-
onChange(next);
|
|
36
|
-
} }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_1.PsPortableTextToolbar, { disabled: disabled, labels: labels }), (0, jsx_runtime_1.jsx)(editor_1.PortableTextEditable, { ...ps_portable_text_editor_renderers_1.PsPortableTextEditorRenderers, className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.editable, readOnly: disabled, renderPlaceholder: () => (0, jsx_runtime_1.jsx)("span", { className: 'text-ink/45', children: placeholder }), style: { minHeight: ps_portable_text_editor_service_1.PsPortableTextEditorService.minHeight(minRows) } })] }, version)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.toolbar, children: (0, jsx_runtime_1.jsx)("span", { className: 'h-9' }) }), (0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.editable, style: { minHeight: ps_portable_text_editor_service_1.PsPortableTextEditorService.minHeight(minRows) } })] })) }));
|
|
37
|
-
};
|
|
12
|
+
const PsPortableTextEditor = ({ className, disabled = false, labels = ps_portable_text_core_config_1.PsPortableTextCoreConfig.labels, minRows = 4, onChange, placeholder, value, }) => ((0, jsx_runtime_1.jsx)(ps_portable_text_core_1.PsPortableTextCore, { ...ps_portable_text_editor_renderers_1.PsPortableTextEditorRenderers, className: ps_design_system_1.PallasoftClassNameService.merge(ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.root, className), disabled: disabled, editableClassName: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.editable, fallback: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.toolbar, children: (0, jsx_runtime_1.jsx)("span", { className: 'h-9' }) }), (0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.editable, style: { minHeight: ps_portable_text_editor_service_1.PsPortableTextEditorService.minHeight(minRows) } })] }), minRows: minRows, onChange: onChange, renderPlaceholder: () => (0, jsx_runtime_1.jsx)("span", { className: 'text-ink/45', children: placeholder }), toolbar: (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_1.PsPortableTextToolbar, { disabled: disabled, labels: labels }), value: value }));
|
|
38
13
|
exports.PsPortableTextEditor = PsPortableTextEditor;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PsPortableTextEditorLabels } from '../interfaces/ps-portable-text-editor-labels';
|
|
1
|
+
import type { PsPortableTextEditorLabels } from '../portable-text-core/interfaces/ps-portable-text-editor-labels';
|
|
2
2
|
export interface PsPortableTextLinkDialogProps {
|
|
3
3
|
isOpen: boolean;
|
|
4
4
|
labels: PsPortableTextEditorLabels;
|
|
@@ -2,34 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsPortableTextLinkDialog = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const react_1 = require("react");
|
|
6
5
|
const ps_popovers_1 = require("@pallasoft/ps-popovers");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
|
|
7
|
+
const use_ps_portable_text_link_form_1 = require("../portable-text-core/hooks/use-ps-portable-text-link-form");
|
|
9
8
|
const ps_url_input_1 = require("./ps-url-input");
|
|
10
9
|
const PsPortableTextLinkDialog = ({ isOpen, labels, onClose, onSubmit, }) => {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const close = () => {
|
|
14
|
-
setAddress('');
|
|
15
|
-
setHasError(false);
|
|
16
|
-
onClose();
|
|
17
|
-
};
|
|
18
|
-
const submit = (event) => {
|
|
19
|
-
event.preventDefault();
|
|
20
|
-
event.stopPropagation();
|
|
21
|
-
const href = ps_portable_text_editor_service_1.PsPortableTextEditorService.normalizeLink(address);
|
|
22
|
-
if (href === null) {
|
|
23
|
-
setHasError(true);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
setAddress('');
|
|
27
|
-
setHasError(false);
|
|
28
|
-
onSubmit(href);
|
|
29
|
-
};
|
|
30
|
-
return ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsFormModal, { cancelLabel: labels.cancel, isOpen: isOpen, isSubmitDisabled: address.trim() === '', onClose: close, onSubmit: submit, size: 'sm', submitLabel: labels.saveLink, title: labels.linkDialogTitle, children: (0, jsx_runtime_1.jsx)(ps_url_input_1.PsUrlInput, { "aria-invalid": hasError, autoFocus: true, helperText: hasError ? labels.invalidLink : undefined, label: labels.linkUrl, name: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.linkAnnotation, onChange: (event) => {
|
|
31
|
-
setAddress(event.target.value);
|
|
32
|
-
setHasError(false);
|
|
33
|
-
}, value: address }) }));
|
|
10
|
+
const form = (0, use_ps_portable_text_link_form_1.usePsPortableTextLinkForm)({ onClose, onSubmit });
|
|
11
|
+
return ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsFormModal, { cancelLabel: labels.cancel, isOpen: isOpen, isSubmitDisabled: form.isSubmitDisabled, onClose: form.close, onSubmit: form.submit, size: 'sm', submitLabel: labels.saveLink, title: labels.linkDialogTitle, children: (0, jsx_runtime_1.jsx)(ps_url_input_1.PsUrlInput, { "aria-invalid": form.hasError, autoFocus: true, helperText: form.hasError ? labels.invalidLink : undefined, label: labels.linkUrl, name: ps_portable_text_core_config_1.PsPortableTextCoreConfig.linkAnnotation, onChange: (event) => form.setAddress(event.target.value), value: form.address }) }));
|
|
34
12
|
};
|
|
35
13
|
exports.PsPortableTextLinkDialog = PsPortableTextLinkDialog;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PsPortableTextEditorLabels } from '../interfaces/ps-portable-text-editor-labels';
|
|
1
|
+
import type { PsPortableTextEditorLabels } from '../portable-text-core/interfaces/ps-portable-text-editor-labels';
|
|
2
2
|
export interface PsPortableTextToolbarProps {
|
|
3
3
|
disabled: boolean;
|
|
4
4
|
labels: PsPortableTextEditorLabels;
|
|
@@ -3,60 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PsPortableTextToolbar = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
-
const editor_1 = require("@portabletext/editor");
|
|
7
|
-
const selectors_1 = require("@portabletext/editor/selectors");
|
|
8
6
|
const ps_portable_text_editor_config_1 = require("../constants/ps-portable-text-editor-config");
|
|
7
|
+
const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
|
|
8
|
+
const use_ps_portable_text_toolbar_1 = require("../portable-text-core/hooks/use-ps-portable-text-toolbar");
|
|
9
9
|
const ps_portable_text_link_dialog_1 = require("./ps-portable-text-link-dialog");
|
|
10
10
|
const ps_portable_text_toolbar_button_1 = require("./ps-portable-text-toolbar-button");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
return ((0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { className: className, disabled: disabled, isActive: isActive, label: label, onClick: () => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
const PsPortableTextToolbar = ({ disabled, labels }) => {
|
|
12
|
+
const toolbar = (0, use_ps_portable_text_toolbar_1.usePsPortableTextToolbar)();
|
|
13
|
+
const [isLinkOpen, setIsLinkOpen] = (0, react_1.useState)(false);
|
|
14
|
+
return ((0, jsx_runtime_1.jsxs)("div", { "aria-label": labels.toolbar, className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.toolbar, role: 'toolbar', children: [(0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { className: 'font-bold', disabled: disabled, isActive: toolbar.active.bold, label: labels.bold, onClick: () => toolbar.toggleDecorator(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.bold), children: "B" }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { className: 'italic', disabled: disabled, isActive: toolbar.active.italic, label: labels.italic, onClick: () => toolbar.toggleDecorator(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.italic), children: "I" }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { className: 'underline', disabled: disabled, isActive: toolbar.active.underline, label: labels.underline, onClick: () => toolbar.toggleDecorator(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.underline), children: "U" }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { className: 'font-mono', disabled: disabled, isActive: toolbar.active.code, label: labels.code, onClick: () => toolbar.toggleDecorator(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.code), children: '</>' }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.link, label: toolbar.active.link ? labels.removeLink : labels.link, onClick: () => (toolbar.active.link ? toolbar.removeLink() : setIsLinkOpen(true)), children: (0, jsx_runtime_1.jsx)("span", { className: 'underline', children: labels.link }) }), (0, jsx_runtime_1.jsx)(ps_portable_text_link_dialog_1.PsPortableTextLinkDialog, { isOpen: isLinkOpen, labels: labels, onClose: () => {
|
|
15
|
+
setIsLinkOpen(false);
|
|
16
|
+
toolbar.focus();
|
|
17
|
+
}, onSubmit: (href) => {
|
|
18
|
+
toolbar.addLink(href);
|
|
19
|
+
setIsLinkOpen(false);
|
|
20
|
+
} }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.separator }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.bulletedList, label: labels.bulletedList, onClick: () => toolbar.toggleListItem(ps_portable_text_core_config_1.PsPortableTextCoreConfig.listItems.bullet), children: "\u2022" }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.numberedList, label: labels.numberedList, onClick: () => toolbar.toggleListItem(ps_portable_text_core_config_1.PsPortableTextCoreConfig.listItems.number), children: "1." }), (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.quote, label: labels.quote, onClick: toolbar.toggleQuote, children: "\u201D" })] }));
|
|
18
21
|
};
|
|
19
|
-
const ListButton = ({ children, disabled, label, listItem, }) => {
|
|
20
|
-
const editor = (0, editor_1.useEditor)();
|
|
21
|
-
const isActive = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveListItem)(listItem));
|
|
22
|
-
return ((0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: isActive, label: label, onClick: () => {
|
|
23
|
-
editor.send({ type: 'list item.toggle', listItem });
|
|
24
|
-
editor.send({ type: 'focus' });
|
|
25
|
-
}, children: children }));
|
|
26
|
-
};
|
|
27
|
-
const QuoteButton = ({ children, disabled, label }) => {
|
|
28
|
-
const editor = (0, editor_1.useEditor)();
|
|
29
|
-
const isActive = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveStyle)(ps_portable_text_editor_config_1.PsPortableTextEditorConfig.styles.blockquote));
|
|
30
|
-
return ((0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: isActive, label: label, onClick: () => {
|
|
31
|
-
editor.send({ type: 'style.toggle', style: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.styles.blockquote });
|
|
32
|
-
editor.send({ type: 'focus' });
|
|
33
|
-
}, children: children }));
|
|
34
|
-
};
|
|
35
|
-
const LinkButton = ({ disabled, labels }) => {
|
|
36
|
-
const editor = (0, editor_1.useEditor)();
|
|
37
|
-
const [isOpen, setIsOpen] = (0, react_1.useState)(false);
|
|
38
|
-
const isActive = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveAnnotation)(ps_portable_text_editor_config_1.PsPortableTextEditorConfig.linkAnnotation));
|
|
39
|
-
const close = () => {
|
|
40
|
-
setIsOpen(false);
|
|
41
|
-
editor.send({ type: 'focus' });
|
|
42
|
-
};
|
|
43
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_button_1.PsPortableTextToolbarButton, { disabled: disabled, isActive: isActive, label: isActive ? labels.removeLink : labels.link, onClick: () => {
|
|
44
|
-
if (isActive) {
|
|
45
|
-
editor.send({
|
|
46
|
-
type: 'annotation.remove',
|
|
47
|
-
annotation: { name: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.linkAnnotation },
|
|
48
|
-
});
|
|
49
|
-
editor.send({ type: 'focus' });
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
setIsOpen(true);
|
|
53
|
-
}, children: (0, jsx_runtime_1.jsx)("span", { className: 'underline', children: labels.link }) }), (0, jsx_runtime_1.jsx)(ps_portable_text_link_dialog_1.PsPortableTextLinkDialog, { isOpen: isOpen, labels: labels, onClose: close, onSubmit: (href) => {
|
|
54
|
-
editor.send({
|
|
55
|
-
type: 'annotation.add',
|
|
56
|
-
annotation: { name: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.linkAnnotation, value: { href } },
|
|
57
|
-
});
|
|
58
|
-
close();
|
|
59
|
-
} })] }));
|
|
60
|
-
};
|
|
61
|
-
const PsPortableTextToolbar = ({ disabled, labels }) => ((0, jsx_runtime_1.jsxs)("div", { "aria-label": labels.toolbar, className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.toolbar, role: 'toolbar', children: [(0, jsx_runtime_1.jsx)(DecoratorButton, { className: 'font-bold', decorator: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.decorators.bold, disabled: disabled, label: labels.bold, children: "B" }), (0, jsx_runtime_1.jsx)(DecoratorButton, { className: 'italic', decorator: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.decorators.italic, disabled: disabled, label: labels.italic, children: "I" }), (0, jsx_runtime_1.jsx)(DecoratorButton, { className: 'underline', decorator: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.decorators.underline, disabled: disabled, label: labels.underline, children: "U" }), (0, jsx_runtime_1.jsx)(DecoratorButton, { className: 'font-mono', decorator: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.decorators.code, disabled: disabled, label: labels.code, children: '</>' }), (0, jsx_runtime_1.jsx)(LinkButton, { disabled: disabled, labels: labels }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.separator }), (0, jsx_runtime_1.jsx)(ListButton, { disabled: disabled, label: labels.bulletedList, listItem: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.listItems.bullet, children: "\u2022" }), (0, jsx_runtime_1.jsx)(ListButton, { disabled: disabled, label: labels.numberedList, listItem: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.listItems.number, children: "1." }), (0, jsx_runtime_1.jsx)(QuoteButton, { disabled: disabled, label: labels.quote, children: "\u201D" })] }));
|
|
62
22
|
exports.PsPortableTextToolbar = PsPortableTextToolbar;
|
|
@@ -1,59 +1,4 @@
|
|
|
1
1
|
export declare const PsPortableTextEditorConfig: {
|
|
2
|
-
readonly linkAnnotation: "link";
|
|
3
|
-
readonly mailtoPrefix: "mailto:";
|
|
4
|
-
readonly emailPattern: RegExp;
|
|
5
|
-
readonly lineHeightRem: 1.5;
|
|
6
|
-
readonly decorators: {
|
|
7
|
-
readonly bold: "strong";
|
|
8
|
-
readonly code: "code";
|
|
9
|
-
readonly italic: "em";
|
|
10
|
-
readonly underline: "underline";
|
|
11
|
-
};
|
|
12
|
-
readonly styles: {
|
|
13
|
-
readonly blockquote: "blockquote";
|
|
14
|
-
readonly normal: "normal";
|
|
15
|
-
};
|
|
16
|
-
readonly listItems: {
|
|
17
|
-
readonly bullet: "bullet";
|
|
18
|
-
readonly number: "number";
|
|
19
|
-
};
|
|
20
|
-
readonly schemaDefinition: {
|
|
21
|
-
readonly annotations: readonly [{
|
|
22
|
-
readonly name: "link";
|
|
23
|
-
readonly fields: readonly [{
|
|
24
|
-
readonly name: "href";
|
|
25
|
-
readonly type: "string";
|
|
26
|
-
}];
|
|
27
|
-
}];
|
|
28
|
-
readonly blockObjects: readonly [];
|
|
29
|
-
readonly decorators: {
|
|
30
|
-
name: "code" | "em" | "strong" | "underline";
|
|
31
|
-
}[];
|
|
32
|
-
readonly inlineObjects: readonly [];
|
|
33
|
-
readonly lists: {
|
|
34
|
-
name: "number" | "bullet";
|
|
35
|
-
}[];
|
|
36
|
-
readonly styles: {
|
|
37
|
-
name: "blockquote" | "normal";
|
|
38
|
-
}[];
|
|
39
|
-
};
|
|
40
|
-
readonly labels: {
|
|
41
|
-
bold: string;
|
|
42
|
-
bulletedList: string;
|
|
43
|
-
cancel: string;
|
|
44
|
-
code: string;
|
|
45
|
-
invalidLink: string;
|
|
46
|
-
italic: string;
|
|
47
|
-
link: string;
|
|
48
|
-
linkDialogTitle: string;
|
|
49
|
-
linkUrl: string;
|
|
50
|
-
numberedList: string;
|
|
51
|
-
quote: string;
|
|
52
|
-
removeLink: string;
|
|
53
|
-
saveLink: string;
|
|
54
|
-
toolbar: string;
|
|
55
|
-
underline: string;
|
|
56
|
-
};
|
|
57
2
|
readonly classNames: {
|
|
58
3
|
readonly editable: "ps-input-control w-full resize-none overflow-y-auto rounded-t-none border-t-0 leading-6 outline-none";
|
|
59
4
|
readonly root: "block";
|
|
@@ -1,47 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsPortableTextEditorConfig = void 0;
|
|
4
|
-
const ps_models_1 = require("@pallasoft/ps-models");
|
|
5
|
-
const editor_1 = require("@portabletext/editor");
|
|
6
|
-
const Models = ps_models_1.Common.PortableTextConfig;
|
|
7
4
|
exports.PsPortableTextEditorConfig = {
|
|
8
|
-
linkAnnotation: Models.linkType,
|
|
9
|
-
mailtoPrefix: 'mailto:',
|
|
10
|
-
emailPattern: /^[^\s@/:]+@[^\s@/:]+\.[^\s@/:]+$/,
|
|
11
|
-
lineHeightRem: 1.5,
|
|
12
|
-
decorators: {
|
|
13
|
-
bold: Models.decorators[0],
|
|
14
|
-
code: Models.decorators[3],
|
|
15
|
-
italic: Models.decorators[1],
|
|
16
|
-
underline: Models.decorators[2],
|
|
17
|
-
},
|
|
18
|
-
styles: { blockquote: Models.styles[1], normal: Models.styles[0] },
|
|
19
|
-
listItems: { bullet: Models.listItems[0], number: Models.listItems[1] },
|
|
20
|
-
schemaDefinition: (0, editor_1.defineSchema)({
|
|
21
|
-
annotations: [{ name: Models.linkType, fields: [{ name: 'href', type: 'string' }] }],
|
|
22
|
-
blockObjects: [],
|
|
23
|
-
decorators: Models.decorators.map((name) => ({ name })),
|
|
24
|
-
inlineObjects: [],
|
|
25
|
-
lists: Models.listItems.map((name) => ({ name })),
|
|
26
|
-
styles: Models.styles.map((name) => ({ name })),
|
|
27
|
-
}),
|
|
28
|
-
labels: {
|
|
29
|
-
bold: 'Bold',
|
|
30
|
-
bulletedList: 'Bulleted list',
|
|
31
|
-
cancel: 'Cancel',
|
|
32
|
-
code: 'Code',
|
|
33
|
-
invalidLink: 'Enter a web address or email address',
|
|
34
|
-
italic: 'Italic',
|
|
35
|
-
link: 'Link',
|
|
36
|
-
linkDialogTitle: 'Link',
|
|
37
|
-
linkUrl: 'Address',
|
|
38
|
-
numberedList: 'Numbered list',
|
|
39
|
-
quote: 'Quote',
|
|
40
|
-
removeLink: 'Remove link',
|
|
41
|
-
saveLink: 'Save',
|
|
42
|
-
toolbar: 'Formatting',
|
|
43
|
-
underline: 'Underline',
|
|
44
|
-
},
|
|
45
5
|
classNames: {
|
|
46
6
|
editable: 'ps-input-control w-full resize-none overflow-y-auto rounded-t-none border-t-0 leading-6 outline-none',
|
|
47
7
|
root: 'block',
|
|
@@ -2,28 +2,28 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsPortableTextEditorRenderers = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const
|
|
5
|
+
const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
|
|
6
6
|
exports.PsPortableTextEditorRenderers = {
|
|
7
7
|
renderBlock: ({ children, listItem, style }) => {
|
|
8
|
-
if (listItem ===
|
|
8
|
+
if (listItem === ps_portable_text_core_config_1.PsPortableTextCoreConfig.listItems.bullet) {
|
|
9
9
|
return ((0, jsx_runtime_1.jsx)("div", { className: "relative pl-6 before:absolute before:left-1 before:content-['\u2022']", children: children }));
|
|
10
10
|
}
|
|
11
|
-
if (listItem ===
|
|
11
|
+
if (listItem === ps_portable_text_core_config_1.PsPortableTextCoreConfig.listItems.number) {
|
|
12
12
|
return ((0, jsx_runtime_1.jsx)("div", { className: "relative pl-6 before:absolute before:left-0 before:content-[counter(ps-list)'.']", style: { counterIncrement: 'ps-list' }, children: children }));
|
|
13
13
|
}
|
|
14
|
-
if (style ===
|
|
14
|
+
if (style === ps_portable_text_core_config_1.PsPortableTextCoreConfig.styles.blockquote) {
|
|
15
15
|
return ((0, jsx_runtime_1.jsx)("div", { className: 'border-l-2 border-primary pl-3 text-ink/80', style: { counterReset: 'ps-list' }, children: children }));
|
|
16
16
|
}
|
|
17
17
|
return (0, jsx_runtime_1.jsx)("div", { style: { counterReset: 'ps-list' }, children: children });
|
|
18
18
|
},
|
|
19
19
|
renderDecorator: ({ children, value }) => {
|
|
20
|
-
if (value ===
|
|
20
|
+
if (value === ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.bold)
|
|
21
21
|
return (0, jsx_runtime_1.jsx)("strong", { children: children });
|
|
22
|
-
if (value ===
|
|
22
|
+
if (value === ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.italic)
|
|
23
23
|
return (0, jsx_runtime_1.jsx)("em", { children: children });
|
|
24
|
-
if (value ===
|
|
24
|
+
if (value === ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.underline)
|
|
25
25
|
return (0, jsx_runtime_1.jsx)("u", { children: children });
|
|
26
|
-
if (value ===
|
|
26
|
+
if (value === ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.code) {
|
|
27
27
|
return (0, jsx_runtime_1.jsx)("code", { className: 'rounded bg-ink/10 px-1 font-mono text-[0.9em]', children: children });
|
|
28
28
|
}
|
|
29
29
|
return children;
|
|
@@ -1,34 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
-
import { EditorProvider, PortableTextEditable } from '@portabletext/editor';
|
|
5
|
-
import { EventListenerPlugin } from '@portabletext/editor/plugins';
|
|
6
3
|
import { PsPortableTextEditorConfig } from '../constants/ps-portable-text-editor-config';
|
|
7
4
|
import { PsPortableTextEditorRenderers } from '../constants/ps-portable-text-editor-renderers';
|
|
8
|
-
import {
|
|
5
|
+
import { PsPortableTextCore } from '../portable-text-core/components/ps-portable-text-core';
|
|
6
|
+
import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
|
|
7
|
+
import { PsPortableTextEditorService } from '../portable-text-core/services/ps-portable-text-editor-service';
|
|
9
8
|
import { PsPortableTextToolbar } from './ps-portable-text-toolbar';
|
|
10
|
-
export const PsPortableTextEditor = ({ className, disabled = false, labels =
|
|
11
|
-
const [isMounted, setIsMounted] = useState(false);
|
|
12
|
-
const [version, setVersion] = useState(0);
|
|
13
|
-
const lastValue = useRef(value);
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
setIsMounted(true);
|
|
16
|
-
}, []);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (PsPortableTextEditorService.isSame(value, lastValue.current))
|
|
19
|
-
return;
|
|
20
|
-
lastValue.current = value;
|
|
21
|
-
setVersion((current) => current + 1);
|
|
22
|
-
}, [value]);
|
|
23
|
-
return (_jsx("div", { className: PallasoftClassNameService.merge(PsPortableTextEditorConfig.classNames.root, className), children: isMounted ? (_jsxs(EditorProvider, { initialConfig: {
|
|
24
|
-
initialValue: value,
|
|
25
|
-
readOnly: disabled,
|
|
26
|
-
schemaDefinition: PsPortableTextEditorConfig.schemaDefinition,
|
|
27
|
-
}, children: [_jsx(EventListenerPlugin, { on: (event) => {
|
|
28
|
-
if (event.type !== 'mutation')
|
|
29
|
-
return;
|
|
30
|
-
const next = Array.isArray(event.value) ? event.value : [];
|
|
31
|
-
lastValue.current = next;
|
|
32
|
-
onChange(next);
|
|
33
|
-
} }), _jsx(PsPortableTextToolbar, { disabled: disabled, labels: labels }), _jsx(PortableTextEditable, { ...PsPortableTextEditorRenderers, className: PsPortableTextEditorConfig.classNames.editable, readOnly: disabled, renderPlaceholder: () => _jsx("span", { className: 'text-ink/45', children: placeholder }), style: { minHeight: PsPortableTextEditorService.minHeight(minRows) } })] }, version)) : (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": 'true', className: PsPortableTextEditorConfig.classNames.toolbar, children: _jsx("span", { className: 'h-9' }) }), _jsx("div", { "aria-hidden": 'true', className: PsPortableTextEditorConfig.classNames.editable, style: { minHeight: PsPortableTextEditorService.minHeight(minRows) } })] })) }));
|
|
34
|
-
};
|
|
9
|
+
export const PsPortableTextEditor = ({ className, disabled = false, labels = PsPortableTextCoreConfig.labels, minRows = 4, onChange, placeholder, value, }) => (_jsx(PsPortableTextCore, { ...PsPortableTextEditorRenderers, className: PallasoftClassNameService.merge(PsPortableTextEditorConfig.classNames.root, className), disabled: disabled, editableClassName: PsPortableTextEditorConfig.classNames.editable, fallback: _jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": 'true', className: PsPortableTextEditorConfig.classNames.toolbar, children: _jsx("span", { className: 'h-9' }) }), _jsx("div", { "aria-hidden": 'true', className: PsPortableTextEditorConfig.classNames.editable, style: { minHeight: PsPortableTextEditorService.minHeight(minRows) } })] }), minRows: minRows, onChange: onChange, renderPlaceholder: () => _jsx("span", { className: 'text-ink/45', children: placeholder }), toolbar: _jsx(PsPortableTextToolbar, { disabled: disabled, labels: labels }), value: value }));
|
|
@@ -1,31 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
2
|
import { PsFormModal } from '@pallasoft/ps-popovers';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
|
|
4
|
+
import { usePsPortableTextLinkForm } from '../portable-text-core/hooks/use-ps-portable-text-link-form';
|
|
6
5
|
import { PsUrlInput } from './ps-url-input';
|
|
7
6
|
export const PsPortableTextLinkDialog = ({ isOpen, labels, onClose, onSubmit, }) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const close = () => {
|
|
11
|
-
setAddress('');
|
|
12
|
-
setHasError(false);
|
|
13
|
-
onClose();
|
|
14
|
-
};
|
|
15
|
-
const submit = (event) => {
|
|
16
|
-
event.preventDefault();
|
|
17
|
-
event.stopPropagation();
|
|
18
|
-
const href = PsPortableTextEditorService.normalizeLink(address);
|
|
19
|
-
if (href === null) {
|
|
20
|
-
setHasError(true);
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
setAddress('');
|
|
24
|
-
setHasError(false);
|
|
25
|
-
onSubmit(href);
|
|
26
|
-
};
|
|
27
|
-
return (_jsx(PsFormModal, { cancelLabel: labels.cancel, isOpen: isOpen, isSubmitDisabled: address.trim() === '', onClose: close, onSubmit: submit, size: 'sm', submitLabel: labels.saveLink, title: labels.linkDialogTitle, children: _jsx(PsUrlInput, { "aria-invalid": hasError, autoFocus: true, helperText: hasError ? labels.invalidLink : undefined, label: labels.linkUrl, name: PsPortableTextEditorConfig.linkAnnotation, onChange: (event) => {
|
|
28
|
-
setAddress(event.target.value);
|
|
29
|
-
setHasError(false);
|
|
30
|
-
}, value: address }) }));
|
|
7
|
+
const form = usePsPortableTextLinkForm({ onClose, onSubmit });
|
|
8
|
+
return (_jsx(PsFormModal, { cancelLabel: labels.cancel, isOpen: isOpen, isSubmitDisabled: form.isSubmitDisabled, onClose: form.close, onSubmit: form.submit, size: 'sm', submitLabel: labels.saveLink, title: labels.linkDialogTitle, children: _jsx(PsUrlInput, { "aria-invalid": form.hasError, autoFocus: true, helperText: form.hasError ? labels.invalidLink : undefined, label: labels.linkUrl, name: PsPortableTextCoreConfig.linkAnnotation, onChange: (event) => form.setAddress(event.target.value), value: form.address }) }));
|
|
31
9
|
};
|
|
@@ -1,58 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { useEditor, useEditorSelector } from '@portabletext/editor';
|
|
4
|
-
import { isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, } from '@portabletext/editor/selectors';
|
|
5
3
|
import { PsPortableTextEditorConfig } from '../constants/ps-portable-text-editor-config';
|
|
4
|
+
import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
|
|
5
|
+
import { usePsPortableTextToolbar } from '../portable-text-core/hooks/use-ps-portable-text-toolbar';
|
|
6
6
|
import { PsPortableTextLinkDialog } from './ps-portable-text-link-dialog';
|
|
7
7
|
import { PsPortableTextToolbarButton } from './ps-portable-text-toolbar-button';
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
return (_jsx(PsPortableTextToolbarButton, { className: className, disabled: disabled, isActive: isActive, label: label, onClick: () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
export const PsPortableTextToolbar = ({ disabled, labels }) => {
|
|
9
|
+
const toolbar = usePsPortableTextToolbar();
|
|
10
|
+
const [isLinkOpen, setIsLinkOpen] = useState(false);
|
|
11
|
+
return (_jsxs("div", { "aria-label": labels.toolbar, className: PsPortableTextEditorConfig.classNames.toolbar, role: 'toolbar', children: [_jsx(PsPortableTextToolbarButton, { className: 'font-bold', disabled: disabled, isActive: toolbar.active.bold, label: labels.bold, onClick: () => toolbar.toggleDecorator(PsPortableTextCoreConfig.decorators.bold), children: "B" }), _jsx(PsPortableTextToolbarButton, { className: 'italic', disabled: disabled, isActive: toolbar.active.italic, label: labels.italic, onClick: () => toolbar.toggleDecorator(PsPortableTextCoreConfig.decorators.italic), children: "I" }), _jsx(PsPortableTextToolbarButton, { className: 'underline', disabled: disabled, isActive: toolbar.active.underline, label: labels.underline, onClick: () => toolbar.toggleDecorator(PsPortableTextCoreConfig.decorators.underline), children: "U" }), _jsx(PsPortableTextToolbarButton, { className: 'font-mono', disabled: disabled, isActive: toolbar.active.code, label: labels.code, onClick: () => toolbar.toggleDecorator(PsPortableTextCoreConfig.decorators.code), children: '</>' }), _jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.link, label: toolbar.active.link ? labels.removeLink : labels.link, onClick: () => (toolbar.active.link ? toolbar.removeLink() : setIsLinkOpen(true)), children: _jsx("span", { className: 'underline', children: labels.link }) }), _jsx(PsPortableTextLinkDialog, { isOpen: isLinkOpen, labels: labels, onClose: () => {
|
|
12
|
+
setIsLinkOpen(false);
|
|
13
|
+
toolbar.focus();
|
|
14
|
+
}, onSubmit: (href) => {
|
|
15
|
+
toolbar.addLink(href);
|
|
16
|
+
setIsLinkOpen(false);
|
|
17
|
+
} }), _jsx("span", { "aria-hidden": 'true', className: PsPortableTextEditorConfig.classNames.separator }), _jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.bulletedList, label: labels.bulletedList, onClick: () => toolbar.toggleListItem(PsPortableTextCoreConfig.listItems.bullet), children: "\u2022" }), _jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.numberedList, label: labels.numberedList, onClick: () => toolbar.toggleListItem(PsPortableTextCoreConfig.listItems.number), children: "1." }), _jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: toolbar.active.quote, label: labels.quote, onClick: toolbar.toggleQuote, children: "\u201D" })] }));
|
|
15
18
|
};
|
|
16
|
-
const ListButton = ({ children, disabled, label, listItem, }) => {
|
|
17
|
-
const editor = useEditor();
|
|
18
|
-
const isActive = useEditorSelector(editor, isActiveListItem(listItem));
|
|
19
|
-
return (_jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: isActive, label: label, onClick: () => {
|
|
20
|
-
editor.send({ type: 'list item.toggle', listItem });
|
|
21
|
-
editor.send({ type: 'focus' });
|
|
22
|
-
}, children: children }));
|
|
23
|
-
};
|
|
24
|
-
const QuoteButton = ({ children, disabled, label }) => {
|
|
25
|
-
const editor = useEditor();
|
|
26
|
-
const isActive = useEditorSelector(editor, isActiveStyle(PsPortableTextEditorConfig.styles.blockquote));
|
|
27
|
-
return (_jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: isActive, label: label, onClick: () => {
|
|
28
|
-
editor.send({ type: 'style.toggle', style: PsPortableTextEditorConfig.styles.blockquote });
|
|
29
|
-
editor.send({ type: 'focus' });
|
|
30
|
-
}, children: children }));
|
|
31
|
-
};
|
|
32
|
-
const LinkButton = ({ disabled, labels }) => {
|
|
33
|
-
const editor = useEditor();
|
|
34
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
35
|
-
const isActive = useEditorSelector(editor, isActiveAnnotation(PsPortableTextEditorConfig.linkAnnotation));
|
|
36
|
-
const close = () => {
|
|
37
|
-
setIsOpen(false);
|
|
38
|
-
editor.send({ type: 'focus' });
|
|
39
|
-
};
|
|
40
|
-
return (_jsxs(_Fragment, { children: [_jsx(PsPortableTextToolbarButton, { disabled: disabled, isActive: isActive, label: isActive ? labels.removeLink : labels.link, onClick: () => {
|
|
41
|
-
if (isActive) {
|
|
42
|
-
editor.send({
|
|
43
|
-
type: 'annotation.remove',
|
|
44
|
-
annotation: { name: PsPortableTextEditorConfig.linkAnnotation },
|
|
45
|
-
});
|
|
46
|
-
editor.send({ type: 'focus' });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
setIsOpen(true);
|
|
50
|
-
}, children: _jsx("span", { className: 'underline', children: labels.link }) }), _jsx(PsPortableTextLinkDialog, { isOpen: isOpen, labels: labels, onClose: close, onSubmit: (href) => {
|
|
51
|
-
editor.send({
|
|
52
|
-
type: 'annotation.add',
|
|
53
|
-
annotation: { name: PsPortableTextEditorConfig.linkAnnotation, value: { href } },
|
|
54
|
-
});
|
|
55
|
-
close();
|
|
56
|
-
} })] }));
|
|
57
|
-
};
|
|
58
|
-
export const PsPortableTextToolbar = ({ disabled, labels }) => (_jsxs("div", { "aria-label": labels.toolbar, className: PsPortableTextEditorConfig.classNames.toolbar, role: 'toolbar', children: [_jsx(DecoratorButton, { className: 'font-bold', decorator: PsPortableTextEditorConfig.decorators.bold, disabled: disabled, label: labels.bold, children: "B" }), _jsx(DecoratorButton, { className: 'italic', decorator: PsPortableTextEditorConfig.decorators.italic, disabled: disabled, label: labels.italic, children: "I" }), _jsx(DecoratorButton, { className: 'underline', decorator: PsPortableTextEditorConfig.decorators.underline, disabled: disabled, label: labels.underline, children: "U" }), _jsx(DecoratorButton, { className: 'font-mono', decorator: PsPortableTextEditorConfig.decorators.code, disabled: disabled, label: labels.code, children: '</>' }), _jsx(LinkButton, { disabled: disabled, labels: labels }), _jsx("span", { "aria-hidden": 'true', className: PsPortableTextEditorConfig.classNames.separator }), _jsx(ListButton, { disabled: disabled, label: labels.bulletedList, listItem: PsPortableTextEditorConfig.listItems.bullet, children: "\u2022" }), _jsx(ListButton, { disabled: disabled, label: labels.numberedList, listItem: PsPortableTextEditorConfig.listItems.number, children: "1." }), _jsx(QuoteButton, { disabled: disabled, label: labels.quote, children: "\u201D" })] }));
|
|
@@ -1,44 +1,4 @@
|
|
|
1
|
-
import { Common } from '@pallasoft/ps-models';
|
|
2
|
-
import { defineSchema } from '@portabletext/editor';
|
|
3
|
-
const Models = Common.PortableTextConfig;
|
|
4
1
|
export const PsPortableTextEditorConfig = {
|
|
5
|
-
linkAnnotation: Models.linkType,
|
|
6
|
-
mailtoPrefix: 'mailto:',
|
|
7
|
-
emailPattern: /^[^\s@/:]+@[^\s@/:]+\.[^\s@/:]+$/,
|
|
8
|
-
lineHeightRem: 1.5,
|
|
9
|
-
decorators: {
|
|
10
|
-
bold: Models.decorators[0],
|
|
11
|
-
code: Models.decorators[3],
|
|
12
|
-
italic: Models.decorators[1],
|
|
13
|
-
underline: Models.decorators[2],
|
|
14
|
-
},
|
|
15
|
-
styles: { blockquote: Models.styles[1], normal: Models.styles[0] },
|
|
16
|
-
listItems: { bullet: Models.listItems[0], number: Models.listItems[1] },
|
|
17
|
-
schemaDefinition: defineSchema({
|
|
18
|
-
annotations: [{ name: Models.linkType, fields: [{ name: 'href', type: 'string' }] }],
|
|
19
|
-
blockObjects: [],
|
|
20
|
-
decorators: Models.decorators.map((name) => ({ name })),
|
|
21
|
-
inlineObjects: [],
|
|
22
|
-
lists: Models.listItems.map((name) => ({ name })),
|
|
23
|
-
styles: Models.styles.map((name) => ({ name })),
|
|
24
|
-
}),
|
|
25
|
-
labels: {
|
|
26
|
-
bold: 'Bold',
|
|
27
|
-
bulletedList: 'Bulleted list',
|
|
28
|
-
cancel: 'Cancel',
|
|
29
|
-
code: 'Code',
|
|
30
|
-
invalidLink: 'Enter a web address or email address',
|
|
31
|
-
italic: 'Italic',
|
|
32
|
-
link: 'Link',
|
|
33
|
-
linkDialogTitle: 'Link',
|
|
34
|
-
linkUrl: 'Address',
|
|
35
|
-
numberedList: 'Numbered list',
|
|
36
|
-
quote: 'Quote',
|
|
37
|
-
removeLink: 'Remove link',
|
|
38
|
-
saveLink: 'Save',
|
|
39
|
-
toolbar: 'Formatting',
|
|
40
|
-
underline: 'Underline',
|
|
41
|
-
},
|
|
42
2
|
classNames: {
|
|
43
3
|
editable: 'ps-input-control w-full resize-none overflow-y-auto rounded-t-none border-t-0 leading-6 outline-none',
|
|
44
4
|
root: 'block',
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
|
|
3
3
|
export const PsPortableTextEditorRenderers = {
|
|
4
4
|
renderBlock: ({ children, listItem, style }) => {
|
|
5
|
-
if (listItem ===
|
|
5
|
+
if (listItem === PsPortableTextCoreConfig.listItems.bullet) {
|
|
6
6
|
return (_jsx("div", { className: "relative pl-6 before:absolute before:left-1 before:content-['\u2022']", children: children }));
|
|
7
7
|
}
|
|
8
|
-
if (listItem ===
|
|
8
|
+
if (listItem === PsPortableTextCoreConfig.listItems.number) {
|
|
9
9
|
return (_jsx("div", { className: "relative pl-6 before:absolute before:left-0 before:content-[counter(ps-list)'.']", style: { counterIncrement: 'ps-list' }, children: children }));
|
|
10
10
|
}
|
|
11
|
-
if (style ===
|
|
11
|
+
if (style === PsPortableTextCoreConfig.styles.blockquote) {
|
|
12
12
|
return (_jsx("div", { className: 'border-l-2 border-primary pl-3 text-ink/80', style: { counterReset: 'ps-list' }, children: children }));
|
|
13
13
|
}
|
|
14
14
|
return _jsx("div", { style: { counterReset: 'ps-list' }, children: children });
|
|
15
15
|
},
|
|
16
16
|
renderDecorator: ({ children, value }) => {
|
|
17
|
-
if (value ===
|
|
17
|
+
if (value === PsPortableTextCoreConfig.decorators.bold)
|
|
18
18
|
return _jsx("strong", { children: children });
|
|
19
|
-
if (value ===
|
|
19
|
+
if (value === PsPortableTextCoreConfig.decorators.italic)
|
|
20
20
|
return _jsx("em", { children: children });
|
|
21
|
-
if (value ===
|
|
21
|
+
if (value === PsPortableTextCoreConfig.decorators.underline)
|
|
22
22
|
return _jsx("u", { children: children });
|
|
23
|
-
if (value ===
|
|
23
|
+
if (value === PsPortableTextCoreConfig.decorators.code) {
|
|
24
24
|
return _jsx("code", { className: 'rounded bg-ink/10 px-1 font-mono text-[0.9em]', children: children });
|
|
25
25
|
}
|
|
26
26
|
return children;
|
package/dist/esm/index.js
CHANGED
|
@@ -46,6 +46,6 @@ export * from './components/ps-select-with-create';
|
|
|
46
46
|
export * from './constants/ps-select-with-create-config';
|
|
47
47
|
export * from './interfaces/ps-date-preset-data';
|
|
48
48
|
export * from './components/ps-portable-text-editor';
|
|
49
|
-
export * from './interfaces/ps-portable-text-editor-labels';
|
|
49
|
+
export * from './portable-text-core/interfaces/ps-portable-text-editor-labels';
|
|
50
50
|
export * from './components/ps-option-card-group';
|
|
51
51
|
export * from './interfaces/ps-option-card-data';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { EditorProvider, PortableTextEditable, } from '@portabletext/editor';
|
|
4
|
+
import { EventListenerPlugin } from '@portabletext/editor/plugins';
|
|
5
|
+
import { PsPortableTextCoreConfig } from '../constants/ps-portable-text-core-config';
|
|
6
|
+
import { PsPortableTextEditorService } from '../services/ps-portable-text-editor-service';
|
|
7
|
+
export const PsPortableTextCore = ({ className, disabled = false, editableClassName, fallback, minRows = 4, onChange, renderAnnotation, renderBlock, renderDecorator, renderPlaceholder, toolbar, value, }) => {
|
|
8
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
9
|
+
const [version, setVersion] = useState(0);
|
|
10
|
+
const lastValue = useRef(value);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
setIsMounted(true);
|
|
13
|
+
}, []);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (PsPortableTextEditorService.isSame(value, lastValue.current))
|
|
16
|
+
return;
|
|
17
|
+
lastValue.current = value;
|
|
18
|
+
setVersion((current) => current + 1);
|
|
19
|
+
}, [value]);
|
|
20
|
+
return (_jsx("div", { className: className, children: isMounted ? (_jsxs(EditorProvider, { initialConfig: {
|
|
21
|
+
initialValue: value,
|
|
22
|
+
readOnly: disabled,
|
|
23
|
+
schemaDefinition: PsPortableTextCoreConfig.schemaDefinition,
|
|
24
|
+
}, children: [_jsx(EventListenerPlugin, { on: (event) => {
|
|
25
|
+
if (event.type !== 'mutation')
|
|
26
|
+
return;
|
|
27
|
+
const next = Array.isArray(event.value) ? event.value : [];
|
|
28
|
+
lastValue.current = next;
|
|
29
|
+
onChange(next);
|
|
30
|
+
} }), toolbar, _jsx(PortableTextEditable, { className: editableClassName, readOnly: disabled, renderAnnotation: renderAnnotation, renderBlock: renderBlock, renderDecorator: renderDecorator, renderPlaceholder: renderPlaceholder, style: { minHeight: PsPortableTextEditorService.minHeight(minRows) } })] }, version)) : (fallback) }));
|
|
31
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Common } from '@pallasoft/ps-models';
|
|
2
|
+
import { defineSchema } from '@portabletext/editor';
|
|
3
|
+
const Models = Common.PortableTextConfig;
|
|
4
|
+
export const PsPortableTextCoreConfig = {
|
|
5
|
+
linkAnnotation: Models.linkType,
|
|
6
|
+
mailtoPrefix: 'mailto:',
|
|
7
|
+
emailPattern: /^[^\s@/:]+@[^\s@/:]+\.[^\s@/:]+$/,
|
|
8
|
+
lineHeightRem: 1.5,
|
|
9
|
+
decorators: {
|
|
10
|
+
bold: Models.decorators[0],
|
|
11
|
+
code: Models.decorators[3],
|
|
12
|
+
italic: Models.decorators[1],
|
|
13
|
+
underline: Models.decorators[2],
|
|
14
|
+
},
|
|
15
|
+
styles: { blockquote: Models.styles[1], normal: Models.styles[0] },
|
|
16
|
+
listItems: { bullet: Models.listItems[0], number: Models.listItems[1] },
|
|
17
|
+
schemaDefinition: defineSchema({
|
|
18
|
+
annotations: [{ name: Models.linkType, fields: [{ name: 'href', type: 'string' }] }],
|
|
19
|
+
blockObjects: [],
|
|
20
|
+
decorators: Models.decorators.map((name) => ({ name })),
|
|
21
|
+
inlineObjects: [],
|
|
22
|
+
lists: Models.listItems.map((name) => ({ name })),
|
|
23
|
+
styles: Models.styles.map((name) => ({ name })),
|
|
24
|
+
}),
|
|
25
|
+
labels: {
|
|
26
|
+
bold: 'Bold',
|
|
27
|
+
bulletedList: 'Bulleted list',
|
|
28
|
+
cancel: 'Cancel',
|
|
29
|
+
code: 'Code',
|
|
30
|
+
invalidLink: 'Enter a web address or email address',
|
|
31
|
+
italic: 'Italic',
|
|
32
|
+
link: 'Link',
|
|
33
|
+
linkDialogTitle: 'Link',
|
|
34
|
+
linkUrl: 'Address',
|
|
35
|
+
numberedList: 'Numbered list',
|
|
36
|
+
quote: 'Quote',
|
|
37
|
+
removeLink: 'Remove link',
|
|
38
|
+
saveLink: 'Save',
|
|
39
|
+
toolbar: 'Formatting',
|
|
40
|
+
underline: 'Underline',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { PsPortableTextEditorService } from '../services/ps-portable-text-editor-service';
|
|
3
|
+
export const usePsPortableTextLinkForm = ({ onClose, onSubmit, }) => {
|
|
4
|
+
const [address, setAddress] = useState('');
|
|
5
|
+
const [hasError, setHasError] = useState(false);
|
|
6
|
+
return {
|
|
7
|
+
address,
|
|
8
|
+
close: () => {
|
|
9
|
+
setAddress('');
|
|
10
|
+
setHasError(false);
|
|
11
|
+
onClose();
|
|
12
|
+
},
|
|
13
|
+
hasError,
|
|
14
|
+
isSubmitDisabled: address.trim() === '',
|
|
15
|
+
setAddress: (next) => {
|
|
16
|
+
setAddress(next);
|
|
17
|
+
setHasError(false);
|
|
18
|
+
},
|
|
19
|
+
submit: (event) => {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
event.stopPropagation();
|
|
22
|
+
const href = PsPortableTextEditorService.normalizeLink(address);
|
|
23
|
+
if (href === null) {
|
|
24
|
+
setHasError(true);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
setAddress('');
|
|
28
|
+
setHasError(false);
|
|
29
|
+
onSubmit(href);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useEditor, useEditorSelector } from '@portabletext/editor';
|
|
2
|
+
import { isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, } from '@portabletext/editor/selectors';
|
|
3
|
+
import { PsPortableTextCoreConfig } from '../constants/ps-portable-text-core-config';
|
|
4
|
+
export const usePsPortableTextToolbar = () => {
|
|
5
|
+
const editor = useEditor();
|
|
6
|
+
const isBold = useEditorSelector(editor, isActiveDecorator(PsPortableTextCoreConfig.decorators.bold));
|
|
7
|
+
const isItalic = useEditorSelector(editor, isActiveDecorator(PsPortableTextCoreConfig.decorators.italic));
|
|
8
|
+
const isUnderline = useEditorSelector(editor, isActiveDecorator(PsPortableTextCoreConfig.decorators.underline));
|
|
9
|
+
const isCode = useEditorSelector(editor, isActiveDecorator(PsPortableTextCoreConfig.decorators.code));
|
|
10
|
+
const isBulletedList = useEditorSelector(editor, isActiveListItem(PsPortableTextCoreConfig.listItems.bullet));
|
|
11
|
+
const isNumberedList = useEditorSelector(editor, isActiveListItem(PsPortableTextCoreConfig.listItems.number));
|
|
12
|
+
const isQuote = useEditorSelector(editor, isActiveStyle(PsPortableTextCoreConfig.styles.blockquote));
|
|
13
|
+
const isLink = useEditorSelector(editor, isActiveAnnotation(PsPortableTextCoreConfig.linkAnnotation));
|
|
14
|
+
const focus = () => editor.send({ type: 'focus' });
|
|
15
|
+
return {
|
|
16
|
+
active: {
|
|
17
|
+
bold: isBold,
|
|
18
|
+
bulletedList: isBulletedList,
|
|
19
|
+
code: isCode,
|
|
20
|
+
italic: isItalic,
|
|
21
|
+
link: isLink,
|
|
22
|
+
numberedList: isNumberedList,
|
|
23
|
+
quote: isQuote,
|
|
24
|
+
underline: isUnderline,
|
|
25
|
+
},
|
|
26
|
+
addLink: (href) => {
|
|
27
|
+
editor.send({
|
|
28
|
+
type: 'annotation.add',
|
|
29
|
+
annotation: { name: PsPortableTextCoreConfig.linkAnnotation, value: { href } },
|
|
30
|
+
});
|
|
31
|
+
focus();
|
|
32
|
+
},
|
|
33
|
+
focus,
|
|
34
|
+
removeLink: () => {
|
|
35
|
+
editor.send({
|
|
36
|
+
type: 'annotation.remove',
|
|
37
|
+
annotation: { name: PsPortableTextCoreConfig.linkAnnotation },
|
|
38
|
+
});
|
|
39
|
+
focus();
|
|
40
|
+
},
|
|
41
|
+
toggleDecorator: (decorator) => {
|
|
42
|
+
editor.send({ type: 'decorator.toggle', decorator });
|
|
43
|
+
focus();
|
|
44
|
+
},
|
|
45
|
+
toggleListItem: (listItem) => {
|
|
46
|
+
editor.send({ type: 'list item.toggle', listItem });
|
|
47
|
+
focus();
|
|
48
|
+
},
|
|
49
|
+
toggleQuote: () => {
|
|
50
|
+
editor.send({ type: 'style.toggle', style: PsPortableTextCoreConfig.styles.blockquote });
|
|
51
|
+
focus();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './components/ps-portable-text-core';
|
|
2
|
+
export * from './constants/ps-portable-text-core-config';
|
|
3
|
+
export * from './hooks/use-ps-portable-text-link-form';
|
|
4
|
+
export * from './hooks/use-ps-portable-text-toolbar';
|
|
5
|
+
export * from './interfaces/ps-portable-text-editor-labels';
|
|
6
|
+
export * from './services/ps-portable-text-editor-service';
|
package/dist/esm/{services → portable-text-core/services}/ps-portable-text-editor-service.js
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Common } from '@pallasoft/ps-models';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { PsTextFormatService } from '../../services/ps-text-format-service';
|
|
3
|
+
import { PsPortableTextCoreConfig } from '../constants/ps-portable-text-core-config';
|
|
4
4
|
export class PsPortableTextEditorService {
|
|
5
5
|
static normalizeLink(raw) {
|
|
6
6
|
const trimmed = raw.trim();
|
|
7
7
|
if (trimmed === '')
|
|
8
8
|
return null;
|
|
9
|
-
const candidate =
|
|
10
|
-
? `${
|
|
11
|
-
: trimmed.toLowerCase().startsWith(
|
|
9
|
+
const candidate = PsPortableTextCoreConfig.emailPattern.test(trimmed)
|
|
10
|
+
? `${PsPortableTextCoreConfig.mailtoPrefix}${trimmed}`
|
|
11
|
+
: trimmed.toLowerCase().startsWith(PsPortableTextCoreConfig.mailtoPrefix)
|
|
12
12
|
? trimmed
|
|
13
13
|
: PsTextFormatService.url(trimmed);
|
|
14
14
|
try {
|
|
@@ -22,7 +22,7 @@ export class PsPortableTextEditorService {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
static minHeight(minRows) {
|
|
25
|
-
return `${minRows *
|
|
25
|
+
return `${minRows * PsPortableTextCoreConfig.lineHeightRem}rem`;
|
|
26
26
|
}
|
|
27
27
|
static isSame(first, second) {
|
|
28
28
|
return JSON.stringify(first) === JSON.stringify(second);
|
package/dist/index.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export * from './components/ps-select-with-create';
|
|
|
46
46
|
export * from './constants/ps-select-with-create-config';
|
|
47
47
|
export * from './interfaces/ps-date-preset-data';
|
|
48
48
|
export * from './components/ps-portable-text-editor';
|
|
49
|
-
export * from './interfaces/ps-portable-text-editor-labels';
|
|
49
|
+
export * from './portable-text-core/interfaces/ps-portable-text-editor-labels';
|
|
50
50
|
export type { PortableTextBlock } from '@portabletext/editor';
|
|
51
51
|
export * from './components/ps-option-card-group';
|
|
52
52
|
export * from './interfaces/ps-option-card-data';
|
package/dist/index.js
CHANGED
|
@@ -62,6 +62,6 @@ __exportStar(require("./components/ps-select-with-create"), exports);
|
|
|
62
62
|
__exportStar(require("./constants/ps-select-with-create-config"), exports);
|
|
63
63
|
__exportStar(require("./interfaces/ps-date-preset-data"), exports);
|
|
64
64
|
__exportStar(require("./components/ps-portable-text-editor"), exports);
|
|
65
|
-
__exportStar(require("./interfaces/ps-portable-text-editor-labels"), exports);
|
|
65
|
+
__exportStar(require("./portable-text-core/interfaces/ps-portable-text-editor-labels"), exports);
|
|
66
66
|
__exportStar(require("./components/ps-option-card-group"), exports);
|
|
67
67
|
__exportStar(require("./interfaces/ps-option-card-data"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type PortableTextBlock, type RenderAnnotationFunction, type RenderBlockFunction, type RenderDecoratorFunction, type RenderPlaceholderFunction } from '@portabletext/editor';
|
|
3
|
+
export interface PsPortableTextCoreProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
editableClassName?: string;
|
|
7
|
+
fallback?: ReactNode;
|
|
8
|
+
minRows?: number;
|
|
9
|
+
onChange: (value: PortableTextBlock[]) => void;
|
|
10
|
+
renderAnnotation?: RenderAnnotationFunction;
|
|
11
|
+
renderBlock?: RenderBlockFunction;
|
|
12
|
+
renderDecorator?: RenderDecoratorFunction;
|
|
13
|
+
renderPlaceholder?: RenderPlaceholderFunction;
|
|
14
|
+
toolbar?: ReactNode;
|
|
15
|
+
value: PortableTextBlock[];
|
|
16
|
+
}
|
|
17
|
+
export declare const PsPortableTextCore: ({ className, disabled, editableClassName, fallback, minRows, onChange, renderAnnotation, renderBlock, renderDecorator, renderPlaceholder, toolbar, value, }: PsPortableTextCoreProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsPortableTextCore = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const editor_1 = require("@portabletext/editor");
|
|
7
|
+
const plugins_1 = require("@portabletext/editor/plugins");
|
|
8
|
+
const ps_portable_text_core_config_1 = require("../constants/ps-portable-text-core-config");
|
|
9
|
+
const ps_portable_text_editor_service_1 = require("../services/ps-portable-text-editor-service");
|
|
10
|
+
const PsPortableTextCore = ({ className, disabled = false, editableClassName, fallback, minRows = 4, onChange, renderAnnotation, renderBlock, renderDecorator, renderPlaceholder, toolbar, value, }) => {
|
|
11
|
+
const [isMounted, setIsMounted] = (0, react_1.useState)(false);
|
|
12
|
+
const [version, setVersion] = (0, react_1.useState)(0);
|
|
13
|
+
const lastValue = (0, react_1.useRef)(value);
|
|
14
|
+
(0, react_1.useEffect)(() => {
|
|
15
|
+
setIsMounted(true);
|
|
16
|
+
}, []);
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
18
|
+
if (ps_portable_text_editor_service_1.PsPortableTextEditorService.isSame(value, lastValue.current))
|
|
19
|
+
return;
|
|
20
|
+
lastValue.current = value;
|
|
21
|
+
setVersion((current) => current + 1);
|
|
22
|
+
}, [value]);
|
|
23
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: className, children: isMounted ? ((0, jsx_runtime_1.jsxs)(editor_1.EditorProvider, { initialConfig: {
|
|
24
|
+
initialValue: value,
|
|
25
|
+
readOnly: disabled,
|
|
26
|
+
schemaDefinition: ps_portable_text_core_config_1.PsPortableTextCoreConfig.schemaDefinition,
|
|
27
|
+
}, children: [(0, jsx_runtime_1.jsx)(plugins_1.EventListenerPlugin, { on: (event) => {
|
|
28
|
+
if (event.type !== 'mutation')
|
|
29
|
+
return;
|
|
30
|
+
const next = Array.isArray(event.value) ? event.value : [];
|
|
31
|
+
lastValue.current = next;
|
|
32
|
+
onChange(next);
|
|
33
|
+
} }), toolbar, (0, jsx_runtime_1.jsx)(editor_1.PortableTextEditable, { className: editableClassName, readOnly: disabled, renderAnnotation: renderAnnotation, renderBlock: renderBlock, renderDecorator: renderDecorator, renderPlaceholder: renderPlaceholder, style: { minHeight: ps_portable_text_editor_service_1.PsPortableTextEditorService.minHeight(minRows) } })] }, version)) : (fallback) }));
|
|
34
|
+
};
|
|
35
|
+
exports.PsPortableTextCore = PsPortableTextCore;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export declare const PsPortableTextCoreConfig: {
|
|
2
|
+
readonly linkAnnotation: "link";
|
|
3
|
+
readonly mailtoPrefix: "mailto:";
|
|
4
|
+
readonly emailPattern: RegExp;
|
|
5
|
+
readonly lineHeightRem: 1.5;
|
|
6
|
+
readonly decorators: {
|
|
7
|
+
readonly bold: "strong";
|
|
8
|
+
readonly code: "code";
|
|
9
|
+
readonly italic: "em";
|
|
10
|
+
readonly underline: "underline";
|
|
11
|
+
};
|
|
12
|
+
readonly styles: {
|
|
13
|
+
readonly blockquote: "blockquote";
|
|
14
|
+
readonly normal: "normal";
|
|
15
|
+
};
|
|
16
|
+
readonly listItems: {
|
|
17
|
+
readonly bullet: "bullet";
|
|
18
|
+
readonly number: "number";
|
|
19
|
+
};
|
|
20
|
+
readonly schemaDefinition: {
|
|
21
|
+
readonly annotations: readonly [{
|
|
22
|
+
readonly name: "link";
|
|
23
|
+
readonly fields: readonly [{
|
|
24
|
+
readonly name: "href";
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
}];
|
|
27
|
+
}];
|
|
28
|
+
readonly blockObjects: readonly [];
|
|
29
|
+
readonly decorators: {
|
|
30
|
+
name: "code" | "em" | "strong" | "underline";
|
|
31
|
+
}[];
|
|
32
|
+
readonly inlineObjects: readonly [];
|
|
33
|
+
readonly lists: {
|
|
34
|
+
name: "number" | "bullet";
|
|
35
|
+
}[];
|
|
36
|
+
readonly styles: {
|
|
37
|
+
name: "blockquote" | "normal";
|
|
38
|
+
}[];
|
|
39
|
+
};
|
|
40
|
+
readonly labels: {
|
|
41
|
+
bold: string;
|
|
42
|
+
bulletedList: string;
|
|
43
|
+
cancel: string;
|
|
44
|
+
code: string;
|
|
45
|
+
invalidLink: string;
|
|
46
|
+
italic: string;
|
|
47
|
+
link: string;
|
|
48
|
+
linkDialogTitle: string;
|
|
49
|
+
linkUrl: string;
|
|
50
|
+
numberedList: string;
|
|
51
|
+
quote: string;
|
|
52
|
+
removeLink: string;
|
|
53
|
+
saveLink: string;
|
|
54
|
+
toolbar: string;
|
|
55
|
+
underline: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsPortableTextCoreConfig = void 0;
|
|
4
|
+
const ps_models_1 = require("@pallasoft/ps-models");
|
|
5
|
+
const editor_1 = require("@portabletext/editor");
|
|
6
|
+
const Models = ps_models_1.Common.PortableTextConfig;
|
|
7
|
+
exports.PsPortableTextCoreConfig = {
|
|
8
|
+
linkAnnotation: Models.linkType,
|
|
9
|
+
mailtoPrefix: 'mailto:',
|
|
10
|
+
emailPattern: /^[^\s@/:]+@[^\s@/:]+\.[^\s@/:]+$/,
|
|
11
|
+
lineHeightRem: 1.5,
|
|
12
|
+
decorators: {
|
|
13
|
+
bold: Models.decorators[0],
|
|
14
|
+
code: Models.decorators[3],
|
|
15
|
+
italic: Models.decorators[1],
|
|
16
|
+
underline: Models.decorators[2],
|
|
17
|
+
},
|
|
18
|
+
styles: { blockquote: Models.styles[1], normal: Models.styles[0] },
|
|
19
|
+
listItems: { bullet: Models.listItems[0], number: Models.listItems[1] },
|
|
20
|
+
schemaDefinition: (0, editor_1.defineSchema)({
|
|
21
|
+
annotations: [{ name: Models.linkType, fields: [{ name: 'href', type: 'string' }] }],
|
|
22
|
+
blockObjects: [],
|
|
23
|
+
decorators: Models.decorators.map((name) => ({ name })),
|
|
24
|
+
inlineObjects: [],
|
|
25
|
+
lists: Models.listItems.map((name) => ({ name })),
|
|
26
|
+
styles: Models.styles.map((name) => ({ name })),
|
|
27
|
+
}),
|
|
28
|
+
labels: {
|
|
29
|
+
bold: 'Bold',
|
|
30
|
+
bulletedList: 'Bulleted list',
|
|
31
|
+
cancel: 'Cancel',
|
|
32
|
+
code: 'Code',
|
|
33
|
+
invalidLink: 'Enter a web address or email address',
|
|
34
|
+
italic: 'Italic',
|
|
35
|
+
link: 'Link',
|
|
36
|
+
linkDialogTitle: 'Link',
|
|
37
|
+
linkUrl: 'Address',
|
|
38
|
+
numberedList: 'Numbered list',
|
|
39
|
+
quote: 'Quote',
|
|
40
|
+
removeLink: 'Remove link',
|
|
41
|
+
saveLink: 'Save',
|
|
42
|
+
toolbar: 'Formatting',
|
|
43
|
+
underline: 'Underline',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type FormEvent } from 'react';
|
|
2
|
+
export interface UsePsPortableTextLinkFormOptions {
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
onSubmit: (href: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare const usePsPortableTextLinkForm: ({ onClose, onSubmit, }: UsePsPortableTextLinkFormOptions) => {
|
|
7
|
+
address: string;
|
|
8
|
+
close: () => void;
|
|
9
|
+
hasError: boolean;
|
|
10
|
+
isSubmitDisabled: boolean;
|
|
11
|
+
setAddress: (next: string) => void;
|
|
12
|
+
submit: (event: FormEvent) => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsPortableTextLinkForm = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ps_portable_text_editor_service_1 = require("../services/ps-portable-text-editor-service");
|
|
6
|
+
const usePsPortableTextLinkForm = ({ onClose, onSubmit, }) => {
|
|
7
|
+
const [address, setAddress] = (0, react_1.useState)('');
|
|
8
|
+
const [hasError, setHasError] = (0, react_1.useState)(false);
|
|
9
|
+
return {
|
|
10
|
+
address,
|
|
11
|
+
close: () => {
|
|
12
|
+
setAddress('');
|
|
13
|
+
setHasError(false);
|
|
14
|
+
onClose();
|
|
15
|
+
},
|
|
16
|
+
hasError,
|
|
17
|
+
isSubmitDisabled: address.trim() === '',
|
|
18
|
+
setAddress: (next) => {
|
|
19
|
+
setAddress(next);
|
|
20
|
+
setHasError(false);
|
|
21
|
+
},
|
|
22
|
+
submit: (event) => {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
event.stopPropagation();
|
|
25
|
+
const href = ps_portable_text_editor_service_1.PsPortableTextEditorService.normalizeLink(address);
|
|
26
|
+
if (href === null) {
|
|
27
|
+
setHasError(true);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
setAddress('');
|
|
31
|
+
setHasError(false);
|
|
32
|
+
onSubmit(href);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
exports.usePsPortableTextLinkForm = usePsPortableTextLinkForm;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const usePsPortableTextToolbar: () => {
|
|
2
|
+
active: {
|
|
3
|
+
bold: boolean;
|
|
4
|
+
bulletedList: boolean;
|
|
5
|
+
code: boolean;
|
|
6
|
+
italic: boolean;
|
|
7
|
+
link: boolean;
|
|
8
|
+
numberedList: boolean;
|
|
9
|
+
quote: boolean;
|
|
10
|
+
underline: boolean;
|
|
11
|
+
};
|
|
12
|
+
addLink: (href: string) => void;
|
|
13
|
+
focus: () => void;
|
|
14
|
+
removeLink: () => void;
|
|
15
|
+
toggleDecorator: (decorator: string) => void;
|
|
16
|
+
toggleListItem: (listItem: string) => void;
|
|
17
|
+
toggleQuote: () => void;
|
|
18
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsPortableTextToolbar = void 0;
|
|
4
|
+
const editor_1 = require("@portabletext/editor");
|
|
5
|
+
const selectors_1 = require("@portabletext/editor/selectors");
|
|
6
|
+
const ps_portable_text_core_config_1 = require("../constants/ps-portable-text-core-config");
|
|
7
|
+
const usePsPortableTextToolbar = () => {
|
|
8
|
+
const editor = (0, editor_1.useEditor)();
|
|
9
|
+
const isBold = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveDecorator)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.bold));
|
|
10
|
+
const isItalic = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveDecorator)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.italic));
|
|
11
|
+
const isUnderline = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveDecorator)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.underline));
|
|
12
|
+
const isCode = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveDecorator)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.decorators.code));
|
|
13
|
+
const isBulletedList = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveListItem)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.listItems.bullet));
|
|
14
|
+
const isNumberedList = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveListItem)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.listItems.number));
|
|
15
|
+
const isQuote = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveStyle)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.styles.blockquote));
|
|
16
|
+
const isLink = (0, editor_1.useEditorSelector)(editor, (0, selectors_1.isActiveAnnotation)(ps_portable_text_core_config_1.PsPortableTextCoreConfig.linkAnnotation));
|
|
17
|
+
const focus = () => editor.send({ type: 'focus' });
|
|
18
|
+
return {
|
|
19
|
+
active: {
|
|
20
|
+
bold: isBold,
|
|
21
|
+
bulletedList: isBulletedList,
|
|
22
|
+
code: isCode,
|
|
23
|
+
italic: isItalic,
|
|
24
|
+
link: isLink,
|
|
25
|
+
numberedList: isNumberedList,
|
|
26
|
+
quote: isQuote,
|
|
27
|
+
underline: isUnderline,
|
|
28
|
+
},
|
|
29
|
+
addLink: (href) => {
|
|
30
|
+
editor.send({
|
|
31
|
+
type: 'annotation.add',
|
|
32
|
+
annotation: { name: ps_portable_text_core_config_1.PsPortableTextCoreConfig.linkAnnotation, value: { href } },
|
|
33
|
+
});
|
|
34
|
+
focus();
|
|
35
|
+
},
|
|
36
|
+
focus,
|
|
37
|
+
removeLink: () => {
|
|
38
|
+
editor.send({
|
|
39
|
+
type: 'annotation.remove',
|
|
40
|
+
annotation: { name: ps_portable_text_core_config_1.PsPortableTextCoreConfig.linkAnnotation },
|
|
41
|
+
});
|
|
42
|
+
focus();
|
|
43
|
+
},
|
|
44
|
+
toggleDecorator: (decorator) => {
|
|
45
|
+
editor.send({ type: 'decorator.toggle', decorator });
|
|
46
|
+
focus();
|
|
47
|
+
},
|
|
48
|
+
toggleListItem: (listItem) => {
|
|
49
|
+
editor.send({ type: 'list item.toggle', listItem });
|
|
50
|
+
focus();
|
|
51
|
+
},
|
|
52
|
+
toggleQuote: () => {
|
|
53
|
+
editor.send({ type: 'style.toggle', style: ps_portable_text_core_config_1.PsPortableTextCoreConfig.styles.blockquote });
|
|
54
|
+
focus();
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
exports.usePsPortableTextToolbar = usePsPortableTextToolbar;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './components/ps-portable-text-core';
|
|
2
|
+
export * from './constants/ps-portable-text-core-config';
|
|
3
|
+
export * from './hooks/use-ps-portable-text-link-form';
|
|
4
|
+
export * from './hooks/use-ps-portable-text-toolbar';
|
|
5
|
+
export * from './interfaces/ps-portable-text-editor-labels';
|
|
6
|
+
export * from './services/ps-portable-text-editor-service';
|
|
7
|
+
export type { BlockAnnotationRenderProps, BlockDecoratorRenderProps, BlockRenderProps, PortableTextBlock, } from '@portabletext/editor';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./components/ps-portable-text-core"), exports);
|
|
18
|
+
__exportStar(require("./constants/ps-portable-text-core-config"), exports);
|
|
19
|
+
__exportStar(require("./hooks/use-ps-portable-text-link-form"), exports);
|
|
20
|
+
__exportStar(require("./hooks/use-ps-portable-text-toolbar"), exports);
|
|
21
|
+
__exportStar(require("./interfaces/ps-portable-text-editor-labels"), exports);
|
|
22
|
+
__exportStar(require("./services/ps-portable-text-editor-service"), exports);
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsPortableTextEditorService = void 0;
|
|
4
4
|
const ps_models_1 = require("@pallasoft/ps-models");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const ps_text_format_service_1 = require("../../services/ps-text-format-service");
|
|
6
|
+
const ps_portable_text_core_config_1 = require("../constants/ps-portable-text-core-config");
|
|
7
7
|
class PsPortableTextEditorService {
|
|
8
8
|
static normalizeLink(raw) {
|
|
9
9
|
const trimmed = raw.trim();
|
|
10
10
|
if (trimmed === '')
|
|
11
11
|
return null;
|
|
12
|
-
const candidate =
|
|
13
|
-
? `${
|
|
14
|
-
: trimmed.toLowerCase().startsWith(
|
|
12
|
+
const candidate = ps_portable_text_core_config_1.PsPortableTextCoreConfig.emailPattern.test(trimmed)
|
|
13
|
+
? `${ps_portable_text_core_config_1.PsPortableTextCoreConfig.mailtoPrefix}${trimmed}`
|
|
14
|
+
: trimmed.toLowerCase().startsWith(ps_portable_text_core_config_1.PsPortableTextCoreConfig.mailtoPrefix)
|
|
15
15
|
? trimmed
|
|
16
16
|
: ps_text_format_service_1.PsTextFormatService.url(trimmed);
|
|
17
17
|
try {
|
|
@@ -25,7 +25,7 @@ class PsPortableTextEditorService {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
static minHeight(minRows) {
|
|
28
|
-
return `${minRows *
|
|
28
|
+
return `${minRows * ps_portable_text_core_config_1.PsPortableTextCoreConfig.lineHeightRem}rem`;
|
|
29
29
|
}
|
|
30
30
|
static isSame(first, second) {
|
|
31
31
|
return JSON.stringify(first) === JSON.stringify(second);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pallasoft/ps-input-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
"module": "./dist/esm/form/index.js",
|
|
19
19
|
"require": "./dist/form/index.js",
|
|
20
20
|
"default": "./dist/form/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./portable-text-core": {
|
|
23
|
+
"types": "./dist/portable-text-core/index.d.ts",
|
|
24
|
+
"module": "./dist/esm/portable-text-core/index.js",
|
|
25
|
+
"require": "./dist/portable-text-core/index.js",
|
|
26
|
+
"default": "./dist/portable-text-core/index.js"
|
|
21
27
|
}
|
|
22
28
|
},
|
|
23
29
|
"files": [
|
|
@@ -102,6 +108,9 @@
|
|
|
102
108
|
"*": {
|
|
103
109
|
"form": [
|
|
104
110
|
"./dist/form/index.d.ts"
|
|
111
|
+
],
|
|
112
|
+
"portable-text-core": [
|
|
113
|
+
"./dist/portable-text-core/index.d.ts"
|
|
105
114
|
]
|
|
106
115
|
}
|
|
107
116
|
},
|
/package/dist/esm/{interfaces → portable-text-core/interfaces}/ps-portable-text-editor-labels.js
RENAMED
|
File without changes
|
/package/dist/{interfaces → portable-text-core/interfaces}/ps-portable-text-editor-labels.d.ts
RENAMED
|
File without changes
|
/package/dist/{interfaces → portable-text-core/interfaces}/ps-portable-text-editor-labels.js
RENAMED
|
File without changes
|
|
File without changes
|