@pallasoft/ps-input-components 2.2.0 → 3.1.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.
Files changed (86) hide show
  1. package/dist/components/ps-combobox-input.d.ts +3 -1
  2. package/dist/components/ps-combobox-input.js +2 -2
  3. package/dist/components/ps-portable-text-editor.d.ts +1 -1
  4. package/dist/components/ps-portable-text-editor.js +1 -2
  5. package/dist/components/ps-portable-text-link-dialog.d.ts +2 -1
  6. package/dist/components/ps-portable-text-link-dialog.js +2 -2
  7. package/dist/components/ps-portable-text-toolbar.js +2 -7
  8. package/dist/constants/ps-portable-text-editor-config.d.ts +8 -0
  9. package/dist/constants/ps-portable-text-editor-config.js +8 -0
  10. package/dist/esm/components/ps-combobox-input.js +2 -2
  11. package/dist/esm/components/ps-portable-text-editor.js +1 -2
  12. package/dist/esm/components/ps-portable-text-link-dialog.js +2 -2
  13. package/dist/esm/components/ps-portable-text-toolbar.js +2 -7
  14. package/dist/esm/constants/ps-portable-text-editor-config.js +8 -0
  15. package/dist/esm/portable-text-core/components/ps-portable-text-core-editor-stub.js +2 -0
  16. package/dist/esm/portable-text-core/components/ps-portable-text-core-editor.js +64 -0
  17. package/dist/esm/portable-text-core/components/ps-portable-text-core.js +5 -26
  18. package/dist/esm/portable-text-core/constants/ps-portable-text-core-config.js +29 -9
  19. package/dist/esm/portable-text-core/contexts/ps-portable-text-core-context.js +9 -0
  20. package/dist/esm/portable-text-core/hooks/use-ps-portable-text-link-form.js +11 -5
  21. package/dist/esm/portable-text-core/hooks/use-ps-portable-text-toolbar.js +17 -49
  22. package/dist/esm/portable-text-core/index.js +4 -0
  23. package/dist/esm/portable-text-core/interfaces/ps-portable-text-active-formats.js +1 -0
  24. package/dist/esm/portable-text-core/interfaces/ps-portable-text-block-draft.js +1 -0
  25. package/dist/esm/portable-text-core/interfaces/ps-portable-text-core-class-names.js +1 -0
  26. package/dist/esm/portable-text-core/interfaces/ps-portable-text-core-context-data.js +1 -0
  27. package/dist/esm/portable-text-core/interfaces/ps-portable-text-core-props.js +1 -0
  28. package/dist/esm/portable-text-core/interfaces/ps-portable-text-extension-options.js +1 -0
  29. package/dist/esm/portable-text-core/interfaces/ps-portable-text-span-draft.js +1 -0
  30. package/dist/esm/portable-text-core/interfaces/ps-portable-text-toolbar-state.js +1 -0
  31. package/dist/esm/portable-text-core/services/ps-portable-text-command-service.js +73 -0
  32. package/dist/esm/portable-text-core/services/ps-portable-text-converter-service.js +196 -0
  33. package/dist/esm/portable-text-core/services/ps-portable-text-editor-service.js +4 -0
  34. package/dist/esm/portable-text-core/services/ps-portable-text-extension-service.js +89 -0
  35. package/dist/esm/portable-text-engine/index.js +1 -0
  36. package/dist/esm/portable-text-engine/worker.js +1 -0
  37. package/dist/portable-text/index.d.ts +1 -1
  38. package/dist/portable-text-core/components/ps-portable-text-core-editor-stub.d.ts +2 -0
  39. package/dist/portable-text-core/components/ps-portable-text-core-editor-stub.js +6 -0
  40. package/dist/portable-text-core/components/ps-portable-text-core-editor.d.ts +2 -0
  41. package/dist/portable-text-core/components/ps-portable-text-core-editor.js +68 -0
  42. package/dist/portable-text-core/components/ps-portable-text-core.d.ts +2 -17
  43. package/dist/portable-text-core/components/ps-portable-text-core.js +3 -24
  44. package/dist/portable-text-core/constants/ps-portable-text-core-config.d.ts +36 -19
  45. package/dist/portable-text-core/constants/ps-portable-text-core-config.js +29 -9
  46. package/dist/portable-text-core/contexts/ps-portable-text-core-context.d.ts +2 -0
  47. package/dist/portable-text-core/contexts/ps-portable-text-core-context.js +12 -0
  48. package/dist/portable-text-core/hooks/use-ps-portable-text-link-form.d.ts +3 -1
  49. package/dist/portable-text-core/hooks/use-ps-portable-text-link-form.js +10 -4
  50. package/dist/portable-text-core/hooks/use-ps-portable-text-toolbar.d.ts +5 -10
  51. package/dist/portable-text-core/hooks/use-ps-portable-text-toolbar.js +17 -49
  52. package/dist/portable-text-core/index.d.ts +5 -1
  53. package/dist/portable-text-core/index.js +4 -0
  54. package/dist/portable-text-core/interfaces/ps-portable-text-active-formats.d.ts +10 -0
  55. package/dist/portable-text-core/interfaces/ps-portable-text-active-formats.js +2 -0
  56. package/dist/portable-text-core/interfaces/ps-portable-text-block-draft.d.ts +7 -0
  57. package/dist/portable-text-core/interfaces/ps-portable-text-block-draft.js +2 -0
  58. package/dist/portable-text-core/interfaces/ps-portable-text-core-class-names.d.ts +8 -0
  59. package/dist/portable-text-core/interfaces/ps-portable-text-core-class-names.js +2 -0
  60. package/dist/portable-text-core/interfaces/ps-portable-text-core-context-data.d.ts +9 -0
  61. package/dist/portable-text-core/interfaces/ps-portable-text-core-context-data.js +2 -0
  62. package/dist/portable-text-core/interfaces/ps-portable-text-core-props.d.ts +16 -0
  63. package/dist/portable-text-core/interfaces/ps-portable-text-core-props.js +2 -0
  64. package/dist/portable-text-core/interfaces/ps-portable-text-editor-labels.d.ts +1 -0
  65. package/dist/portable-text-core/interfaces/ps-portable-text-extension-options.d.ts +6 -0
  66. package/dist/portable-text-core/interfaces/ps-portable-text-extension-options.js +2 -0
  67. package/dist/portable-text-core/interfaces/ps-portable-text-span-draft.d.ts +4 -0
  68. package/dist/portable-text-core/interfaces/ps-portable-text-span-draft.js +2 -0
  69. package/dist/portable-text-core/interfaces/ps-portable-text-toolbar-state.d.ts +5 -0
  70. package/dist/portable-text-core/interfaces/ps-portable-text-toolbar-state.js +2 -0
  71. package/dist/portable-text-core/services/ps-portable-text-command-service.d.ts +14 -0
  72. package/dist/portable-text-core/services/ps-portable-text-command-service.js +77 -0
  73. package/dist/portable-text-core/services/ps-portable-text-converter-service.d.ts +18 -0
  74. package/dist/portable-text-core/services/ps-portable-text-converter-service.js +200 -0
  75. package/dist/portable-text-core/services/ps-portable-text-editor-service.d.ts +1 -0
  76. package/dist/portable-text-core/services/ps-portable-text-editor-service.js +4 -0
  77. package/dist/portable-text-core/services/ps-portable-text-extension-service.d.ts +8 -0
  78. package/dist/portable-text-core/services/ps-portable-text-extension-service.js +93 -0
  79. package/dist/portable-text-engine/index.d.ts +1 -0
  80. package/dist/portable-text-engine/index.js +17 -0
  81. package/dist/portable-text-engine/worker.d.ts +1 -0
  82. package/dist/portable-text-engine/worker.js +17 -0
  83. package/package.json +29 -5
  84. package/dist/constants/ps-portable-text-editor-renderers.d.ts +0 -6
  85. package/dist/constants/ps-portable-text-editor-renderers.js +0 -32
  86. package/dist/esm/constants/ps-portable-text-editor-renderers.js +0 -29
@@ -6,9 +6,11 @@ export interface PsComboboxInputProps<T> {
6
6
  getItemLabel: (item: T) => string;
7
7
  hideLabel?: boolean;
8
8
  inputValue?: string;
9
+ isDisabled?: boolean;
9
10
  isLoading?: boolean;
10
11
  items: T[];
11
12
  label?: string;
13
+ menuTrigger?: 'focus' | 'input' | 'manual';
12
14
  onInputChange?: (value: string) => void;
13
15
  onSelectionChange?: (item: T | null) => void;
14
16
  placeholder?: string;
@@ -16,4 +18,4 @@ export interface PsComboboxInputProps<T> {
16
18
  required?: boolean;
17
19
  selectedKey?: string | null;
18
20
  }
19
- export declare const PsComboboxInput: <T>({ className, emptyText, getItemKey, getItemLabel, hideLabel, inputValue, isLoading, items, label, onInputChange, onSelectionChange, placeholder, renderItem, required, selectedKey, }: PsComboboxInputProps<T>) => import("react").JSX.Element;
21
+ export declare const PsComboboxInput: <T>({ className, emptyText, getItemKey, getItemLabel, hideLabel, inputValue, isDisabled, isLoading, items, label, menuTrigger, onInputChange, onSelectionChange, placeholder, renderItem, required, selectedKey, }: PsComboboxInputProps<T>) => import("react").JSX.Element;
@@ -7,8 +7,8 @@ const ps_design_system_1 = require("@pallasoft/ps-design-system");
7
7
  const react_aria_components_1 = require("react-aria-components");
8
8
  const ps_combobox_input_service_1 = require("../services/ps-combobox-input-service");
9
9
  const ps_required_mark_1 = require("./ps-required-mark");
10
- const PsComboboxInput = ({ className, emptyText, getItemKey, getItemLabel, hideLabel = false, inputValue, isLoading = false, items, label, onInputChange, onSelectionChange, placeholder, renderItem, required, selectedKey, }) => {
10
+ const PsComboboxInput = ({ className, emptyText, getItemKey, getItemLabel, hideLabel = false, inputValue, isDisabled = false, isLoading = false, items, label, menuTrigger = 'input', onInputChange, onSelectionChange, placeholder, renderItem, required, selectedKey, }) => {
11
11
  const itemsByKey = new Map(items.map((item) => [getItemKey(item), item]));
12
- return ((0, jsx_runtime_1.jsxs)(react_aria_components_1.ComboBox, { allowsEmptyCollection: true, "aria-label": hideLabel ? label : undefined, className: ps_design_system_1.PallasoftClassNameService.merge('block', className), defaultFilter: () => true, inputValue: inputValue, items: items, onInputChange: onInputChange, selectedKey: selectedKey, onSelectionChange: (key) => onSelectionChange?.(key === null ? null : ps_combobox_input_service_1.PsComboboxInputService.getItem(itemsByKey, key)), children: [label && !hideLabel ? ((0, jsx_runtime_1.jsxs)(react_aria_components_1.Label, { className: 'ps-input-label', children: [label, (0, jsx_runtime_1.jsx)(ps_required_mark_1.PsRequiredMark, { isRequired: required })] })) : null, (0, jsx_runtime_1.jsx)("span", { className: hideLabel || !label ? 'relative block' : 'relative mt-2 block', children: (0, jsx_runtime_1.jsx)(react_aria_components_1.Input, { className: 'ps-input-control ps-focus-ring w-full text-sm font-semibold', placeholder: placeholder }) }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Popover, { className: 'w-[--trigger-width] rounded-2xl border border-line bg-white/95 p-1.5 shadow-lg dark:border-white/10 dark:bg-slate-900/95', children: isLoading ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex items-center justify-center py-4', children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { label: 'Loading results', size: 'small' }) })) : ((0, jsx_runtime_1.jsx)(react_aria_components_1.ListBox, { className: 'max-h-72 overflow-y-auto outline-none', items: items, renderEmptyState: () => emptyText ? (0, jsx_runtime_1.jsx)("div", { className: 'px-3 py-2 text-sm text-ink/55', children: emptyText }) : null, children: (item) => ((0, jsx_runtime_1.jsx)(react_aria_components_1.ListBoxItem, { className: 'ps-focus-ring cursor-pointer rounded-ps-control px-3 py-2 text-sm font-semibold text-ink selected:bg-primary-soft selected:text-primary hover:bg-slate-50 dark:hover:bg-white/6', id: getItemKey(item), textValue: getItemLabel(item), children: renderItem ? renderItem(item) : getItemLabel(item) })) })) })] }));
12
+ return ((0, jsx_runtime_1.jsxs)(react_aria_components_1.ComboBox, { allowsEmptyCollection: true, "aria-label": hideLabel ? label : undefined, className: ps_design_system_1.PallasoftClassNameService.merge('block', className), defaultFilter: () => true, inputValue: inputValue, isDisabled: isDisabled, items: items, menuTrigger: menuTrigger, onInputChange: onInputChange, selectedKey: selectedKey, onSelectionChange: (key) => onSelectionChange?.(key === null ? null : ps_combobox_input_service_1.PsComboboxInputService.getItem(itemsByKey, key)), children: [label && !hideLabel ? ((0, jsx_runtime_1.jsxs)(react_aria_components_1.Label, { className: 'ps-input-label', children: [label, (0, jsx_runtime_1.jsx)(ps_required_mark_1.PsRequiredMark, { isRequired: required })] })) : null, (0, jsx_runtime_1.jsx)("span", { className: hideLabel || !label ? 'relative block' : 'relative mt-2 block', children: (0, jsx_runtime_1.jsx)(react_aria_components_1.Input, { className: 'ps-input-control ps-focus-ring w-full text-sm font-semibold', placeholder: placeholder }) }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Popover, { className: 'w-[--trigger-width] rounded-2xl border border-line bg-white/95 p-1.5 shadow-lg dark:border-white/10 dark:bg-slate-900/95', children: isLoading && items.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex items-center justify-center py-4', children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { label: 'Loading results', size: 'small' }) })) : ((0, jsx_runtime_1.jsx)(react_aria_components_1.ListBox, { "aria-busy": isLoading, className: 'max-h-72 overflow-y-auto outline-none', items: items, renderEmptyState: () => emptyText ? (0, jsx_runtime_1.jsx)("div", { className: 'px-3 py-2 text-sm text-ink/55', children: emptyText }) : null, children: (item) => ((0, jsx_runtime_1.jsx)(react_aria_components_1.ListBoxItem, { className: 'ps-focus-ring cursor-pointer rounded-ps-control px-3 py-2 text-sm font-semibold text-ink selected:bg-primary-soft selected:text-primary hover:bg-slate-50 dark:hover:bg-white/6', id: getItemKey(item), textValue: getItemLabel(item), children: renderItem ? renderItem(item) : getItemLabel(item) })) })) })] }));
13
13
  };
14
14
  exports.PsComboboxInput = PsComboboxInput;
@@ -1,4 +1,4 @@
1
- import type { PortableTextBlock } from '@portabletext/editor';
1
+ import type { PortableTextBlock } from '@portabletext/schema';
2
2
  import type { PsPortableTextEditorLabels } from '../portable-text-core/interfaces/ps-portable-text-editor-labels';
3
3
  export interface PsPortableTextEditorProps {
4
4
  className?: string;
@@ -4,10 +4,9 @@ exports.PsPortableTextEditor = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
6
  const ps_portable_text_editor_config_1 = require("../constants/ps-portable-text-editor-config");
7
- const ps_portable_text_editor_renderers_1 = require("../constants/ps-portable-text-editor-renderers");
8
7
  const ps_portable_text_core_1 = require("../portable-text-core/components/ps-portable-text-core");
9
8
  const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
10
9
  const ps_portable_text_editor_service_1 = require("../portable-text-core/services/ps-portable-text-editor-service");
11
10
  const ps_portable_text_toolbar_1 = require("./ps-portable-text-toolbar");
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 }));
11
+ 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, { className: ps_design_system_1.PallasoftClassNameService.merge(ps_portable_text_editor_config_1.PsPortableTextEditorConfig.classNames.root, className), classNames: ps_portable_text_editor_config_1.PsPortableTextEditorConfig.contentClassNames, 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) } })] }), label: labels.editor, minRows: minRows, onChange: onChange, placeholder: placeholder, toolbar: (0, jsx_runtime_1.jsx)(ps_portable_text_toolbar_1.PsPortableTextToolbar, { disabled: disabled, labels: labels }), value: value }));
13
12
  exports.PsPortableTextEditor = PsPortableTextEditor;
@@ -1,8 +1,9 @@
1
1
  import type { PsPortableTextEditorLabels } from '../portable-text-core/interfaces/ps-portable-text-editor-labels';
2
2
  export interface PsPortableTextLinkDialogProps {
3
+ href?: string;
3
4
  isOpen: boolean;
4
5
  labels: PsPortableTextEditorLabels;
5
6
  onClose: () => void;
6
7
  onSubmit: (href: string) => void;
7
8
  }
8
- export declare const PsPortableTextLinkDialog: ({ isOpen, labels, onClose, onSubmit, }: PsPortableTextLinkDialogProps) => import("react").JSX.Element;
9
+ export declare const PsPortableTextLinkDialog: ({ href, isOpen, labels, onClose, onSubmit, }: PsPortableTextLinkDialogProps) => import("react").JSX.Element;
@@ -6,8 +6,8 @@ const ps_popovers_1 = require("@pallasoft/ps-popovers");
6
6
  const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
7
7
  const use_ps_portable_text_link_form_1 = require("../portable-text-core/hooks/use-ps-portable-text-link-form");
8
8
  const ps_url_input_1 = require("./ps-url-input");
9
- const PsPortableTextLinkDialog = ({ isOpen, labels, onClose, onSubmit, }) => {
10
- const form = (0, use_ps_portable_text_link_form_1.usePsPortableTextLinkForm)({ onClose, onSubmit });
9
+ const PsPortableTextLinkDialog = ({ href, isOpen, labels, onClose, onSubmit, }) => {
10
+ const form = (0, use_ps_portable_text_link_form_1.usePsPortableTextLinkForm)({ href, isOpen, onClose, onSubmit });
11
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 }) }));
12
12
  };
13
13
  exports.PsPortableTextLinkDialog = PsPortableTextLinkDialog;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsPortableTextToolbar = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_1 = require("react");
6
5
  const ps_portable_text_editor_config_1 = require("../constants/ps-portable-text-editor-config");
7
6
  const ps_portable_text_core_config_1 = require("../portable-text-core/constants/ps-portable-text-core-config");
8
7
  const use_ps_portable_text_toolbar_1 = require("../portable-text-core/hooks/use-ps-portable-text-toolbar");
@@ -10,13 +9,9 @@ const ps_portable_text_link_dialog_1 = require("./ps-portable-text-link-dialog")
10
9
  const ps_portable_text_toolbar_button_1 = require("./ps-portable-text-toolbar-button");
11
10
  const PsPortableTextToolbar = ({ disabled, labels }) => {
12
11
  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) => {
12
+ 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() : toolbar.openLink()), children: (0, jsx_runtime_1.jsx)("span", { className: 'underline', children: labels.link }) }), (0, jsx_runtime_1.jsx)(ps_portable_text_link_dialog_1.PsPortableTextLinkDialog, { href: toolbar.linkHref, isOpen: toolbar.isLinkOpen, labels: labels, onClose: toolbar.closeLink, onSubmit: (href) => {
13
+ toolbar.closeLink();
18
14
  toolbar.addLink(href);
19
- setIsLinkOpen(false);
20
15
  } }), (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" })] }));
21
16
  };
22
17
  exports.PsPortableTextToolbar = PsPortableTextToolbar;
@@ -7,4 +7,12 @@ export declare const PsPortableTextEditorConfig: {
7
7
  readonly buttonActive: "bg-primary text-white hover:bg-primary hover:text-white";
8
8
  readonly separator: "mx-1 h-5 w-px bg-line";
9
9
  };
10
+ readonly contentClassNames: {
11
+ readonly blockquote: "border-l-2 border-primary pl-3 text-ink/80";
12
+ readonly bulletList: "list-disc pl-6";
13
+ readonly code: "rounded bg-ink/10 px-1 font-mono text-[0.9em]";
14
+ readonly link: "text-primary underline";
15
+ readonly orderedList: "list-decimal pl-6";
16
+ readonly placeholder: "before:pointer-events-none before:float-left before:h-0 before:text-ink/45 before:content-[attr(data-placeholder)]";
17
+ };
10
18
  };
@@ -10,4 +10,12 @@ exports.PsPortableTextEditorConfig = {
10
10
  buttonActive: 'bg-primary text-white hover:bg-primary hover:text-white',
11
11
  separator: 'mx-1 h-5 w-px bg-line',
12
12
  },
13
+ contentClassNames: {
14
+ blockquote: 'border-l-2 border-primary pl-3 text-ink/80',
15
+ bulletList: 'list-disc pl-6',
16
+ code: 'rounded bg-ink/10 px-1 font-mono text-[0.9em]',
17
+ link: 'text-primary underline',
18
+ orderedList: 'list-decimal pl-6',
19
+ placeholder: 'before:pointer-events-none before:float-left before:h-0 before:text-ink/45 before:content-[attr(data-placeholder)]',
20
+ },
13
21
  };
@@ -4,7 +4,7 @@ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
4
  import { ComboBox, Input, Label, ListBox, ListBoxItem, Popover } from 'react-aria-components';
5
5
  import { PsComboboxInputService } from '../services/ps-combobox-input-service';
6
6
  import { PsRequiredMark } from './ps-required-mark';
7
- export const PsComboboxInput = ({ className, emptyText, getItemKey, getItemLabel, hideLabel = false, inputValue, isLoading = false, items, label, onInputChange, onSelectionChange, placeholder, renderItem, required, selectedKey, }) => {
7
+ export const PsComboboxInput = ({ className, emptyText, getItemKey, getItemLabel, hideLabel = false, inputValue, isDisabled = false, isLoading = false, items, label, menuTrigger = 'input', onInputChange, onSelectionChange, placeholder, renderItem, required, selectedKey, }) => {
8
8
  const itemsByKey = new Map(items.map((item) => [getItemKey(item), item]));
9
- return (_jsxs(ComboBox, { allowsEmptyCollection: true, "aria-label": hideLabel ? label : undefined, className: PallasoftClassNameService.merge('block', className), defaultFilter: () => true, inputValue: inputValue, items: items, onInputChange: onInputChange, selectedKey: selectedKey, onSelectionChange: (key) => onSelectionChange?.(key === null ? null : PsComboboxInputService.getItem(itemsByKey, key)), children: [label && !hideLabel ? (_jsxs(Label, { className: 'ps-input-label', children: [label, _jsx(PsRequiredMark, { isRequired: required })] })) : null, _jsx("span", { className: hideLabel || !label ? 'relative block' : 'relative mt-2 block', children: _jsx(Input, { className: 'ps-input-control ps-focus-ring w-full text-sm font-semibold', placeholder: placeholder }) }), _jsx(Popover, { className: 'w-[--trigger-width] rounded-2xl border border-line bg-white/95 p-1.5 shadow-lg dark:border-white/10 dark:bg-slate-900/95', children: isLoading ? (_jsx("div", { className: 'flex items-center justify-center py-4', children: _jsx(PsSpinner, { label: 'Loading results', size: 'small' }) })) : (_jsx(ListBox, { className: 'max-h-72 overflow-y-auto outline-none', items: items, renderEmptyState: () => emptyText ? _jsx("div", { className: 'px-3 py-2 text-sm text-ink/55', children: emptyText }) : null, children: (item) => (_jsx(ListBoxItem, { className: 'ps-focus-ring cursor-pointer rounded-ps-control px-3 py-2 text-sm font-semibold text-ink selected:bg-primary-soft selected:text-primary hover:bg-slate-50 dark:hover:bg-white/6', id: getItemKey(item), textValue: getItemLabel(item), children: renderItem ? renderItem(item) : getItemLabel(item) })) })) })] }));
9
+ return (_jsxs(ComboBox, { allowsEmptyCollection: true, "aria-label": hideLabel ? label : undefined, className: PallasoftClassNameService.merge('block', className), defaultFilter: () => true, inputValue: inputValue, isDisabled: isDisabled, items: items, menuTrigger: menuTrigger, onInputChange: onInputChange, selectedKey: selectedKey, onSelectionChange: (key) => onSelectionChange?.(key === null ? null : PsComboboxInputService.getItem(itemsByKey, key)), children: [label && !hideLabel ? (_jsxs(Label, { className: 'ps-input-label', children: [label, _jsx(PsRequiredMark, { isRequired: required })] })) : null, _jsx("span", { className: hideLabel || !label ? 'relative block' : 'relative mt-2 block', children: _jsx(Input, { className: 'ps-input-control ps-focus-ring w-full text-sm font-semibold', placeholder: placeholder }) }), _jsx(Popover, { className: 'w-[--trigger-width] rounded-2xl border border-line bg-white/95 p-1.5 shadow-lg dark:border-white/10 dark:bg-slate-900/95', children: isLoading && items.length === 0 ? (_jsx("div", { className: 'flex items-center justify-center py-4', children: _jsx(PsSpinner, { label: 'Loading results', size: 'small' }) })) : (_jsx(ListBox, { "aria-busy": isLoading, className: 'max-h-72 overflow-y-auto outline-none', items: items, renderEmptyState: () => emptyText ? _jsx("div", { className: 'px-3 py-2 text-sm text-ink/55', children: emptyText }) : null, children: (item) => (_jsx(ListBoxItem, { className: 'ps-focus-ring cursor-pointer rounded-ps-control px-3 py-2 text-sm font-semibold text-ink selected:bg-primary-soft selected:text-primary hover:bg-slate-50 dark:hover:bg-white/6', id: getItemKey(item), textValue: getItemLabel(item), children: renderItem ? renderItem(item) : getItemLabel(item) })) })) })] }));
10
10
  };
@@ -1,9 +1,8 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
3
  import { PsPortableTextEditorConfig } from '../constants/ps-portable-text-editor-config';
4
- import { PsPortableTextEditorRenderers } from '../constants/ps-portable-text-editor-renderers';
5
4
  import { PsPortableTextCore } from '../portable-text-core/components/ps-portable-text-core';
6
5
  import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
7
6
  import { PsPortableTextEditorService } from '../portable-text-core/services/ps-portable-text-editor-service';
8
7
  import { PsPortableTextToolbar } from './ps-portable-text-toolbar';
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 }));
8
+ export const PsPortableTextEditor = ({ className, disabled = false, labels = PsPortableTextCoreConfig.labels, minRows = 4, onChange, placeholder, value, }) => (_jsx(PsPortableTextCore, { className: PallasoftClassNameService.merge(PsPortableTextEditorConfig.classNames.root, className), classNames: PsPortableTextEditorConfig.contentClassNames, 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) } })] }), label: labels.editor, minRows: minRows, onChange: onChange, placeholder: placeholder, toolbar: _jsx(PsPortableTextToolbar, { disabled: disabled, labels: labels }), value: value }));
@@ -3,7 +3,7 @@ import { PsFormModal } from '@pallasoft/ps-popovers';
3
3
  import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
4
4
  import { usePsPortableTextLinkForm } from '../portable-text-core/hooks/use-ps-portable-text-link-form';
5
5
  import { PsUrlInput } from './ps-url-input';
6
- export const PsPortableTextLinkDialog = ({ isOpen, labels, onClose, onSubmit, }) => {
7
- const form = usePsPortableTextLinkForm({ onClose, onSubmit });
6
+ export const PsPortableTextLinkDialog = ({ href, isOpen, labels, onClose, onSubmit, }) => {
7
+ const form = usePsPortableTextLinkForm({ href, isOpen, onClose, onSubmit });
8
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 }) }));
9
9
  };
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
2
  import { PsPortableTextEditorConfig } from '../constants/ps-portable-text-editor-config';
4
3
  import { PsPortableTextCoreConfig } from '../portable-text-core/constants/ps-portable-text-core-config';
5
4
  import { usePsPortableTextToolbar } from '../portable-text-core/hooks/use-ps-portable-text-toolbar';
@@ -7,12 +6,8 @@ import { PsPortableTextLinkDialog } from './ps-portable-text-link-dialog';
7
6
  import { PsPortableTextToolbarButton } from './ps-portable-text-toolbar-button';
8
7
  export const PsPortableTextToolbar = ({ disabled, labels }) => {
9
8
  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) => {
9
+ 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() : toolbar.openLink()), children: _jsx("span", { className: 'underline', children: labels.link }) }), _jsx(PsPortableTextLinkDialog, { href: toolbar.linkHref, isOpen: toolbar.isLinkOpen, labels: labels, onClose: toolbar.closeLink, onSubmit: (href) => {
10
+ toolbar.closeLink();
15
11
  toolbar.addLink(href);
16
- setIsLinkOpen(false);
17
12
  } }), _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" })] }));
18
13
  };
@@ -7,4 +7,12 @@ export const PsPortableTextEditorConfig = {
7
7
  buttonActive: 'bg-primary text-white hover:bg-primary hover:text-white',
8
8
  separator: 'mx-1 h-5 w-px bg-line',
9
9
  },
10
+ contentClassNames: {
11
+ blockquote: 'border-l-2 border-primary pl-3 text-ink/80',
12
+ bulletList: 'list-disc pl-6',
13
+ code: 'rounded bg-ink/10 px-1 font-mono text-[0.9em]',
14
+ link: 'text-primary underline',
15
+ orderedList: 'list-decimal pl-6',
16
+ placeholder: 'before:pointer-events-none before:float-left before:h-0 before:text-ink/45 before:content-[attr(data-placeholder)]',
17
+ },
10
18
  };
@@ -0,0 +1,2 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export const PsPortableTextCoreEditor = ({ className, fallback }) => (_jsx("div", { className: className, children: fallback }));
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { EditorContent, useEditor, useEditorState } from '@tiptap/react';
4
+ import { PsPortableTextCoreContext } from '../contexts/ps-portable-text-core-context';
5
+ import { PsPortableTextCommandService } from '../services/ps-portable-text-command-service';
6
+ import { PsPortableTextConverterService } from '../services/ps-portable-text-converter-service';
7
+ import { PsPortableTextEditorService } from '../services/ps-portable-text-editor-service';
8
+ import { PsPortableTextExtensionService } from '../services/ps-portable-text-extension-service';
9
+ export const PsPortableTextCoreEditor = ({ className, classNames, disabled = false, editableClassName = '', label, minRows = 4, onChange, placeholder = '', toolbar, value, }) => {
10
+ const [isLinkOpen, setIsLinkOpen] = useState(false);
11
+ const lastValue = useRef(value);
12
+ const editor = useEditor({
13
+ content: PsPortableTextConverterService.toDocument(value),
14
+ editable: !disabled,
15
+ editorProps: {
16
+ attributes: {
17
+ ...(label === undefined ? {} : { 'aria-label': label }),
18
+ 'aria-multiline': 'true',
19
+ 'class': editableClassName,
20
+ 'role': 'textbox',
21
+ 'style': `min-height: ${PsPortableTextEditorService.minHeight(minRows)}`,
22
+ },
23
+ },
24
+ extensions: PsPortableTextExtensionService.extensions({
25
+ classNames,
26
+ onLinkShortcut: () => setIsLinkOpen(true),
27
+ placeholder,
28
+ }),
29
+ immediatelyRender: true,
30
+ }, []);
31
+ const state = useEditorState({
32
+ editor,
33
+ selector: ({ editor: current }) => PsPortableTextCommandService.state(current),
34
+ });
35
+ useEffect(() => {
36
+ editor.setEditable(!disabled, false);
37
+ }, [editor, disabled]);
38
+ useEffect(() => {
39
+ const update = () => {
40
+ const next = PsPortableTextConverterService.toBlocks(editor.getJSON());
41
+ lastValue.current = next;
42
+ onChange(next);
43
+ };
44
+ editor.on('update', update);
45
+ return () => {
46
+ editor.off('update', update);
47
+ };
48
+ }, [editor, onChange]);
49
+ useEffect(() => {
50
+ if (PsPortableTextEditorService.isSame(value, lastValue.current))
51
+ return;
52
+ lastValue.current = value;
53
+ editor.commands.setContent(PsPortableTextConverterService.toDocument(value), {
54
+ emitUpdate: false,
55
+ });
56
+ }, [editor, value]);
57
+ return (_jsx("div", { className: className, children: _jsxs(PsPortableTextCoreContext.Provider, { value: {
58
+ closeLink: () => setIsLinkOpen(false),
59
+ editor,
60
+ isLinkOpen,
61
+ openLink: () => setIsLinkOpen(true),
62
+ state,
63
+ }, children: [toolbar, _jsx(EditorContent, { editor: editor })] }) }));
64
+ };
@@ -1,31 +1,10 @@
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, }) => {
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { PsPortableTextCoreEditor } from '@pallasoft/ps-input-components/portable-text-engine';
4
+ export const PsPortableTextCore = (props) => {
8
5
  const [isMounted, setIsMounted] = useState(false);
9
- const [version, setVersion] = useState(0);
10
- const lastValue = useRef(value);
11
6
  useEffect(() => {
12
7
  setIsMounted(true);
13
8
  }, []);
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) }));
9
+ return isMounted ? (_jsx(PsPortableTextCoreEditor, { ...props })) : (_jsx("div", { className: props.className, children: props.fallback }));
31
10
  };
@@ -1,11 +1,13 @@
1
1
  import { Common } from '@pallasoft/ps-models';
2
- import { defineSchema } from '@portabletext/editor';
3
2
  const Models = Common.PortableTextConfig;
4
3
  export const PsPortableTextCoreConfig = {
5
4
  linkAnnotation: Models.linkType,
6
5
  mailtoPrefix: 'mailto:',
7
6
  emailPattern: /^[^\s@/:]+@[^\s@/:]+\.[^\s@/:]+$/,
8
7
  lineHeightRem: 1.5,
8
+ lineBreak: '\n',
9
+ defaultLinkProtocol: 'https',
10
+ keyPrefix: { block: 'b', link: 'l', span: 's' },
9
11
  decorators: {
10
12
  bold: Models.decorators[0],
11
13
  code: Models.decorators[3],
@@ -14,19 +16,37 @@ export const PsPortableTextCoreConfig = {
14
16
  },
15
17
  styles: { blockquote: Models.styles[1], normal: Models.styles[0] },
16
18
  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
- }),
19
+ decoratorMarks: {
20
+ [Models.decorators[0]]: 'bold',
21
+ [Models.decorators[1]]: 'italic',
22
+ [Models.decorators[2]]: 'underline',
23
+ [Models.decorators[3]]: 'code',
24
+ },
25
+ listNodes: {
26
+ [Models.listItems[0]]: 'bulletList',
27
+ [Models.listItems[1]]: 'orderedList',
28
+ },
29
+ nodes: {
30
+ blockquote: 'blockquote',
31
+ bulletList: 'bulletList',
32
+ document: 'doc',
33
+ hardBreak: 'hardBreak',
34
+ listItem: 'listItem',
35
+ orderedList: 'orderedList',
36
+ paragraph: 'paragraph',
37
+ text: 'text',
38
+ },
39
+ linkMark: 'link',
40
+ linkShortcutExtension: 'psLinkShortcut',
41
+ listItemContent: 'paragraph (bulletList | orderedList)*',
42
+ blockquoteContent: 'paragraph+',
43
+ shortcuts: { link: 'Mod-k', nest: 'Tab', unnest: 'Shift-Tab' },
25
44
  labels: {
26
45
  bold: 'Bold',
27
46
  bulletedList: 'Bulleted list',
28
47
  cancel: 'Cancel',
29
48
  code: 'Code',
49
+ editor: 'Text',
30
50
  invalidLink: 'Enter a web address or email address',
31
51
  italic: 'Italic',
32
52
  link: 'Link',
@@ -0,0 +1,9 @@
1
+ import { createContext } from 'react';
2
+ import { PsPortableTextCommandService } from '../services/ps-portable-text-command-service';
3
+ export const PsPortableTextCoreContext = createContext({
4
+ closeLink: () => undefined,
5
+ editor: null,
6
+ isLinkOpen: false,
7
+ openLink: () => undefined,
8
+ state: PsPortableTextCommandService.state(null),
9
+ });
@@ -1,8 +1,14 @@
1
- import { useState } from 'react';
1
+ import { useEffect, useState } from 'react';
2
2
  import { PsPortableTextEditorService } from '../services/ps-portable-text-editor-service';
3
- export const usePsPortableTextLinkForm = ({ onClose, onSubmit, }) => {
3
+ export const usePsPortableTextLinkForm = ({ href, isOpen = false, onClose, onSubmit, }) => {
4
4
  const [address, setAddress] = useState('');
5
5
  const [hasError, setHasError] = useState(false);
6
+ useEffect(() => {
7
+ if (!isOpen)
8
+ return;
9
+ setAddress(href === undefined ? '' : href);
10
+ setHasError(false);
11
+ }, [isOpen]);
6
12
  return {
7
13
  address,
8
14
  close: () => {
@@ -19,14 +25,14 @@ export const usePsPortableTextLinkForm = ({ onClose, onSubmit, }) => {
19
25
  submit: (event) => {
20
26
  event.preventDefault();
21
27
  event.stopPropagation();
22
- const href = PsPortableTextEditorService.normalizeLink(address);
23
- if (href === null) {
28
+ const normalized = PsPortableTextEditorService.normalizeLink(address);
29
+ if (normalized === null) {
24
30
  setHasError(true);
25
31
  return;
26
32
  }
27
33
  setAddress('');
28
34
  setHasError(false);
29
- onSubmit(href);
35
+ onSubmit(normalized);
30
36
  },
31
37
  };
32
38
  };
@@ -1,54 +1,22 @@
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';
1
+ import { useContext } from 'react';
2
+ import { PsPortableTextCoreContext } from '../contexts/ps-portable-text-core-context';
3
+ import { PsPortableTextCommandService } from '../services/ps-portable-text-command-service';
4
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' });
5
+ const { closeLink, editor, isLinkOpen, openLink, state } = useContext(PsPortableTextCoreContext);
15
6
  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();
7
+ active: state.active,
8
+ addLink: (href) => PsPortableTextCommandService.setLink(editor, href),
9
+ closeLink: () => {
10
+ closeLink();
11
+ PsPortableTextCommandService.focus(editor);
52
12
  },
13
+ focus: () => PsPortableTextCommandService.focus(editor),
14
+ isLinkOpen,
15
+ linkHref: state.linkHref,
16
+ openLink,
17
+ removeLink: () => PsPortableTextCommandService.removeLink(editor),
18
+ toggleDecorator: (decorator) => PsPortableTextCommandService.toggleDecorator(editor, decorator),
19
+ toggleListItem: (listItem) => PsPortableTextCommandService.toggleListItem(editor, listItem),
20
+ toggleQuote: () => PsPortableTextCommandService.toggleQuote(editor),
53
21
  };
54
22
  };
@@ -2,5 +2,9 @@ export * from './components/ps-portable-text-core';
2
2
  export * from './constants/ps-portable-text-core-config';
3
3
  export * from './hooks/use-ps-portable-text-link-form';
4
4
  export * from './hooks/use-ps-portable-text-toolbar';
5
+ export * from './interfaces/ps-portable-text-active-formats';
6
+ export * from './interfaces/ps-portable-text-core-class-names';
7
+ export * from './interfaces/ps-portable-text-core-props';
5
8
  export * from './interfaces/ps-portable-text-editor-labels';
9
+ export * from './services/ps-portable-text-converter-service';
6
10
  export * from './services/ps-portable-text-editor-service';