@manuscripts/body-editor 3.12.67 → 3.13.1
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/cjs/components/toolbar/type-selector/styles.js +2 -1
- package/dist/cjs/versions.js +1 -1
- package/dist/es/components/toolbar/type-selector/styles.js +2 -1
- package/dist/es/versions.js +1 -1
- package/dist/types/components/toolbar/type-selector/styles.d.ts +2 -2
- package/dist/types/lib/footnotes.d.ts +4 -2
- package/dist/types/lib/utils.d.ts +5 -4
- package/dist/types/versions.d.ts +1 -1
- package/package.json +37 -36
- package/{dist/es/lib/statistics.js → src/__mocks__/styleMock.ts} +2 -14
- package/src/assets.ts +20 -0
- package/src/classes/collabProvider.ts +38 -0
- package/src/clipboard.ts +54 -0
- package/src/commands.ts +2109 -0
- package/src/components/ChangeHandlingForm.tsx +54 -0
- package/src/components/LanguageDropdown/index.tsx +343 -0
- package/src/components/affiliations/AffiliationForm.tsx +238 -0
- package/src/components/affiliations/AffiliationItem.tsx +151 -0
- package/src/components/affiliations/AffiliationList.tsx +71 -0
- package/src/components/affiliations/AffiliationsModal.tsx +676 -0
- package/src/components/affiliations/AffiliationsPanel.tsx +76 -0
- package/src/components/affiliations/CreateAffiliationModal.tsx +135 -0
- package/src/components/authors/AuthorDetailsForm.tsx +438 -0
- package/src/components/authors/AuthorList.tsx +81 -0
- package/src/components/authors/AuthorsModal.tsx +642 -0
- package/src/components/authors/AuthorsPanel.tsx +69 -0
- package/src/components/authors/CreateAuthorModal.tsx +172 -0
- package/src/components/authors/CreditDrawer.tsx +71 -0
- package/src/components/authors/DraggableAuthor.tsx +228 -0
- package/src/components/authors/useManageAffiliations.tsx +77 -0
- package/src/components/authors/useManageCredit.tsx +63 -0
- package/src/components/authors-affiliations/AuthorsAndAffiliationsModals.tsx +170 -0
- package/src/components/authors-affiliations/GenericPanel.tsx +164 -0
- package/src/components/authors-affiliations/ModalTabs.tsx +100 -0
- package/src/components/awards/AwardForm.tsx +338 -0
- package/src/components/awards/AwardModal.tsx +120 -0
- package/src/components/awards/DeleteAwardDiaolog.tsx +52 -0
- package/src/components/dialog/ConfirmationDialog.tsx +131 -0
- package/src/components/form/CreateModalStyles.tsx +37 -0
- package/src/components/form/FormFooter.tsx +54 -0
- package/src/components/form/FormPlaceholder.tsx +71 -0
- package/src/components/form/ModalFormActions.tsx +88 -0
- package/src/components/form/UnsavedLabel.tsx +49 -0
- package/src/components/hooks/use-debounce.ts +56 -0
- package/src/components/hooks/useAffiliationShowsErrorIndicator.ts +51 -0
- package/src/components/hooks/useAuthorShowsErrorIndicator.ts +73 -0
- package/src/components/keyboard-shortcuts-modal/FormattedShortcut.tsx +56 -0
- package/src/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.tsx +283 -0
- package/src/components/keyboard-shortcuts-modal/keyboard-shortcuts.ts +304 -0
- package/src/components/keywords/AddKeywordInline.tsx +284 -0
- package/src/components/keywords/DeleteKeywordDialog.tsx +52 -0
- package/src/components/modal-drawer/GenericDrawer.tsx +65 -0
- package/src/components/modal-drawer/GenericDrawerGroup.tsx +125 -0
- package/src/components/outline/DraggableTree.tsx +438 -0
- package/src/components/outline/ManuscriptOutline.tsx +127 -0
- package/src/components/outline/Outline.tsx +141 -0
- package/src/components/references/BibliographyItemSource.ts +80 -0
- package/src/components/references/CitationEditor.tsx +301 -0
- package/src/components/references/CitationViewer.tsx +62 -0
- package/src/components/references/ImportBibliographyForm.tsx +232 -0
- package/src/components/references/ImportBibliographyModal.tsx +159 -0
- package/src/components/references/ReferenceForm/PersonDropDown.tsx +93 -0
- package/src/components/references/ReferenceForm/ReferenceForm.tsx +701 -0
- package/src/components/references/ReferenceForm/config.ts +133 -0
- package/src/components/references/ReferenceForm/styled-components.ts +156 -0
- package/src/components/references/ReferenceLine.tsx +43 -0
- package/src/components/references/ReferenceSearch.tsx +163 -0
- package/src/components/references/ReferenceSearchResults.tsx +113 -0
- package/src/components/references/ReferenceSearchResultsPlaceholder.tsx +71 -0
- package/src/components/references/ReferenceSearchSection.tsx +124 -0
- package/src/components/references/ReferencesEditor.tsx +61 -0
- package/src/components/references/ReferencesModal.tsx +352 -0
- package/src/components/references/SearchInput.tsx +98 -0
- package/src/components/toolbar/InsertEmbedDialog.tsx +222 -0
- package/src/components/toolbar/InsertTableDialog.tsx +208 -0
- package/src/components/toolbar/ListMenuItem.tsx +203 -0
- package/src/components/toolbar/helpers.ts +547 -0
- package/src/components/toolbar/type-selector/OptionComponent.tsx +89 -0
- package/src/components/toolbar/type-selector/TypeSelector.tsx +157 -0
- package/src/components/toolbar/type-selector/styles.ts +79 -0
- package/src/components/views/CrossReferenceItems.tsx +199 -0
- package/src/components/views/DeleteEmbedDialog.tsx +83 -0
- package/src/components/views/DeleteFootnoteDialog.tsx +54 -0
- package/src/components/views/FigureDropdown.tsx +423 -0
- package/src/components/views/FootnotesSelector.tsx +255 -0
- package/src/components/views/InsertSpecialCharacter.tsx +245 -0
- package/src/components/views/LinkForm.tsx +220 -0
- package/src/components/views/TableCellContextMenu.tsx +241 -0
- package/src/configs/ManuscriptsEditor.ts +140 -0
- package/src/configs/editor-plugins.ts +119 -0
- package/src/configs/editor-views.ts +139 -0
- package/src/icons.ts +62 -0
- package/src/index.ts +65 -0
- package/src/keys/highlight.ts +65 -0
- package/src/keys/index.ts +33 -0
- package/src/keys/keyword.ts +69 -0
- package/src/keys/list.ts +156 -0
- package/src/keys/misc.ts +112 -0
- package/src/keys/title.ts +221 -0
- package/src/lib/__tests__/__fixtures__/doc.json +388 -0
- package/src/lib/__tests__/__snapshots__/paste.test.ts.snap +24 -0
- package/src/lib/__tests__/authors.test.ts +202 -0
- package/src/lib/__tests__/helpers.test.ts +46 -0
- package/src/lib/__tests__/paste.test.ts +57 -0
- package/src/lib/__tests__/plugins.test.ts +176 -0
- package/src/lib/__tests__/url.test.ts +31 -0
- package/src/lib/array-reducer.ts +68 -0
- package/src/lib/authors-and-affiliations.ts +81 -0
- package/src/lib/authors.ts +67 -0
- package/src/lib/capabilities.ts +55 -0
- package/src/lib/codemirror.ts +37 -0
- package/src/lib/comments.ts +123 -0
- package/src/lib/context-menu.ts +745 -0
- package/src/lib/copy.ts +30 -0
- package/src/lib/crossref.ts +90 -0
- package/src/lib/dnd.ts +29 -0
- package/src/lib/doc.ts +167 -0
- package/src/lib/dompurify.ts +28 -0
- package/src/lib/files.ts +180 -0
- package/src/lib/footnotes.ts +83 -0
- package/src/lib/get-media-type.ts +105 -0
- package/src/lib/helpers.ts +199 -0
- package/src/lib/hierarchy.ts +117 -0
- package/src/lib/languages.ts +37 -0
- package/src/lib/lists.ts +56 -0
- package/src/lib/math.ts +111 -0
- package/src/lib/media.ts +334 -0
- package/src/lib/navigation-utils.ts +235 -0
- package/src/lib/normalize.ts +70 -0
- package/src/lib/paste.ts +304 -0
- package/src/lib/placeholder.ts +32 -0
- package/src/lib/platform.ts +18 -0
- package/src/lib/plugins.ts +22 -0
- package/src/lib/popper.ts +180 -0
- package/src/lib/position-menu.ts +184 -0
- package/src/lib/references.ts +94 -0
- package/src/lib/template.ts +29 -0
- package/src/lib/track-changes-utils.ts +34 -0
- package/{dist/types/lib/statistics.d.ts → src/lib/url.ts} +11 -8
- package/src/lib/use-do-with-debounce.ts +39 -0
- package/src/lib/utils.ts +323 -0
- package/src/lib/view.ts +100 -0
- package/src/menus.tsx +684 -0
- package/src/node-type-icons.tsx +83 -0
- package/src/plugins/__tests__/__fixtures__/deleted-footnote.json +140 -0
- package/src/plugins/__tests__/__fixtures__/inserted-footnote.json +153 -0
- package/src/plugins/__tests__/footnotes.test.ts +67 -0
- package/src/plugins/accessibility_element.ts +219 -0
- package/src/plugins/add-subtitle.ts +102 -0
- package/src/plugins/affiliations.ts +136 -0
- package/src/plugins/alt-titles.ts +219 -0
- package/src/plugins/bibliography.ts +235 -0
- package/src/plugins/comments.ts +328 -0
- package/src/plugins/cross-references.ts +70 -0
- package/src/plugins/detect-inconsistency/detect-inconsistency-utils.ts +102 -0
- package/src/plugins/detect-inconsistency/index.ts +55 -0
- package/src/plugins/detect-inconsistency/validators.ts +283 -0
- package/src/plugins/doi.ts +66 -0
- package/src/plugins/editor-props.ts +39 -0
- package/src/plugins/elements.ts +60 -0
- package/src/plugins/footnotes.ts +298 -0
- package/src/plugins/link.ts +53 -0
- package/src/plugins/lock-body.ts +136 -0
- package/src/plugins/move-node.ts +55 -0
- package/src/plugins/objects.ts +125 -0
- package/src/plugins/paragraphs.ts +90 -0
- package/src/plugins/persist.ts +64 -0
- package/src/plugins/persistent-cursor.ts +66 -0
- package/src/plugins/placeholder.ts +219 -0
- package/src/plugins/prevent-empty.ts +50 -0
- package/src/plugins/search-replace/index.ts +157 -0
- package/src/plugins/search-replace/lib.ts +95 -0
- package/src/plugins/section_category.ts +212 -0
- package/src/plugins/section_title/autocompletion.ts +87 -0
- package/src/plugins/section_title/index.ts +105 -0
- package/src/plugins/selected-suggestion.ts +243 -0
- package/src/plugins/tables-cursor-fix.ts +143 -0
- package/src/plugins/translations.ts +263 -0
- package/src/rules.ts +60 -0
- package/src/selection.ts +112 -0
- package/src/testing/default-capabilities.ts +48 -0
- package/src/testing/default-editor-data.ts +73 -0
- package/src/testing/empty-editor-doc.json +62 -0
- package/src/testing/polyfill-dom.ts +43 -0
- package/src/testing/section-categories.json +161 -0
- package/src/testing/setup-editor.ts +55 -0
- package/src/tests.ts +42 -0
- package/src/toolbar.tsx +259 -0
- package/src/types/@citation-js/core.d.ts +16 -0
- package/src/types/@citation-js/index.d.ts +16 -0
- package/src/types/@citation-js/plugin-bibtex.d.ts +16 -0
- package/src/types/@citation-js/plugin-csl.d.ts +16 -0
- package/src/types/@citation-js/plugin-doi.d.ts +16 -0
- package/src/types/@citation-js/plugin-enw.d.ts +16 -0
- package/src/types/@citation-js/plugin-pubmed.d.ts +16 -0
- package/src/types/@citation-js/plugin-ris.d.ts +16 -0
- package/src/types/citeproc.d.ts +124 -0
- package/src/types/css.d.ts +16 -0
- package/src/types/json.d.ts +17 -0
- package/src/types/prosemirror-dev-tools.d.ts +16 -0
- package/src/types/prosemirror-dropcursor.d.ts +16 -0
- package/src/types/styled-components.d.ts +23 -0
- package/src/types.ts +49 -0
- package/src/useEditor.ts +244 -0
- package/src/versions.ts +3 -0
- package/src/views/ReactSubView.tsx +145 -0
- package/src/views/abstracts.ts +46 -0
- package/src/views/accessibility_element.ts +86 -0
- package/src/views/affiliations.ts +254 -0
- package/src/views/alt_title.ts +85 -0
- package/src/views/alt_titles_section.ts +97 -0
- package/src/views/attachment.ts +160 -0
- package/src/views/attachments.ts +72 -0
- package/src/views/author_notes.ts +96 -0
- package/src/views/award.ts +194 -0
- package/src/views/awards.ts +59 -0
- package/src/views/base_node_view.ts +85 -0
- package/src/views/bibliography_element.ts +313 -0
- package/src/views/block_view.ts +100 -0
- package/src/views/blockquote_element.ts +26 -0
- package/src/views/blockquote_element_editable.ts +24 -0
- package/src/views/box_element.ts +33 -0
- package/src/views/caption.ts +37 -0
- package/src/views/caption_title.ts +33 -0
- package/src/views/citation.ts +67 -0
- package/src/views/citation_editable.ts +312 -0
- package/src/views/contributors.ts +341 -0
- package/src/views/creators.ts +82 -0
- package/src/views/cross_reference.ts +74 -0
- package/src/views/cross_reference_editable.ts +113 -0
- package/src/views/editable_block.ts +141 -0
- package/src/views/embed.ts +345 -0
- package/src/views/empty.ts +26 -0
- package/src/views/equation.ts +47 -0
- package/src/views/equation_editable.ts +84 -0
- package/src/views/equation_element.ts +29 -0
- package/src/views/equation_element_editable.ts +21 -0
- package/src/views/figure.ts +84 -0
- package/src/views/figure_editable.ts +361 -0
- package/src/views/figure_element.ts +197 -0
- package/src/views/figure_element_editable.ts +21 -0
- package/src/views/footnote.ts +236 -0
- package/src/views/footnotes_element.ts +54 -0
- package/src/views/general_table_footnote.ts +140 -0
- package/src/views/hero_image.ts +76 -0
- package/src/views/hero_image_editable.ts +21 -0
- package/src/views/image_element.ts +373 -0
- package/src/views/image_element_editable.ts +21 -0
- package/src/views/inline_equation.ts +47 -0
- package/src/views/inline_equation_editable.ts +70 -0
- package/src/views/inline_footnote.ts +264 -0
- package/src/views/inline_footnote_editable.ts +20 -0
- package/src/views/keyword.ts +127 -0
- package/src/views/keyword_group.ts +109 -0
- package/src/views/link.ts +27 -0
- package/src/views/link_editable.ts +169 -0
- package/src/views/list.ts +46 -0
- package/src/views/list_item.ts +26 -0
- package/src/views/paragraph.ts +26 -0
- package/src/views/paragraph_editable.ts +21 -0
- package/src/views/placeholder.ts +35 -0
- package/src/views/placeholder_element.ts +38 -0
- package/src/views/placeholder_element_editable.ts +21 -0
- package/src/views/pullquote_element.ts +106 -0
- package/src/views/pullquote_element_editable.ts +24 -0
- package/src/views/quote_image_editable.ts +84 -0
- package/src/views/section.ts +64 -0
- package/src/views/section_label.ts +20 -0
- package/src/views/section_title.ts +87 -0
- package/src/views/section_title_editable.ts +21 -0
- package/src/views/subtitle.ts +42 -0
- package/src/views/subtitle_editable.ts +20 -0
- package/src/views/subtitles.ts +34 -0
- package/src/views/subtitles_editable.ts +21 -0
- package/src/views/supplement.ts +236 -0
- package/src/views/supplement_editable.ts +20 -0
- package/src/views/supplements.ts +65 -0
- package/src/views/supplements_editable.ts +21 -0
- package/src/views/table_cell.ts +150 -0
- package/src/views/table_element.ts +33 -0
- package/src/views/table_element_editable.ts +21 -0
- package/src/views/table_element_footer.ts +41 -0
- package/src/views/title.ts +46 -0
- package/src/views/title_editable.ts +20 -0
- package/src/views/trans_graphical_abstract.ts +55 -0
- package/src/views/trans_graphical_abstract_editable.ts +21 -0
- package/src/views/translated_abstract.ts +53 -0
- package/src/views/translated_abstract_editable.ts +21 -0
- package/dist/cjs/lib/statistics.js +0 -38
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* © 2025 Atypon Systems LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import {
|
|
17
|
+
isDeleted,
|
|
18
|
+
isValid,
|
|
19
|
+
setAction,
|
|
20
|
+
TrackChangesAction,
|
|
21
|
+
TrackedAttrs,
|
|
22
|
+
} from '@manuscripts/track-changes-plugin'
|
|
23
|
+
import {
|
|
24
|
+
generateNodeID,
|
|
25
|
+
isSectionNodeType,
|
|
26
|
+
ManuscriptEditorView,
|
|
27
|
+
ManuscriptNodeType,
|
|
28
|
+
nodeNames,
|
|
29
|
+
schema,
|
|
30
|
+
SectionTitleNode,
|
|
31
|
+
} from '@manuscripts/transform'
|
|
32
|
+
import { Fragment, Node } from 'prosemirror-model'
|
|
33
|
+
import { EditorState, TextSelection, Transaction } from 'prosemirror-state'
|
|
34
|
+
import { findChildrenByType, hasParentNodeOfType } from 'prosemirror-utils'
|
|
35
|
+
import { EditorView } from 'prosemirror-view'
|
|
36
|
+
|
|
37
|
+
import { Dispatch } from '../../commands'
|
|
38
|
+
import { filterBlockNodes } from '../../lib/utils'
|
|
39
|
+
import { Option } from './type-selector/TypeSelector'
|
|
40
|
+
|
|
41
|
+
export const optionName = (nodeType: ManuscriptNodeType) => {
|
|
42
|
+
switch (nodeType) {
|
|
43
|
+
case nodeType.schema.nodes.section:
|
|
44
|
+
return 'Heading'
|
|
45
|
+
|
|
46
|
+
default:
|
|
47
|
+
return nodeNames.get(nodeType) || nodeType.name
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const titleCase = (text: string) =>
|
|
52
|
+
text.replace(/\b([a-z])/g, (match) => match.toUpperCase())
|
|
53
|
+
|
|
54
|
+
export const findSelectedOption = (options: Option[]): Option | undefined => {
|
|
55
|
+
for (const option of options) {
|
|
56
|
+
if (option.isSelected) {
|
|
57
|
+
return option
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Helper function to find the deepest subsection (the last subsection that doesn't have subsections itself)
|
|
63
|
+
export const getDeepestSubsectionPosition = (
|
|
64
|
+
subsection: Node,
|
|
65
|
+
position: number
|
|
66
|
+
) => {
|
|
67
|
+
while (subsection.lastChild && isSectionNodeType(subsection.lastChild.type)) {
|
|
68
|
+
if (isDeleted(subsection.lastChild)) {
|
|
69
|
+
return (
|
|
70
|
+
position +
|
|
71
|
+
jumpToPreviousValidNode(
|
|
72
|
+
subsection.nodeSize - subsection.lastChild.nodeSize - 2,
|
|
73
|
+
subsection
|
|
74
|
+
) +
|
|
75
|
+
1
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
position += subsection.nodeSize - subsection.lastChild.nodeSize
|
|
79
|
+
subsection = subsection.lastChild
|
|
80
|
+
}
|
|
81
|
+
return position + subsection.content.size
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const getInsertDataTracked = (node: Node) => {
|
|
85
|
+
const dataTracked = node.attrs.dataTracked as TrackedAttrs[] | null
|
|
86
|
+
const change = dataTracked?.find((c) => c.operation === 'insert')
|
|
87
|
+
return change ? [change] : null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Look at current position if node is deleted move to the previous sibling and
|
|
91
|
+
* check if it's deleted until we find a valid node */
|
|
92
|
+
const jumpToPreviousValidNode = (beforeSection: number, doc: Node) => {
|
|
93
|
+
const $beforeSection = doc.resolve(beforeSection)
|
|
94
|
+
let pos = beforeSection
|
|
95
|
+
for (let i = $beforeSection.index() - 1; i >= 0; i--) {
|
|
96
|
+
pos = $beforeSection.posAtIndex(i)
|
|
97
|
+
const node = doc.resolve(pos + 1).node()
|
|
98
|
+
if (!(isDeleted(node) && node.type === schema.nodes.section) && i >= 0) {
|
|
99
|
+
pos = $beforeSection.posAtIndex(i + 1)
|
|
100
|
+
break
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return pos
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const demoteSectionToParagraph = (
|
|
108
|
+
state: EditorState,
|
|
109
|
+
dispatch: (tr: Transaction) => void,
|
|
110
|
+
view?: ManuscriptEditorView
|
|
111
|
+
) => {
|
|
112
|
+
const {
|
|
113
|
+
doc,
|
|
114
|
+
selection: { $from },
|
|
115
|
+
schema,
|
|
116
|
+
tr,
|
|
117
|
+
} = state
|
|
118
|
+
const { nodes } = schema
|
|
119
|
+
|
|
120
|
+
const sectionTitle = $from.node($from.depth)
|
|
121
|
+
const afterSectionTitle = $from.after($from.depth)
|
|
122
|
+
const $afterSectionTitle = doc.resolve(afterSectionTitle)
|
|
123
|
+
const afterSectionTitleOffset = $afterSectionTitle.parentOffset
|
|
124
|
+
|
|
125
|
+
const sectionDepth = $from.depth - 1
|
|
126
|
+
const section = $from.node(sectionDepth)
|
|
127
|
+
let beforeSection = $from.before(sectionDepth)
|
|
128
|
+
beforeSection = jumpToPreviousValidNode(beforeSection, doc)
|
|
129
|
+
const $beforeSection = doc.resolve(beforeSection)
|
|
130
|
+
const previousNode = $beforeSection.nodeBefore
|
|
131
|
+
const paragraph = nodes.paragraph.create(
|
|
132
|
+
{
|
|
133
|
+
id: generateNodeID(nodes.paragraph),
|
|
134
|
+
dataTracked: getInsertDataTracked(section),
|
|
135
|
+
},
|
|
136
|
+
sectionTitle.content
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
let anchor
|
|
140
|
+
|
|
141
|
+
const sectionContent = section.content.cut(afterSectionTitleOffset)
|
|
142
|
+
|
|
143
|
+
if (previousNode && isSectionNodeType(previousNode.type)) {
|
|
144
|
+
const hasSubsections =
|
|
145
|
+
previousNode.lastChild && isSectionNodeType(previousNode.lastChild.type)
|
|
146
|
+
|
|
147
|
+
if (hasSubsections) {
|
|
148
|
+
beforeSection = getDeepestSubsectionPosition(
|
|
149
|
+
previousNode,
|
|
150
|
+
beforeSection - previousNode.nodeSize
|
|
151
|
+
)
|
|
152
|
+
} else {
|
|
153
|
+
beforeSection = beforeSection - 1
|
|
154
|
+
}
|
|
155
|
+
anchor = beforeSection + 1
|
|
156
|
+
} else {
|
|
157
|
+
anchor = beforeSection + 1
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// TC plugins is the one who has to drop shadow content and not body-editor has to do that
|
|
161
|
+
const content = filterBlockNodes(
|
|
162
|
+
Fragment.from(paragraph).append(sectionContent)
|
|
163
|
+
)
|
|
164
|
+
tr.insert(beforeSection, content)
|
|
165
|
+
|
|
166
|
+
const afterSection = tr.mapping.map($from.after(sectionDepth))
|
|
167
|
+
tr.delete(afterSection - section.nodeSize, afterSection)
|
|
168
|
+
|
|
169
|
+
tr.setSelection(TextSelection.create(tr.doc, anchor))
|
|
170
|
+
|
|
171
|
+
dispatch(
|
|
172
|
+
setAction(
|
|
173
|
+
tr,
|
|
174
|
+
TrackChangesAction.structuralChangeAction,
|
|
175
|
+
'convert-to-paragraph'
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
view && view.focus()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const promoteParagraphToSection = (
|
|
182
|
+
state: EditorState,
|
|
183
|
+
dispatch: (tr: Transaction) => void,
|
|
184
|
+
view?: ManuscriptEditorView
|
|
185
|
+
) => {
|
|
186
|
+
const {
|
|
187
|
+
doc,
|
|
188
|
+
selection: { $from },
|
|
189
|
+
schema,
|
|
190
|
+
tr,
|
|
191
|
+
} = state
|
|
192
|
+
const { nodes } = schema
|
|
193
|
+
const paragraph = $from.node($from.depth)
|
|
194
|
+
const beforeParagraph = $from.before($from.depth)
|
|
195
|
+
const $beforeParagraph = doc.resolve(beforeParagraph)
|
|
196
|
+
const beforeParagraphOffset = $beforeParagraph.parentOffset
|
|
197
|
+
const afterParagraphOffset = beforeParagraphOffset + paragraph.nodeSize
|
|
198
|
+
|
|
199
|
+
const sectionDepth = $from.depth - 1
|
|
200
|
+
const parentSection = $from.node(sectionDepth)
|
|
201
|
+
const startIndex = $from.index(sectionDepth)
|
|
202
|
+
const endIndex = $from.indexAfter(sectionDepth)
|
|
203
|
+
let afterParentSection = $from.after(sectionDepth)
|
|
204
|
+
const items: Node[] = []
|
|
205
|
+
|
|
206
|
+
if (startIndex > 0) {
|
|
207
|
+
// add the original section with content up to the paragraph
|
|
208
|
+
const precedingSection = parentSection.cut(0, beforeParagraphOffset)
|
|
209
|
+
items.push(precedingSection)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const textContent = findChildrenByType(paragraph, schema.nodes.text).map(
|
|
213
|
+
({ node }) => node
|
|
214
|
+
)
|
|
215
|
+
const sectionTitle: SectionTitleNode = textContent
|
|
216
|
+
? nodes.section_title.create({}, Fragment.from(textContent))
|
|
217
|
+
: nodes.section_title.create()
|
|
218
|
+
|
|
219
|
+
let sectionContent = Fragment.from(sectionTitle)
|
|
220
|
+
if (endIndex < parentSection.childCount) {
|
|
221
|
+
sectionContent = sectionContent.append(
|
|
222
|
+
parentSection.content.cut(afterParagraphOffset)
|
|
223
|
+
)
|
|
224
|
+
} else {
|
|
225
|
+
sectionContent = sectionContent.append(Fragment.from(paragraph.copy()))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (parentSection.type.name === 'body') {
|
|
229
|
+
sectionContent = Fragment.from(sectionTitle)
|
|
230
|
+
afterParentSection = beforeParagraph + paragraph.nodeSize
|
|
231
|
+
// get position before first section in body, starting from the selection node index
|
|
232
|
+
parentSection.forEach((node, offset, index) => {
|
|
233
|
+
if (
|
|
234
|
+
node.type !== nodes.section &&
|
|
235
|
+
index > tr.doc.resolve(beforeParagraph).index()
|
|
236
|
+
) {
|
|
237
|
+
const pos = $from.start($from.depth - 1)
|
|
238
|
+
afterParentSection = pos + offset + node.nodeSize
|
|
239
|
+
sectionContent = sectionContent.append(Fragment.from(node))
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
items[0] = nodes.section.create(
|
|
243
|
+
{ dataTracked: getInsertDataTracked(paragraph) },
|
|
244
|
+
sectionContent
|
|
245
|
+
)
|
|
246
|
+
} else {
|
|
247
|
+
items.push(
|
|
248
|
+
parentSection.type.create(
|
|
249
|
+
{ dataTracked: getInsertDataTracked(paragraph) },
|
|
250
|
+
sectionContent
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
const content = filterBlockNodes(Fragment.from(items[items.length - 1]))
|
|
255
|
+
tr.insert(afterParentSection, content)
|
|
256
|
+
tr.delete(beforeParagraph, afterParentSection - 1)
|
|
257
|
+
|
|
258
|
+
const anchor = tr.mapping.map(afterParentSection) - content.size + 2
|
|
259
|
+
tr.setSelection(TextSelection.create(tr.doc, anchor))
|
|
260
|
+
|
|
261
|
+
dispatch(
|
|
262
|
+
setAction(
|
|
263
|
+
tr,
|
|
264
|
+
TrackChangesAction.structuralChangeAction,
|
|
265
|
+
'convert-to-section'
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
view && view.focus()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export const isIndentationAllowed =
|
|
272
|
+
(action: string) => (state: EditorState) => {
|
|
273
|
+
const { $from } = state.selection
|
|
274
|
+
const nodeType = $from.node().type
|
|
275
|
+
|
|
276
|
+
const allowedNodeTypes = [
|
|
277
|
+
schema.nodes.section_title,
|
|
278
|
+
schema.nodes.paragraph,
|
|
279
|
+
]
|
|
280
|
+
if (!allowedNodeTypes.includes(nodeType)) {
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const isInBody = hasParentNodeOfType(schema.nodes.body)(state.selection)
|
|
285
|
+
const isDeletedNode = isDeleted($from.node($from.depth))
|
|
286
|
+
if (!isInBody || isDeletedNode) {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// It is not allowed to unindent the top level parent sections, headers and paragraphs
|
|
291
|
+
if (action === 'unindent') {
|
|
292
|
+
const grandparentNode = $from.node($from.depth - 2)
|
|
293
|
+
if (grandparentNode?.type === schema.nodes.body) {
|
|
294
|
+
return false
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (nodeType === schema.nodes.paragraph) {
|
|
299
|
+
const parentNode = $from.node($from.depth - 1)
|
|
300
|
+
|
|
301
|
+
const isIndentNotAllowed = ![
|
|
302
|
+
schema.nodes.section,
|
|
303
|
+
schema.nodes.body,
|
|
304
|
+
].includes(parentNode?.type)
|
|
305
|
+
|
|
306
|
+
const isUnindentNotAllowed = parentNode?.type !== schema.nodes.section
|
|
307
|
+
|
|
308
|
+
if (action === 'indent' && isIndentNotAllowed) {
|
|
309
|
+
return false
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (action === 'unindent' && isUnindentNotAllowed) {
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return true
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export const indentSection =
|
|
321
|
+
() => (state: EditorState, dispatch: Dispatch, view?: EditorView) => {
|
|
322
|
+
const {
|
|
323
|
+
selection: { $from },
|
|
324
|
+
schema,
|
|
325
|
+
tr,
|
|
326
|
+
} = state
|
|
327
|
+
const { nodes } = schema
|
|
328
|
+
|
|
329
|
+
const sectionDepth = $from.depth - 1
|
|
330
|
+
const section = $from.node(sectionDepth)
|
|
331
|
+
const beforeSection = $from.before(sectionDepth)
|
|
332
|
+
const afterSection = $from.after(sectionDepth)
|
|
333
|
+
|
|
334
|
+
const parentSectionDepth = sectionDepth - 1
|
|
335
|
+
const parentSection = $from.node(parentSectionDepth)
|
|
336
|
+
const startIndex = $from.index(parentSectionDepth)
|
|
337
|
+
|
|
338
|
+
// Check if there's a valid previous section to indent into
|
|
339
|
+
// Look backwards through siblings to find a valid (non-deleted, non-moved) section
|
|
340
|
+
let previousSection: Node | null = null
|
|
341
|
+
for (let i = startIndex - 1; i >= 0; i--) {
|
|
342
|
+
const candidate = parentSection.child(i)
|
|
343
|
+
if (candidate.type === nodes.section && isValid(candidate)) {
|
|
344
|
+
previousSection = candidate
|
|
345
|
+
break
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let anchor
|
|
350
|
+
if (!previousSection) {
|
|
351
|
+
// No valid previous section, creating new parent section
|
|
352
|
+
const emptyTitle = nodes.section_title.create()
|
|
353
|
+
const newParentSectionContent = Fragment.fromArray([emptyTitle, section])
|
|
354
|
+
const newParentSection = nodes.section.create({}, newParentSectionContent)
|
|
355
|
+
|
|
356
|
+
tr.insert(beforeSection, newParentSection)
|
|
357
|
+
|
|
358
|
+
// Delete the original section (now at shifted position)
|
|
359
|
+
const newBeforeSection = beforeSection + newParentSection.nodeSize
|
|
360
|
+
const newAfterSection = afterSection + newParentSection.nodeSize
|
|
361
|
+
|
|
362
|
+
tr.delete(newBeforeSection, newAfterSection)
|
|
363
|
+
|
|
364
|
+
anchor = beforeSection + 1
|
|
365
|
+
} else {
|
|
366
|
+
// Moving section into previous section as subsection
|
|
367
|
+
// Find the actual position of the previous section by walking through the document
|
|
368
|
+
let beforePreviousSection: number | null = null
|
|
369
|
+
|
|
370
|
+
$from.doc.descendants((node, pos) => {
|
|
371
|
+
if (node === previousSection) {
|
|
372
|
+
beforePreviousSection = pos
|
|
373
|
+
return false // stop iteration
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
if (beforePreviousSection === null) {
|
|
378
|
+
return false
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const insertPos = beforePreviousSection + previousSection.nodeSize - 1
|
|
382
|
+
tr.insert(insertPos, section)
|
|
383
|
+
|
|
384
|
+
// Delete the original section (positions have shifted due to insert)
|
|
385
|
+
const newBeforeSection = beforeSection + section.nodeSize
|
|
386
|
+
const newAfterSection = afterSection + section.nodeSize
|
|
387
|
+
tr.delete(newBeforeSection, newAfterSection)
|
|
388
|
+
|
|
389
|
+
anchor = insertPos + 1
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
tr.setSelection(TextSelection.create(tr.doc, anchor))
|
|
393
|
+
|
|
394
|
+
setAction(tr, TrackChangesAction.indentationAction, { action: 'indent' })
|
|
395
|
+
|
|
396
|
+
dispatch(tr)
|
|
397
|
+
view && view.focus()
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export const indentParagraph =
|
|
401
|
+
() => (state: EditorState, dispatch: Dispatch, view?: EditorView) => {
|
|
402
|
+
const { $from } = state.selection
|
|
403
|
+
const { schema, tr } = state
|
|
404
|
+
|
|
405
|
+
const beforeParagraph = $from.before($from.depth)
|
|
406
|
+
const sectionDepth = $from.depth - 1
|
|
407
|
+
const parentSection = $from.node(sectionDepth)
|
|
408
|
+
const sectionStart = $from.start(sectionDepth)
|
|
409
|
+
const sectionEnd = $from.end(sectionDepth)
|
|
410
|
+
|
|
411
|
+
// Build section content
|
|
412
|
+
const sectionContent = Fragment.from(
|
|
413
|
+
schema.nodes.section_title.create()
|
|
414
|
+
).append(parentSection.content.cut(beforeParagraph - sectionStart))
|
|
415
|
+
|
|
416
|
+
// Create new section
|
|
417
|
+
const newSection = schema.nodes.section.create(
|
|
418
|
+
{ id: generateNodeID(schema.nodes.section) },
|
|
419
|
+
sectionContent
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
setAction(tr, TrackChangesAction.indentationAction, { action: 'indent' })
|
|
423
|
+
|
|
424
|
+
tr.delete(beforeParagraph, sectionEnd)
|
|
425
|
+
tr.insert(beforeParagraph, newSection)
|
|
426
|
+
|
|
427
|
+
// Set selection inside the title of the new section
|
|
428
|
+
tr.setSelection(
|
|
429
|
+
TextSelection.create(tr.doc, beforeParagraph + newSection.nodeSize - 2)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
dispatch(tr)
|
|
433
|
+
view?.focus()
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export const unindentSection =
|
|
437
|
+
() => (state: EditorState, dispatch: Dispatch, view?: EditorView) => {
|
|
438
|
+
const {
|
|
439
|
+
selection: { $from },
|
|
440
|
+
tr,
|
|
441
|
+
} = state
|
|
442
|
+
const sectionDepth = $from.depth - 1
|
|
443
|
+
const section = $from.node(sectionDepth)
|
|
444
|
+
const beforeSection = $from.before(sectionDepth)
|
|
445
|
+
const sectionTitle = $from.node($from.depth)
|
|
446
|
+
|
|
447
|
+
const $beforeSection = tr.doc.resolve(beforeSection)
|
|
448
|
+
const beforeSectionOffset = $beforeSection.parentOffset
|
|
449
|
+
const afterSectionOffset = beforeSectionOffset + section.nodeSize
|
|
450
|
+
|
|
451
|
+
const parentSectionDepth = $from.depth - 2
|
|
452
|
+
const parentSection = $from.node(parentSectionDepth)
|
|
453
|
+
const endIndex = $from.indexAfter(parentSectionDepth)
|
|
454
|
+
const afterParentSection = $from.after(parentSectionDepth)
|
|
455
|
+
|
|
456
|
+
const hasFollowingSiblings = endIndex < parentSection.childCount
|
|
457
|
+
|
|
458
|
+
let extendedSection = section
|
|
459
|
+
|
|
460
|
+
if (hasFollowingSiblings) {
|
|
461
|
+
// If there are following siblings, we need to extend the section
|
|
462
|
+
const siblingsContent = parentSection.content.cut(afterSectionOffset)
|
|
463
|
+
extendedSection = section.copy(section.content.append(siblingsContent))
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Insert the unindented section at the parent level
|
|
467
|
+
tr.insert(afterParentSection, Fragment.from(extendedSection))
|
|
468
|
+
|
|
469
|
+
// Delete the original section (positions inside parent haven't changed)
|
|
470
|
+
tr.delete(beforeSection, beforeSection + extendedSection.nodeSize)
|
|
471
|
+
|
|
472
|
+
const anchor = afterParentSection + 2
|
|
473
|
+
|
|
474
|
+
tr.setSelection(
|
|
475
|
+
TextSelection.create(tr.doc, anchor, anchor + sectionTitle.content.size)
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
setAction(tr, TrackChangesAction.indentationAction, { action: 'unindent' })
|
|
479
|
+
|
|
480
|
+
dispatch(tr)
|
|
481
|
+
view && view.focus()
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export const unindentParagraph =
|
|
485
|
+
() => (state: EditorState, dispatch: Dispatch, view?: EditorView) => {
|
|
486
|
+
const {
|
|
487
|
+
selection: { $from },
|
|
488
|
+
schema,
|
|
489
|
+
tr,
|
|
490
|
+
} = state
|
|
491
|
+
|
|
492
|
+
const paragraphPos = $from.before($from.depth)
|
|
493
|
+
|
|
494
|
+
const sectionDepth = $from.depth - 1
|
|
495
|
+
const section = $from.node(sectionDepth)
|
|
496
|
+
const afterSection = $from.after(sectionDepth)
|
|
497
|
+
const sectionStart = $from.start(sectionDepth)
|
|
498
|
+
|
|
499
|
+
const parentSectionDepth = sectionDepth - 1
|
|
500
|
+
const parentSection = $from.node(parentSectionDepth)
|
|
501
|
+
const parentSectionStart = $from.start(parentSectionDepth)
|
|
502
|
+
const parentSectionEnd = $from.end(parentSectionDepth)
|
|
503
|
+
|
|
504
|
+
let sectionContent = Fragment.from(schema.nodes.section_title.create())
|
|
505
|
+
|
|
506
|
+
sectionContent = sectionContent
|
|
507
|
+
.append(section.content.cut(paragraphPos - sectionStart))
|
|
508
|
+
.append(parentSection.content.cut(afterSection - parentSectionStart))
|
|
509
|
+
|
|
510
|
+
const newSection = schema.nodes.section.create(
|
|
511
|
+
{ id: generateNodeID(schema.nodes.section) },
|
|
512
|
+
sectionContent
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
tr.delete(paragraphPos, parentSectionEnd)
|
|
516
|
+
tr.insert(paragraphPos + 2, newSection)
|
|
517
|
+
|
|
518
|
+
tr.setSelection(
|
|
519
|
+
TextSelection.create(tr.doc, paragraphPos + newSection.nodeSize)
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
setAction(tr, TrackChangesAction.indentationAction, { action: 'unindent' })
|
|
523
|
+
|
|
524
|
+
dispatch(tr)
|
|
525
|
+
view?.focus()
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const indentationHandlers = {
|
|
529
|
+
section_title: {
|
|
530
|
+
indent: indentSection,
|
|
531
|
+
unindent: unindentSection,
|
|
532
|
+
},
|
|
533
|
+
paragraph: {
|
|
534
|
+
indent: indentParagraph,
|
|
535
|
+
unindent: unindentParagraph,
|
|
536
|
+
},
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export const changeIndentation =
|
|
540
|
+
(action: 'indent' | 'unindent') =>
|
|
541
|
+
(state: EditorState, dispatch: Dispatch, view?: EditorView) => {
|
|
542
|
+
const { $from } = state.selection
|
|
543
|
+
const nodeName = $from.node().type.name as 'section_title' | 'paragraph'
|
|
544
|
+
|
|
545
|
+
const handler = indentationHandlers[nodeName]?.[action]
|
|
546
|
+
handler()(state, dispatch, view)
|
|
547
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* © 2025 Atypon Systems LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { TickIcon } from '@manuscripts/style-guide'
|
|
17
|
+
import React, { useLayoutEffect, useRef } from 'react'
|
|
18
|
+
import { components, ControlProps, OptionProps } from 'react-select'
|
|
19
|
+
import styled from 'styled-components'
|
|
20
|
+
|
|
21
|
+
import { optionName, titleCase } from '../helpers'
|
|
22
|
+
import { Option } from './TypeSelector'
|
|
23
|
+
|
|
24
|
+
const OptionContainer = styled.div<{ isFocused?: boolean }>`
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
padding: 8px;
|
|
31
|
+
background: ${(props) => (props.isFocused ? '#f2fbfc' : 'transparent')};
|
|
32
|
+
|
|
33
|
+
&:hover {
|
|
34
|
+
background: ${(props) => props.theme.colors.background.fifth};
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
const OptionLabel = styled.span``
|
|
38
|
+
|
|
39
|
+
const TickIconWrapper = styled.div``
|
|
40
|
+
export const OptionComponent: React.FC<OptionProps<Option, false>> = ({
|
|
41
|
+
innerProps,
|
|
42
|
+
data,
|
|
43
|
+
isFocused,
|
|
44
|
+
innerRef,
|
|
45
|
+
}) => {
|
|
46
|
+
return (
|
|
47
|
+
<OptionContainer {...innerProps} isFocused={isFocused} ref={innerRef}>
|
|
48
|
+
<OptionLabel>{titleCase(optionName(data.nodeType))}</OptionLabel>
|
|
49
|
+
{data.isSelected && (
|
|
50
|
+
<TickIconWrapper>
|
|
51
|
+
<TickIcon />
|
|
52
|
+
</TickIconWrapper>
|
|
53
|
+
)}
|
|
54
|
+
</OptionContainer>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const CustomControl = (props: ControlProps<Option, false>) => {
|
|
59
|
+
const controlRef = useRef<HTMLDivElement>(null)
|
|
60
|
+
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
if (controlRef.current) {
|
|
63
|
+
controlRef.current.setAttribute('data-toolbar-button', 'true')
|
|
64
|
+
}
|
|
65
|
+
}, [])
|
|
66
|
+
|
|
67
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
68
|
+
if (e.key === 'Enter') {
|
|
69
|
+
e.preventDefault()
|
|
70
|
+
const { selectProps } = props
|
|
71
|
+
if (selectProps.menuIsOpen) {
|
|
72
|
+
selectProps.onMenuClose?.()
|
|
73
|
+
} else {
|
|
74
|
+
selectProps.onMenuOpen?.()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<components.Control
|
|
81
|
+
{...props}
|
|
82
|
+
innerRef={controlRef}
|
|
83
|
+
innerProps={{
|
|
84
|
+
...props.innerProps,
|
|
85
|
+
onKeyDown: handleKeyDown,
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|