@quadrats/react 1.1.7 → 1.1.8
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/CHANGELOG.md +610 -0
- package/_internal/package.json +1 -4
- package/_internal/src/renderer/composeRenderElementsBase.ts +19 -0
- package/_internal/src/renderer/composeRenderLeafsBase.ts +17 -0
- package/_internal/src/renderer/createRenderElementBase.ts +14 -0
- package/_internal/src/renderer/createRenderElementsBase.ts +15 -0
- package/_internal/src/renderer/createRenderMarkBase.ts +19 -0
- package/_internal/src/renderer/typings.ts +30 -0
- package/_internal/tsconfig.build.json +5 -0
- package/accordion/jsx-serializer/package.json +6 -3
- package/accordion/jsx-serializer/src/createJsxSerializeAccordion.ts +79 -0
- package/accordion/jsx-serializer/src/defaultRenderAccordionElements.tsx +21 -0
- package/accordion/jsx-serializer/src/typings.ts +16 -0
- package/accordion/jsx-serializer/tsconfig.build.json +5 -0
- package/accordion/package.json +8 -3
- package/accordion/src/components/Accordion.tsx +53 -0
- package/accordion/src/components/AccordionContent.tsx +38 -0
- package/accordion/src/components/AccordionTitle.tsx +33 -0
- package/accordion/src/contexts/AccordionContext.ts +4 -0
- package/accordion/src/createReactAccordion.ts +194 -0
- package/accordion/src/defaultRenderAccordionElements.tsx +11 -0
- package/accordion/src/hooks/useAccordion.ts +6 -0
- package/accordion/src/typings.ts +41 -0
- package/accordion/toolbar/package.json +6 -3
- package/accordion/toolbar/src/AccordionToolbarIcon.tsx +17 -0
- package/accordion/toolbar/src/index.ts +3 -0
- package/accordion/toolbar/src/useAccordionTool.ts +10 -0
- package/accordion/toolbar/tsconfig.build.json +5 -0
- package/accordion/tsconfig.build.json +5 -0
- package/align/package.json +5 -3
- package/align/src/constants.ts +8 -0
- package/align/src/createReactAlign.ts +27 -0
- package/align/src/typings.ts +20 -0
- package/align/toolbar/package.json +7 -3
- package/align/toolbar/src/AlignToolbarIcon.tsx +19 -0
- package/align/toolbar/src/index.ts +3 -0
- package/align/toolbar/src/useAlignTool.ts +18 -0
- package/align/toolbar/tsconfig.build.json +5 -0
- package/align/tsconfig.build.json +5 -0
- package/blockquote/jsx-serializer/package.json +6 -3
- package/blockquote/jsx-serializer/src/createJsxSerializeBlockquote.ts +14 -0
- package/blockquote/jsx-serializer/src/typings.ts +4 -0
- package/blockquote/jsx-serializer/tsconfig.build.json +5 -0
- package/blockquote/package.json +9 -3
- package/blockquote/src/ReactBlockquote.spec.tsx +42 -0
- package/blockquote/src/constants.ts +6 -0
- package/blockquote/src/createReactBlockquote.ts +105 -0
- package/blockquote/src/defaultRenderBlockquoteElement.tsx +15 -0
- package/blockquote/src/typings.ts +22 -0
- package/blockquote/toolbar/package.json +6 -3
- package/blockquote/toolbar/src/BlockquoteToolbarIcon.tsx +17 -0
- package/blockquote/toolbar/src/useBlockquoteTool.ts +11 -0
- package/blockquote/toolbar/tsconfig.build.json +5 -0
- package/blockquote/tsconfig.build.json +5 -0
- package/bold/jsx-serializer/package.json +6 -3
- package/bold/jsx-serializer/src/createJsxSerializeBold.ts +8 -0
- package/bold/jsx-serializer/tsconfig.build.json +5 -0
- package/bold/package.json +5 -3
- package/bold/src/constants.ts +6 -0
- package/bold/src/createReactBold.ts +11 -0
- package/bold/src/defaultRenderBold.tsx +12 -0
- package/bold/src/typings.ts +6 -0
- package/bold/tsconfig.build.json +5 -0
- package/card/jsx-serializer/package.json +6 -3
- package/card/jsx-serializer/src/createJsxSerializeCard.ts +65 -0
- package/card/jsx-serializer/src/defaultRenderCardElements.tsx +9 -0
- package/card/jsx-serializer/src/typings.ts +20 -0
- package/card/jsx-serializer/tsconfig.build.json +5 -0
- package/card/package.json +11 -3
- package/card/src/components/Card.tsx +139 -0
- package/card/src/components/CardContents.tsx +35 -0
- package/card/src/components/CardImage.tsx +30 -0
- package/card/src/components/CardPlaceholder.tsx +40 -0
- package/card/src/createReactCard.ts +101 -0
- package/card/src/defaultRenderCardElements.tsx +16 -0
- package/card/src/typings.ts +46 -0
- package/card/toolbar/package.json +6 -3
- package/card/toolbar/src/CardToolbarIcon.tsx +17 -0
- package/card/toolbar/src/useCardTool.tsx +43 -0
- package/card/toolbar/tsconfig.build.json +5 -0
- package/card/tsconfig.build.json +5 -0
- package/carousel/jsx-serializer/package.json +6 -3
- package/carousel/jsx-serializer/src/createJsxSerializeCarousel.ts +71 -0
- package/carousel/jsx-serializer/src/defaultRenderCarouselElements.tsx +9 -0
- package/carousel/jsx-serializer/src/typings.ts +20 -0
- package/carousel/jsx-serializer/tsconfig.build.json +5 -0
- package/carousel/package.json +10 -3
- package/carousel/src/components/Carousel.tsx +102 -0
- package/carousel/src/components/CarouselCaption.tsx +27 -0
- package/carousel/src/components/CarouselImages.tsx +97 -0
- package/carousel/src/components/CarouselPlaceholder.tsx +26 -0
- package/carousel/src/contexts/CarouselContext.ts +4 -0
- package/carousel/src/createReactCarousel.ts +104 -0
- package/carousel/src/defaultRenderCarouselElements.tsx +16 -0
- package/carousel/src/hooks/useCarousel.ts +6 -0
- package/carousel/src/typings.ts +55 -0
- package/carousel/toolbar/package.json +6 -3
- package/carousel/toolbar/src/CarouselToolbarIcon.tsx +17 -0
- package/carousel/toolbar/src/useCarouselTool.tsx +34 -0
- package/carousel/toolbar/tsconfig.build.json +5 -0
- package/carousel/tsconfig.build.json +5 -0
- package/components/package.json +21 -3
- package/components/src/BaseField/index.tsx +35 -0
- package/components/src/Button/index.tsx +48 -0
- package/components/src/Hint/index.tsx +61 -0
- package/components/src/Icon/index.tsx +45 -0
- package/components/src/ImageUploader/index.tsx +306 -0
- package/components/src/Input/index.tsx +72 -0
- package/components/src/Message/index.tsx +138 -0
- package/components/src/Modal/index.tsx +161 -0
- package/components/src/Notifier/NotifierManager.tsx +50 -0
- package/components/src/Notifier/createNotifier.tsx +100 -0
- package/components/src/Notifier/typings.ts +33 -0
- package/components/src/Portal/index.tsx +15 -0
- package/components/src/Progress/index.tsx +49 -0
- package/components/src/SegmentedControl/index.tsx +37 -0
- package/components/src/Textarea/index.tsx +74 -0
- package/components/src/Toggle/index.tsx +34 -0
- package/components/src/Tooltip/calculatePosition.ts +84 -0
- package/components/src/Tooltip/index.tsx +187 -0
- package/components/src/Tooltip/typings.ts +40 -0
- package/components/src/Transition/SlideFade.tsx +159 -0
- package/components/src/Transition/Transition.tsx +176 -0
- package/components/src/Transition/getTransitionStyleProps.ts +48 -0
- package/components/src/Transition/useSetNodeTransition.ts +46 -0
- package/components/src/index.ts +17 -0
- package/components/tsconfig.build.json +5 -0
- package/configs/package.json +1 -4
- package/configs/src/ConfigsProvider.tsx +39 -0
- package/configs/src/locale.ts +8 -0
- package/configs/src/theme.ts +50 -0
- package/configs/tsconfig.build.json +5 -0
- package/divider/jsx-serializer/package.json +6 -3
- package/divider/jsx-serializer/src/createJsxSerializeDivider.ts +12 -0
- package/divider/jsx-serializer/src/defaultRenderDividerElement.tsx +3 -0
- package/divider/jsx-serializer/src/typings.ts +4 -0
- package/divider/jsx-serializer/tsconfig.build.json +5 -0
- package/divider/package.json +8 -3
- package/divider/src/createReactDivider.ts +16 -0
- package/divider/src/defaultRenderDividerElement.tsx +8 -0
- package/divider/src/typings.ts +13 -0
- package/divider/toolbar/package.json +6 -3
- package/divider/toolbar/src/DividerToolbarIcon.tsx +17 -0
- package/divider/toolbar/src/index.ts +3 -0
- package/divider/toolbar/src/useDividerTool.ts +10 -0
- package/divider/toolbar/tsconfig.build.json +5 -0
- package/divider/tsconfig.build.json +5 -0
- package/embed/jsx-serializer/facebook/package.json +6 -3
- package/embed/jsx-serializer/facebook/tsconfig.build.json +5 -0
- package/embed/jsx-serializer/instagram/package.json +6 -3
- package/embed/jsx-serializer/instagram/tsconfig.build.json +5 -0
- package/embed/jsx-serializer/package.json +6 -3
- package/embed/jsx-serializer/src/createJsxSerializeEmbed.ts +20 -0
- package/embed/jsx-serializer/src/typings.ts +7 -0
- package/embed/jsx-serializer/tsconfig.build.json +5 -0
- package/embed/jsx-serializer/twitter/package.json +6 -3
- package/embed/jsx-serializer/twitter/tsconfig.build.json +5 -0
- package/embed/jsx-serializer/vimeo/package.json +6 -3
- package/embed/jsx-serializer/vimeo/tsconfig.build.json +5 -0
- package/embed/jsx-serializer/youtube/package.json +6 -3
- package/embed/jsx-serializer/youtube/tsconfig.build.json +5 -0
- package/embed/package.json +8 -3
- package/embed/renderers/base/package.json +6 -3
- package/embed/renderers/base/src/components/BaseEmbedElement.tsx +107 -0
- package/embed/renderers/base/src/index.ts +5 -0
- package/embed/renderers/base/tsconfig.build.json +5 -0
- package/embed/renderers/facebook/package.json +6 -3
- package/embed/renderers/facebook/src/components/Facebook.tsx +56 -0
- package/embed/renderers/facebook/src/defaultRenderFacebookEmbedElement.tsx +81 -0
- package/embed/renderers/facebook/tsconfig.build.json +5 -0
- package/embed/renderers/instagram/package.json +6 -3
- package/embed/renderers/instagram/src/components/Instagram.tsx +86 -0
- package/embed/renderers/instagram/src/defaultRenderInstagramEmbedElement.tsx +74 -0
- package/embed/renderers/instagram/src/hooks/useLoadInstagramEmbedApi.ts +39 -0
- package/embed/renderers/instagram/tsconfig.build.json +5 -0
- package/embed/renderers/podcast-apple/package.json +6 -3
- package/embed/renderers/podcast-apple/src/components/PodcastApple.tsx +37 -0
- package/embed/renderers/podcast-apple/src/defaultRenderPodcastAppleEmbedElement.tsx +69 -0
- package/embed/renderers/podcast-apple/tsconfig.build.json +5 -0
- package/embed/renderers/spotify/package.json +6 -3
- package/embed/renderers/spotify/src/components/Spotify.tsx +51 -0
- package/embed/renderers/spotify/src/defaultRenderSpotifyEmbedElement.tsx +63 -0
- package/embed/renderers/spotify/tsconfig.build.json +5 -0
- package/embed/renderers/twitter/package.json +6 -3
- package/embed/renderers/twitter/src/components/Twitter.tsx +47 -0
- package/embed/renderers/twitter/src/defaultRenderTwitterEmbedElement.tsx +78 -0
- package/embed/renderers/twitter/src/hooks/useLoadTwitterEmbedApi.ts +55 -0
- package/embed/renderers/twitter/tsconfig.build.json +5 -0
- package/embed/renderers/vimeo/package.json +6 -3
- package/embed/renderers/vimeo/src/defaultRenderVimeoEmbedElement.tsx +52 -0
- package/embed/renderers/vimeo/tsconfig.build.json +5 -0
- package/embed/renderers/youtube/package.json +6 -3
- package/embed/renderers/youtube/src/defaultRenderYoutubeEmbedElement.tsx +52 -0
- package/embed/renderers/youtube/tsconfig.build.json +5 -0
- package/embed/src/components/VideoIframe.tsx +41 -0
- package/embed/src/createReactEmbed.ts +23 -0
- package/embed/src/createRenderEmbedElementBase.ts +30 -0
- package/embed/src/hooks/useVideoIframeSize.ts +28 -0
- package/embed/src/typings.ts +26 -0
- package/embed/toolbar/package.json +6 -3
- package/embed/toolbar/src/EmbedToolbarIcon.tsx +25 -0
- package/embed/toolbar/src/index.ts +3 -0
- package/embed/toolbar/src/useEmbedTool.tsx +106 -0
- package/embed/toolbar/tsconfig.build.json +5 -0
- package/embed/tsconfig.build.json +5 -0
- package/file-uploader/package.json +9 -3
- package/file-uploader/src/components/FileUploader.tsx +18 -0
- package/file-uploader/src/createReactFileUploader.ts +28 -0
- package/file-uploader/src/defaultRenderFileUploaderElement.tsx +5 -0
- package/file-uploader/src/hooks/useFileUploader.ts +14 -0
- package/file-uploader/src/typings.ts +19 -0
- package/file-uploader/toolbar/package.json +6 -3
- package/file-uploader/toolbar/src/FileUploaderToolbarIcon.tsx +19 -0
- package/file-uploader/toolbar/src/index.ts +3 -0
- package/file-uploader/toolbar/src/useFileUploaderTool.ts +14 -0
- package/file-uploader/toolbar/tsconfig.build.json +5 -0
- package/file-uploader/tsconfig.build.json +5 -0
- package/footnote/jsx-serializer/package.json +6 -3
- package/footnote/jsx-serializer/src/createJsxSerializeFootnote.ts +13 -0
- package/footnote/jsx-serializer/src/defaultRenderFootnoteElement.tsx +18 -0
- package/footnote/jsx-serializer/src/typings.ts +4 -0
- package/footnote/jsx-serializer/tsconfig.build.json +5 -0
- package/footnote/package.json +5 -3
- package/footnote/src/createReactFootnote.ts +22 -0
- package/footnote/src/defaultRenderFootnoteElement.tsx +36 -0
- package/footnote/src/typings.ts +14 -0
- package/footnote/src/useFootnotes.ts +28 -0
- package/footnote/src/useFootnotesFromNodes.ts +33 -0
- package/footnote/toolbar/package.json +6 -3
- package/footnote/toolbar/src/FootnoteToolbarIcon.tsx +18 -0
- package/footnote/toolbar/src/index.ts +3 -0
- package/footnote/toolbar/src/useFootnoteTool.ts +22 -0
- package/footnote/toolbar/tsconfig.build.json +5 -0
- package/footnote/tsconfig.build.json +5 -0
- package/heading/jsx-serializer/package.json +6 -3
- package/heading/jsx-serializer/src/createJsxSerializeHeading.ts +13 -0
- package/heading/jsx-serializer/src/typings.ts +4 -0
- package/heading/jsx-serializer/tsconfig.build.json +5 -0
- package/heading/package.json +9 -3
- package/heading/src/constants.ts +9 -0
- package/heading/src/createReactHeading.ts +56 -0
- package/heading/src/defaultRenderHeadingElement.tsx +36 -0
- package/heading/src/typings.ts +21 -0
- package/heading/toolbar/package.json +6 -3
- package/heading/toolbar/src/HeadingToolbarIcon.tsx +22 -0
- package/heading/toolbar/src/index.ts +3 -0
- package/heading/toolbar/src/useToggleHeadingTool.ts +15 -0
- package/heading/toolbar/tsconfig.build.json +5 -0
- package/heading/tsconfig.build.json +5 -0
- package/highlight/jsx-serializer/package.json +6 -3
- package/highlight/jsx-serializer/src/createJsxSerializeHighlight.ts +8 -0
- package/highlight/jsx-serializer/tsconfig.build.json +5 -0
- package/highlight/package.json +5 -3
- package/highlight/src/constants.ts +6 -0
- package/highlight/src/createReactHighlight.ts +11 -0
- package/highlight/src/defaultRenderHighlight.tsx +12 -0
- package/highlight/src/typings.ts +6 -0
- package/highlight/tsconfig.build.json +5 -0
- package/image/jsx-serializer/package.json +6 -3
- package/image/jsx-serializer/src/createJsxSerializeImage.ts +85 -0
- package/image/jsx-serializer/src/defaultRenderImageElements.tsx +21 -0
- package/image/jsx-serializer/src/typings.ts +34 -0
- package/image/jsx-serializer/tsconfig.build.json +5 -0
- package/image/package.json +10 -3
- package/image/src/components/Image.tsx +107 -0
- package/image/src/components/ImageCaption.tsx +35 -0
- package/image/src/components/ImageFigure.tsx +26 -0
- package/image/src/components/ImagePlaceholder.tsx +16 -0
- package/image/src/createReactImage.ts +191 -0
- package/image/src/defaultRenderImageElements.tsx +11 -0
- package/image/src/hooks/useImageResizer.ts +135 -0
- package/image/src/typings.ts +50 -0
- package/image/tsconfig.build.json +5 -0
- package/input-block/package.json +8 -3
- package/input-block/src/components/InputBlock.tsx +70 -0
- package/input-block/src/createReactInputBlock.ts +34 -0
- package/input-block/src/defaultRenderInputBlockElement.tsx +5 -0
- package/input-block/src/hooks/useInputBlock.ts +61 -0
- package/input-block/src/typings.ts +19 -0
- package/input-block/tsconfig.build.json +5 -0
- package/italic/jsx-serializer/package.json +6 -3
- package/italic/jsx-serializer/src/createJsxSerializeItalic.ts +8 -0
- package/italic/jsx-serializer/tsconfig.build.json +5 -0
- package/italic/package.json +5 -3
- package/italic/src/constants.ts +6 -0
- package/italic/src/createReactItalic.ts +11 -0
- package/italic/src/defaultRenderItalic.tsx +12 -0
- package/italic/src/typings.ts +6 -0
- package/italic/tsconfig.build.json +5 -0
- package/jsx-serializer/package.json +5 -3
- package/jsx-serializer/src/createJsxSerializeElement.ts +8 -0
- package/jsx-serializer/src/createJsxSerializeElements.ts +8 -0
- package/jsx-serializer/src/createJsxSerializeMark.ts +6 -0
- package/jsx-serializer/src/createJsxSerializer.tsx +84 -0
- package/jsx-serializer/src/typings.ts +20 -0
- package/jsx-serializer/tsconfig.build.json +5 -0
- package/line-break/jsx-serializer/package.json +6 -3
- package/line-break/jsx-serializer/src/createJsxSerializeLineBreak.ts +13 -0
- package/line-break/jsx-serializer/src/defaultRenderLineBreakElement.tsx +7 -0
- package/line-break/jsx-serializer/src/typings.ts +4 -0
- package/line-break/jsx-serializer/tsconfig.build.json +5 -0
- package/line-break/package.json +8 -3
- package/line-break/src/commonBreak.ts +32 -0
- package/line-break/src/createOnKeyDownBreak.ts +88 -0
- package/line-break/src/createReactLineBreak.ts +20 -0
- package/line-break/src/defaultRenderLineBreakElement.tsx +8 -0
- package/line-break/src/renderLineBreakElementWithSymbol.tsx +8 -0
- package/line-break/src/typings.ts +38 -0
- package/line-break/tsconfig.build.json +5 -0
- package/link/jsx-serializer/package.json +6 -3
- package/link/jsx-serializer/src/createJsxSerializeLink.ts +11 -0
- package/link/jsx-serializer/src/defaultRenderLinkElement.tsx +8 -0
- package/link/jsx-serializer/src/typings.ts +4 -0
- package/link/jsx-serializer/tsconfig.build.json +5 -0
- package/link/package.json +5 -3
- package/link/src/createReactLink.ts +49 -0
- package/link/src/defaultRenderLinkElement.tsx +27 -0
- package/link/src/typings.ts +14 -0
- package/link/toolbar/package.json +6 -3
- package/link/toolbar/src/LinkToolbarIcon.tsx +18 -0
- package/link/toolbar/src/UnlinkToolbarIcon.tsx +17 -0
- package/link/toolbar/src/index.ts +5 -0
- package/link/toolbar/src/useLinkTool.ts +31 -0
- package/link/toolbar/src/useUnlinkTool.ts +10 -0
- package/link/toolbar/tsconfig.build.json +5 -0
- package/link/tsconfig.build.json +5 -0
- package/list/jsx-serializer/package.json +6 -3
- package/list/jsx-serializer/src/createJsxSerializeList.ts +21 -0
- package/list/jsx-serializer/src/typings.ts +4 -0
- package/list/jsx-serializer/tsconfig.build.json +5 -0
- package/list/package.json +8 -3
- package/list/src/createReactList.ts +52 -0
- package/list/src/defaultRenderListElements.tsx +12 -0
- package/list/src/typings.ts +14 -0
- package/list/toolbar/package.json +6 -3
- package/list/toolbar/src/ListToolbarIcon.tsx +19 -0
- package/list/toolbar/src/index.ts +3 -0
- package/list/toolbar/src/useListTool.ts +12 -0
- package/list/toolbar/tsconfig.build.json +5 -0
- package/list/tsconfig.build.json +5 -0
- package/package.json +18 -5
- package/paragraph/jsx-serializer/package.json +6 -3
- package/paragraph/jsx-serializer/src/createJsxSerializeParagraph.ts +14 -0
- package/paragraph/jsx-serializer/src/typings.ts +4 -0
- package/paragraph/jsx-serializer/tsconfig.build.json +5 -0
- package/paragraph/package.json +8 -3
- package/paragraph/src/createReactParagraph.ts +9 -0
- package/paragraph/src/createRenderParagraphElement.ts +17 -0
- package/paragraph/src/defaultRenderParagraphElement.tsx +21 -0
- package/paragraph/src/renderParagraphElementWithSymbol.tsx +21 -0
- package/paragraph/toolbar/package.json +6 -3
- package/paragraph/toolbar/src/ParagraphToolbarIcon.tsx +17 -0
- package/paragraph/toolbar/src/useToggleParagraphTool.ts +11 -0
- package/paragraph/toolbar/tsconfig.build.json +5 -0
- package/paragraph/tsconfig.build.json +5 -0
- package/read-more/jsx-serializer/package.json +6 -3
- package/read-more/jsx-serializer/src/createJsxSerializeReadMore.ts +11 -0
- package/read-more/jsx-serializer/src/typings.ts +4 -0
- package/read-more/jsx-serializer/tsconfig.build.json +5 -0
- package/read-more/package.json +8 -3
- package/read-more/src/components/ReadMore.tsx +24 -0
- package/read-more/src/createReactReadMore.ts +16 -0
- package/read-more/src/defaultRenderReadMoreElement.tsx +5 -0
- package/read-more/src/typings.ts +12 -0
- package/read-more/toolbar/package.json +6 -3
- package/read-more/toolbar/src/ReadMoreToolbarIcon.tsx +17 -0
- package/read-more/toolbar/src/index.ts +3 -0
- package/read-more/toolbar/src/useReadMoreTool.ts +10 -0
- package/read-more/toolbar/tsconfig.build.json +5 -0
- package/read-more/tsconfig.build.json +5 -0
- package/src/core/components/DefaultElement.tsx +6 -0
- package/src/core/components/DefaultLeaf.tsx +6 -0
- package/src/core/components/Editable.tsx +129 -0
- package/src/core/components/Quadrats.tsx +40 -0
- package/src/core/composeHandlers.ts +58 -0
- package/src/core/composeRenderElements.tsx +13 -0
- package/src/core/composeRenderLeafs.tsx +11 -0
- package/src/core/constants.ts +1 -0
- package/src/core/contexts/composition/CompositionProvider.tsx +15 -0
- package/src/core/contexts/composition/composition.ts +16 -0
- package/src/core/contexts/message/MessageProvider.tsx +46 -0
- package/src/core/contexts/message/message.ts +14 -0
- package/src/core/contexts/modal/CardModal/CardModal.tsx +256 -0
- package/src/core/contexts/modal/CarouselModal/CarouselItem.tsx +97 -0
- package/src/core/contexts/modal/CarouselModal/CarouselModal.tsx +280 -0
- package/src/core/contexts/modal/CarouselModal/FilesDropZone.tsx +118 -0
- package/src/core/contexts/modal/ConfirmModal/ConfirmModal.tsx +72 -0
- package/src/core/contexts/modal/EmbedModal/EmbedModal.tsx +57 -0
- package/src/core/contexts/modal/ModalProvider.tsx +114 -0
- package/src/core/contexts/modal/modal.ts +36 -0
- package/src/core/createReactEditor.ts +7 -0
- package/src/core/createRenderElement.ts +6 -0
- package/src/core/createRenderElements.ts +6 -0
- package/src/core/createRenderMark.ts +6 -0
- package/src/core/index.ts +53 -0
- package/src/core/typings/descendant.ts +3 -0
- package/src/core/typings/handler.ts +32 -0
- package/src/core/typings/renderer.ts +33 -0
- package/src/core/typings/with.ts +5 -0
- package/strikethrough/jsx-serializer/package.json +6 -3
- package/strikethrough/jsx-serializer/src/createJsxSerializeStrikethrough.ts +8 -0
- package/strikethrough/jsx-serializer/tsconfig.build.json +5 -0
- package/strikethrough/package.json +5 -3
- package/strikethrough/src/constants.ts +6 -0
- package/strikethrough/src/createReactStrikethrough.ts +11 -0
- package/strikethrough/src/defaultRenderStrikethrough.tsx +12 -0
- package/strikethrough/src/typings.ts +6 -0
- package/strikethrough/tsconfig.build.json +5 -0
- package/table/jsx-serializer/package.json +6 -3
- package/table/jsx-serializer/src/components/Table.tsx +6 -0
- package/table/jsx-serializer/src/components/TableBody.tsx +6 -0
- package/table/jsx-serializer/src/components/TableCell.tsx +51 -0
- package/table/jsx-serializer/src/components/TableHeader.tsx +16 -0
- package/table/jsx-serializer/src/components/TableMain.tsx +36 -0
- package/table/jsx-serializer/src/components/TableRow.tsx +6 -0
- package/table/jsx-serializer/src/components/TableTitle.tsx +6 -0
- package/table/jsx-serializer/src/contexts/TableHeaderContext.ts +3 -0
- package/table/jsx-serializer/src/contexts/TableScrollContext.ts +6 -0
- package/table/jsx-serializer/src/createJsxSerializeTable.tsx +163 -0
- package/table/jsx-serializer/src/defaultRenderTableElements.tsx +19 -0
- package/table/jsx-serializer/src/typings.ts +36 -0
- package/table/jsx-serializer/tsconfig.build.json +5 -0
- package/table/package.json +12 -3
- package/table/src/components/ColumnDragButton.tsx +72 -0
- package/table/src/components/RowDragButton.tsx +69 -0
- package/table/src/components/Table.tsx +351 -0
- package/table/src/components/TableBody.tsx +15 -0
- package/table/src/components/TableCell.tsx +415 -0
- package/table/src/components/TableDragLayer.tsx +127 -0
- package/table/src/components/TableHeader.tsx +26 -0
- package/table/src/components/TableMain.tsx +322 -0
- package/table/src/components/TableRow.tsx +11 -0
- package/table/src/components/TableTitle.tsx +28 -0
- package/table/src/contexts/TableActionsContext.ts +23 -0
- package/table/src/contexts/TableDragContext.tsx +58 -0
- package/table/src/contexts/TableHeaderContext.ts +6 -0
- package/table/src/contexts/TableMetadataContext.ts +21 -0
- package/table/src/contexts/TableScrollContext.ts +8 -0
- package/table/src/contexts/TableStateContext.ts +9 -0
- package/table/src/createReactTable.ts +371 -0
- package/table/src/defaultRenderTableElements.tsx +19 -0
- package/table/src/hooks/useColumnResize.ts +242 -0
- package/table/src/hooks/useTableActions.ts +1394 -0
- package/table/src/hooks/useTableActionsContext.ts +12 -0
- package/table/src/hooks/useTableCell.ts +223 -0
- package/table/src/hooks/useTableCellToolbarActions.tsx +694 -0
- package/table/src/hooks/useTableMetadata.ts +12 -0
- package/table/src/hooks/useTableStateContext.ts +12 -0
- package/table/src/hooks/useTableStates.ts +14 -0
- package/table/src/typings.ts +107 -0
- package/table/src/utils/helper.ts +1106 -0
- package/table/toolbar/package.json +6 -3
- package/table/toolbar/src/TableToolbarIcon.tsx +17 -0
- package/table/toolbar/src/useTableTool.ts +14 -0
- package/table/toolbar/tsconfig.build.json +5 -0
- package/table/tsconfig.build.json +5 -0
- package/toggle-mark/jsx-serializer/package.json +6 -3
- package/toggle-mark/jsx-serializer/src/createJsxSerializeToggleMarkCreator.ts +12 -0
- package/toggle-mark/jsx-serializer/src/index.ts +6 -0
- package/toggle-mark/jsx-serializer/src/typings.ts +3 -0
- package/toggle-mark/jsx-serializer/tsconfig.build.json +5 -0
- package/toggle-mark/package.json +5 -3
- package/toggle-mark/src/createReactToggleMarkCreator.ts +33 -0
- package/toggle-mark/src/index.ts +6 -0
- package/toggle-mark/src/typings.ts +24 -0
- package/toggle-mark/toolbar/package.json +6 -3
- package/toggle-mark/toolbar/src/ToggleMarkToolbarIcon.tsx +17 -0
- package/toggle-mark/toolbar/src/useToggleMarkTool.ts +11 -0
- package/toggle-mark/toolbar/tsconfig.build.json +5 -0
- package/toggle-mark/tsconfig.build.json +5 -0
- package/toolbar/package.json +9 -3
- package/toolbar/src/components/InlineToolbar.tsx +81 -0
- package/toolbar/src/components/Toolbar.tsx +243 -0
- package/toolbar/src/components/ToolbarGroupIcon.tsx +131 -0
- package/toolbar/src/components/ToolbarIcon.tsx +84 -0
- package/toolbar/src/components/ToolbarInput.tsx +66 -0
- package/toolbar/src/components/toolbarIconName.ts +125 -0
- package/toolbar/src/constants.tsx +6 -0
- package/toolbar/src/contexts/StartToolInputContext.ts +4 -0
- package/toolbar/src/contexts/toolbar.ts +14 -0
- package/toolbar/src/contexts/toolbarMenu.ts +15 -0
- package/toolbar/src/hooks/useAutoGroupIcons.tsx +98 -0
- package/toolbar/src/hooks/useStartToolInput.ts +6 -0
- package/toolbar/src/index.ts +7 -0
- package/toolbar/src/typings.ts +8 -0
- package/toolbar/tsconfig.build.json +5 -0
- package/tsconfig.build.json +8 -0
- package/underline/jsx-serializer/package.json +6 -3
- package/underline/jsx-serializer/src/createJsxSerializeUnderline.ts +8 -0
- package/underline/jsx-serializer/tsconfig.build.json +5 -0
- package/underline/package.json +5 -3
- package/underline/src/constants.ts +6 -0
- package/underline/src/createReactUnderline.ts +11 -0
- package/underline/src/defaultRenderUnderline.tsx +12 -0
- package/underline/src/typings.ts +6 -0
- package/underline/tsconfig.build.json +5 -0
- package/utils/package.json +1 -4
- package/utils/src/composeRefs.ts +34 -0
- package/utils/src/removePreviousElement.ts +62 -0
- package/utils/src/upload.ts +83 -0
- package/utils/src/useClickAway.ts +34 -0
- package/utils/src/useDocumentEvents.ts +35 -0
- package/utils/src/useIsomorphicLayoutEffect.ts +8 -0
- package/utils/src/usePreviousValue.ts +11 -0
- package/utils/tsconfig.build.json +5 -0
- package/LICENSE +0 -21
- package/_internal/index.cjs.js +0 -59
- package/_internal/index.js +0 -5
- package/_internal/renderer/composeRenderElementsBase.d.ts +0 -3
- package/_internal/renderer/composeRenderElementsBase.js +0 -13
- package/_internal/renderer/composeRenderLeafsBase.d.ts +0 -3
- package/_internal/renderer/composeRenderLeafsBase.js +0 -11
- package/_internal/renderer/createRenderElementBase.d.ts +0 -3
- package/_internal/renderer/createRenderElementBase.js +0 -9
- package/_internal/renderer/createRenderElementsBase.d.ts +0 -3
- package/_internal/renderer/createRenderElementsBase.js +0 -11
- package/_internal/renderer/createRenderMarkBase.d.ts +0 -3
- package/_internal/renderer/createRenderMarkBase.js +0 -13
- package/_internal/renderer/typings.d.ts +0 -19
- package/accordion/accordion.css +0 -1
- package/accordion/components/Accordion.d.ts +0 -9
- package/accordion/components/Accordion.js +0 -22
- package/accordion/components/AccordionContent.d.ts +0 -8
- package/accordion/components/AccordionContent.js +0 -24
- package/accordion/components/AccordionTitle.d.ts +0 -8
- package/accordion/components/AccordionTitle.js +0 -20
- package/accordion/contexts/AccordionContext.d.ts +0 -2
- package/accordion/contexts/AccordionContext.js +0 -5
- package/accordion/createReactAccordion.d.ts +0 -4
- package/accordion/createReactAccordion.js +0 -144
- package/accordion/defaultRenderAccordionElements.d.ts +0 -2
- package/accordion/defaultRenderAccordionElements.js +0 -12
- package/accordion/hooks/useAccordion.d.ts +0 -1
- package/accordion/hooks/useAccordion.js +0 -8
- package/accordion/index.cjs.js +0 -212
- package/accordion/index.js +0 -10
- package/accordion/jsx-serializer/createJsxSerializeAccordion.d.ts +0 -5
- package/accordion/jsx-serializer/createJsxSerializeAccordion.js +0 -48
- package/accordion/jsx-serializer/defaultRenderAccordionElements.d.ts +0 -2
- package/accordion/jsx-serializer/defaultRenderAccordionElements.js +0 -16
- package/accordion/jsx-serializer/index.cjs.js +0 -64
- package/accordion/jsx-serializer/index.js +0 -2
- package/accordion/jsx-serializer/typings.d.ts +0 -4
- package/accordion/toolbar/AccordionToolbarIcon.d.ts +0 -8
- package/accordion/toolbar/AccordionToolbarIcon.js +0 -12
- package/accordion/toolbar/index.cjs.js +0 -22
- package/accordion/toolbar/index.d.ts +0 -2
- package/accordion/toolbar/index.js +0 -2
- package/accordion/toolbar/useAccordionTool.d.ts +0 -4
- package/accordion/toolbar/useAccordionTool.js +0 -10
- package/accordion/typings.d.ts +0 -25
- package/align/constants.d.ts +0 -8
- package/align/constants.js +0 -10
- package/align/createReactAlign.d.ts +0 -2
- package/align/createReactAlign.js +0 -26
- package/align/index.cjs.js +0 -39
- package/align/index.js +0 -2
- package/align/toolbar/AlignToolbarIcon.d.ts +0 -10
- package/align/toolbar/AlignToolbarIcon.js +0 -12
- package/align/toolbar/index.cjs.js +0 -30
- package/align/toolbar/index.d.ts +0 -2
- package/align/toolbar/index.js +0 -2
- package/align/toolbar/useAlignTool.d.ts +0 -6
- package/align/toolbar/useAlignTool.js +0 -18
- package/align/typings.d.ts +0 -17
- package/blockquote/blockquote.css +0 -1
- package/blockquote/constants.d.ts +0 -6
- package/blockquote/constants.js +0 -8
- package/blockquote/createReactBlockquote.d.ts +0 -5
- package/blockquote/createReactBlockquote.js +0 -84
- package/blockquote/defaultRenderBlockquoteElement.d.ts +0 -6
- package/blockquote/defaultRenderBlockquoteElement.js +0 -7
- package/blockquote/index.cjs.js +0 -98
- package/blockquote/index.js +0 -3
- package/blockquote/jsx-serializer/createJsxSerializeBlockquote.d.ts +0 -4
- package/blockquote/jsx-serializer/createJsxSerializeBlockquote.js +0 -10
- package/blockquote/jsx-serializer/index.cjs.js +0 -13
- package/blockquote/jsx-serializer/index.js +0 -2
- package/blockquote/jsx-serializer/typings.d.ts +0 -3
- package/blockquote/toolbar/BlockquoteToolbarIcon.d.ts +0 -8
- package/blockquote/toolbar/BlockquoteToolbarIcon.js +0 -12
- package/blockquote/toolbar/index.cjs.js +0 -23
- package/blockquote/toolbar/index.js +0 -2
- package/blockquote/toolbar/useBlockquoteTool.d.ts +0 -5
- package/blockquote/toolbar/useBlockquoteTool.js +0 -11
- package/blockquote/typings.d.ts +0 -16
- package/bold/constants.d.ts +0 -6
- package/bold/constants.js +0 -8
- package/bold/createReactBold.d.ts +0 -1
- package/bold/createReactBold.js +0 -11
- package/bold/defaultRenderBold.d.ts +0 -3
- package/bold/defaultRenderBold.js +0 -7
- package/bold/index.cjs.js +0 -24
- package/bold/index.js +0 -3
- package/bold/jsx-serializer/createJsxSerializeBold.d.ts +0 -1
- package/bold/jsx-serializer/createJsxSerializeBold.js +0 -10
- package/bold/jsx-serializer/index.cjs.js +0 -13
- package/bold/jsx-serializer/index.js +0 -2
- package/bold/typings.d.ts +0 -5
- package/card/card.css +0 -1
- package/card/components/Card.d.ts +0 -15
- package/card/components/Card.js +0 -109
- package/card/components/CardContents.d.ts +0 -9
- package/card/components/CardContents.js +0 -18
- package/card/components/CardImage.d.ts +0 -9
- package/card/components/CardImage.js +0 -13
- package/card/components/CardPlaceholder.d.ts +0 -4
- package/card/components/CardPlaceholder.js +0 -21
- package/card/createReactCard.d.ts +0 -4
- package/card/createReactCard.js +0 -82
- package/card/defaultRenderCardElements.d.ts +0 -3
- package/card/defaultRenderCardElements.js +0 -14
- package/card/index.cjs.js +0 -242
- package/card/index.js +0 -6
- package/card/jsx-serializer/createJsxSerializeCard.d.ts +0 -5
- package/card/jsx-serializer/createJsxSerializeCard.js +0 -48
- package/card/jsx-serializer/defaultRenderCardElements.d.ts +0 -2
- package/card/jsx-serializer/defaultRenderCardElements.js +0 -10
- package/card/jsx-serializer/index.cjs.js +0 -58
- package/card/jsx-serializer/index.js +0 -2
- package/card/jsx-serializer/typings.d.ts +0 -9
- package/card/toolbar/CardToolbarIcon.d.ts +0 -8
- package/card/toolbar/CardToolbarIcon.js +0 -12
- package/card/toolbar/index.cjs.js +0 -52
- package/card/toolbar/index.js +0 -2
- package/card/toolbar/useCardTool.d.ts +0 -4
- package/card/toolbar/useCardTool.js +0 -41
- package/card/typings.d.ts +0 -24
- package/carousel/carousel.css +0 -1
- package/carousel/components/Carousel.d.ts +0 -15
- package/carousel/components/Carousel.js +0 -66
- package/carousel/components/CarouselCaption.d.ts +0 -9
- package/carousel/components/CarouselCaption.js +0 -12
- package/carousel/components/CarouselImages.d.ts +0 -9
- package/carousel/components/CarouselImages.js +0 -52
- package/carousel/components/CarouselPlaceholder.d.ts +0 -4
- package/carousel/components/CarouselPlaceholder.js +0 -17
- package/carousel/contexts/CarouselContext.d.ts +0 -2
- package/carousel/contexts/CarouselContext.js +0 -5
- package/carousel/createReactCarousel.d.ts +0 -4
- package/carousel/createReactCarousel.js +0 -82
- package/carousel/defaultRenderCarouselElements.d.ts +0 -3
- package/carousel/defaultRenderCarouselElements.js +0 -14
- package/carousel/hooks/useCarousel.d.ts +0 -1
- package/carousel/hooks/useCarousel.js +0 -8
- package/carousel/index.cjs.js +0 -236
- package/carousel/index.js +0 -8
- package/carousel/jsx-serializer/createJsxSerializeCarousel.d.ts +0 -5
- package/carousel/jsx-serializer/createJsxSerializeCarousel.js +0 -48
- package/carousel/jsx-serializer/defaultRenderCarouselElements.d.ts +0 -2
- package/carousel/jsx-serializer/defaultRenderCarouselElements.js +0 -10
- package/carousel/jsx-serializer/index.cjs.js +0 -58
- package/carousel/jsx-serializer/index.js +0 -2
- package/carousel/jsx-serializer/typings.d.ts +0 -9
- package/carousel/toolbar/CarouselToolbarIcon.d.ts +0 -8
- package/carousel/toolbar/CarouselToolbarIcon.js +0 -12
- package/carousel/toolbar/index.cjs.js +0 -43
- package/carousel/toolbar/index.js +0 -2
- package/carousel/toolbar/useCarouselTool.d.ts +0 -4
- package/carousel/toolbar/useCarouselTool.js +0 -32
- package/carousel/typings.d.ts +0 -27
- package/components/BaseField/index.d.ts +0 -11
- package/components/BaseField/index.js +0 -17
- package/components/Button/index.d.ts +0 -14
- package/components/Button/index.js +0 -12
- package/components/Hint/index.d.ts +0 -15
- package/components/Hint/index.js +0 -30
- package/components/Icon/index.d.ts +0 -15
- package/components/Icon/index.js +0 -21
- package/components/ImageUploader/index.d.ts +0 -25
- package/components/ImageUploader/index.js +0 -174
- package/components/Input/index.d.ts +0 -14
- package/components/Input/index.js +0 -24
- package/components/Message/index.d.ts +0 -18
- package/components/Message/index.js +0 -72
- package/components/Modal/index.d.ts +0 -28
- package/components/Modal/index.js +0 -61
- package/components/Notifier/NotifierManager.d.ts +0 -17
- package/components/Notifier/NotifierManager.js +0 -24
- package/components/Notifier/createNotifier.d.ts +0 -21
- package/components/Notifier/createNotifier.js +0 -60
- package/components/Notifier/typings.d.ts +0 -28
- package/components/Portal/index.d.ts +0 -7
- package/components/Portal/index.js +0 -8
- package/components/Progress/index.d.ts +0 -7
- package/components/Progress/index.js +0 -20
- package/components/SegmentedControl/index.d.ts +0 -13
- package/components/SegmentedControl/index.js +0 -14
- package/components/Textarea/index.d.ts +0 -15
- package/components/Textarea/index.js +0 -24
- package/components/Toggle/index.d.ts +0 -8
- package/components/Toggle/index.js +0 -15
- package/components/Tooltip/calculatePosition.d.ts +0 -5
- package/components/Tooltip/calculatePosition.js +0 -69
- package/components/Tooltip/index.d.ts +0 -10
- package/components/Tooltip/index.js +0 -124
- package/components/Tooltip/typings.d.ts +0 -29
- package/components/Transition/SlideFade.d.ts +0 -20
- package/components/Transition/SlideFade.js +0 -91
- package/components/Transition/Transition.d.ts +0 -98
- package/components/Transition/Transition.js +0 -27
- package/components/Transition/getTransitionStyleProps.d.ts +0 -16
- package/components/Transition/getTransitionStyleProps.js +0 -25
- package/components/Transition/useSetNodeTransition.d.ts +0 -7
- package/components/Transition/useSetNodeTransition.js +0 -35
- package/components/baseField.css +0 -1
- package/components/button.css +0 -1
- package/components/components.css +0 -1
- package/components/hint.css +0 -1
- package/components/imageUploader.css +0 -1
- package/components/index.cjs.js +0 -867
- package/components/index.d.ts +0 -16
- package/components/index.js +0 -15
- package/components/input.css +0 -1
- package/components/message.css +0 -1
- package/components/modal.css +0 -1
- package/components/progress.css +0 -1
- package/components/segmentedControl.css +0 -1
- package/components/textarea.css +0 -1
- package/components/toggle.css +0 -1
- package/components/tooltip.css +0 -1
- package/configs/ConfigsProvider.d.ts +0 -18
- package/configs/ConfigsProvider.js +0 -15
- package/configs/index.cjs.js +0 -58
- package/configs/index.js +0 -3
- package/configs/locale.d.ts +0 -3
- package/configs/locale.js +0 -9
- package/configs/theme.d.ts +0 -18
- package/configs/theme.js +0 -34
- package/core/components/DefaultElement.d.ts +0 -4
- package/core/components/DefaultElement.js +0 -5
- package/core/components/DefaultLeaf.d.ts +0 -4
- package/core/components/DefaultLeaf.js +0 -5
- package/core/components/Editable.d.ts +0 -9
- package/core/components/Editable.js +0 -71
- package/core/components/Quadrats.d.ts +0 -16
- package/core/components/Quadrats.js +0 -21
- package/core/composeHandlers.d.ts +0 -8
- package/core/composeHandlers.js +0 -33
- package/core/composeRenderElements.d.ts +0 -6
- package/core/composeRenderElements.js +0 -12
- package/core/composeRenderLeafs.d.ts +0 -6
- package/core/composeRenderLeafs.js +0 -12
- package/core/constants.d.ts +0 -1
- package/core/constants.js +0 -3
- package/core/contexts/composition/CompositionProvider.d.ts +0 -8
- package/core/contexts/composition/CompositionProvider.js +0 -8
- package/core/contexts/composition/composition.d.ts +0 -7
- package/core/contexts/composition/composition.js +0 -11
- package/core/contexts/message/MessageProvider.d.ts +0 -5
- package/core/contexts/message/MessageProvider.js +0 -35
- package/core/contexts/message/message.d.ts +0 -10
- package/core/contexts/message/message.js +0 -10
- package/core/contexts/modal/CardModal/CardModal.d.ts +0 -28
- package/core/contexts/modal/CardModal/CardModal.js +0 -111
- package/core/contexts/modal/CarouselModal/CarouselItem.d.ts +0 -15
- package/core/contexts/modal/CarouselModal/CarouselItem.js +0 -46
- package/core/contexts/modal/CarouselModal/CarouselModal.d.ts +0 -11
- package/core/contexts/modal/CarouselModal/CarouselModal.js +0 -169
- package/core/contexts/modal/CarouselModal/FilesDropZone.d.ts +0 -13
- package/core/contexts/modal/CarouselModal/FilesDropZone.js +0 -64
- package/core/contexts/modal/ConfirmModal/ConfirmModal.d.ts +0 -14
- package/core/contexts/modal/ConfirmModal/ConfirmModal.js +0 -24
- package/core/contexts/modal/EmbedModal/EmbedModal.d.ts +0 -11
- package/core/contexts/modal/EmbedModal/EmbedModal.js +0 -27
- package/core/contexts/modal/ModalProvider.d.ts +0 -8
- package/core/contexts/modal/ModalProvider.js +0 -66
- package/core/contexts/modal/modal.d.ts +0 -24
- package/core/contexts/modal/modal.js +0 -15
- package/core/createReactEditor.d.ts +0 -2
- package/core/createReactEditor.js +0 -8
- package/core/createRenderElement.d.ts +0 -2
- package/core/createRenderElement.js +0 -7
- package/core/createRenderElements.d.ts +0 -2
- package/core/createRenderElements.js +0 -7
- package/core/createRenderMark.d.ts +0 -2
- package/core/createRenderMark.js +0 -7
- package/core/index.d.ts +0 -37
- package/core/typings/descendant.d.ts +0 -2
- package/core/typings/handler.d.ts +0 -15
- package/core/typings/renderer.d.ts +0 -15
- package/core/typings/with.d.ts +0 -4
- package/divider/createReactDivider.d.ts +0 -4
- package/divider/createReactDivider.js +0 -11
- package/divider/defaultRenderDividerElement.d.ts +0 -3
- package/divider/defaultRenderDividerElement.js +0 -6
- package/divider/divider.css +0 -1
- package/divider/index.cjs.js +0 -17
- package/divider/index.js +0 -2
- package/divider/jsx-serializer/createJsxSerializeDivider.d.ts +0 -4
- package/divider/jsx-serializer/createJsxSerializeDivider.js +0 -10
- package/divider/jsx-serializer/defaultRenderDividerElement.d.ts +0 -2
- package/divider/jsx-serializer/defaultRenderDividerElement.js +0 -6
- package/divider/jsx-serializer/index.cjs.js +0 -16
- package/divider/jsx-serializer/index.js +0 -2
- package/divider/jsx-serializer/typings.d.ts +0 -3
- package/divider/toolbar/DividerToolbarIcon.d.ts +0 -8
- package/divider/toolbar/DividerToolbarIcon.js +0 -12
- package/divider/toolbar/index.cjs.js +0 -22
- package/divider/toolbar/index.d.ts +0 -2
- package/divider/toolbar/index.js +0 -2
- package/divider/toolbar/useDividerTool.d.ts +0 -4
- package/divider/toolbar/useDividerTool.js +0 -10
- package/divider/typings.d.ts +0 -10
- package/editable.css +0 -1
- package/embed/components/VideoIframe.d.ts +0 -12
- package/embed/components/VideoIframe.js +0 -16
- package/embed/createReactEmbed.d.ts +0 -4
- package/embed/createReactEmbed.js +0 -17
- package/embed/createRenderEmbedElementBase.d.ts +0 -10
- package/embed/createRenderEmbedElementBase.js +0 -16
- package/embed/embed.css +0 -1
- package/embed/hooks/useVideoIframeSize.d.ts +0 -7
- package/embed/hooks/useVideoIframeSize.js +0 -24
- package/embed/index.cjs.js +0 -68
- package/embed/index.js +0 -4
- package/embed/jsx-serializer/createJsxSerializeEmbed.d.ts +0 -9
- package/embed/jsx-serializer/createJsxSerializeEmbed.js +0 -13
- package/embed/jsx-serializer/facebook/index.cjs.js +0 -16
- package/embed/jsx-serializer/facebook/index.js +0 -1
- package/embed/jsx-serializer/index.cjs.js +0 -15
- package/embed/jsx-serializer/index.js +0 -1
- package/embed/jsx-serializer/instagram/index.cjs.js +0 -16
- package/embed/jsx-serializer/instagram/index.js +0 -1
- package/embed/jsx-serializer/twitter/index.cjs.js +0 -16
- package/embed/jsx-serializer/twitter/index.js +0 -1
- package/embed/jsx-serializer/typings.d.ts +0 -5
- package/embed/jsx-serializer/vimeo/index.cjs.js +0 -16
- package/embed/jsx-serializer/vimeo/index.js +0 -1
- package/embed/jsx-serializer/youtube/index.cjs.js +0 -7
- package/embed/jsx-serializer/youtube/index.js +0 -1
- package/embed/renderers/base/components/BaseEmbedElement.d.ts +0 -21
- package/embed/renderers/base/components/BaseEmbedElement.js +0 -70
- package/embed/renderers/base/index.cjs.js +0 -73
- package/embed/renderers/base/index.d.ts +0 -1
- package/embed/renderers/base/index.js +0 -1
- package/embed/renderers/facebook/components/Facebook.d.ts +0 -12
- package/embed/renderers/facebook/components/Facebook.js +0 -23
- package/embed/renderers/facebook/defaultRenderFacebookEmbedElement.d.ts +0 -5
- package/embed/renderers/facebook/defaultRenderFacebookEmbedElement.js +0 -46
- package/embed/renderers/facebook/index.cjs.js +0 -70
- package/embed/renderers/facebook/index.js +0 -2
- package/embed/renderers/instagram/components/Instagram.d.ts +0 -12
- package/embed/renderers/instagram/components/Instagram.js +0 -53
- package/embed/renderers/instagram/defaultRenderInstagramEmbedElement.d.ts +0 -5
- package/embed/renderers/instagram/defaultRenderInstagramEmbedElement.js +0 -46
- package/embed/renderers/instagram/hooks/useLoadInstagramEmbedApi.d.ts +0 -1
- package/embed/renderers/instagram/hooks/useLoadInstagramEmbedApi.js +0 -27
- package/embed/renderers/instagram/index.cjs.js +0 -124
- package/embed/renderers/instagram/index.js +0 -3
- package/embed/renderers/podcast-apple/components/PodcastApple.d.ts +0 -11
- package/embed/renderers/podcast-apple/components/PodcastApple.js +0 -13
- package/embed/renderers/podcast-apple/defaultRenderPodcastAppleEmbedElement.d.ts +0 -5
- package/embed/renderers/podcast-apple/defaultRenderPodcastAppleEmbedElement.js +0 -39
- package/embed/renderers/podcast-apple/index.cjs.js +0 -53
- package/embed/renderers/podcast-apple/index.js +0 -2
- package/embed/renderers/spotify/components/Spotify.d.ts +0 -12
- package/embed/renderers/spotify/components/Spotify.js +0 -18
- package/embed/renderers/spotify/defaultRenderSpotifyEmbedElement.d.ts +0 -5
- package/embed/renderers/spotify/defaultRenderSpotifyEmbedElement.js +0 -36
- package/embed/renderers/spotify/index.cjs.js +0 -55
- package/embed/renderers/spotify/index.js +0 -2
- package/embed/renderers/twitter/components/Twitter.d.ts +0 -12
- package/embed/renderers/twitter/components/Twitter.js +0 -24
- package/embed/renderers/twitter/defaultRenderTwitterEmbedElement.d.ts +0 -5
- package/embed/renderers/twitter/defaultRenderTwitterEmbedElement.js +0 -46
- package/embed/renderers/twitter/hooks/useLoadTwitterEmbedApi.d.ts +0 -2
- package/embed/renderers/twitter/hooks/useLoadTwitterEmbedApi.js +0 -33
- package/embed/renderers/twitter/index.cjs.js +0 -101
- package/embed/renderers/twitter/index.js +0 -3
- package/embed/renderers/vimeo/defaultRenderVimeoEmbedElement.d.ts +0 -6
- package/embed/renderers/vimeo/defaultRenderVimeoEmbedElement.js +0 -30
- package/embed/renderers/vimeo/index.cjs.js +0 -34
- package/embed/renderers/vimeo/index.js +0 -1
- package/embed/renderers/youtube/defaultRenderYoutubeEmbedElement.d.ts +0 -6
- package/embed/renderers/youtube/defaultRenderYoutubeEmbedElement.js +0 -30
- package/embed/renderers/youtube/index.cjs.js +0 -34
- package/embed/renderers/youtube/index.js +0 -1
- package/embed/toolbar/EmbedToolbarIcon.d.ts +0 -12
- package/embed/toolbar/EmbedToolbarIcon.js +0 -12
- package/embed/toolbar/index.cjs.js +0 -98
- package/embed/toolbar/index.d.ts +0 -2
- package/embed/toolbar/index.js +0 -2
- package/embed/toolbar/useEmbedTool.d.ts +0 -4
- package/embed/toolbar/useEmbedTool.js +0 -87
- package/embed/typings.d.ts +0 -15
- package/file-uploader/components/FileUploader.d.ts +0 -4
- package/file-uploader/components/FileUploader.js +0 -13
- package/file-uploader/createReactFileUploader.d.ts +0 -5
- package/file-uploader/createReactFileUploader.js +0 -17
- package/file-uploader/defaultRenderFileUploaderElement.d.ts +0 -2
- package/file-uploader/defaultRenderFileUploaderElement.js +0 -6
- package/file-uploader/file-uploader.css +0 -1
- package/file-uploader/hooks/useFileUploader.d.ts +0 -4
- package/file-uploader/hooks/useFileUploader.js +0 -13
- package/file-uploader/index.cjs.js +0 -42
- package/file-uploader/index.js +0 -4
- package/file-uploader/toolbar/FileUploaderToolbarIcon.d.ts +0 -10
- package/file-uploader/toolbar/FileUploaderToolbarIcon.js +0 -12
- package/file-uploader/toolbar/index.cjs.js +0 -25
- package/file-uploader/toolbar/index.d.ts +0 -2
- package/file-uploader/toolbar/index.js +0 -2
- package/file-uploader/toolbar/useFileUploaderTool.d.ts +0 -5
- package/file-uploader/toolbar/useFileUploaderTool.js +0 -13
- package/file-uploader/typings.d.ts +0 -14
- package/footnote/createReactFootnote.d.ts +0 -4
- package/footnote/createReactFootnote.js +0 -13
- package/footnote/defaultRenderFootnoteElement.d.ts +0 -9
- package/footnote/defaultRenderFootnoteElement.js +0 -15
- package/footnote/index.cjs.js +0 -71
- package/footnote/index.js +0 -4
- package/footnote/jsx-serializer/createJsxSerializeFootnote.d.ts +0 -4
- package/footnote/jsx-serializer/createJsxSerializeFootnote.js +0 -11
- package/footnote/jsx-serializer/defaultRenderFootnoteElement.d.ts +0 -5
- package/footnote/jsx-serializer/defaultRenderFootnoteElement.js +0 -12
- package/footnote/jsx-serializer/index.cjs.js +0 -23
- package/footnote/jsx-serializer/index.js +0 -2
- package/footnote/jsx-serializer/typings.d.ts +0 -3
- package/footnote/toolbar/FootnoteToolbarIcon.d.ts +0 -10
- package/footnote/toolbar/FootnoteToolbarIcon.js +0 -12
- package/footnote/toolbar/index.cjs.js +0 -30
- package/footnote/toolbar/index.d.ts +0 -2
- package/footnote/toolbar/index.js +0 -2
- package/footnote/toolbar/useFootnoteTool.d.ts +0 -7
- package/footnote/toolbar/useFootnoteTool.js +0 -19
- package/footnote/typings.d.ts +0 -9
- package/footnote/useFootnotes.d.ts +0 -4
- package/footnote/useFootnotes.js +0 -21
- package/footnote/useFootnotesFromNodes.d.ts +0 -4
- package/footnote/useFootnotesFromNodes.js +0 -27
- package/heading/constants.d.ts +0 -9
- package/heading/constants.js +0 -11
- package/heading/createReactHeading.d.ts +0 -4
- package/heading/createReactHeading.js +0 -47
- package/heading/defaultRenderHeadingElement.d.ts +0 -16
- package/heading/defaultRenderHeadingElement.js +0 -20
- package/heading/heading.css +0 -1
- package/heading/index.cjs.js +0 -77
- package/heading/index.js +0 -3
- package/heading/jsx-serializer/createJsxSerializeHeading.d.ts +0 -4
- package/heading/jsx-serializer/createJsxSerializeHeading.js +0 -10
- package/heading/jsx-serializer/index.cjs.js +0 -13
- package/heading/jsx-serializer/index.js +0 -2
- package/heading/jsx-serializer/typings.d.ts +0 -3
- package/heading/toolbar/HeadingToolbarIcon.d.ts +0 -10
- package/heading/toolbar/HeadingToolbarIcon.js +0 -12
- package/heading/toolbar/index.cjs.js +0 -23
- package/heading/toolbar/index.d.ts +0 -2
- package/heading/toolbar/index.js +0 -2
- package/heading/toolbar/useToggleHeadingTool.d.ts +0 -6
- package/heading/toolbar/useToggleHeadingTool.js +0 -11
- package/heading/typings.d.ts +0 -15
- package/highlight/constants.d.ts +0 -6
- package/highlight/constants.js +0 -8
- package/highlight/createReactHighlight.d.ts +0 -1
- package/highlight/createReactHighlight.js +0 -11
- package/highlight/defaultRenderHighlight.d.ts +0 -3
- package/highlight/defaultRenderHighlight.js +0 -7
- package/highlight/index.cjs.js +0 -24
- package/highlight/index.js +0 -3
- package/highlight/jsx-serializer/createJsxSerializeHighlight.d.ts +0 -1
- package/highlight/jsx-serializer/createJsxSerializeHighlight.js +0 -10
- package/highlight/jsx-serializer/index.cjs.js +0 -13
- package/highlight/jsx-serializer/index.js +0 -2
- package/highlight/typings.d.ts +0 -5
- package/image/components/Image.d.ts +0 -4
- package/image/components/Image.js +0 -68
- package/image/components/ImageCaption.d.ts +0 -4
- package/image/components/ImageCaption.js +0 -23
- package/image/components/ImageFigure.d.ts +0 -4
- package/image/components/ImageFigure.js +0 -15
- package/image/components/ImagePlaceholder.d.ts +0 -4
- package/image/components/ImagePlaceholder.js +0 -11
- package/image/createReactImage.d.ts +0 -8
- package/image/createReactImage.js +0 -121
- package/image/defaultRenderImageElements.d.ts +0 -2
- package/image/defaultRenderImageElements.js +0 -12
- package/image/hooks/useImageResizer.d.ts +0 -7
- package/image/hooks/useImageResizer.js +0 -95
- package/image/image.css +0 -1
- package/image/index.cjs.js +0 -324
- package/image/index.js +0 -7
- package/image/jsx-serializer/createJsxSerializeImage.d.ts +0 -7
- package/image/jsx-serializer/createJsxSerializeImage.js +0 -42
- package/image/jsx-serializer/defaultRenderImageElements.d.ts +0 -2
- package/image/jsx-serializer/defaultRenderImageElements.js +0 -10
- package/image/jsx-serializer/index.cjs.js +0 -52
- package/image/jsx-serializer/index.js +0 -2
- package/image/jsx-serializer/typings.d.ts +0 -18
- package/image/typings.d.ts +0 -22
- package/index.cjs.js +0 -709
- package/index.js +0 -21
- package/input-block/components/InputBlock.d.ts +0 -4
- package/input-block/components/InputBlock.js +0 -35
- package/input-block/createReactInputBlock.d.ts +0 -4
- package/input-block/createReactInputBlock.js +0 -17
- package/input-block/defaultRenderInputBlockElement.d.ts +0 -2
- package/input-block/defaultRenderInputBlockElement.js +0 -6
- package/input-block/hooks/useInputBlock.d.ts +0 -10
- package/input-block/hooks/useInputBlock.js +0 -57
- package/input-block/index.cjs.js +0 -108
- package/input-block/index.js +0 -4
- package/input-block/input-block.css +0 -1
- package/input-block/typings.d.ts +0 -12
- package/italic/constants.d.ts +0 -6
- package/italic/constants.js +0 -8
- package/italic/createReactItalic.d.ts +0 -1
- package/italic/createReactItalic.js +0 -11
- package/italic/defaultRenderItalic.d.ts +0 -3
- package/italic/defaultRenderItalic.js +0 -7
- package/italic/index.cjs.js +0 -24
- package/italic/index.js +0 -3
- package/italic/jsx-serializer/createJsxSerializeItalic.d.ts +0 -1
- package/italic/jsx-serializer/createJsxSerializeItalic.js +0 -10
- package/italic/jsx-serializer/index.cjs.js +0 -13
- package/italic/jsx-serializer/index.js +0 -2
- package/italic/typings.d.ts +0 -5
- package/jsx-serializer/createJsxSerializeElement.d.ts +0 -2
- package/jsx-serializer/createJsxSerializeElement.js +0 -7
- package/jsx-serializer/createJsxSerializeElements.d.ts +0 -2
- package/jsx-serializer/createJsxSerializeElements.js +0 -7
- package/jsx-serializer/createJsxSerializeMark.d.ts +0 -2
- package/jsx-serializer/createJsxSerializeMark.js +0 -7
- package/jsx-serializer/createJsxSerializer.d.ts +0 -18
- package/jsx-serializer/createJsxSerializer.js +0 -49
- package/jsx-serializer/index.cjs.js +0 -66
- package/jsx-serializer/index.js +0 -4
- package/jsx-serializer/typings.d.ts +0 -8
- package/line-break/commonBreak.d.ts +0 -14
- package/line-break/commonBreak.js +0 -31
- package/line-break/createOnKeyDownBreak.d.ts +0 -11
- package/line-break/createOnKeyDownBreak.js +0 -48
- package/line-break/createReactLineBreak.d.ts +0 -4
- package/line-break/createReactLineBreak.js +0 -14
- package/line-break/defaultRenderLineBreakElement.d.ts +0 -3
- package/line-break/defaultRenderLineBreakElement.js +0 -5
- package/line-break/index.cjs.js +0 -102
- package/line-break/index.js +0 -4
- package/line-break/jsx-serializer/createJsxSerializeLineBreak.d.ts +0 -4
- package/line-break/jsx-serializer/createJsxSerializeLineBreak.js +0 -11
- package/line-break/jsx-serializer/defaultRenderLineBreakElement.d.ts +0 -2
- package/line-break/jsx-serializer/defaultRenderLineBreakElement.js +0 -6
- package/line-break/jsx-serializer/index.cjs.js +0 -17
- package/line-break/jsx-serializer/index.js +0 -2
- package/line-break/jsx-serializer/typings.d.ts +0 -3
- package/line-break/line-break.css +0 -1
- package/line-break/renderLineBreakElementWithSymbol.d.ts +0 -3
- package/line-break/renderLineBreakElementWithSymbol.js +0 -5
- package/line-break/typings.d.ts +0 -32
- package/link/createReactLink.d.ts +0 -11
- package/link/createReactLink.js +0 -29
- package/link/defaultRenderLinkElement.d.ts +0 -9
- package/link/defaultRenderLinkElement.js +0 -13
- package/link/index.cjs.js +0 -42
- package/link/index.js +0 -2
- package/link/jsx-serializer/createJsxSerializeLink.d.ts +0 -4
- package/link/jsx-serializer/createJsxSerializeLink.js +0 -10
- package/link/jsx-serializer/defaultRenderLinkElement.d.ts +0 -3
- package/link/jsx-serializer/defaultRenderLinkElement.js +0 -5
- package/link/jsx-serializer/index.cjs.js +0 -15
- package/link/jsx-serializer/index.js +0 -2
- package/link/jsx-serializer/typings.d.ts +0 -3
- package/link/toolbar/LinkToolbarIcon.d.ts +0 -10
- package/link/toolbar/LinkToolbarIcon.js +0 -12
- package/link/toolbar/UnlinkToolbarIcon.d.ts +0 -8
- package/link/toolbar/UnlinkToolbarIcon.js +0 -12
- package/link/toolbar/index.cjs.js +0 -51
- package/link/toolbar/index.d.ts +0 -4
- package/link/toolbar/index.js +0 -4
- package/link/toolbar/useLinkTool.d.ts +0 -7
- package/link/toolbar/useLinkTool.js +0 -25
- package/link/toolbar/useUnlinkTool.d.ts +0 -4
- package/link/toolbar/useUnlinkTool.js +0 -10
- package/link/typings.d.ts +0 -9
- package/list/createReactList.d.ts +0 -4
- package/list/createReactList.js +0 -35
- package/list/defaultRenderListElements.d.ts +0 -7
- package/list/defaultRenderListElements.js +0 -9
- package/list/index.cjs.js +0 -44
- package/list/index.js +0 -2
- package/list/jsx-serializer/createJsxSerializeList.d.ts +0 -7
- package/list/jsx-serializer/createJsxSerializeList.js +0 -15
- package/list/jsx-serializer/index.cjs.js +0 -18
- package/list/jsx-serializer/index.js +0 -2
- package/list/jsx-serializer/typings.d.ts +0 -3
- package/list/list.css +0 -1
- package/list/toolbar/ListToolbarIcon.d.ts +0 -10
- package/list/toolbar/ListToolbarIcon.js +0 -12
- package/list/toolbar/index.cjs.js +0 -23
- package/list/toolbar/index.d.ts +0 -2
- package/list/toolbar/index.js +0 -2
- package/list/toolbar/useListTool.d.ts +0 -6
- package/list/toolbar/useListTool.js +0 -11
- package/list/typings.d.ts +0 -9
- package/paragraph/createReactParagraph.d.ts +0 -2
- package/paragraph/createReactParagraph.js +0 -8
- package/paragraph/createRenderParagraphElement.d.ts +0 -8
- package/paragraph/createRenderParagraphElement.js +0 -10
- package/paragraph/defaultRenderParagraphElement.d.ts +0 -8
- package/paragraph/defaultRenderParagraphElement.js +0 -8
- package/paragraph/index.cjs.js +0 -31
- package/paragraph/index.js +0 -4
- package/paragraph/jsx-serializer/createJsxSerializeParagraph.d.ts +0 -4
- package/paragraph/jsx-serializer/createJsxSerializeParagraph.js +0 -10
- package/paragraph/jsx-serializer/index.cjs.js +0 -13
- package/paragraph/jsx-serializer/index.js +0 -2
- package/paragraph/jsx-serializer/typings.d.ts +0 -3
- package/paragraph/paragraph.css +0 -1
- package/paragraph/renderParagraphElementWithSymbol.d.ts +0 -8
- package/paragraph/renderParagraphElementWithSymbol.js +0 -8
- package/paragraph/toolbar/ParagraphToolbarIcon.d.ts +0 -8
- package/paragraph/toolbar/ParagraphToolbarIcon.js +0 -12
- package/paragraph/toolbar/index.cjs.js +0 -23
- package/paragraph/toolbar/index.js +0 -2
- package/paragraph/toolbar/useToggleParagraphTool.d.ts +0 -5
- package/paragraph/toolbar/useToggleParagraphTool.js +0 -11
- package/read-more/components/ReadMore.d.ts +0 -8
- package/read-more/components/ReadMore.js +0 -14
- package/read-more/createReactReadMore.d.ts +0 -4
- package/read-more/createReactReadMore.js +0 -11
- package/read-more/defaultRenderReadMoreElement.d.ts +0 -3
- package/read-more/defaultRenderReadMoreElement.js +0 -6
- package/read-more/index.cjs.js +0 -27
- package/read-more/index.js +0 -3
- package/read-more/jsx-serializer/createJsxSerializeReadMore.d.ts +0 -4
- package/read-more/jsx-serializer/createJsxSerializeReadMore.js +0 -10
- package/read-more/jsx-serializer/index.cjs.js +0 -13
- package/read-more/jsx-serializer/index.js +0 -2
- package/read-more/jsx-serializer/typings.d.ts +0 -3
- package/read-more/read-more.css +0 -1
- package/read-more/toolbar/ReadMoreToolbarIcon.d.ts +0 -8
- package/read-more/toolbar/ReadMoreToolbarIcon.js +0 -12
- package/read-more/toolbar/index.cjs.js +0 -22
- package/read-more/toolbar/index.d.ts +0 -2
- package/read-more/toolbar/index.js +0 -2
- package/read-more/toolbar/useReadMoreTool.d.ts +0 -4
- package/read-more/toolbar/useReadMoreTool.js +0 -10
- package/read-more/typings.d.ts +0 -9
- package/strikethrough/constants.d.ts +0 -6
- package/strikethrough/constants.js +0 -8
- package/strikethrough/createReactStrikethrough.d.ts +0 -1
- package/strikethrough/createReactStrikethrough.js +0 -11
- package/strikethrough/defaultRenderStrikethrough.d.ts +0 -3
- package/strikethrough/defaultRenderStrikethrough.js +0 -7
- package/strikethrough/index.cjs.js +0 -24
- package/strikethrough/index.js +0 -3
- package/strikethrough/jsx-serializer/createJsxSerializeStrikethrough.d.ts +0 -1
- package/strikethrough/jsx-serializer/createJsxSerializeStrikethrough.js +0 -10
- package/strikethrough/jsx-serializer/index.cjs.js +0 -13
- package/strikethrough/jsx-serializer/index.js +0 -2
- package/strikethrough/typings.d.ts +0 -5
- package/table/components/ColumnDragButton.d.ts +0 -10
- package/table/components/ColumnDragButton.js +0 -41
- package/table/components/RowDragButton.d.ts +0 -10
- package/table/components/RowDragButton.js +0 -42
- package/table/components/Table.d.ts +0 -9
- package/table/components/Table.js +0 -236
- package/table/components/TableBody.d.ts +0 -5
- package/table/components/TableBody.js +0 -8
- package/table/components/TableCell.d.ts +0 -5
- package/table/components/TableCell.js +0 -297
- package/table/components/TableDragLayer.d.ts +0 -6
- package/table/components/TableDragLayer.js +0 -89
- package/table/components/TableHeader.d.ts +0 -5
- package/table/components/TableHeader.js +0 -13
- package/table/components/TableMain.d.ts +0 -5
- package/table/components/TableMain.js +0 -233
- package/table/components/TableRow.d.ts +0 -5
- package/table/components/TableRow.js +0 -8
- package/table/components/TableTitle.d.ts +0 -5
- package/table/components/TableTitle.js +0 -18
- package/table/contexts/TableActionsContext.d.ts +0 -3
- package/table/contexts/TableActionsContext.js +0 -5
- package/table/contexts/TableDragContext.d.ts +0 -26
- package/table/contexts/TableDragContext.js +0 -26
- package/table/contexts/TableHeaderContext.d.ts +0 -2
- package/table/contexts/TableHeaderContext.js +0 -7
- package/table/contexts/TableMetadataContext.d.ts +0 -3
- package/table/contexts/TableMetadataContext.js +0 -5
- package/table/contexts/TableScrollContext.d.ts +0 -2
- package/table/contexts/TableScrollContext.js +0 -9
- package/table/contexts/TableStateContext.d.ts +0 -3
- package/table/contexts/TableStateContext.js +0 -5
- package/table/createReactTable.d.ts +0 -4
- package/table/createReactTable.js +0 -297
- package/table/defaultRenderTableElements.d.ts +0 -2
- package/table/defaultRenderTableElements.js +0 -20
- package/table/hooks/useColumnResize.d.ts +0 -12
- package/table/hooks/useColumnResize.js +0 -168
- package/table/hooks/useTableActions.d.ts +0 -27
- package/table/hooks/useTableActions.js +0 -1092
- package/table/hooks/useTableActionsContext.d.ts +0 -1
- package/table/hooks/useTableActionsContext.js +0 -12
- package/table/hooks/useTableCell.d.ts +0 -16
- package/table/hooks/useTableCell.js +0 -166
- package/table/hooks/useTableCellToolbarActions.d.ts +0 -34
- package/table/hooks/useTableCellToolbarActions.js +0 -540
- package/table/hooks/useTableMetadata.d.ts +0 -1
- package/table/hooks/useTableMetadata.js +0 -12
- package/table/hooks/useTableStateContext.d.ts +0 -1
- package/table/hooks/useTableStateContext.js +0 -12
- package/table/hooks/useTableStates.d.ts +0 -18
- package/table/hooks/useTableStates.js +0 -14
- package/table/index.cjs.js +0 -4016
- package/table/index.js +0 -27
- package/table/jsx-serializer/components/Table.d.ts +0 -3
- package/table/jsx-serializer/components/Table.js +0 -7
- package/table/jsx-serializer/components/TableBody.d.ts +0 -3
- package/table/jsx-serializer/components/TableBody.js +0 -7
- package/table/jsx-serializer/components/TableCell.d.ts +0 -5
- package/table/jsx-serializer/components/TableCell.js +0 -33
- package/table/jsx-serializer/components/TableHeader.d.ts +0 -3
- package/table/jsx-serializer/components/TableHeader.js +0 -10
- package/table/jsx-serializer/components/TableMain.d.ts +0 -6
- package/table/jsx-serializer/components/TableMain.js +0 -18
- package/table/jsx-serializer/components/TableRow.d.ts +0 -3
- package/table/jsx-serializer/components/TableRow.js +0 -7
- package/table/jsx-serializer/components/TableTitle.d.ts +0 -3
- package/table/jsx-serializer/components/TableTitle.js +0 -7
- package/table/jsx-serializer/contexts/TableHeaderContext.d.ts +0 -1
- package/table/jsx-serializer/contexts/TableHeaderContext.js +0 -5
- package/table/jsx-serializer/contexts/TableScrollContext.d.ts +0 -2
- package/table/jsx-serializer/contexts/TableScrollContext.js +0 -7
- package/table/jsx-serializer/createJsxSerializeTable.d.ts +0 -5
- package/table/jsx-serializer/createJsxSerializeTable.js +0 -113
- package/table/jsx-serializer/defaultRenderTableElements.d.ts +0 -2
- package/table/jsx-serializer/defaultRenderTableElements.js +0 -20
- package/table/jsx-serializer/index.cjs.js +0 -195
- package/table/jsx-serializer/index.js +0 -2
- package/table/jsx-serializer/typings.d.ts +0 -12
- package/table/table.css +0 -1
- package/table/toolbar/TableToolbarIcon.d.ts +0 -8
- package/table/toolbar/TableToolbarIcon.js +0 -12
- package/table/toolbar/index.cjs.js +0 -24
- package/table/toolbar/index.js +0 -2
- package/table/toolbar/useTableTool.d.ts +0 -4
- package/table/toolbar/useTableTool.js +0 -13
- package/table/typings.d.ts +0 -68
- package/table/utils/helper.d.ts +0 -186
- package/table/utils/helper.js +0 -799
- package/toggle-mark/createReactToggleMarkCreator.d.ts +0 -6
- package/toggle-mark/createReactToggleMarkCreator.js +0 -20
- package/toggle-mark/index.cjs.js +0 -22
- package/toggle-mark/index.d.ts +0 -2
- package/toggle-mark/index.js +0 -1
- package/toggle-mark/jsx-serializer/createJsxSerializeToggleMarkCreator.d.ts +0 -4
- package/toggle-mark/jsx-serializer/createJsxSerializeToggleMarkCreator.js +0 -7
- package/toggle-mark/jsx-serializer/index.cjs.js +0 -9
- package/toggle-mark/jsx-serializer/index.d.ts +0 -2
- package/toggle-mark/jsx-serializer/index.js +0 -1
- package/toggle-mark/jsx-serializer/typings.d.ts +0 -2
- package/toggle-mark/toolbar/ToggleMarkToolbarIcon.d.ts +0 -8
- package/toggle-mark/toolbar/ToggleMarkToolbarIcon.js +0 -12
- package/toggle-mark/toolbar/index.cjs.js +0 -23
- package/toggle-mark/toolbar/index.js +0 -2
- package/toggle-mark/toolbar/useToggleMarkTool.d.ts +0 -5
- package/toggle-mark/toolbar/useToggleMarkTool.js +0 -11
- package/toggle-mark/typings.d.ts +0 -12
- package/toolbar/components/InlineToolbar.d.ts +0 -19
- package/toolbar/components/InlineToolbar.js +0 -31
- package/toolbar/components/Toolbar.d.ts +0 -25
- package/toolbar/components/Toolbar.js +0 -137
- package/toolbar/components/ToolbarGroupIcon.d.ts +0 -7
- package/toolbar/components/ToolbarGroupIcon.js +0 -80
- package/toolbar/components/ToolbarIcon.d.ts +0 -9
- package/toolbar/components/ToolbarIcon.js +0 -32
- package/toolbar/components/ToolbarInput.d.ts +0 -8
- package/toolbar/components/ToolbarInput.js +0 -38
- package/toolbar/components/toolbarIconName.d.ts +0 -2
- package/toolbar/components/toolbarIconName.js +0 -86
- package/toolbar/constants.d.ts +0 -5
- package/toolbar/constants.js +0 -8
- package/toolbar/contexts/StartToolInputContext.d.ts +0 -2
- package/toolbar/contexts/StartToolInputContext.js +0 -5
- package/toolbar/contexts/toolbar.d.ts +0 -7
- package/toolbar/contexts/toolbar.js +0 -10
- package/toolbar/contexts/toolbarMenu.d.ts +0 -6
- package/toolbar/contexts/toolbarMenu.js +0 -11
- package/toolbar/hooks/useAutoGroupIcons.d.ts +0 -12
- package/toolbar/hooks/useAutoGroupIcons.js +0 -68
- package/toolbar/hooks/useStartToolInput.d.ts +0 -1
- package/toolbar/hooks/useStartToolInput.js +0 -8
- package/toolbar/index.cjs.js +0 -468
- package/toolbar/index.d.ts +0 -7
- package/toolbar/index.js +0 -6
- package/toolbar/toolbar.css +0 -1
- package/toolbar/typings.d.ts +0 -6
- package/underline/constants.d.ts +0 -6
- package/underline/constants.js +0 -8
- package/underline/createReactUnderline.d.ts +0 -1
- package/underline/createReactUnderline.js +0 -11
- package/underline/defaultRenderUnderline.d.ts +0 -3
- package/underline/defaultRenderUnderline.js +0 -7
- package/underline/index.cjs.js +0 -24
- package/underline/index.js +0 -3
- package/underline/jsx-serializer/createJsxSerializeUnderline.d.ts +0 -1
- package/underline/jsx-serializer/createJsxSerializeUnderline.js +0 -10
- package/underline/jsx-serializer/index.cjs.js +0 -13
- package/underline/jsx-serializer/index.js +0 -2
- package/underline/typings.d.ts +0 -5
- package/utils/composeRefs.d.ts +0 -19
- package/utils/composeRefs.js +0 -32
- package/utils/index.cjs.js +0 -193
- package/utils/index.js +0 -7
- package/utils/removePreviousElement.d.ts +0 -9
- package/utils/removePreviousElement.js +0 -44
- package/utils/upload.d.ts +0 -11
- package/utils/upload.js +0 -57
- package/utils/useClickAway.d.ts +0 -5
- package/utils/useClickAway.js +0 -23
- package/utils/useDocumentEvents.d.ts +0 -5
- package/utils/useDocumentEvents.js +0 -21
- package/utils/useIsomorphicLayoutEffect.d.ts +0 -2
- package/utils/useIsomorphicLayoutEffect.js +0 -9
- package/utils/usePreviousValue.d.ts +0 -1
- package/utils/usePreviousValue.js +0 -11
- /package/_internal/{index.d.ts → src/index.ts} +0 -0
- /package/accordion/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/accordion/{accordion.scss → src/accordion.scss} +0 -0
- /package/accordion/{index.d.ts → src/index.ts} +0 -0
- /package/align/{index.d.ts → src/index.ts} +0 -0
- /package/blockquote/jsx-serializer/{defaultRenderBlockquoteElement.d.ts → src/defaultRenderBlockquoteElement.ts} +0 -0
- /package/blockquote/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/blockquote/{blockquote.scss → src/blockquote.scss} +0 -0
- /package/blockquote/{index.d.ts → src/index.ts} +0 -0
- /package/blockquote/toolbar/{index.d.ts → src/index.ts} +0 -0
- /package/bold/jsx-serializer/{defaultRenderBold.d.ts → src/defaultRenderBold.ts} +0 -0
- /package/bold/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/bold/{index.d.ts → src/index.ts} +0 -0
- /package/card/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/card/{card.scss → src/card.scss} +0 -0
- /package/card/{index.d.ts → src/index.ts} +0 -0
- /package/card/toolbar/{index.d.ts → src/index.ts} +0 -0
- /package/carousel/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/carousel/{carousel.scss → src/carousel.scss} +0 -0
- /package/carousel/{index.d.ts → src/index.ts} +0 -0
- /package/carousel/toolbar/{index.d.ts → src/index.ts} +0 -0
- /package/components/{baseField.scss → src/BaseField/baseField.scss} +0 -0
- /package/components/{button.scss → src/Button/button.scss} +0 -0
- /package/components/{hint.scss → src/Hint/hint.scss} +0 -0
- /package/components/{imageUploader.scss → src/ImageUploader/imageUploader.scss} +0 -0
- /package/components/{input.scss → src/Input/input.scss} +0 -0
- /package/components/{message.scss → src/Message/message.scss} +0 -0
- /package/components/{modal.scss → src/Modal/modal.scss} +0 -0
- /package/components/{progress.scss → src/Progress/progress.scss} +0 -0
- /package/components/{segmentedControl.scss → src/SegmentedControl/segmentedControl.scss} +0 -0
- /package/components/{textarea.scss → src/Textarea/textarea.scss} +0 -0
- /package/components/{toggle.scss → src/Toggle/toggle.scss} +0 -0
- /package/components/{tooltip.scss → src/Tooltip/tooltip.scss} +0 -0
- /package/components/{components.scss → src/components.scss} +0 -0
- /package/configs/{index.d.ts → src/index.ts} +0 -0
- /package/divider/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/divider/{divider.scss → src/divider.scss} +0 -0
- /package/divider/{index.d.ts → src/index.ts} +0 -0
- /package/embed/jsx-serializer/facebook/{index.d.ts → src/index.ts} +0 -0
- /package/embed/jsx-serializer/instagram/{index.d.ts → src/index.ts} +0 -0
- /package/embed/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/embed/jsx-serializer/twitter/{index.d.ts → src/index.ts} +0 -0
- /package/embed/jsx-serializer/vimeo/{index.d.ts → src/index.ts} +0 -0
- /package/embed/jsx-serializer/youtube/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/facebook/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/instagram/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/podcast-apple/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/spotify/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/twitter/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/vimeo/{index.d.ts → src/index.ts} +0 -0
- /package/embed/renderers/youtube/{index.d.ts → src/index.ts} +0 -0
- /package/embed/{embed.scss → src/embed.scss} +0 -0
- /package/embed/{index.d.ts → src/index.ts} +0 -0
- /package/file-uploader/{file-uploader.scss → src/components/file-uploader.scss} +0 -0
- /package/file-uploader/{index.d.ts → src/index.ts} +0 -0
- /package/footnote/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/footnote/{index.d.ts → src/index.ts} +0 -0
- /package/heading/jsx-serializer/{defaultRenderHeadingElement.d.ts → src/defaultRenderHeadingElement.ts} +0 -0
- /package/heading/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/heading/{heading.scss → src/heading.scss} +0 -0
- /package/heading/{index.d.ts → src/index.ts} +0 -0
- /package/highlight/jsx-serializer/{defaultRenderHighlight.d.ts → src/defaultRenderHighlight.ts} +0 -0
- /package/highlight/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/highlight/{index.d.ts → src/index.ts} +0 -0
- /package/image/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/image/{image.scss → src/components/image.scss} +0 -0
- /package/image/{index.d.ts → src/index.ts} +0 -0
- /package/input-block/{index.d.ts → src/index.ts} +0 -0
- /package/input-block/{input-block.scss → src/input-block.scss} +0 -0
- /package/italic/jsx-serializer/{defaultRenderItalic.d.ts → src/defaultRenderItalic.ts} +0 -0
- /package/italic/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/italic/{index.d.ts → src/index.ts} +0 -0
- /package/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/line-break/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/line-break/{index.d.ts → src/index.ts} +0 -0
- /package/line-break/{line-break.scss → src/line-break.scss} +0 -0
- /package/link/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/link/{index.d.ts → src/index.ts} +0 -0
- /package/list/jsx-serializer/{defaultRenderListElements.d.ts → src/defaultRenderListElements.ts} +0 -0
- /package/list/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/list/{index.d.ts → src/index.ts} +0 -0
- /package/list/{list.scss → src/list.scss} +0 -0
- /package/paragraph/jsx-serializer/{defaultRenderParagraphElement.d.ts → src/defaultRenderParagraphElement.ts} +0 -0
- /package/paragraph/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/paragraph/{index.d.ts → src/index.ts} +0 -0
- /package/paragraph/{paragraph.scss → src/paragraph.scss} +0 -0
- /package/paragraph/toolbar/{index.d.ts → src/index.ts} +0 -0
- /package/read-more/jsx-serializer/{defaultRenderReadMoreElement.d.ts → src/defaultRenderReadMoreElement.ts} +0 -0
- /package/read-more/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/read-more/{read-more.scss → src/components/read-more.scss} +0 -0
- /package/read-more/{index.d.ts → src/index.ts} +0 -0
- /package/{editable.scss → src/core/components/editable.scss} +0 -0
- /package/{index.d.ts → src/index.ts} +0 -0
- /package/strikethrough/jsx-serializer/{defaultRenderStrikethrough.d.ts → src/defaultRenderStrikethrough.ts} +0 -0
- /package/strikethrough/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/strikethrough/{index.d.ts → src/index.ts} +0 -0
- /package/table/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/table/{index.d.ts → src/index.ts} +0 -0
- /package/table/{table.scss → src/table.scss} +0 -0
- /package/table/toolbar/{index.d.ts → src/index.ts} +0 -0
- /package/toggle-mark/toolbar/{index.d.ts → src/index.ts} +0 -0
- /package/toolbar/{toolbar.scss → src/components/toolbar.scss} +0 -0
- /package/underline/jsx-serializer/{defaultRenderUnderline.d.ts → src/defaultRenderUnderline.ts} +0 -0
- /package/underline/jsx-serializer/{index.d.ts → src/index.ts} +0 -0
- /package/underline/{index.d.ts → src/index.ts} +0 -0
- /package/utils/{index.d.ts → src/index.ts} +0 -0
package/table/index.cjs.js
DELETED
|
@@ -1,4016 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var React = require('react');
|
|
4
|
-
var icons = require('@quadrats/icons');
|
|
5
|
-
var core = require('@quadrats/core');
|
|
6
|
-
var list = require('@quadrats/common/list');
|
|
7
|
-
var toolbar = require('@quadrats/react/toolbar');
|
|
8
|
-
var utils = require('@quadrats/react/utils');
|
|
9
|
-
var slateReact = require('slate-react');
|
|
10
|
-
var table = require('@quadrats/common/table');
|
|
11
|
-
var align = require('@quadrats/common/align');
|
|
12
|
-
var react = require('@quadrats/react');
|
|
13
|
-
var components = require('@quadrats/react/components');
|
|
14
|
-
var clsx = require('clsx');
|
|
15
|
-
var slate = require('slate');
|
|
16
|
-
var reactDnd = require('react-dnd');
|
|
17
|
-
var reactDndHtml5Backend = require('react-dnd-html5-backend');
|
|
18
|
-
|
|
19
|
-
const TableActionsContext = React.createContext(undefined);
|
|
20
|
-
|
|
21
|
-
const TableMetadataContext = React.createContext(undefined);
|
|
22
|
-
|
|
23
|
-
const TableStateContext = React.createContext(undefined);
|
|
24
|
-
|
|
25
|
-
function useTableActionsContext() {
|
|
26
|
-
const context = React.useContext(TableActionsContext);
|
|
27
|
-
if (!context) {
|
|
28
|
-
throw new Error('useTableActionsContext must be used within a TableActionsContext.Provider');
|
|
29
|
-
}
|
|
30
|
-
return context;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function useTableMetadata() {
|
|
34
|
-
const context = React.useContext(TableMetadataContext);
|
|
35
|
-
if (!context) {
|
|
36
|
-
throw new Error('useTableMetadata must be used within a TableMetadataContext.Provider');
|
|
37
|
-
}
|
|
38
|
-
return context;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function useTableStateContext() {
|
|
42
|
-
const context = React.useContext(TableStateContext);
|
|
43
|
-
if (!context) {
|
|
44
|
-
throw new Error('useTableState must be used within a TableStateContext.Provider');
|
|
45
|
-
}
|
|
46
|
-
return context;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 分配百分比寬度,確保總和為 100%
|
|
51
|
-
* 前 n-1 個欄位使用四捨五入的平均值,最後一個欄位使用剩餘寬度
|
|
52
|
-
* @param count - 欄位數量
|
|
53
|
-
* @returns 百分比陣列,總和為 100%
|
|
54
|
-
*/
|
|
55
|
-
function distributeEqualPercentages(count) {
|
|
56
|
-
if (count <= 0)
|
|
57
|
-
return [];
|
|
58
|
-
if (count === 1)
|
|
59
|
-
return [100];
|
|
60
|
-
const percentages = [];
|
|
61
|
-
const averagePercentage = Math.round((100 / count) * 10) / 10;
|
|
62
|
-
// 前 n-1 個欄位使用四捨五入的平均值
|
|
63
|
-
for (let i = 0; i < count - 1; i++) {
|
|
64
|
-
percentages.push(averagePercentage);
|
|
65
|
-
}
|
|
66
|
-
// 最後一個欄位使用剩餘寬度,確保總和為 100%
|
|
67
|
-
const sumOfFirst = percentages.reduce((sum, val) => sum + val, 0);
|
|
68
|
-
const lastPercentage = Math.round((100 - sumOfFirst) * 10) / 10;
|
|
69
|
-
percentages.push(lastPercentage);
|
|
70
|
-
return percentages;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* 等比例縮減現有百分比並加入新欄位
|
|
74
|
-
* 前 n-1 個縮減後的欄位使用四捨五入,最後一個縮減欄位使用剩餘寬度
|
|
75
|
-
* @param currentPercentages - 當前的百分比陣列
|
|
76
|
-
* @param newColumnPercentage - 新欄位要佔的百分比
|
|
77
|
-
* @param insertIndex - 新欄位插入的位置(0-based)
|
|
78
|
-
* @returns 新的百分比陣列,總和為 100%
|
|
79
|
-
*/
|
|
80
|
-
function scalePercentagesWithNewColumn(currentPercentages, newColumnPercentage, insertIndex) {
|
|
81
|
-
if (currentPercentages.length === 0)
|
|
82
|
-
return [100];
|
|
83
|
-
const currentTotal = currentPercentages.reduce((sum, val) => sum + val, 0);
|
|
84
|
-
const targetTotal = 100 - newColumnPercentage;
|
|
85
|
-
const scaleFactor = targetTotal / currentTotal;
|
|
86
|
-
const scaledPercentages = [];
|
|
87
|
-
// 前 n-1 個欄位使用縮放後四捨五入的值
|
|
88
|
-
for (let i = 0; i < currentPercentages.length - 1; i++) {
|
|
89
|
-
const scaledValue = Math.round(currentPercentages[i] * scaleFactor * 10) / 10;
|
|
90
|
-
scaledPercentages.push(scaledValue);
|
|
91
|
-
}
|
|
92
|
-
// 最後一個現有欄位使用剩餘寬度,確保總和正確
|
|
93
|
-
const sumOfScaled = scaledPercentages.reduce((sum, val) => sum + val, 0);
|
|
94
|
-
const lastScaledValue = Math.round((targetTotal - sumOfScaled) * 10) / 10;
|
|
95
|
-
scaledPercentages.push(lastScaledValue);
|
|
96
|
-
// 在指定位置插入新欄位
|
|
97
|
-
const result = [...scaledPercentages];
|
|
98
|
-
result.splice(insertIndex, 0, newColumnPercentage);
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* 提取表格的所有基本元素
|
|
103
|
-
*/
|
|
104
|
-
function getTableElements(element) {
|
|
105
|
-
const tableMainElement = element.children.find((child) => core.Element.isElement(child) && child.type.includes(table.TABLE_MAIN_TYPE));
|
|
106
|
-
if (!tableMainElement || !core.Element.isElement(tableMainElement)) {
|
|
107
|
-
return {
|
|
108
|
-
tableMainElement: null,
|
|
109
|
-
tableBodyElement: null,
|
|
110
|
-
tableHeaderElement: null,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
const tableBodyElement = tableMainElement.children.find((child) => core.Element.isElement(child) && child.type.includes(table.TABLE_BODY_TYPE));
|
|
114
|
-
const tableHeaderElement = tableMainElement.children.find((child) => core.Element.isElement(child) && child.type.includes(table.TABLE_HEADER_TYPE));
|
|
115
|
-
return {
|
|
116
|
-
tableMainElement: core.Element.isElement(tableMainElement) ? tableMainElement : null,
|
|
117
|
-
tableBodyElement: core.Element.isElement(tableBodyElement) ? tableBodyElement : null,
|
|
118
|
-
tableHeaderElement: core.Element.isElement(tableHeaderElement) ? tableHeaderElement : null,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* 獲取表格的完整結構
|
|
123
|
-
*/
|
|
124
|
-
function getTableStructure(editor, element) {
|
|
125
|
-
var _a;
|
|
126
|
-
const elements = getTableElements(element);
|
|
127
|
-
if (!elements.tableMainElement || !elements.tableBodyElement) {
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
const tableMainPath = slateReact.ReactEditor.findPath(editor, elements.tableMainElement);
|
|
131
|
-
const tableBodyPath = slateReact.ReactEditor.findPath(editor, elements.tableBodyElement);
|
|
132
|
-
const tableHeaderPath = elements.tableHeaderElement
|
|
133
|
-
? slateReact.ReactEditor.findPath(editor, elements.tableHeaderElement)
|
|
134
|
-
: null;
|
|
135
|
-
const headerRowCount = ((_a = elements.tableHeaderElement) === null || _a === void 0 ? void 0 : _a.children.length) || 0;
|
|
136
|
-
const firstRow = elements.tableBodyElement.children[0];
|
|
137
|
-
const columnCount = core.Element.isElement(firstRow) ? firstRow.children.length : 0;
|
|
138
|
-
return Object.assign(Object.assign({}, elements), { tableMainPath,
|
|
139
|
-
tableBodyPath,
|
|
140
|
-
tableHeaderPath,
|
|
141
|
-
headerRowCount,
|
|
142
|
-
columnCount, firstRow: core.Element.isElement(firstRow) ? firstRow : null });
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* 檢查是否有任何 pinned columns
|
|
146
|
-
*/
|
|
147
|
-
function hasAnyPinnedColumns(tableStructure) {
|
|
148
|
-
if (core.Element.isElement(tableStructure.tableBodyElement)) {
|
|
149
|
-
const lastRow = tableStructure.tableBodyElement.children[tableStructure.tableBodyElement.children.length - 1];
|
|
150
|
-
if (core.Element.isElement(lastRow) && lastRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
151
|
-
if (lastRow.children[0] &&
|
|
152
|
-
core.Element.isElement(lastRow.children[0]) &&
|
|
153
|
-
lastRow.children[0].type.includes(table.TABLE_CELL_TYPE)) {
|
|
154
|
-
// 如果最後一行 body row 的第一個 cell 是 pinned,代表有任何 pinned columns
|
|
155
|
-
return lastRow.children[0].pinned === true;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* 檢查是否有任何 pinned rows
|
|
163
|
-
*/
|
|
164
|
-
function hasAnyPinnedRows(tableStructure) {
|
|
165
|
-
if (!tableStructure.tableHeaderElement)
|
|
166
|
-
return false;
|
|
167
|
-
if (tableStructure.tableHeaderElement.children[0]) {
|
|
168
|
-
const firstRow = tableStructure.tableHeaderElement.children[0];
|
|
169
|
-
if (core.Element.isElement(firstRow) && firstRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
170
|
-
// 如果第一個 header row 的所有 cell 都是 pinned,代表有任何 pinned rows
|
|
171
|
-
return firstRow.children.every((cell) => core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE) && cell.pinned);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* 創建新的 table cell
|
|
178
|
-
*/
|
|
179
|
-
function createTableCell(referenceCell, overrideProps) {
|
|
180
|
-
var _a;
|
|
181
|
-
const baseCell = {
|
|
182
|
-
type: table.TABLE_CELL_TYPE,
|
|
183
|
-
children: [
|
|
184
|
-
{
|
|
185
|
-
type: core.PARAGRAPH_TYPE,
|
|
186
|
-
children: [{ text: '' }],
|
|
187
|
-
},
|
|
188
|
-
],
|
|
189
|
-
};
|
|
190
|
-
if (referenceCell) {
|
|
191
|
-
if (referenceCell.treatAsTitle) {
|
|
192
|
-
baseCell.treatAsTitle = true;
|
|
193
|
-
}
|
|
194
|
-
if (referenceCell.pinned) {
|
|
195
|
-
baseCell.pinned = true;
|
|
196
|
-
}
|
|
197
|
-
if ((_a = referenceCell.children[0]) === null || _a === void 0 ? void 0 : _a.align) {
|
|
198
|
-
baseCell.children[0].align = referenceCell.children[0].align;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return Object.assign(Object.assign({}, baseCell), overrideProps);
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* 獲取參考 row
|
|
205
|
-
*/
|
|
206
|
-
function getReferenceRowFromHeaderOrBody(HeaderOrBodyContainer, rowIndex) {
|
|
207
|
-
const row = HeaderOrBodyContainer.children[rowIndex];
|
|
208
|
-
if (!core.Element.isElement(row) || !row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
209
|
-
return undefined;
|
|
210
|
-
}
|
|
211
|
-
return row;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* 收集指定範圍的 cells
|
|
215
|
-
* @param tableStructure - 表格結構
|
|
216
|
-
* @param scope - 'table' | 'column'
|
|
217
|
-
* @param columnIndex - 當 scope 為 'column' 時需要指定
|
|
218
|
-
* @returns cells 陣列
|
|
219
|
-
*/
|
|
220
|
-
function collectCells(tableStructure, scope, columnIndex) {
|
|
221
|
-
const containers = [tableStructure.tableBodyElement, tableStructure.tableHeaderElement].filter(Boolean);
|
|
222
|
-
const cells = [];
|
|
223
|
-
for (const container of containers) {
|
|
224
|
-
if (!core.Element.isElement(container))
|
|
225
|
-
continue;
|
|
226
|
-
for (const row of container.children) {
|
|
227
|
-
if (!core.Element.isElement(row))
|
|
228
|
-
continue;
|
|
229
|
-
if (scope === 'column' && typeof columnIndex === 'number') {
|
|
230
|
-
// 收集指定 column 的 cells
|
|
231
|
-
const cell = row.children[columnIndex];
|
|
232
|
-
if (core.Element.isElement(cell)) {
|
|
233
|
-
cells.push(cell);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else if (scope === 'table') {
|
|
237
|
-
// 收集所有 cells
|
|
238
|
-
for (const cell of row.children) {
|
|
239
|
-
if (core.Element.isElement(cell)) {
|
|
240
|
-
cells.push(cell);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return cells;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* 設定指定 cells 的 align
|
|
250
|
-
* @param editor - Slate editor
|
|
251
|
-
* @param cells - 要設定 align 的 cell 元素陣列
|
|
252
|
-
* @param alignValue - align 值
|
|
253
|
-
*/
|
|
254
|
-
function setAlignForCells(editor, cells, alignValue) {
|
|
255
|
-
for (const cell of cells) {
|
|
256
|
-
if (!core.Element.isElement(cell))
|
|
257
|
-
continue;
|
|
258
|
-
const cellPath = slateReact.ReactEditor.findPath(editor, cell);
|
|
259
|
-
// 對 cell 內的所有可 align 的元素設定 align
|
|
260
|
-
for (let contentIndex = 0; contentIndex < cell.children.length; contentIndex++) {
|
|
261
|
-
const content = cell.children[contentIndex];
|
|
262
|
-
if (core.Element.isElement(content) &&
|
|
263
|
-
content.type &&
|
|
264
|
-
align.ALIGNABLE_TYPES.includes(content.type)) {
|
|
265
|
-
const contentPath = [...cellPath, contentIndex];
|
|
266
|
-
core.Transforms.setNodes(editor, { [align.ALIGN_TYPE]: alignValue }, {
|
|
267
|
-
at: contentPath,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* 獲取指定 cells 的 align 狀態
|
|
275
|
-
* @param cells - 要檢查的 cell 元素陣列
|
|
276
|
-
* @returns 如果所有 cell 的 align 都相同則返回該值,否則返回 'left'
|
|
277
|
-
*/
|
|
278
|
-
function getAlignFromCells(cells) {
|
|
279
|
-
const alignValues = [];
|
|
280
|
-
for (const cell of cells) {
|
|
281
|
-
if (!core.Element.isElement(cell))
|
|
282
|
-
continue;
|
|
283
|
-
// 檢查 cell 內的第一個可 align 元素
|
|
284
|
-
for (const content of cell.children) {
|
|
285
|
-
if (core.Element.isElement(content) &&
|
|
286
|
-
content.type &&
|
|
287
|
-
align.ALIGNABLE_TYPES.includes(content.type)) {
|
|
288
|
-
const alignValue = content[align.ALIGN_TYPE];
|
|
289
|
-
if (alignValue) {
|
|
290
|
-
alignValues.push(alignValue);
|
|
291
|
-
}
|
|
292
|
-
break; // 只檢查第一個可 align 元素
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// 如果所有 align 值都相同,返回該值;否則返回預設的 'left'
|
|
297
|
-
if (alignValues.length > 0) {
|
|
298
|
-
const firstAlign = alignValues[0];
|
|
299
|
-
const allSame = alignValues.every((align) => align === firstAlign);
|
|
300
|
-
if (allSame) {
|
|
301
|
-
return firstAlign;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return 'left';
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* 獲取釘選欄位的資訊
|
|
308
|
-
* @param tableElement - 表格最外層元素
|
|
309
|
-
* @returns 釘選欄位的索引陣列和總寬度百分比
|
|
310
|
-
*/
|
|
311
|
-
function getPinnedColumnsInfo(tableElement) {
|
|
312
|
-
const { tableBodyElement } = getTableElements(tableElement);
|
|
313
|
-
if (!tableBodyElement || !core.Element.isElement(tableBodyElement)) {
|
|
314
|
-
return { pinnedColumnIndices: [], totalPinnedPercentage: 0 };
|
|
315
|
-
}
|
|
316
|
-
const firstRow = tableBodyElement.children[0];
|
|
317
|
-
if (!core.Element.isElement(firstRow)) {
|
|
318
|
-
return { pinnedColumnIndices: [], totalPinnedPercentage: 0 };
|
|
319
|
-
}
|
|
320
|
-
const pinnedColumnIndices = [];
|
|
321
|
-
let totalPinnedPercentage = 0;
|
|
322
|
-
firstRow.children.forEach((cell, index) => {
|
|
323
|
-
if (core.Element.isElement(cell) && cell.treatAsTitle && cell.pinned) {
|
|
324
|
-
pinnedColumnIndices.push(index);
|
|
325
|
-
// 如果有設定 columnWidths,使用設定的寬度
|
|
326
|
-
if (tableElement.columnWidths && tableElement.columnWidths[index]) {
|
|
327
|
-
const width = tableElement.columnWidths[index];
|
|
328
|
-
if (width.type === 'percentage') {
|
|
329
|
-
totalPinnedPercentage += width.value;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
return { pinnedColumnIndices, totalPinnedPercentage };
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* 強制調整釘選欄位寬度以符合最大限制
|
|
338
|
-
* @param columnWidths - 當前欄位寬度陣列
|
|
339
|
-
* @param pinnedColumnIndices - 釘選欄位索引陣列
|
|
340
|
-
* @returns 調整後的欄位寬度陣列
|
|
341
|
-
*/
|
|
342
|
-
function enforcePinnedColumnsMaxWidth(columnWidths, pinnedColumnIndices) {
|
|
343
|
-
if (pinnedColumnIndices.length === 0) {
|
|
344
|
-
return columnWidths;
|
|
345
|
-
}
|
|
346
|
-
// 計算釘選欄位的總寬度
|
|
347
|
-
let totalPinnedPercentage = 0;
|
|
348
|
-
pinnedColumnIndices.forEach((index) => {
|
|
349
|
-
const width = columnWidths[index];
|
|
350
|
-
if (width && width.type === 'percentage') {
|
|
351
|
-
totalPinnedPercentage += width.value;
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
// 如果超過最大限制,按比例縮減
|
|
355
|
-
if (totalPinnedPercentage > table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE) {
|
|
356
|
-
const scaleFactor = table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE / totalPinnedPercentage;
|
|
357
|
-
const newWidths = [...columnWidths];
|
|
358
|
-
pinnedColumnIndices.forEach((index) => {
|
|
359
|
-
const width = newWidths[index];
|
|
360
|
-
if (width && width.type === 'percentage') {
|
|
361
|
-
newWidths[index] = {
|
|
362
|
-
type: 'percentage',
|
|
363
|
-
value: Math.round(width.value * scaleFactor * 10) / 10,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
return newWidths;
|
|
368
|
-
}
|
|
369
|
-
return columnWidths;
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* 獲取表格的欄位寬度陣列
|
|
373
|
-
* 當有釘選欄位時:
|
|
374
|
-
* - 釘選欄位使用 percentage
|
|
375
|
-
* - 未釘選欄位使用 pixel(基於剩餘空間平均分配)
|
|
376
|
-
* @param tableElement - 表格最外層元素
|
|
377
|
-
* @param tableWidth - 表格容器的實際寬度(pixel),用於計算 pixel 寬度
|
|
378
|
-
* @returns 欄位寬度陣列
|
|
379
|
-
*/
|
|
380
|
-
function getColumnWidths(tableElement, tableWidth) {
|
|
381
|
-
const tableStructure = getTableElements(tableElement);
|
|
382
|
-
const { tableBodyElement } = tableStructure;
|
|
383
|
-
if (!tableBodyElement || !core.Element.isElement(tableBodyElement)) {
|
|
384
|
-
return [];
|
|
385
|
-
}
|
|
386
|
-
const firstRow = tableBodyElement.children[0];
|
|
387
|
-
if (!core.Element.isElement(firstRow)) {
|
|
388
|
-
return [];
|
|
389
|
-
}
|
|
390
|
-
const columnCount = firstRow.children.length;
|
|
391
|
-
// 獲取釘選欄位資訊
|
|
392
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(tableElement);
|
|
393
|
-
const hasPinnedColumns = pinnedColumnIndices.length > 0;
|
|
394
|
-
// 如果 tableElement 有 columnWidths
|
|
395
|
-
if (tableElement.columnWidths && tableElement.columnWidths.length === columnCount) {
|
|
396
|
-
let widths = [...tableElement.columnWidths];
|
|
397
|
-
// 強制檢查釘選欄位是否超過 40%,如果超過則調整
|
|
398
|
-
if (hasPinnedColumns) {
|
|
399
|
-
widths = enforcePinnedColumnsMaxWidth(widths, pinnedColumnIndices);
|
|
400
|
-
}
|
|
401
|
-
return widths;
|
|
402
|
-
}
|
|
403
|
-
// 如果沒有設定 columnWidths,需要初始化
|
|
404
|
-
// 如果有釘選欄位,使用混合模式
|
|
405
|
-
if (hasPinnedColumns && tableWidth) {
|
|
406
|
-
const widths = [];
|
|
407
|
-
// 先計算釘選欄位的總寬度
|
|
408
|
-
const pinnedPercentagePerColumn = Math.min(Math.round((table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE / pinnedColumnIndices.length) * 10) / 10, table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE);
|
|
409
|
-
// 計算實際釘選欄位總百分比
|
|
410
|
-
const actualPinnedPercentage = Math.min(pinnedPercentagePerColumn * pinnedColumnIndices.length, table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE);
|
|
411
|
-
// 計算剩餘空間(pixel)
|
|
412
|
-
const remainingPercentage = 100 - actualPinnedPercentage;
|
|
413
|
-
const remainingPixelWidth = (tableWidth * remainingPercentage) / 100;
|
|
414
|
-
const unpinnedColumnCount = columnCount - pinnedColumnIndices.length;
|
|
415
|
-
const pixelWidthPerColumn = unpinnedColumnCount > 0 ? Math.floor(remainingPixelWidth / unpinnedColumnCount) : 0;
|
|
416
|
-
// 建立寬度陣列
|
|
417
|
-
for (let i = 0; i < columnCount; i++) {
|
|
418
|
-
if (pinnedColumnIndices.includes(i)) {
|
|
419
|
-
widths.push({ type: 'percentage', value: pinnedPercentagePerColumn });
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
widths.push({ type: 'pixel', value: pixelWidthPerColumn });
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
return widths;
|
|
426
|
-
}
|
|
427
|
-
// 否則返回平均分配的 percentage(總和為 100%)
|
|
428
|
-
const percentages = distributeEqualPercentages(columnCount);
|
|
429
|
-
return percentages.map((value) => ({ type: 'percentage', value }));
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* 設定表格的欄位寬度
|
|
433
|
-
* @param editor - Slate editor
|
|
434
|
-
* @param tableElement - 表格最外層元素
|
|
435
|
-
* @param columnWidths - 欄位寬度陣列
|
|
436
|
-
*/
|
|
437
|
-
function setColumnWidths(editor, tableElement, columnWidths) {
|
|
438
|
-
const tablePath = slateReact.ReactEditor.findPath(editor, tableElement);
|
|
439
|
-
core.Transforms.setNodes(editor, { columnWidths: [...columnWidths] }, { at: tablePath });
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* 計算新增欄位後的欄位寬度
|
|
443
|
-
* - 如果所有欄位都是 percentage:按比例縮減現有欄位,新欄位佔平均寬度
|
|
444
|
-
* - 如果有混合模式(percentage + pixel):
|
|
445
|
-
* * 如果用戶操作的欄位是 pinned column:新欄位使用 percentage(需要調整 pinned columns 的百分比)
|
|
446
|
-
* * 如果用戶操作的欄位是 unpinned column:新欄位使用 pixel(與其他 pixel 欄位相同寬度)
|
|
447
|
-
*
|
|
448
|
-
* @param currentWidths - 當前的欄位寬度陣列
|
|
449
|
-
* @param insertIndex - 新欄位插入的位置(0-based)
|
|
450
|
-
* @param pinnedColumnIndices - 當前釘選欄位的索引陣列(插入前的索引)
|
|
451
|
-
* @param operatingColumnIndex - 用戶實際操作的欄位索引(用於判斷是在 pinned 還是 unpinned column 操作)
|
|
452
|
-
* @returns 新的欄位寬度陣列
|
|
453
|
-
*/
|
|
454
|
-
function calculateColumnWidthsAfterAdd(currentWidths, insertIndex, pinnedColumnIndices = [], operatingColumnIndex) {
|
|
455
|
-
const newColumnCount = currentWidths.length + 1;
|
|
456
|
-
// 分離 percentage 和 pixel 欄位
|
|
457
|
-
const percentageColumns = [];
|
|
458
|
-
const pixelColumns = [];
|
|
459
|
-
currentWidths.forEach((width, index) => {
|
|
460
|
-
if (width.type === 'percentage') {
|
|
461
|
-
percentageColumns.push({ index, value: width.value });
|
|
462
|
-
}
|
|
463
|
-
else {
|
|
464
|
-
pixelColumns.push({ index, value: width.value });
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
// 如果所有欄位都是 percentage(正常模式,無 pinned columns)
|
|
468
|
-
if (percentageColumns.length === currentWidths.length) {
|
|
469
|
-
// 計算新欄位的平均百分比
|
|
470
|
-
const averagePercentage = Math.round((100 / newColumnCount) * 10) / 10;
|
|
471
|
-
// 提取當前的百分比值
|
|
472
|
-
const currentPercentages = currentWidths.map((w) => w.value);
|
|
473
|
-
// 等比例縮減現有欄位並插入新欄位
|
|
474
|
-
const newPercentages = scalePercentagesWithNewColumn(currentPercentages, averagePercentage, insertIndex);
|
|
475
|
-
return newPercentages.map((value) => ({ type: 'percentage', value }));
|
|
476
|
-
}
|
|
477
|
-
// 如果有混合的 pixel 和 percentage 欄位(有 pinned columns)
|
|
478
|
-
if (percentageColumns.length && pixelColumns.length) {
|
|
479
|
-
// 判斷新欄位是否應該是 pinned column
|
|
480
|
-
const isNewColumnPinned = typeof operatingColumnIndex === 'number' ? pinnedColumnIndices.includes(operatingColumnIndex) : false;
|
|
481
|
-
if (isNewColumnPinned) {
|
|
482
|
-
// 新欄位應該是 pinned column,使用 percentage
|
|
483
|
-
// 需要調整所有 pinned columns 的百分比
|
|
484
|
-
const newWidths = [];
|
|
485
|
-
const pinnedColumnCount = pinnedColumnIndices.length + 1; // 加上新欄位
|
|
486
|
-
const pinnedPercentagePerColumn = Math.min(Math.round((table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE / pinnedColumnCount) * 10) / 10, table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE);
|
|
487
|
-
currentWidths.forEach((width, index) => {
|
|
488
|
-
if (index === insertIndex) {
|
|
489
|
-
newWidths.push({ type: 'percentage', value: pinnedPercentagePerColumn });
|
|
490
|
-
}
|
|
491
|
-
if (pinnedColumnIndices.includes(index)) {
|
|
492
|
-
// 調整現有 pinned column 的百分比
|
|
493
|
-
newWidths.push({ type: 'percentage', value: pinnedPercentagePerColumn });
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
// 保持非 pinned column (pixel) 不變
|
|
497
|
-
newWidths.push(Object.assign({}, width));
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
// 如果插入位置在最後(但仍在 pinned 區域內)
|
|
501
|
-
if (insertIndex >= currentWidths.length) {
|
|
502
|
-
newWidths.push({ type: 'percentage', value: pinnedPercentagePerColumn });
|
|
503
|
-
}
|
|
504
|
-
return newWidths;
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
// 新欄位應該是 unpinned column,使用 pixel
|
|
508
|
-
const newWidths = [];
|
|
509
|
-
// 找到最後一個 pixel 欄位的寬度,新欄位將複製這個寬度
|
|
510
|
-
const lastPixelWidth = pixelColumns.length > 0 ? pixelColumns[pixelColumns.length - 1].value : 150;
|
|
511
|
-
currentWidths.forEach((width, index) => {
|
|
512
|
-
if (index === insertIndex) {
|
|
513
|
-
newWidths.push({ type: 'pixel', value: lastPixelWidth });
|
|
514
|
-
}
|
|
515
|
-
newWidths.push(Object.assign({}, width));
|
|
516
|
-
});
|
|
517
|
-
// 如果插入位置在最後
|
|
518
|
-
if (insertIndex >= currentWidths.length) {
|
|
519
|
-
newWidths.push({ type: 'pixel', value: lastPixelWidth });
|
|
520
|
-
}
|
|
521
|
-
return newWidths;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
// Fallback: 等比例縮減並插入新欄位
|
|
525
|
-
const averagePercentage = Math.round((100 / newColumnCount) * 10) / 10;
|
|
526
|
-
// 提取當前的百分比值(假設都是 percentage,否則轉為平均分配)
|
|
527
|
-
const currentPercentages = currentWidths.map((w) => (w.type === 'percentage' ? w.value : 100 / currentWidths.length));
|
|
528
|
-
// 等比例縮減現有欄位並插入新欄位
|
|
529
|
-
const newPercentages = scalePercentagesWithNewColumn(currentPercentages, averagePercentage, insertIndex);
|
|
530
|
-
return newPercentages.map((value) => ({ type: 'percentage', value }));
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* 計算刪除欄位後的欄位寬度
|
|
534
|
-
* 此函數會智慧處理欄位寬度的重新分配:
|
|
535
|
-
* - 如果所有欄位都是 percentage:按比例放大剩餘欄位
|
|
536
|
-
* - 如果有 pixel 欄位:保持 pixel 欄位不變,只調整 percentage 欄位
|
|
537
|
-
*
|
|
538
|
-
* @param currentWidths - 當前的欄位寬度陣列
|
|
539
|
-
* @param deleteIndex - 要刪除的欄位索引(0-based)
|
|
540
|
-
* @returns 新的欄位寬度陣列
|
|
541
|
-
*/
|
|
542
|
-
function calculateColumnWidthsAfterDelete(currentWidths, deleteIndex) {
|
|
543
|
-
if (currentWidths.length <= 1) {
|
|
544
|
-
return currentWidths;
|
|
545
|
-
}
|
|
546
|
-
const deletedWidth = currentWidths[deleteIndex];
|
|
547
|
-
const newWidths = currentWidths.filter((_, index) => index !== deleteIndex);
|
|
548
|
-
// 如果刪除的是 pixel 欄位,其他欄位保持不變
|
|
549
|
-
if (deletedWidth.type === 'pixel') {
|
|
550
|
-
return newWidths;
|
|
551
|
-
}
|
|
552
|
-
// 刪除的是 percentage 欄位
|
|
553
|
-
const deletedPercentage = deletedWidth.value;
|
|
554
|
-
// 分離 percentage 和 pixel 欄位
|
|
555
|
-
const percentageIndices = [];
|
|
556
|
-
newWidths.forEach((width, index) => {
|
|
557
|
-
if (width.type === 'percentage') {
|
|
558
|
-
percentageIndices.push(index);
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
// 如果沒有 percentage 欄位,直接返回
|
|
562
|
-
if (percentageIndices.length === 0) {
|
|
563
|
-
return newWidths;
|
|
564
|
-
}
|
|
565
|
-
// 將刪除欄位的百分比按比例分配給其他 percentage 欄位
|
|
566
|
-
const currentPercentageTotal = percentageIndices.reduce((sum, index) => sum + newWidths[index].value, 0);
|
|
567
|
-
return newWidths.map((width) => {
|
|
568
|
-
if (width.type === 'percentage') {
|
|
569
|
-
const proportion = width.value / currentPercentageTotal;
|
|
570
|
-
const additionalPercentage = deletedPercentage * proportion;
|
|
571
|
-
const newValue = Math.round((width.value + additionalPercentage) * 10) / 10;
|
|
572
|
-
return { type: 'percentage', value: newValue };
|
|
573
|
-
}
|
|
574
|
-
return width;
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* 計算拖曳後的欄位寬度
|
|
579
|
-
* 處理釘選欄位的特殊邏輯:
|
|
580
|
-
* - 情況 1:未釘選欄位(pixel)與未釘選欄位(pixel)之間:直接改變 pixel 值,允許超出容器
|
|
581
|
-
* - 情況 2:釘選欄位(percentage)與釘選欄位(percentage)之間:互相調整,且總和不超過 40%
|
|
582
|
-
* - 情況 3:釘選欄位(percentage)與未釘選欄位(pixel)之間:
|
|
583
|
-
* * 只調整釘選欄位的百分比(不超過 40% 總限制)
|
|
584
|
-
* * 當 table 未溢出(寬度 < container)時,重新計算所有未釘選欄位的 pixel 值以維持 100%
|
|
585
|
-
* * 當 table 已溢出時,只調整下一個欄位的 pixel 值
|
|
586
|
-
* * 當達到 40% 上限時,停止調整
|
|
587
|
-
* - 預設情況:兩個都是 percentage 但都不是釘選欄位(正常模式)
|
|
588
|
-
*
|
|
589
|
-
* @param currentWidths - 當前的欄位寬度陣列
|
|
590
|
-
* @param columnIndex - 被調整的欄位索引
|
|
591
|
-
* @param deltaPercentage - 寬度變化量(百分比)
|
|
592
|
-
* @param deltaPixel - 寬度變化量(pixel)
|
|
593
|
-
* @param pinnedColumnIndices - 釘選欄位的索引陣列
|
|
594
|
-
* @returns 新的欄位寬度陣列
|
|
595
|
-
*/
|
|
596
|
-
function calculateResizedColumnWidths(currentWidths, columnIndex, deltaPercentage, deltaPixel, pinnedColumnIndices = []) {
|
|
597
|
-
const newWidths = [...currentWidths];
|
|
598
|
-
const nextColumnIndex = columnIndex + 1;
|
|
599
|
-
const isLastColumn = nextColumnIndex >= newWidths.length;
|
|
600
|
-
const currentCol = newWidths[columnIndex];
|
|
601
|
-
const nextCol = isLastColumn ? null : newWidths[nextColumnIndex];
|
|
602
|
-
const isCurrentPinned = pinnedColumnIndices.includes(columnIndex);
|
|
603
|
-
const isNextPinned = nextCol ? pinnedColumnIndices.includes(nextColumnIndex) : false;
|
|
604
|
-
// **特殊情況:最後一欄,只調整當前欄位**
|
|
605
|
-
// 最後一欄必定不是 pinned column
|
|
606
|
-
if (isLastColumn) {
|
|
607
|
-
if (currentCol.type === 'pixel') {
|
|
608
|
-
// pixel 欄位(有 pinned column 情境):直接調整寬度
|
|
609
|
-
const currentPixel = currentCol.value;
|
|
610
|
-
const newCurrentPixel = Math.max(table.MIN_COLUMN_WIDTH_PIXEL, currentPixel + deltaPixel);
|
|
611
|
-
newWidths[columnIndex] = { type: 'pixel', value: Math.floor(newCurrentPixel) };
|
|
612
|
-
}
|
|
613
|
-
else if (currentCol.type === 'percentage') {
|
|
614
|
-
// percentage 欄位(無 pinned column 情境):調整當前欄位並將差額分配給前一欄以確保總和為 100%
|
|
615
|
-
const currentWidth = currentCol.value;
|
|
616
|
-
let newCurrentWidth = Math.max(table.MIN_COLUMN_WIDTH_PERCENTAGE, Math.round((currentWidth + deltaPercentage) * 10) / 10);
|
|
617
|
-
// 計算其他欄位的總百分比
|
|
618
|
-
const otherColumnsTotal = newWidths
|
|
619
|
-
.filter((_, idx) => idx !== columnIndex)
|
|
620
|
-
.reduce((sum, width) => sum + (width.type === 'percentage' ? width.value : 0), 0);
|
|
621
|
-
// 確保總和為 100%
|
|
622
|
-
const maxAllowedWidth = 100 - otherColumnsTotal;
|
|
623
|
-
newCurrentWidth = Math.min(newCurrentWidth, maxAllowedWidth);
|
|
624
|
-
// 計算寬度變化量
|
|
625
|
-
const widthChange = currentWidth - newCurrentWidth;
|
|
626
|
-
// 如果有寬度變化且存在前一欄,將差額分配給前一欄
|
|
627
|
-
if (widthChange !== 0 && columnIndex > 0) {
|
|
628
|
-
const prevCol = newWidths[columnIndex - 1];
|
|
629
|
-
if (prevCol.type === 'percentage') {
|
|
630
|
-
const newPrevWidth = Math.max(table.MIN_COLUMN_WIDTH_PERCENTAGE, Math.round((prevCol.value + widthChange) * 10) / 10);
|
|
631
|
-
newWidths[columnIndex - 1] = { type: 'percentage', value: newPrevWidth };
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
newWidths[columnIndex] = { type: 'percentage', value: newCurrentWidth };
|
|
635
|
-
}
|
|
636
|
-
return newWidths;
|
|
637
|
-
}
|
|
638
|
-
// 以下是有 next column 的邏輯
|
|
639
|
-
if (!nextCol) {
|
|
640
|
-
return newWidths;
|
|
641
|
-
}
|
|
642
|
-
// 情況 1:當前欄位是 pixel(未釘選),下一欄位也是 pixel(未釘選)
|
|
643
|
-
if (currentCol.type === 'pixel' && nextCol.type === 'pixel') {
|
|
644
|
-
const currentPixel = currentCol.value;
|
|
645
|
-
const newCurrentPixel = Math.max(table.MIN_COLUMN_WIDTH_PIXEL, currentPixel + deltaPixel);
|
|
646
|
-
newWidths[columnIndex] = { type: 'pixel', value: Math.floor(newCurrentPixel) };
|
|
647
|
-
return newWidths;
|
|
648
|
-
}
|
|
649
|
-
// 情況 2:當前欄位是 percentage(釘選),下一欄位也是 percentage(釘選)
|
|
650
|
-
if (currentCol.type === 'percentage' && nextCol.type === 'percentage' && isCurrentPinned && isNextPinned) {
|
|
651
|
-
const currentWidth = currentCol.value;
|
|
652
|
-
const nextWidth = nextCol.value;
|
|
653
|
-
// 計算新寬度
|
|
654
|
-
let newCurrentWidth = Math.round((currentWidth + deltaPercentage) * 10) / 10;
|
|
655
|
-
let newNextWidth = Math.round((nextWidth - deltaPercentage) * 10) / 10;
|
|
656
|
-
if (newCurrentWidth < table.MIN_COLUMN_WIDTH_PERCENTAGE) {
|
|
657
|
-
newCurrentWidth = table.MIN_COLUMN_WIDTH_PERCENTAGE;
|
|
658
|
-
newNextWidth = Math.round((currentWidth + nextWidth - table.MIN_COLUMN_WIDTH_PERCENTAGE) * 10) / 10;
|
|
659
|
-
}
|
|
660
|
-
else if (newNextWidth < table.MIN_COLUMN_WIDTH_PERCENTAGE) {
|
|
661
|
-
newNextWidth = table.MIN_COLUMN_WIDTH_PERCENTAGE;
|
|
662
|
-
newCurrentWidth = Math.round((currentWidth + nextWidth - table.MIN_COLUMN_WIDTH_PERCENTAGE) * 10) / 10;
|
|
663
|
-
}
|
|
664
|
-
// 確保釘選欄位總和不超過 40%
|
|
665
|
-
const otherPinnedTotal = pinnedColumnIndices
|
|
666
|
-
.filter((idx) => idx !== columnIndex && idx !== nextColumnIndex)
|
|
667
|
-
.reduce((sum, idx) => sum + (newWidths[idx].type === 'percentage' ? newWidths[idx].value : 0), 0);
|
|
668
|
-
const twoColumnsTotal = newCurrentWidth + newNextWidth;
|
|
669
|
-
if (otherPinnedTotal + twoColumnsTotal > table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE) {
|
|
670
|
-
// 按比例縮減這兩個欄位
|
|
671
|
-
const allowedTotal = table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE - otherPinnedTotal;
|
|
672
|
-
const scale = allowedTotal / twoColumnsTotal;
|
|
673
|
-
newCurrentWidth = Math.round(newCurrentWidth * scale * 10) / 10;
|
|
674
|
-
newNextWidth = Math.round(newNextWidth * scale * 10) / 10;
|
|
675
|
-
}
|
|
676
|
-
newWidths[columnIndex] = { type: 'percentage', value: newCurrentWidth };
|
|
677
|
-
newWidths[nextColumnIndex] = { type: 'percentage', value: newNextWidth };
|
|
678
|
-
return newWidths;
|
|
679
|
-
}
|
|
680
|
-
// 情況 3:當前欄位是 percentage(釘選),下一欄位是 pixel(未釘選)
|
|
681
|
-
if (currentCol.type === 'percentage' && nextCol.type === 'pixel' && isCurrentPinned && !isNextPinned) {
|
|
682
|
-
const currentWidth = currentCol.value;
|
|
683
|
-
// 計算新的釘選欄位寬度
|
|
684
|
-
let newCurrentWidth = Math.round((currentWidth + deltaPercentage) * 10) / 10;
|
|
685
|
-
newCurrentWidth = Math.max(table.MIN_COLUMN_WIDTH_PERCENTAGE, newCurrentWidth);
|
|
686
|
-
// 確保所有釘選欄位總和不超過 40%
|
|
687
|
-
const otherPinnedTotal = pinnedColumnIndices
|
|
688
|
-
.filter((idx) => idx !== columnIndex)
|
|
689
|
-
.reduce((sum, idx) => sum + (newWidths[idx].type === 'percentage' ? newWidths[idx].value : 0), 0);
|
|
690
|
-
const maxAllowedWidth = table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE - otherPinnedTotal;
|
|
691
|
-
// 計算調整前後的實際寬度
|
|
692
|
-
const beforeAdjustWidth = Math.min(currentWidth, maxAllowedWidth);
|
|
693
|
-
const afterAdjustWidth = Math.min(newCurrentWidth, maxAllowedWidth);
|
|
694
|
-
// 如果調整前後的寬度相同(表示已經被限制在上限),則不調整任何欄位
|
|
695
|
-
if (Math.abs(afterAdjustWidth - beforeAdjustWidth) < 0.1) {
|
|
696
|
-
return newWidths;
|
|
697
|
-
}
|
|
698
|
-
// 應用上限限制
|
|
699
|
-
newCurrentWidth = afterAdjustWidth;
|
|
700
|
-
// 更新當前釘選欄位的寬度
|
|
701
|
-
newWidths[columnIndex] = { type: 'percentage', value: newCurrentWidth };
|
|
702
|
-
return newWidths;
|
|
703
|
-
}
|
|
704
|
-
// 預設情況:兩個都是 percentage 但都不是釘選欄位(正常模式)
|
|
705
|
-
if (currentCol.type === 'percentage' && nextCol.type === 'percentage') {
|
|
706
|
-
const currentWidth = currentCol.value;
|
|
707
|
-
const nextWidth = nextCol.value;
|
|
708
|
-
let newCurrentWidth = Math.round((currentWidth + deltaPercentage) * 10) / 10;
|
|
709
|
-
let newNextWidth = Math.round((nextWidth - deltaPercentage) * 10) / 10;
|
|
710
|
-
if (newCurrentWidth < table.MIN_COLUMN_WIDTH_PERCENTAGE) {
|
|
711
|
-
newCurrentWidth = table.MIN_COLUMN_WIDTH_PERCENTAGE;
|
|
712
|
-
newNextWidth = Math.round((currentWidth + nextWidth - table.MIN_COLUMN_WIDTH_PERCENTAGE) * 10) / 10;
|
|
713
|
-
}
|
|
714
|
-
else if (newNextWidth < table.MIN_COLUMN_WIDTH_PERCENTAGE) {
|
|
715
|
-
newNextWidth = table.MIN_COLUMN_WIDTH_PERCENTAGE;
|
|
716
|
-
newCurrentWidth = Math.round((currentWidth + nextWidth - table.MIN_COLUMN_WIDTH_PERCENTAGE) * 10) / 10;
|
|
717
|
-
}
|
|
718
|
-
newWidths[columnIndex] = { type: 'percentage', value: newCurrentWidth };
|
|
719
|
-
newWidths[nextColumnIndex] = { type: 'percentage', value: newNextWidth };
|
|
720
|
-
return newWidths;
|
|
721
|
-
}
|
|
722
|
-
return newWidths;
|
|
723
|
-
}
|
|
724
|
-
/**
|
|
725
|
-
* 移動或交換欄位寬度設定
|
|
726
|
-
* @param currentWidths - 當前的欄位寬度陣列
|
|
727
|
-
* @param sourceIndex - 來源欄位的索引
|
|
728
|
-
* @param targetIndex - 目標欄位的索引
|
|
729
|
-
* @param mode - 'swap' 為交換兩個位置,'move' 為移動到目標位置
|
|
730
|
-
* @returns 處理後的欄位寬度陣列
|
|
731
|
-
*/
|
|
732
|
-
function moveOrSwapColumnWidth(currentWidths, sourceIndex, targetIndex, mode = 'move') {
|
|
733
|
-
if (sourceIndex === targetIndex ||
|
|
734
|
-
sourceIndex < 0 ||
|
|
735
|
-
targetIndex < 0 ||
|
|
736
|
-
sourceIndex >= currentWidths.length ||
|
|
737
|
-
targetIndex >= currentWidths.length) {
|
|
738
|
-
return currentWidths;
|
|
739
|
-
}
|
|
740
|
-
const newWidths = [...currentWidths];
|
|
741
|
-
if (mode === 'swap') {
|
|
742
|
-
// swap 邏輯:直接交換兩個位置的值
|
|
743
|
-
const temp = newWidths[sourceIndex];
|
|
744
|
-
newWidths[sourceIndex] = newWidths[targetIndex];
|
|
745
|
-
newWidths[targetIndex] = temp;
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
748
|
-
// move 邏輯:移除再插入
|
|
749
|
-
const [movedWidth] = newWidths.splice(sourceIndex, 1);
|
|
750
|
-
newWidths.splice(targetIndex, 0, movedWidth);
|
|
751
|
-
}
|
|
752
|
-
return newWidths;
|
|
753
|
-
}
|
|
754
|
-
/**
|
|
755
|
-
* 將 columnWidths 轉換為混合模式(釘選欄位用 percentage,未釘選欄位用 pixel)
|
|
756
|
-
* @param currentWidths - 當前的欄位寬度陣列
|
|
757
|
-
* @param pinnedColumnIndices - 釘選欄位的索引陣列
|
|
758
|
-
* @param tableWidth - 表格的實際寬度(pixel)
|
|
759
|
-
* @returns 轉換後的欄位寬度陣列
|
|
760
|
-
*/
|
|
761
|
-
function convertToMixedWidthMode(currentWidths, pinnedColumnIndices, tableWidth) {
|
|
762
|
-
if (pinnedColumnIndices.length === 0 || tableWidth === 0) {
|
|
763
|
-
return currentWidths;
|
|
764
|
-
}
|
|
765
|
-
const newWidths = [];
|
|
766
|
-
// 計算釘選欄位的總百分比
|
|
767
|
-
let totalPinnedPercentage = 0;
|
|
768
|
-
pinnedColumnIndices.forEach((index) => {
|
|
769
|
-
const width = currentWidths[index];
|
|
770
|
-
if (width) {
|
|
771
|
-
if (width.type === 'percentage') {
|
|
772
|
-
totalPinnedPercentage += width.value;
|
|
773
|
-
}
|
|
774
|
-
else {
|
|
775
|
-
// 如果是 pixel,轉換為百分比
|
|
776
|
-
const percentage = (width.value / tableWidth) * 100;
|
|
777
|
-
totalPinnedPercentage += percentage;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
// 確保釘選欄位總和不超過指定範圍
|
|
782
|
-
const scaleFactor = totalPinnedPercentage > table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE
|
|
783
|
-
? table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE / totalPinnedPercentage
|
|
784
|
-
: 1;
|
|
785
|
-
totalPinnedPercentage = Math.min(totalPinnedPercentage, table.MAX_PINNED_COLUMNS_WIDTH_PERCENTAGE);
|
|
786
|
-
// 調整釘選欄位的百分比
|
|
787
|
-
currentWidths.forEach((width, index) => {
|
|
788
|
-
if (pinnedColumnIndices.includes(index)) {
|
|
789
|
-
if (width.type === 'percentage') {
|
|
790
|
-
currentWidths[index] = {
|
|
791
|
-
type: 'percentage',
|
|
792
|
-
value: Math.round(width.value * scaleFactor * 10) / 10,
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
else {
|
|
796
|
-
const percentage = (width.value / tableWidth) * 100;
|
|
797
|
-
currentWidths[index] = {
|
|
798
|
-
type: 'percentage',
|
|
799
|
-
value: Math.round(percentage * scaleFactor * 10) / 10,
|
|
800
|
-
};
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
// 計算剩餘空間(用於未釘選欄位)
|
|
805
|
-
const remainingPercentage = 100 - totalPinnedPercentage;
|
|
806
|
-
const remainingPixelWidth = (tableWidth * remainingPercentage) / 100;
|
|
807
|
-
const unpinnedColumnCount = currentWidths.length - pinnedColumnIndices.length;
|
|
808
|
-
const pixelWidthPerColumn = unpinnedColumnCount > 0 ? Math.floor(remainingPixelWidth / unpinnedColumnCount) : 0;
|
|
809
|
-
// 建立新的寬度陣列
|
|
810
|
-
currentWidths.forEach((width, index) => {
|
|
811
|
-
if (pinnedColumnIndices.includes(index)) {
|
|
812
|
-
// 釘選欄位:使用 percentage
|
|
813
|
-
newWidths.push(width);
|
|
814
|
-
}
|
|
815
|
-
else {
|
|
816
|
-
// 未釘選欄位:使用 pixel
|
|
817
|
-
newWidths.push({ type: 'pixel', value: pixelWidthPerColumn });
|
|
818
|
-
}
|
|
819
|
-
});
|
|
820
|
-
return newWidths;
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* 將混合模式的欄位寬度轉換回純百分比模式
|
|
824
|
-
* 當所有 pinned columns 都被 unpin 後,需要將 pixel 欄位轉換回 percentage
|
|
825
|
-
* @param currentWidths - 當前的欄位寬度陣列(混合模式)
|
|
826
|
-
* @returns 轉換後的純百分比欄位寬度陣列,總和為 100%
|
|
827
|
-
*/
|
|
828
|
-
function convertToPercentageMode(currentWidths) {
|
|
829
|
-
if (currentWidths.length === 0)
|
|
830
|
-
return [];
|
|
831
|
-
// 檢查是否有 pixel 欄位
|
|
832
|
-
const hasPixelColumns = currentWidths.some((width) => width.type === 'pixel');
|
|
833
|
-
// 如果沒有 pixel 欄位,表示已經是純百分比模式,直接返回
|
|
834
|
-
if (!hasPixelColumns) {
|
|
835
|
-
return currentWidths;
|
|
836
|
-
}
|
|
837
|
-
// 使用 distributeEqualPercentages 重新分配百分比,確保總和為 100%
|
|
838
|
-
const percentages = distributeEqualPercentages(currentWidths.length);
|
|
839
|
-
return percentages.map((value) => ({ type: 'percentage', value }));
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/** 檢查 table cell 是否在 focused 狀態 */
|
|
843
|
-
function useTableCellFocused(element, editor) {
|
|
844
|
-
const { tableSelectedOn, setTableHoveredOn, setTableSelectedOn } = useTableStateContext();
|
|
845
|
-
const cellPath = slateReact.ReactEditor.findPath(editor, element);
|
|
846
|
-
const focused = slateReact.useFocused();
|
|
847
|
-
const isCellFocused = React.useMemo(() => {
|
|
848
|
-
if (!focused || !editor.selection || (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region))
|
|
849
|
-
return false;
|
|
850
|
-
const isSelectionCollapsed = core.Range.isCollapsed(editor.selection);
|
|
851
|
-
if (!isSelectionCollapsed)
|
|
852
|
-
return false;
|
|
853
|
-
const selectionPath = editor.selection.anchor.path;
|
|
854
|
-
return selectionPath.slice(0, cellPath.length).every((segment, index) => segment === cellPath[index]);
|
|
855
|
-
}, [focused, editor.selection, cellPath, tableSelectedOn]);
|
|
856
|
-
const isPreviousCellFocused = utils.usePreviousValue(isCellFocused);
|
|
857
|
-
React.useEffect(() => {
|
|
858
|
-
if (isCellFocused) {
|
|
859
|
-
setTableHoveredOn(undefined);
|
|
860
|
-
}
|
|
861
|
-
if (isCellFocused && !isPreviousCellFocused) {
|
|
862
|
-
setTableSelectedOn(undefined);
|
|
863
|
-
}
|
|
864
|
-
}, [isCellFocused, isPreviousCellFocused, setTableSelectedOn, setTableHoveredOn]);
|
|
865
|
-
return isCellFocused;
|
|
866
|
-
}
|
|
867
|
-
/** 取得 table cell 在 table 裡的 columnIndex 及 rowIndex */
|
|
868
|
-
function useTableCellPosition(element, _editor) {
|
|
869
|
-
const { cellPositions } = useTableMetadata();
|
|
870
|
-
const cellPosition = React.useMemo(() => {
|
|
871
|
-
const position = cellPositions.get(element);
|
|
872
|
-
if (position) {
|
|
873
|
-
return position;
|
|
874
|
-
}
|
|
875
|
-
return { columnIndex: -1, rowIndex: -1 };
|
|
876
|
-
}, [cellPositions, element]);
|
|
877
|
-
return cellPosition;
|
|
878
|
-
}
|
|
879
|
-
/** 轉換 paragraph, order list, un-order list */
|
|
880
|
-
function useTableCellTransformContent(element, editor) {
|
|
881
|
-
const splitContentByNewlines = React.useCallback((cellPath) => {
|
|
882
|
-
try {
|
|
883
|
-
const cellNode = core.Node.get(editor, cellPath);
|
|
884
|
-
if (!core.Element.isElement(cellNode))
|
|
885
|
-
return false;
|
|
886
|
-
let hasChanges = false;
|
|
887
|
-
const nodesToProcess = [...cellNode.children];
|
|
888
|
-
// Process each content node in reverse order to maintain indices
|
|
889
|
-
for (let contentIndex = nodesToProcess.length - 1; contentIndex >= 0; contentIndex--) {
|
|
890
|
-
const contentNode = nodesToProcess[contentIndex];
|
|
891
|
-
if (core.Element.isElement(contentNode)) {
|
|
892
|
-
const textContent = core.Node.string(contentNode);
|
|
893
|
-
if (textContent.includes('\n')) {
|
|
894
|
-
const contentPath = [...cellPath, contentIndex];
|
|
895
|
-
// Verify the path is still valid
|
|
896
|
-
try {
|
|
897
|
-
const currentNode = core.Node.get(editor, contentPath);
|
|
898
|
-
if (!currentNode)
|
|
899
|
-
continue;
|
|
900
|
-
}
|
|
901
|
-
catch (_a) {
|
|
902
|
-
continue;
|
|
903
|
-
}
|
|
904
|
-
// Split by newlines and filter out empty strings
|
|
905
|
-
const lines = textContent.split('\n').filter((line) => line.trim() !== '');
|
|
906
|
-
if (lines.length > 1) {
|
|
907
|
-
hasChanges = true;
|
|
908
|
-
// Use Editor.withoutNormalizing to batch operations
|
|
909
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
910
|
-
core.Transforms.removeNodes(editor, { at: contentPath });
|
|
911
|
-
lines.forEach((line, lineIndex) => {
|
|
912
|
-
const newParagraph = {
|
|
913
|
-
type: core.PARAGRAPH_TYPE,
|
|
914
|
-
children: [{ text: line.trim() }],
|
|
915
|
-
};
|
|
916
|
-
core.Transforms.insertNodes(editor, newParagraph, {
|
|
917
|
-
at: [...cellPath, contentIndex + lineIndex],
|
|
918
|
-
});
|
|
919
|
-
});
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
return hasChanges;
|
|
926
|
-
}
|
|
927
|
-
catch (error) {
|
|
928
|
-
console.warn('Failed to split content by newlines:', error);
|
|
929
|
-
return false;
|
|
930
|
-
}
|
|
931
|
-
}, [editor]);
|
|
932
|
-
const transformCellContent = React.useCallback((elementType) => {
|
|
933
|
-
try {
|
|
934
|
-
const cellPath = slateReact.ReactEditor.findPath(editor, element);
|
|
935
|
-
const listHelper = list.createList();
|
|
936
|
-
if (elementType === list.LIST_TYPES.ol || elementType === list.LIST_TYPES.ul) {
|
|
937
|
-
/** 將 paragraph 的換行符號轉換成 list item */
|
|
938
|
-
const wasSplit = splitContentByNewlines(cellPath);
|
|
939
|
-
if (wasSplit) {
|
|
940
|
-
core.Editor.normalize(editor, { force: true });
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
// 選擇整個儲存格內容
|
|
944
|
-
const cellStartPoint = core.Editor.start(editor, cellPath);
|
|
945
|
-
const cellEndPoint = core.Editor.end(editor, cellPath);
|
|
946
|
-
core.Transforms.select(editor, { anchor: cellStartPoint, focus: cellEndPoint });
|
|
947
|
-
if (elementType === list.LIST_TYPES.ol || elementType === list.LIST_TYPES.ul) {
|
|
948
|
-
listHelper.toggleList(editor, elementType, core.PARAGRAPH_TYPE);
|
|
949
|
-
}
|
|
950
|
-
else if (elementType === core.PARAGRAPH_TYPE) {
|
|
951
|
-
const cellNode = core.Node.get(editor, cellPath);
|
|
952
|
-
if (core.Element.isElement(cellNode)) {
|
|
953
|
-
const currentListElement = cellNode.children.find((child) => core.Element.isElement(child) && [list.LIST_TYPES.ol, list.LIST_TYPES.ul].includes(child.type));
|
|
954
|
-
if (currentListElement && core.Element.isElement(currentListElement)) {
|
|
955
|
-
listHelper.toggleList(editor, currentListElement.type, core.PARAGRAPH_TYPE);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
// 因為底層結構改變了,取消選取以避免錯誤
|
|
960
|
-
core.Transforms.deselect(editor);
|
|
961
|
-
}
|
|
962
|
-
catch (error) {
|
|
963
|
-
console.warn('Failed to transform cell content:', error);
|
|
964
|
-
}
|
|
965
|
-
}, [editor, element, splitContentByNewlines]);
|
|
966
|
-
return transformCellContent;
|
|
967
|
-
}
|
|
968
|
-
/** 設定 column 或 table 的 align */
|
|
969
|
-
function useTableCellAlign(tableElement, editor) {
|
|
970
|
-
const setAlign = React.useCallback((alignValue, scope, columnIndex) => {
|
|
971
|
-
const tableStructure = getTableStructure(editor, tableElement);
|
|
972
|
-
if (!tableStructure) {
|
|
973
|
-
console.warn('Failed to get table structure');
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
// 使用 helper 函數收集 cells
|
|
977
|
-
const cells = collectCells(tableStructure, scope, columnIndex);
|
|
978
|
-
// 使用 helper 函數設定 align
|
|
979
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
980
|
-
setAlignForCells(editor, cells, alignValue);
|
|
981
|
-
});
|
|
982
|
-
}, [editor, tableElement]);
|
|
983
|
-
return setAlign;
|
|
984
|
-
}
|
|
985
|
-
/** 獲取 column 或 table 的 align 狀態 */
|
|
986
|
-
function useTableCellAlignStatus(tableElement, editor) {
|
|
987
|
-
const getAlign = React.useCallback((scope, columnIndex) => {
|
|
988
|
-
const tableStructure = getTableStructure(editor, tableElement);
|
|
989
|
-
if (!tableStructure) {
|
|
990
|
-
return 'left';
|
|
991
|
-
}
|
|
992
|
-
const cells = collectCells(tableStructure, scope, columnIndex);
|
|
993
|
-
return getAlignFromCells(cells);
|
|
994
|
-
}, [editor, tableElement]);
|
|
995
|
-
return getAlign;
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
/**
|
|
999
|
-
* 將所有與 toolbar icons 相關的邏輯集中管理
|
|
1000
|
-
*/
|
|
1001
|
-
function useTableCellToolbarActions({ element, cellPosition, isHeader, transformCellContent, }) {
|
|
1002
|
-
const editor = slateReact.useSlateStatic();
|
|
1003
|
-
const { tableSelectedOn, setTableSelectedOn } = useTableStateContext();
|
|
1004
|
-
const { tableElement, columnCount, rowCount, isReachMaximumColumns, isReachMinimumNormalColumns, isReachMinimumBodyRows, isColumnPinned, isRowPinned, } = useTableMetadata();
|
|
1005
|
-
const setAlign = useTableCellAlign(tableElement, editor);
|
|
1006
|
-
const getAlign = useTableCellAlignStatus(tableElement, editor);
|
|
1007
|
-
const { deleteRow, deleteColumn, addRow, addColumn, moveRowToBody, moveRowToHeader, unsetColumnAsTitle, setColumnAsTitle, pinColumn, unpinColumn, pinRow, unpinRow, moveOrSwapRow, moveOrSwapColumn, } = useTableActionsContext();
|
|
1008
|
-
// 獲取當前 cell 內容的類型(用於顯示對應的 icon)
|
|
1009
|
-
const getCurrentContentType = React.useMemo(() => {
|
|
1010
|
-
// 檢查 cell 的第一個 child 的類型
|
|
1011
|
-
if (element.children && element.children.length > 0) {
|
|
1012
|
-
const firstChild = element.children[0];
|
|
1013
|
-
if (core.Element.isElement(firstChild)) {
|
|
1014
|
-
const childType = firstChild.type;
|
|
1015
|
-
// 檢查是否為 list 類型
|
|
1016
|
-
if (childType === list.LIST_TYPES.ul) {
|
|
1017
|
-
return list.LIST_TYPES.ul;
|
|
1018
|
-
}
|
|
1019
|
-
if (childType === list.LIST_TYPES.ol) {
|
|
1020
|
-
return list.LIST_TYPES.ol;
|
|
1021
|
-
}
|
|
1022
|
-
// 檢查是否為 paragraph(預設)
|
|
1023
|
-
if (childType === core.PARAGRAPH_TYPE) {
|
|
1024
|
-
return core.PARAGRAPH_TYPE;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
// 預設返回 paragraph
|
|
1029
|
-
return core.PARAGRAPH_TYPE;
|
|
1030
|
-
}, [element.children]);
|
|
1031
|
-
// 根據當前內容類型選擇對應的 icon
|
|
1032
|
-
const getCurrentIcon = React.useMemo(() => {
|
|
1033
|
-
if (getCurrentContentType === list.LIST_TYPES.ul) {
|
|
1034
|
-
return icons.UnorderedList;
|
|
1035
|
-
}
|
|
1036
|
-
if (getCurrentContentType === list.LIST_TYPES.ol) {
|
|
1037
|
-
return icons.OrderedList;
|
|
1038
|
-
}
|
|
1039
|
-
return icons.Paragraph;
|
|
1040
|
-
}, [getCurrentContentType]);
|
|
1041
|
-
// Focus toolbar - 當 cell 被 focus 時顯示的工具列
|
|
1042
|
-
const focusToolbarIconGroups = React.useMemo(() => {
|
|
1043
|
-
return [
|
|
1044
|
-
{
|
|
1045
|
-
icons: [
|
|
1046
|
-
React.createElement(toolbar.ToolbarGroupIcon, { key: "typography-change", icon: getCurrentIcon },
|
|
1047
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.Paragraph, onClick: () => {
|
|
1048
|
-
transformCellContent(core.PARAGRAPH_TYPE);
|
|
1049
|
-
} }),
|
|
1050
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.UnorderedList, onClick: () => {
|
|
1051
|
-
transformCellContent(list.LIST_TYPES.ul);
|
|
1052
|
-
} }),
|
|
1053
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.OrderedList, onClick: () => {
|
|
1054
|
-
transformCellContent(list.LIST_TYPES.ol);
|
|
1055
|
-
} })),
|
|
1056
|
-
],
|
|
1057
|
-
},
|
|
1058
|
-
{
|
|
1059
|
-
icons: [
|
|
1060
|
-
{
|
|
1061
|
-
icon: icons.AddRowAtBottom,
|
|
1062
|
-
onClick: () => {
|
|
1063
|
-
addRow({ position: 'bottom', rowIndex: cellPosition.rowIndex });
|
|
1064
|
-
},
|
|
1065
|
-
},
|
|
1066
|
-
{
|
|
1067
|
-
icon: icons.AddRowAtTop,
|
|
1068
|
-
onClick: () => {
|
|
1069
|
-
addRow({ position: 'top', rowIndex: cellPosition.rowIndex });
|
|
1070
|
-
},
|
|
1071
|
-
},
|
|
1072
|
-
isReachMaximumColumns
|
|
1073
|
-
? undefined
|
|
1074
|
-
: {
|
|
1075
|
-
icon: icons.AddColumnAtLeft,
|
|
1076
|
-
onClick: () => {
|
|
1077
|
-
addColumn({
|
|
1078
|
-
position: 'left',
|
|
1079
|
-
columnIndex: cellPosition.columnIndex,
|
|
1080
|
-
});
|
|
1081
|
-
},
|
|
1082
|
-
},
|
|
1083
|
-
isReachMaximumColumns
|
|
1084
|
-
? undefined
|
|
1085
|
-
: {
|
|
1086
|
-
icon: icons.AddColumnAtRight,
|
|
1087
|
-
onClick: () => {
|
|
1088
|
-
addColumn({
|
|
1089
|
-
position: 'right',
|
|
1090
|
-
columnIndex: cellPosition.columnIndex,
|
|
1091
|
-
});
|
|
1092
|
-
},
|
|
1093
|
-
},
|
|
1094
|
-
].filter((i) => i !== undefined),
|
|
1095
|
-
},
|
|
1096
|
-
];
|
|
1097
|
-
}, [transformCellContent, addRow, addColumn, cellPosition, isReachMaximumColumns, getCurrentIcon]);
|
|
1098
|
-
// Row actions
|
|
1099
|
-
const rowActions = React.useMemo(() => {
|
|
1100
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'row' || typeof tableSelectedOn.index !== 'number') {
|
|
1101
|
-
return null;
|
|
1102
|
-
}
|
|
1103
|
-
const removeTitleAction = {
|
|
1104
|
-
icon: icons.TableRemoveTitle,
|
|
1105
|
-
onClick: () => {
|
|
1106
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1107
|
-
moveRowToBody(tableSelectedOn.index);
|
|
1108
|
-
setTableSelectedOn(undefined);
|
|
1109
|
-
}
|
|
1110
|
-
},
|
|
1111
|
-
};
|
|
1112
|
-
const setColumnTitleAction = isReachMinimumBodyRows
|
|
1113
|
-
? undefined
|
|
1114
|
-
: {
|
|
1115
|
-
icon: icons.TableSetColumnTitle,
|
|
1116
|
-
onClick: () => {
|
|
1117
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1118
|
-
moveRowToHeader(tableSelectedOn.index);
|
|
1119
|
-
setTableSelectedOn(undefined);
|
|
1120
|
-
}
|
|
1121
|
-
},
|
|
1122
|
-
};
|
|
1123
|
-
const unpinnedAction = {
|
|
1124
|
-
icon: icons.Unpinned,
|
|
1125
|
-
onClick: () => {
|
|
1126
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1127
|
-
unpinRow();
|
|
1128
|
-
setTableSelectedOn(undefined);
|
|
1129
|
-
}
|
|
1130
|
-
},
|
|
1131
|
-
};
|
|
1132
|
-
const pinnedAction = isReachMinimumBodyRows && !isHeader
|
|
1133
|
-
? undefined
|
|
1134
|
-
: {
|
|
1135
|
-
icon: icons.Pinned,
|
|
1136
|
-
onClick: () => {
|
|
1137
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1138
|
-
pinRow(tableSelectedOn.index);
|
|
1139
|
-
setTableSelectedOn(undefined);
|
|
1140
|
-
}
|
|
1141
|
-
},
|
|
1142
|
-
};
|
|
1143
|
-
const actions = [];
|
|
1144
|
-
const rowIsPinned = isRowPinned(tableSelectedOn.index);
|
|
1145
|
-
if (rowIsPinned) {
|
|
1146
|
-
actions.push(unpinnedAction);
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
if (pinnedAction)
|
|
1150
|
-
actions.push(pinnedAction);
|
|
1151
|
-
}
|
|
1152
|
-
if (isHeader) {
|
|
1153
|
-
actions.push(removeTitleAction);
|
|
1154
|
-
}
|
|
1155
|
-
else {
|
|
1156
|
-
if (setColumnTitleAction)
|
|
1157
|
-
actions.push(setColumnTitleAction);
|
|
1158
|
-
}
|
|
1159
|
-
return actions;
|
|
1160
|
-
}, [
|
|
1161
|
-
tableSelectedOn,
|
|
1162
|
-
moveRowToBody,
|
|
1163
|
-
moveRowToHeader,
|
|
1164
|
-
unpinRow,
|
|
1165
|
-
pinRow,
|
|
1166
|
-
setTableSelectedOn,
|
|
1167
|
-
isReachMinimumBodyRows,
|
|
1168
|
-
isHeader,
|
|
1169
|
-
isRowPinned,
|
|
1170
|
-
]);
|
|
1171
|
-
// Column actions
|
|
1172
|
-
const columnActions = React.useMemo(() => {
|
|
1173
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
1174
|
-
return null;
|
|
1175
|
-
}
|
|
1176
|
-
const removeTitleAction = {
|
|
1177
|
-
icon: icons.TableRemoveTitle,
|
|
1178
|
-
onClick: () => {
|
|
1179
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1180
|
-
unsetColumnAsTitle(tableSelectedOn.index);
|
|
1181
|
-
setTableSelectedOn(undefined);
|
|
1182
|
-
}
|
|
1183
|
-
},
|
|
1184
|
-
};
|
|
1185
|
-
const setRowTitleAction = isReachMinimumNormalColumns
|
|
1186
|
-
? undefined
|
|
1187
|
-
: {
|
|
1188
|
-
icon: icons.TableSetRowTitle,
|
|
1189
|
-
onClick: () => {
|
|
1190
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1191
|
-
setColumnAsTitle(tableSelectedOn.index);
|
|
1192
|
-
setTableSelectedOn(undefined);
|
|
1193
|
-
}
|
|
1194
|
-
},
|
|
1195
|
-
};
|
|
1196
|
-
const unpinnedAction = {
|
|
1197
|
-
icon: icons.Unpinned,
|
|
1198
|
-
onClick: () => {
|
|
1199
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1200
|
-
unpinColumn();
|
|
1201
|
-
setTableSelectedOn(undefined);
|
|
1202
|
-
}
|
|
1203
|
-
},
|
|
1204
|
-
};
|
|
1205
|
-
const pinnedAction = isReachMinimumNormalColumns && !element.treatAsTitle
|
|
1206
|
-
? undefined
|
|
1207
|
-
: {
|
|
1208
|
-
icon: icons.Pinned,
|
|
1209
|
-
onClick: () => {
|
|
1210
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1211
|
-
pinColumn(tableSelectedOn.index);
|
|
1212
|
-
setTableSelectedOn(undefined);
|
|
1213
|
-
}
|
|
1214
|
-
},
|
|
1215
|
-
};
|
|
1216
|
-
const actions = [];
|
|
1217
|
-
const columnIsPinned = isColumnPinned(tableSelectedOn.index);
|
|
1218
|
-
if (columnIsPinned) {
|
|
1219
|
-
actions.push(unpinnedAction);
|
|
1220
|
-
}
|
|
1221
|
-
else {
|
|
1222
|
-
if (pinnedAction)
|
|
1223
|
-
actions.push(pinnedAction);
|
|
1224
|
-
}
|
|
1225
|
-
if (element.treatAsTitle) {
|
|
1226
|
-
actions.push(removeTitleAction);
|
|
1227
|
-
}
|
|
1228
|
-
else {
|
|
1229
|
-
if (setRowTitleAction)
|
|
1230
|
-
actions.push(setRowTitleAction);
|
|
1231
|
-
}
|
|
1232
|
-
return actions;
|
|
1233
|
-
}, [
|
|
1234
|
-
tableSelectedOn,
|
|
1235
|
-
unsetColumnAsTitle,
|
|
1236
|
-
setColumnAsTitle,
|
|
1237
|
-
unpinColumn,
|
|
1238
|
-
pinColumn,
|
|
1239
|
-
setTableSelectedOn,
|
|
1240
|
-
isReachMinimumNormalColumns,
|
|
1241
|
-
element.treatAsTitle,
|
|
1242
|
-
isColumnPinned,
|
|
1243
|
-
]);
|
|
1244
|
-
// Column align actions
|
|
1245
|
-
const columnAlignActions = React.useMemo(() => {
|
|
1246
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
1247
|
-
return null;
|
|
1248
|
-
}
|
|
1249
|
-
// 獲取當前 column 的 align 狀態
|
|
1250
|
-
const currentAlign = getAlign('column', tableSelectedOn.index);
|
|
1251
|
-
// 根據當前 align 狀態選擇對應的 icon
|
|
1252
|
-
const getCurrentAlignIcon = () => {
|
|
1253
|
-
switch (currentAlign) {
|
|
1254
|
-
case 'left':
|
|
1255
|
-
return icons.AlignLeft;
|
|
1256
|
-
case 'center':
|
|
1257
|
-
return icons.AlignCenter;
|
|
1258
|
-
case 'right':
|
|
1259
|
-
return icons.AlignRight;
|
|
1260
|
-
default:
|
|
1261
|
-
return icons.AlignLeft;
|
|
1262
|
-
}
|
|
1263
|
-
};
|
|
1264
|
-
return [
|
|
1265
|
-
React.createElement(toolbar.ToolbarGroupIcon, { key: "align-change", icon: getCurrentAlignIcon() },
|
|
1266
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.AlignLeft, onClick: () => {
|
|
1267
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1268
|
-
setAlign('left', 'column', tableSelectedOn.index);
|
|
1269
|
-
setTableSelectedOn(undefined);
|
|
1270
|
-
}
|
|
1271
|
-
} }),
|
|
1272
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.AlignCenter, onClick: () => {
|
|
1273
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1274
|
-
setAlign('center', 'column', tableSelectedOn.index);
|
|
1275
|
-
setTableSelectedOn(undefined);
|
|
1276
|
-
}
|
|
1277
|
-
} }),
|
|
1278
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.AlignRight, onClick: () => {
|
|
1279
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1280
|
-
setAlign('right', 'column', tableSelectedOn.index);
|
|
1281
|
-
setTableSelectedOn(undefined);
|
|
1282
|
-
}
|
|
1283
|
-
} })),
|
|
1284
|
-
];
|
|
1285
|
-
}, [tableSelectedOn, setAlign, setTableSelectedOn, getAlign]);
|
|
1286
|
-
// Row add actions
|
|
1287
|
-
const rowAddActions = React.useMemo(() => {
|
|
1288
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'row' || typeof tableSelectedOn.index !== 'number') {
|
|
1289
|
-
return null;
|
|
1290
|
-
}
|
|
1291
|
-
return [
|
|
1292
|
-
{
|
|
1293
|
-
icon: icons.AddRowAtBottom,
|
|
1294
|
-
onClick: () => {
|
|
1295
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1296
|
-
addRow({ position: 'bottom', rowIndex: tableSelectedOn.index });
|
|
1297
|
-
setTableSelectedOn(undefined);
|
|
1298
|
-
}
|
|
1299
|
-
},
|
|
1300
|
-
},
|
|
1301
|
-
{
|
|
1302
|
-
icon: icons.AddRowAtTop,
|
|
1303
|
-
onClick: () => {
|
|
1304
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1305
|
-
addRow({ position: 'top', rowIndex: tableSelectedOn.index });
|
|
1306
|
-
setTableSelectedOn(undefined);
|
|
1307
|
-
}
|
|
1308
|
-
},
|
|
1309
|
-
},
|
|
1310
|
-
];
|
|
1311
|
-
}, [tableSelectedOn, addRow, setTableSelectedOn]);
|
|
1312
|
-
// Column add actions
|
|
1313
|
-
const columnAddActions = React.useMemo(() => {
|
|
1314
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
1315
|
-
return null;
|
|
1316
|
-
}
|
|
1317
|
-
return [
|
|
1318
|
-
isReachMaximumColumns
|
|
1319
|
-
? undefined
|
|
1320
|
-
: {
|
|
1321
|
-
icon: icons.AddColumnAtLeft,
|
|
1322
|
-
onClick: () => {
|
|
1323
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1324
|
-
addColumn({
|
|
1325
|
-
position: 'left',
|
|
1326
|
-
columnIndex: tableSelectedOn.index,
|
|
1327
|
-
});
|
|
1328
|
-
setTableSelectedOn(undefined);
|
|
1329
|
-
}
|
|
1330
|
-
},
|
|
1331
|
-
},
|
|
1332
|
-
isReachMaximumColumns
|
|
1333
|
-
? undefined
|
|
1334
|
-
: {
|
|
1335
|
-
icon: icons.AddColumnAtRight,
|
|
1336
|
-
onClick: () => {
|
|
1337
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1338
|
-
addColumn({
|
|
1339
|
-
position: 'right',
|
|
1340
|
-
columnIndex: tableSelectedOn.index,
|
|
1341
|
-
});
|
|
1342
|
-
setTableSelectedOn(undefined);
|
|
1343
|
-
}
|
|
1344
|
-
},
|
|
1345
|
-
},
|
|
1346
|
-
].filter((i) => i !== undefined);
|
|
1347
|
-
}, [tableSelectedOn, addColumn, setTableSelectedOn, isReachMaximumColumns]);
|
|
1348
|
-
// Row move actions
|
|
1349
|
-
const rowMoveActions = React.useMemo(() => {
|
|
1350
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'row' || typeof tableSelectedOn.index !== 'number') {
|
|
1351
|
-
return null;
|
|
1352
|
-
}
|
|
1353
|
-
const rowIndex = tableSelectedOn.index;
|
|
1354
|
-
// 從 tableElement 獲取 header 和 body 的資訊
|
|
1355
|
-
const tableMainElement = tableElement.children.find((child) => core.Element.isElement(child) && child.type.includes('table_main'));
|
|
1356
|
-
if (!tableMainElement || !core.Element.isElement(tableMainElement)) {
|
|
1357
|
-
return null;
|
|
1358
|
-
}
|
|
1359
|
-
const tableHeaderElement = tableMainElement.children.find((child) => core.Element.isElement(child) && child.type.includes('table_header'));
|
|
1360
|
-
const headerRowCount = tableHeaderElement && core.Element.isElement(tableHeaderElement) ? tableHeaderElement.children.length : 0;
|
|
1361
|
-
// 判斷當前列是在 header 還是 body 中
|
|
1362
|
-
const currentInHeader = rowIndex < headerRowCount;
|
|
1363
|
-
// 判斷上方和下方是否可以互換
|
|
1364
|
-
const canMoveUp = rowIndex > 0; // 不在第一列
|
|
1365
|
-
const canMoveDown = rowIndex < rowCount - 1; // 不在最後一列
|
|
1366
|
-
// 檢查目標位置是否在相同的容器中(header 或 body)
|
|
1367
|
-
const targetUpInHeader = rowIndex - 1 < headerRowCount;
|
|
1368
|
-
const targetDownInHeader = rowIndex + 1 < headerRowCount;
|
|
1369
|
-
// 標題列只能與標題列互換,一般列只能與一般列互換
|
|
1370
|
-
const canSwapUp = canMoveUp && currentInHeader === targetUpInHeader;
|
|
1371
|
-
const canSwapDown = canMoveDown && currentInHeader === targetDownInHeader;
|
|
1372
|
-
return [
|
|
1373
|
-
canSwapDown
|
|
1374
|
-
? {
|
|
1375
|
-
icon: icons.TableMoveDown,
|
|
1376
|
-
onClick: () => {
|
|
1377
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1378
|
-
moveOrSwapRow(tableSelectedOn.index, tableSelectedOn.index + 1, 'swap');
|
|
1379
|
-
setTableSelectedOn(undefined);
|
|
1380
|
-
}
|
|
1381
|
-
},
|
|
1382
|
-
}
|
|
1383
|
-
: undefined,
|
|
1384
|
-
canSwapUp
|
|
1385
|
-
? {
|
|
1386
|
-
icon: icons.TableMoveUp,
|
|
1387
|
-
onClick: () => {
|
|
1388
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1389
|
-
moveOrSwapRow(tableSelectedOn.index, tableSelectedOn.index - 1, 'swap');
|
|
1390
|
-
setTableSelectedOn(undefined);
|
|
1391
|
-
}
|
|
1392
|
-
},
|
|
1393
|
-
}
|
|
1394
|
-
: undefined,
|
|
1395
|
-
].filter((i) => i !== undefined);
|
|
1396
|
-
}, [tableSelectedOn, setTableSelectedOn, rowCount, tableElement, moveOrSwapRow]);
|
|
1397
|
-
// Column move actions
|
|
1398
|
-
const columnMoveActions = React.useMemo(() => {
|
|
1399
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) !== 'column' || typeof tableSelectedOn.index !== 'number') {
|
|
1400
|
-
return null;
|
|
1401
|
-
}
|
|
1402
|
-
const columnIndex = tableSelectedOn.index;
|
|
1403
|
-
// 檢查當前行是否為標題行
|
|
1404
|
-
const currentIsTitle = !!element.treatAsTitle;
|
|
1405
|
-
// 判斷左右是否可以移動
|
|
1406
|
-
const canMoveLeft = columnIndex > 0;
|
|
1407
|
-
const canMoveRight = columnIndex < columnCount - 1;
|
|
1408
|
-
// 檢查相鄰行是否為相同類型(都是標題行或都不是標題行)
|
|
1409
|
-
const checkAdjacentColumnType = (adjacentIndex) => {
|
|
1410
|
-
// 從 tableElement 中獲取相鄰行的 treatAsTitle 狀態
|
|
1411
|
-
const tableMainElement = tableElement.children.find((child) => core.Element.isElement(child) && child.type.includes('table_main'));
|
|
1412
|
-
if (!tableMainElement || !core.Element.isElement(tableMainElement)) {
|
|
1413
|
-
return false;
|
|
1414
|
-
}
|
|
1415
|
-
const tableBodyElement = tableMainElement.children.find((child) => core.Element.isElement(child) && child.type.includes('table_body'));
|
|
1416
|
-
if (!tableBodyElement || !core.Element.isElement(tableBodyElement)) {
|
|
1417
|
-
return false;
|
|
1418
|
-
}
|
|
1419
|
-
// 從 body 的第一列獲取相鄰 cell 的 treatAsTitle 屬性
|
|
1420
|
-
const firstRow = tableBodyElement.children[0];
|
|
1421
|
-
if (!core.Element.isElement(firstRow)) {
|
|
1422
|
-
return false;
|
|
1423
|
-
}
|
|
1424
|
-
const adjacentCell = firstRow.children[adjacentIndex];
|
|
1425
|
-
if (!core.Element.isElement(adjacentCell)) {
|
|
1426
|
-
return false;
|
|
1427
|
-
}
|
|
1428
|
-
return !!adjacentCell.treatAsTitle;
|
|
1429
|
-
};
|
|
1430
|
-
// 標題行只能與標題行互換,一般行只能與一般行互換
|
|
1431
|
-
const canSwapLeft = canMoveLeft && currentIsTitle === checkAdjacentColumnType(columnIndex - 1);
|
|
1432
|
-
const canSwapRight = canMoveRight && currentIsTitle === checkAdjacentColumnType(columnIndex + 1);
|
|
1433
|
-
return [
|
|
1434
|
-
canSwapRight
|
|
1435
|
-
? {
|
|
1436
|
-
icon: icons.TableMoveRight,
|
|
1437
|
-
onClick: () => {
|
|
1438
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1439
|
-
moveOrSwapColumn(tableSelectedOn.index, tableSelectedOn.index + 1, 'swap');
|
|
1440
|
-
setTableSelectedOn(undefined);
|
|
1441
|
-
}
|
|
1442
|
-
},
|
|
1443
|
-
}
|
|
1444
|
-
: undefined,
|
|
1445
|
-
canSwapLeft
|
|
1446
|
-
? {
|
|
1447
|
-
icon: icons.TableMoveLeft,
|
|
1448
|
-
onClick: () => {
|
|
1449
|
-
if (typeof tableSelectedOn.index === 'number') {
|
|
1450
|
-
moveOrSwapColumn(tableSelectedOn.index, tableSelectedOn.index - 1, 'swap');
|
|
1451
|
-
setTableSelectedOn(undefined);
|
|
1452
|
-
}
|
|
1453
|
-
},
|
|
1454
|
-
}
|
|
1455
|
-
: undefined,
|
|
1456
|
-
].filter((i) => i !== undefined);
|
|
1457
|
-
}, [tableSelectedOn, setTableSelectedOn, columnCount, element.treatAsTitle, tableElement, moveOrSwapColumn]);
|
|
1458
|
-
// Delete actions
|
|
1459
|
-
const deleteActions = React.useMemo(() => {
|
|
1460
|
-
// 當選中 row 時,檢查是否為最後一行
|
|
1461
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'row' && typeof tableSelectedOn.index === 'number') {
|
|
1462
|
-
// 如果只剩一行,不顯示刪除按鈕
|
|
1463
|
-
if (isReachMinimumBodyRows && !isHeader) {
|
|
1464
|
-
return [];
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
// 當選中 column 時,檢查是否為最後一列
|
|
1468
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'column' && typeof tableSelectedOn.index === 'number') {
|
|
1469
|
-
// 如果只剩一列(考慮 title columns),不顯示刪除按鈕
|
|
1470
|
-
if (columnCount <= 1) {
|
|
1471
|
-
return [];
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
return [
|
|
1475
|
-
{
|
|
1476
|
-
icon: icons.Trash,
|
|
1477
|
-
className: 'qdr-table__delete',
|
|
1478
|
-
onClick: () => {
|
|
1479
|
-
if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'row' && typeof tableSelectedOn.index === 'number') {
|
|
1480
|
-
deleteRow(tableSelectedOn.index);
|
|
1481
|
-
setTableSelectedOn(undefined);
|
|
1482
|
-
}
|
|
1483
|
-
else if ((tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'column' && typeof tableSelectedOn.index === 'number') {
|
|
1484
|
-
deleteColumn(tableSelectedOn.index);
|
|
1485
|
-
setTableSelectedOn(undefined);
|
|
1486
|
-
}
|
|
1487
|
-
},
|
|
1488
|
-
},
|
|
1489
|
-
];
|
|
1490
|
-
}, [tableSelectedOn, deleteRow, deleteColumn, setTableSelectedOn, isReachMinimumBodyRows, isHeader, columnCount]);
|
|
1491
|
-
// Inline toolbar icon groups - 當選中行/列時顯示的完整工具列
|
|
1492
|
-
const inlineToolbarIconGroups = React.useMemo(() => {
|
|
1493
|
-
return [
|
|
1494
|
-
{
|
|
1495
|
-
icons: rowActions || columnActions || [],
|
|
1496
|
-
},
|
|
1497
|
-
{
|
|
1498
|
-
icons: columnAlignActions || [],
|
|
1499
|
-
},
|
|
1500
|
-
{
|
|
1501
|
-
icons: rowAddActions || columnAddActions || [],
|
|
1502
|
-
},
|
|
1503
|
-
{
|
|
1504
|
-
icons: rowMoveActions || columnMoveActions || [],
|
|
1505
|
-
},
|
|
1506
|
-
{
|
|
1507
|
-
icons: deleteActions,
|
|
1508
|
-
},
|
|
1509
|
-
];
|
|
1510
|
-
}, [
|
|
1511
|
-
rowActions,
|
|
1512
|
-
columnActions,
|
|
1513
|
-
columnAlignActions,
|
|
1514
|
-
rowAddActions,
|
|
1515
|
-
columnAddActions,
|
|
1516
|
-
rowMoveActions,
|
|
1517
|
-
columnMoveActions,
|
|
1518
|
-
deleteActions,
|
|
1519
|
-
]);
|
|
1520
|
-
return {
|
|
1521
|
-
focusToolbarIconGroups,
|
|
1522
|
-
inlineToolbarIconGroups,
|
|
1523
|
-
};
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
const TableDragContext = React.createContext(null);
|
|
1527
|
-
const TableDragProvider = ({ children }) => {
|
|
1528
|
-
const [dragState, setDragState] = React.useState(null);
|
|
1529
|
-
const [dropTargetIndex, setDropTargetIndex] = React.useState(null);
|
|
1530
|
-
const [dragDirection, setDragDirection] = React.useState(null);
|
|
1531
|
-
const value = React.useMemo(() => ({
|
|
1532
|
-
dragState,
|
|
1533
|
-
setDragState,
|
|
1534
|
-
dropTargetIndex,
|
|
1535
|
-
setDropTargetIndex,
|
|
1536
|
-
dragDirection,
|
|
1537
|
-
setDragDirection,
|
|
1538
|
-
}), [dragState, dropTargetIndex, dragDirection]);
|
|
1539
|
-
return React.createElement(TableDragContext.Provider, { value: value }, children);
|
|
1540
|
-
};
|
|
1541
|
-
const useTableDragContext = () => {
|
|
1542
|
-
const context = React.useContext(TableDragContext);
|
|
1543
|
-
if (!context) {
|
|
1544
|
-
throw new Error('useTableDragContext must be used within TableDragProvider');
|
|
1545
|
-
}
|
|
1546
|
-
return context;
|
|
1547
|
-
};
|
|
1548
|
-
|
|
1549
|
-
function useTableActions(element) {
|
|
1550
|
-
const editor = react.useQuadrats();
|
|
1551
|
-
const isColumnPinned = React.useCallback((columnIndex) => {
|
|
1552
|
-
try {
|
|
1553
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1554
|
-
if (!tableStructure)
|
|
1555
|
-
return false;
|
|
1556
|
-
const { tableMainElement } = tableStructure;
|
|
1557
|
-
if (!tableMainElement)
|
|
1558
|
-
return false;
|
|
1559
|
-
for (const container of tableMainElement.children) {
|
|
1560
|
-
if (!core.Element.isElement(container))
|
|
1561
|
-
continue;
|
|
1562
|
-
for (const row of container.children) {
|
|
1563
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1564
|
-
const cell = row.children[columnIndex];
|
|
1565
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
1566
|
-
// 如果有任何一個 cell 沒有 pinned 屬性,則整個 column 不算 pinned
|
|
1567
|
-
if (!cell.pinned) {
|
|
1568
|
-
return false;
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
return true;
|
|
1575
|
-
}
|
|
1576
|
-
catch (error) {
|
|
1577
|
-
return false;
|
|
1578
|
-
}
|
|
1579
|
-
}, [element, editor]);
|
|
1580
|
-
const isRowPinned = React.useCallback((rowIndex) => {
|
|
1581
|
-
try {
|
|
1582
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1583
|
-
if (!tableStructure)
|
|
1584
|
-
return false;
|
|
1585
|
-
const { tableHeaderElement, tableBodyElement } = tableStructure;
|
|
1586
|
-
const headerRowCount = tableHeaderElement && core.Element.isElement(tableHeaderElement) ? tableHeaderElement.children.length : 0;
|
|
1587
|
-
let targetRow;
|
|
1588
|
-
if (rowIndex < headerRowCount && tableHeaderElement && core.Element.isElement(tableHeaderElement)) {
|
|
1589
|
-
// 在 Header 中
|
|
1590
|
-
const rowElement = tableHeaderElement.children[rowIndex];
|
|
1591
|
-
if (core.Element.isElement(rowElement)) {
|
|
1592
|
-
targetRow = rowElement;
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
else if (tableBodyElement && core.Element.isElement(tableBodyElement)) {
|
|
1596
|
-
// 在 Body 中
|
|
1597
|
-
const bodyRowIndex = rowIndex - headerRowCount;
|
|
1598
|
-
const rowElement = tableBodyElement.children[bodyRowIndex];
|
|
1599
|
-
if (core.Element.isElement(rowElement)) {
|
|
1600
|
-
targetRow = rowElement;
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
if (!core.Element.isElement(targetRow) || !targetRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1604
|
-
return false;
|
|
1605
|
-
}
|
|
1606
|
-
// 檢查所有 cell 是否都有 pinned 屬性
|
|
1607
|
-
for (const cell of targetRow.children) {
|
|
1608
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
1609
|
-
if (!cell.pinned) {
|
|
1610
|
-
return false;
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
return true;
|
|
1615
|
-
}
|
|
1616
|
-
catch (error) {
|
|
1617
|
-
return false;
|
|
1618
|
-
}
|
|
1619
|
-
}, [element, editor]);
|
|
1620
|
-
const addColumn = React.useCallback((options = {}) => {
|
|
1621
|
-
const { position = 'right', columnIndex } = options;
|
|
1622
|
-
try {
|
|
1623
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1624
|
-
if (!tableStructure)
|
|
1625
|
-
return;
|
|
1626
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath, tableBodyPath, columnCount } = tableStructure;
|
|
1627
|
-
if (columnCount >= table.TABLE_DEFAULT_MAX_COLUMNS) {
|
|
1628
|
-
console.warn(`Maximum columns limit (${table.TABLE_DEFAULT_MAX_COLUMNS}) reached`);
|
|
1629
|
-
return;
|
|
1630
|
-
}
|
|
1631
|
-
// 計算插入位置
|
|
1632
|
-
let insertIndex;
|
|
1633
|
-
if (typeof columnIndex === 'number') {
|
|
1634
|
-
insertIndex = position === 'left' ? Math.max(0, columnIndex) : Math.min(columnCount, columnIndex + 1);
|
|
1635
|
-
}
|
|
1636
|
-
else {
|
|
1637
|
-
insertIndex = columnCount;
|
|
1638
|
-
}
|
|
1639
|
-
// 使用 Editor.withoutNormalizing 來批次執行所有操作
|
|
1640
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
1641
|
-
// 在 Header 中加入 cell
|
|
1642
|
-
if (tableHeaderElement && tableHeaderPath) {
|
|
1643
|
-
tableHeaderElement.children.forEach((row, rowIndex) => {
|
|
1644
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1645
|
-
const referenceCell = row.children[insertIndex - (position === 'left' ? 0 : 1)];
|
|
1646
|
-
const newCell = createTableCell(referenceCell);
|
|
1647
|
-
const cellPath = [...tableHeaderPath, rowIndex, insertIndex];
|
|
1648
|
-
core.Transforms.insertNodes(editor, newCell, { at: cellPath });
|
|
1649
|
-
}
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
// 在 Body 中加入 cell
|
|
1653
|
-
tableBodyElement.children.forEach((row, rowIndex) => {
|
|
1654
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1655
|
-
const referenceCell = row.children[insertIndex - (position === 'left' ? 0 : 1)];
|
|
1656
|
-
const newCell = createTableCell(referenceCell);
|
|
1657
|
-
const cellPath = [...tableBodyPath, rowIndex, insertIndex];
|
|
1658
|
-
core.Transforms.insertNodes(editor, newCell, { at: cellPath });
|
|
1659
|
-
}
|
|
1660
|
-
});
|
|
1661
|
-
// 調整欄位寬度
|
|
1662
|
-
const currentWidths = getColumnWidths(element);
|
|
1663
|
-
if (currentWidths.length > 0) {
|
|
1664
|
-
// 獲取當前的 pinned columns 資訊
|
|
1665
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(element);
|
|
1666
|
-
const newWidths = calculateColumnWidthsAfterAdd(currentWidths, insertIndex, pinnedColumnIndices, columnIndex);
|
|
1667
|
-
setColumnWidths(editor, element, newWidths);
|
|
1668
|
-
}
|
|
1669
|
-
});
|
|
1670
|
-
}
|
|
1671
|
-
catch (error) {
|
|
1672
|
-
console.warn('Failed to add column:', error);
|
|
1673
|
-
}
|
|
1674
|
-
}, [editor, element]);
|
|
1675
|
-
const addRow = React.useCallback((options = {}) => {
|
|
1676
|
-
const { position = 'bottom', rowIndex } = options;
|
|
1677
|
-
try {
|
|
1678
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1679
|
-
if (!tableStructure)
|
|
1680
|
-
return;
|
|
1681
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath, tableBodyPath, headerRowCount, columnCount } = tableStructure;
|
|
1682
|
-
// 計算插入位置和參考行
|
|
1683
|
-
let insertIndex;
|
|
1684
|
-
let referenceRowElement;
|
|
1685
|
-
let targetPath;
|
|
1686
|
-
if (typeof rowIndex === 'number') {
|
|
1687
|
-
// 檢查是在 Header / Body 之中
|
|
1688
|
-
if (tableHeaderElement && rowIndex < headerRowCount) {
|
|
1689
|
-
targetPath = tableHeaderPath;
|
|
1690
|
-
if (position === 'top') {
|
|
1691
|
-
insertIndex = Math.max(0, rowIndex);
|
|
1692
|
-
referenceRowElement = getReferenceRowFromHeaderOrBody(tableHeaderElement, insertIndex);
|
|
1693
|
-
}
|
|
1694
|
-
else {
|
|
1695
|
-
insertIndex = Math.min(headerRowCount, rowIndex + 1);
|
|
1696
|
-
referenceRowElement = getReferenceRowFromHeaderOrBody(tableHeaderElement, rowIndex);
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
else {
|
|
1700
|
-
targetPath = tableBodyPath;
|
|
1701
|
-
const bodyRowIndex = rowIndex - headerRowCount;
|
|
1702
|
-
if (position === 'top') {
|
|
1703
|
-
insertIndex = Math.max(0, bodyRowIndex);
|
|
1704
|
-
referenceRowElement = getReferenceRowFromHeaderOrBody(tableBodyElement, insertIndex);
|
|
1705
|
-
}
|
|
1706
|
-
else {
|
|
1707
|
-
insertIndex = Math.min(tableBodyElement.children.length, bodyRowIndex + 1);
|
|
1708
|
-
referenceRowElement = getReferenceRowFromHeaderOrBody(tableBodyElement, bodyRowIndex);
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
else {
|
|
1713
|
-
// 預設:在 Body 尾端加入列
|
|
1714
|
-
targetPath = tableBodyPath;
|
|
1715
|
-
insertIndex = tableBodyElement.children.length;
|
|
1716
|
-
referenceRowElement = getReferenceRowFromHeaderOrBody(tableBodyElement, insertIndex - 1);
|
|
1717
|
-
}
|
|
1718
|
-
// 創建新行
|
|
1719
|
-
const newRow = {
|
|
1720
|
-
type: table.TABLE_ROW_TYPE,
|
|
1721
|
-
children: Array.from({ length: columnCount }, (_, cellIndex) => {
|
|
1722
|
-
let referenceCell;
|
|
1723
|
-
if (referenceRowElement && referenceRowElement.children[cellIndex]) {
|
|
1724
|
-
const cell = referenceRowElement.children[cellIndex];
|
|
1725
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
1726
|
-
referenceCell = cell;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
return createTableCell(referenceCell);
|
|
1730
|
-
}),
|
|
1731
|
-
};
|
|
1732
|
-
// 插入新行
|
|
1733
|
-
const newRowPath = [...targetPath, insertIndex];
|
|
1734
|
-
core.Transforms.insertNodes(editor, newRow, { at: newRowPath });
|
|
1735
|
-
}
|
|
1736
|
-
catch (error) {
|
|
1737
|
-
console.warn('Failed to add row:', error);
|
|
1738
|
-
}
|
|
1739
|
-
}, [editor, element]);
|
|
1740
|
-
const addColumnAndRow = React.useCallback(() => {
|
|
1741
|
-
try {
|
|
1742
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1743
|
-
if (!tableStructure)
|
|
1744
|
-
return;
|
|
1745
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath, tableBodyPath, columnCount } = tableStructure;
|
|
1746
|
-
if (columnCount >= table.TABLE_DEFAULT_MAX_COLUMNS) {
|
|
1747
|
-
console.warn(`Maximum columns limit (${table.TABLE_DEFAULT_MAX_COLUMNS}) reached`);
|
|
1748
|
-
return;
|
|
1749
|
-
}
|
|
1750
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
1751
|
-
// 在 Header 中加入新列
|
|
1752
|
-
if (tableHeaderElement && tableHeaderPath) {
|
|
1753
|
-
tableHeaderElement.children.forEach((row, rowIndex) => {
|
|
1754
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1755
|
-
const lastCell = row.children[row.children.length - 1];
|
|
1756
|
-
const newHeaderCell = createTableCell(lastCell);
|
|
1757
|
-
const cellPath = [...tableHeaderPath, rowIndex, row.children.length];
|
|
1758
|
-
core.Transforms.insertNodes(editor, newHeaderCell, { at: cellPath });
|
|
1759
|
-
}
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
|
-
// 在 Body 中加入新列
|
|
1763
|
-
tableBodyElement.children.forEach((row, rowIndex) => {
|
|
1764
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1765
|
-
const lastCell = row.children[row.children.length - 1];
|
|
1766
|
-
const newCell = createTableCell(lastCell);
|
|
1767
|
-
const cellPath = [...tableBodyPath, rowIndex, row.children.length];
|
|
1768
|
-
core.Transforms.insertNodes(editor, newCell, { at: cellPath });
|
|
1769
|
-
}
|
|
1770
|
-
});
|
|
1771
|
-
// 加入新行
|
|
1772
|
-
const newColumnCount = columnCount + 1;
|
|
1773
|
-
const lastRow = getReferenceRowFromHeaderOrBody(tableBodyElement, tableBodyElement.children.length - 1);
|
|
1774
|
-
const newRow = {
|
|
1775
|
-
type: table.TABLE_ROW_TYPE,
|
|
1776
|
-
children: Array.from({ length: newColumnCount }, (_, cellIndex) => {
|
|
1777
|
-
let referenceCell;
|
|
1778
|
-
if (cellIndex < newColumnCount - 1 && core.Element.isElement(lastRow) && lastRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1779
|
-
const cell = lastRow.children[cellIndex];
|
|
1780
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
1781
|
-
referenceCell = cell;
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
else {
|
|
1785
|
-
if (core.Element.isElement(lastRow) && lastRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1786
|
-
const cell = lastRow.children[lastRow.children.length - 1];
|
|
1787
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
1788
|
-
referenceCell = cell;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
return createTableCell(referenceCell);
|
|
1793
|
-
}),
|
|
1794
|
-
};
|
|
1795
|
-
const newRowPath = [...tableBodyPath, tableBodyElement.children.length];
|
|
1796
|
-
core.Transforms.insertNodes(editor, newRow, { at: newRowPath });
|
|
1797
|
-
// 調整欄位寬度(新增欄位在最後)
|
|
1798
|
-
const currentWidths = getColumnWidths(element);
|
|
1799
|
-
if (currentWidths.length > 0) {
|
|
1800
|
-
// 新欄位插入在最後(columnCount 位置)
|
|
1801
|
-
const insertIndex = columnCount;
|
|
1802
|
-
// 獲取當前的 pinned columns 資訊
|
|
1803
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(element);
|
|
1804
|
-
const newWidths = calculateColumnWidthsAfterAdd(currentWidths, insertIndex, pinnedColumnIndices);
|
|
1805
|
-
setColumnWidths(editor, element, newWidths);
|
|
1806
|
-
}
|
|
1807
|
-
});
|
|
1808
|
-
}
|
|
1809
|
-
catch (error) {
|
|
1810
|
-
console.warn('Failed to add column and row:', error);
|
|
1811
|
-
}
|
|
1812
|
-
}, [editor, element]);
|
|
1813
|
-
const deleteRow = React.useCallback((rowIndex) => {
|
|
1814
|
-
try {
|
|
1815
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1816
|
-
if (!tableStructure)
|
|
1817
|
-
return;
|
|
1818
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath, tableBodyPath, headerRowCount } = tableStructure;
|
|
1819
|
-
// 檢查是否刪除 Header 行
|
|
1820
|
-
if (rowIndex < headerRowCount) {
|
|
1821
|
-
if (!tableHeaderElement || !tableHeaderPath)
|
|
1822
|
-
return;
|
|
1823
|
-
const headerRowPath = [...tableHeaderPath, rowIndex];
|
|
1824
|
-
core.Transforms.removeNodes(editor, { at: headerRowPath });
|
|
1825
|
-
// 如果是最後一個 header 行,移除整個 header 元素
|
|
1826
|
-
if (headerRowCount <= 1) {
|
|
1827
|
-
core.Transforms.removeNodes(editor, { at: tableHeaderPath });
|
|
1828
|
-
}
|
|
1829
|
-
return;
|
|
1830
|
-
}
|
|
1831
|
-
// 刪除 Body 行
|
|
1832
|
-
const bodyRowIndex = rowIndex - headerRowCount;
|
|
1833
|
-
if (bodyRowIndex < 0 || bodyRowIndex >= tableBodyElement.children.length) {
|
|
1834
|
-
console.warn('Invalid row index for deletion');
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
if (tableBodyElement.children.length <= 1) {
|
|
1838
|
-
console.warn('Cannot delete the last row');
|
|
1839
|
-
return;
|
|
1840
|
-
}
|
|
1841
|
-
const rowPath = [...tableBodyPath, bodyRowIndex];
|
|
1842
|
-
core.Transforms.removeNodes(editor, { at: rowPath });
|
|
1843
|
-
}
|
|
1844
|
-
catch (error) {
|
|
1845
|
-
console.warn('Failed to delete row:', error);
|
|
1846
|
-
}
|
|
1847
|
-
}, [editor, element]);
|
|
1848
|
-
const deleteColumn = React.useCallback((columnIndex) => {
|
|
1849
|
-
try {
|
|
1850
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1851
|
-
if (!tableStructure)
|
|
1852
|
-
return;
|
|
1853
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath, tableBodyPath, columnCount } = tableStructure;
|
|
1854
|
-
// 檢查是否有足夠的列(不允許刪除最後一列)
|
|
1855
|
-
if (columnCount <= 1) {
|
|
1856
|
-
console.warn('Cannot delete the last column');
|
|
1857
|
-
return;
|
|
1858
|
-
}
|
|
1859
|
-
// 檢查 columnIndex 是否有效
|
|
1860
|
-
if (columnIndex < 0 || columnIndex >= columnCount) {
|
|
1861
|
-
console.warn('Invalid column index for deletion');
|
|
1862
|
-
return;
|
|
1863
|
-
}
|
|
1864
|
-
editor.withoutNormalizing(() => {
|
|
1865
|
-
// 從 Header 中刪除列
|
|
1866
|
-
if (tableHeaderElement && tableHeaderPath) {
|
|
1867
|
-
// 以反向順序刪除
|
|
1868
|
-
for (let rowIndex = tableHeaderElement.children.length - 1; rowIndex >= 0; rowIndex--) {
|
|
1869
|
-
const headerRow = tableHeaderElement.children[rowIndex];
|
|
1870
|
-
if (core.Element.isElement(headerRow) && headerRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1871
|
-
const headerCellPath = [...tableHeaderPath, rowIndex, columnIndex];
|
|
1872
|
-
core.Transforms.removeNodes(editor, { at: headerCellPath });
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
// 從 Body 中刪除列
|
|
1877
|
-
for (let rowIndex = tableBodyElement.children.length - 1; rowIndex >= 0; rowIndex--) {
|
|
1878
|
-
const row = tableBodyElement.children[rowIndex];
|
|
1879
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
1880
|
-
const cellPath = [...tableBodyPath, rowIndex, columnIndex];
|
|
1881
|
-
core.Transforms.removeNodes(editor, { at: cellPath });
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
// 調整欄位寬度
|
|
1885
|
-
const currentWidths = getColumnWidths(element);
|
|
1886
|
-
if (currentWidths.length > 0) {
|
|
1887
|
-
const newWidths = calculateColumnWidthsAfterDelete(currentWidths, columnIndex);
|
|
1888
|
-
setColumnWidths(editor, element, newWidths);
|
|
1889
|
-
}
|
|
1890
|
-
});
|
|
1891
|
-
}
|
|
1892
|
-
catch (error) {
|
|
1893
|
-
console.warn('Failed to delete column:', error);
|
|
1894
|
-
}
|
|
1895
|
-
}, [editor, element]);
|
|
1896
|
-
const moveRowToBody = React.useCallback((rowIndex) => {
|
|
1897
|
-
try {
|
|
1898
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1899
|
-
if (!tableStructure)
|
|
1900
|
-
return;
|
|
1901
|
-
const { tableHeaderElement, tableHeaderPath, tableBodyPath } = tableStructure;
|
|
1902
|
-
if (!tableHeaderElement || !tableHeaderPath)
|
|
1903
|
-
return;
|
|
1904
|
-
// 檢查行是否存在於 header 中
|
|
1905
|
-
if (rowIndex >= tableHeaderElement.children.length) {
|
|
1906
|
-
console.warn('Invalid header row index:', rowIndex);
|
|
1907
|
-
return;
|
|
1908
|
-
}
|
|
1909
|
-
const rowToMove = tableHeaderElement.children[rowIndex];
|
|
1910
|
-
if (!core.Element.isElement(rowToMove) || !rowToMove.type.includes(table.TABLE_ROW_TYPE))
|
|
1911
|
-
return;
|
|
1912
|
-
const rowPath = [...tableHeaderPath, rowIndex];
|
|
1913
|
-
// 移動前移除所有 cell 的 pinned 屬性
|
|
1914
|
-
rowToMove.children.forEach((cell, columnIndex) => {
|
|
1915
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE) && cell.pinned) {
|
|
1916
|
-
if (cell.pinned && isColumnPinned(columnIndex)) {
|
|
1917
|
-
const cellPath = [...rowPath, columnIndex];
|
|
1918
|
-
core.Transforms.unsetNodes(editor, 'pinned', { at: cellPath });
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
});
|
|
1922
|
-
// 移動行到 body 的開始位置
|
|
1923
|
-
const bodyTargetPath = [...tableBodyPath, 0];
|
|
1924
|
-
core.Transforms.moveNodes(editor, {
|
|
1925
|
-
at: rowPath,
|
|
1926
|
-
to: bodyTargetPath,
|
|
1927
|
-
});
|
|
1928
|
-
}
|
|
1929
|
-
catch (error) {
|
|
1930
|
-
console.warn('Failed to move row to body:', error);
|
|
1931
|
-
}
|
|
1932
|
-
}, [editor, element, isColumnPinned]);
|
|
1933
|
-
const moveRowToHeader = React.useCallback((rowIndex, customProps) => {
|
|
1934
|
-
try {
|
|
1935
|
-
const tableStructure = getTableStructure(editor, element);
|
|
1936
|
-
if (!tableStructure)
|
|
1937
|
-
return;
|
|
1938
|
-
const { tableHeaderElement, tableBodyElement, tableMainPath, tableHeaderPath, tableBodyPath, headerRowCount } = tableStructure;
|
|
1939
|
-
// 計算正確的 body 行索引
|
|
1940
|
-
const bodyRowIndex = rowIndex - headerRowCount;
|
|
1941
|
-
// 檢查 body 行索引是否有效
|
|
1942
|
-
if (bodyRowIndex < 0 || bodyRowIndex >= tableBodyElement.children.length) {
|
|
1943
|
-
console.warn('Invalid body row index:', bodyRowIndex);
|
|
1944
|
-
return;
|
|
1945
|
-
}
|
|
1946
|
-
// 檢查行是否存在
|
|
1947
|
-
const rowToMove = tableBodyElement.children[bodyRowIndex];
|
|
1948
|
-
if (!core.Element.isElement(rowToMove) || !rowToMove.type.includes(table.TABLE_ROW_TYPE))
|
|
1949
|
-
return;
|
|
1950
|
-
// 檢查 header 中是否已有 pinned rows(一致性規則檢查)
|
|
1951
|
-
const hasExistingPinnedRows = tableStructure ? hasAnyPinnedRows(tableStructure) : false;
|
|
1952
|
-
// 如果有現有的 pinned rows 且沒有提供自定義屬性,自動設置 pinned 以保持一致性
|
|
1953
|
-
const finalProps = customProps || (hasExistingPinnedRows ? { pinned: true } : undefined);
|
|
1954
|
-
// 如果提供了 finalProps,則應用到 cells
|
|
1955
|
-
const processedRow = finalProps
|
|
1956
|
-
? Object.assign(Object.assign({}, rowToMove), { children: rowToMove.children.map((cell) => {
|
|
1957
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
1958
|
-
return Object.assign(Object.assign({}, cell), finalProps);
|
|
1959
|
-
}
|
|
1960
|
-
return cell;
|
|
1961
|
-
}) }) : rowToMove;
|
|
1962
|
-
const rowPath = [...tableBodyPath, bodyRowIndex];
|
|
1963
|
-
// 如果 header 不存在,先創建它
|
|
1964
|
-
if (!tableHeaderElement) {
|
|
1965
|
-
const newHeader = {
|
|
1966
|
-
type: table.TABLE_HEADER_TYPE,
|
|
1967
|
-
children: [processedRow],
|
|
1968
|
-
};
|
|
1969
|
-
const headerInsertPath = [...tableMainPath, 0];
|
|
1970
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
1971
|
-
core.Transforms.removeNodes(editor, { at: rowPath });
|
|
1972
|
-
core.Transforms.insertNodes(editor, newHeader, { at: headerInsertPath });
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
else {
|
|
1976
|
-
// 如果這是 pinned row,找到正確的插入位置(pinned rows 在頂部)
|
|
1977
|
-
let headerTargetPath;
|
|
1978
|
-
if (finalProps === null || finalProps === void 0 ? void 0 : finalProps.pinned) {
|
|
1979
|
-
let insertIndex = 0;
|
|
1980
|
-
for (const [index, headerRow] of tableHeaderElement.children.entries()) {
|
|
1981
|
-
if (core.Element.isElement(headerRow)) {
|
|
1982
|
-
const hasNonPinnedCell = headerRow.children.some((cell) => core.Element.isElement(cell) && !cell.pinned);
|
|
1983
|
-
if (hasNonPinnedCell) {
|
|
1984
|
-
break;
|
|
1985
|
-
}
|
|
1986
|
-
insertIndex = index + 1;
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
headerTargetPath = [...tableHeaderPath, insertIndex];
|
|
1990
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
1991
|
-
core.Transforms.removeNodes(editor, { at: rowPath });
|
|
1992
|
-
core.Transforms.insertNodes(editor, processedRow, { at: headerTargetPath });
|
|
1993
|
-
});
|
|
1994
|
-
}
|
|
1995
|
-
else {
|
|
1996
|
-
// 移動行到現有 header 的末尾
|
|
1997
|
-
headerTargetPath = [...tableHeaderPath, tableHeaderElement.children.length];
|
|
1998
|
-
core.Transforms.moveNodes(editor, {
|
|
1999
|
-
at: rowPath,
|
|
2000
|
-
to: headerTargetPath,
|
|
2001
|
-
});
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
catch (error) {
|
|
2006
|
-
console.warn('Failed to move row to header:', error);
|
|
2007
|
-
}
|
|
2008
|
-
}, [editor, element]);
|
|
2009
|
-
const unsetColumnAsTitle = React.useCallback((columnIndex) => {
|
|
2010
|
-
try {
|
|
2011
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2012
|
-
if (!tableStructure)
|
|
2013
|
-
return;
|
|
2014
|
-
const { tableHeaderElement, tableBodyElement, tableMainElement } = tableStructure;
|
|
2015
|
-
// 獲取 table 的實際寬度(用於轉換為混合模式)
|
|
2016
|
-
let tableWidth = 0;
|
|
2017
|
-
if (tableMainElement) {
|
|
2018
|
-
const tableDOMElement = slateReact.ReactEditor.toDOMNode(editor, tableMainElement);
|
|
2019
|
-
if (tableDOMElement instanceof HTMLElement) {
|
|
2020
|
-
tableWidth = tableDOMElement.getBoundingClientRect().width;
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
const processContainer = (containerElement) => {
|
|
2024
|
-
if (!core.Element.isElement(containerElement))
|
|
2025
|
-
return;
|
|
2026
|
-
const containerPath = slateReact.ReactEditor.findPath(editor, containerElement);
|
|
2027
|
-
const firstRow = containerElement.children[0];
|
|
2028
|
-
// 找到 column 標題列的尾端
|
|
2029
|
-
let targetColumnIndex = 0;
|
|
2030
|
-
if (core.Element.isElement(firstRow) && firstRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2031
|
-
for (let i = 0; i < firstRow.children.length; i++) {
|
|
2032
|
-
const cell = firstRow.children[i];
|
|
2033
|
-
if (core.Element.isElement(cell) &&
|
|
2034
|
-
cell.type.includes(table.TABLE_CELL_TYPE) &&
|
|
2035
|
-
cell.treatAsTitle &&
|
|
2036
|
-
i !== columnIndex) {
|
|
2037
|
-
targetColumnIndex = i + 1;
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
containerElement.children.forEach((row, rowIndex) => {
|
|
2042
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2043
|
-
const cell = row.children[columnIndex];
|
|
2044
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2045
|
-
const cellPath = [...containerPath, rowIndex, columnIndex];
|
|
2046
|
-
core.Transforms.unsetNodes(editor, 'treatAsTitle', { at: cellPath });
|
|
2047
|
-
if (cell.pinned && !isRowPinned(rowIndex)) {
|
|
2048
|
-
core.Transforms.unsetNodes(editor, 'pinned', { at: cellPath });
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
});
|
|
2053
|
-
if (columnIndex < targetColumnIndex) {
|
|
2054
|
-
const actualTargetIndex = targetColumnIndex - 1;
|
|
2055
|
-
for (let rowIndex = containerElement.children.length - 1; rowIndex >= 0; rowIndex--) {
|
|
2056
|
-
const row = containerElement.children[rowIndex];
|
|
2057
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2058
|
-
const fromPath = [...containerPath, rowIndex, columnIndex];
|
|
2059
|
-
const toPath = [...containerPath, rowIndex, actualTargetIndex];
|
|
2060
|
-
core.Transforms.moveNodes(editor, {
|
|
2061
|
-
at: fromPath,
|
|
2062
|
-
to: toPath,
|
|
2063
|
-
});
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
// 調整 columnWidths:將 columnIndex 的寬度移動到 actualTargetIndex
|
|
2067
|
-
const currentWidths = getColumnWidths(element);
|
|
2068
|
-
if (currentWidths.length > 0) {
|
|
2069
|
-
const movedWidths = moveOrSwapColumnWidth(currentWidths, columnIndex, actualTargetIndex, 'move');
|
|
2070
|
-
// 檢查移動後是否還有 pinned columns
|
|
2071
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(element);
|
|
2072
|
-
// 更新釘選欄位索引(移除當前欄位,並調整其他欄位的索引)
|
|
2073
|
-
const updatedPinnedIndices = pinnedColumnIndices
|
|
2074
|
-
.filter((idx) => idx !== columnIndex)
|
|
2075
|
-
.map((idx) => {
|
|
2076
|
-
if (idx > columnIndex && idx <= actualTargetIndex)
|
|
2077
|
-
return idx - 1;
|
|
2078
|
-
return idx;
|
|
2079
|
-
})
|
|
2080
|
-
.sort((a, b) => a - b);
|
|
2081
|
-
// 如果還有 pinned columns,轉換為混合模式;否則可能轉回全 percentage 模式
|
|
2082
|
-
if (updatedPinnedIndices.length > 0 && tableWidth > 0) {
|
|
2083
|
-
const mixedWidths = convertToMixedWidthMode(movedWidths, updatedPinnedIndices, tableWidth);
|
|
2084
|
-
setColumnWidths(editor, element, mixedWidths);
|
|
2085
|
-
}
|
|
2086
|
-
else {
|
|
2087
|
-
// 沒有 pinned columns 了,使用原本的寬度
|
|
2088
|
-
setColumnWidths(editor, element, movedWidths);
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
else {
|
|
2093
|
-
// 即使沒有移動位置,也需要檢查是否需要更新寬度模式
|
|
2094
|
-
const currentWidths = getColumnWidths(element);
|
|
2095
|
-
if (currentWidths.length > 0) {
|
|
2096
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(element);
|
|
2097
|
-
// 移除當前欄位
|
|
2098
|
-
const updatedPinnedIndices = pinnedColumnIndices
|
|
2099
|
-
.filter((idx) => idx !== columnIndex)
|
|
2100
|
-
.sort((a, b) => a - b);
|
|
2101
|
-
// 如果還有 pinned columns,轉換為混合模式;否則可能轉回全 percentage 模式
|
|
2102
|
-
if (updatedPinnedIndices.length > 0 && tableWidth > 0) {
|
|
2103
|
-
const mixedWidths = convertToMixedWidthMode(currentWidths, updatedPinnedIndices, tableWidth);
|
|
2104
|
-
setColumnWidths(editor, element, mixedWidths);
|
|
2105
|
-
}
|
|
2106
|
-
// 如果沒有 pinned columns,保持原樣(可能已經是全 percentage 了)
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
};
|
|
2110
|
-
if (tableHeaderElement) {
|
|
2111
|
-
processContainer(tableHeaderElement);
|
|
2112
|
-
}
|
|
2113
|
-
if (tableBodyElement) {
|
|
2114
|
-
processContainer(tableBodyElement);
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
catch (error) {
|
|
2118
|
-
console.warn('Failed to unset column as title:', error);
|
|
2119
|
-
}
|
|
2120
|
-
}, [editor, element, isRowPinned]);
|
|
2121
|
-
const setColumnAsTitle = React.useCallback((columnIndex, customProps) => {
|
|
2122
|
-
try {
|
|
2123
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2124
|
-
if (!tableStructure)
|
|
2125
|
-
return;
|
|
2126
|
-
const { tableHeaderElement, tableBodyElement, tableMainElement } = tableStructure;
|
|
2127
|
-
// 檢查是否已有 pinned columns
|
|
2128
|
-
const hasExistingPinnedColumns = hasAnyPinnedColumns(tableStructure);
|
|
2129
|
-
// 如果有現有的 pinned columns 且沒有提供自定義屬性,自動設置 pinned 以保持一致性
|
|
2130
|
-
const finalProps = customProps || (hasExistingPinnedColumns ? { pinned: true } : undefined);
|
|
2131
|
-
// 獲取 table 的實際寬度(用於轉換為混合模式)
|
|
2132
|
-
let tableWidth = 0;
|
|
2133
|
-
if (tableMainElement) {
|
|
2134
|
-
const tableDOMElement = slateReact.ReactEditor.toDOMNode(editor, tableMainElement);
|
|
2135
|
-
if (tableDOMElement instanceof HTMLElement) {
|
|
2136
|
-
tableWidth = tableDOMElement.getBoundingClientRect().width;
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
const processContainer = (containerElement) => {
|
|
2140
|
-
if (!core.Element.isElement(containerElement))
|
|
2141
|
-
return;
|
|
2142
|
-
const containerPath = slateReact.ReactEditor.findPath(editor, containerElement);
|
|
2143
|
-
const firstRow = containerElement.children[0];
|
|
2144
|
-
// 先找到 column 標題列的尾端
|
|
2145
|
-
let targetColumnIndex = 0;
|
|
2146
|
-
if (core.Element.isElement(firstRow) && firstRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2147
|
-
for (let i = 0; i < firstRow.children.length; i++) {
|
|
2148
|
-
const cell = firstRow.children[i];
|
|
2149
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE) && cell.treatAsTitle) {
|
|
2150
|
-
targetColumnIndex = i + 1;
|
|
2151
|
-
}
|
|
2152
|
-
else {
|
|
2153
|
-
break;
|
|
2154
|
-
}
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
|
-
containerElement.children.forEach((row, rowIndex) => {
|
|
2158
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2159
|
-
row.children.forEach((cell, childColIndex) => {
|
|
2160
|
-
const cellPath = [...containerPath, rowIndex, childColIndex];
|
|
2161
|
-
if (childColIndex === columnIndex) {
|
|
2162
|
-
const nodeProps = finalProps ? Object.assign({ treatAsTitle: true }, finalProps) : { treatAsTitle: true };
|
|
2163
|
-
core.Transforms.setNodes(editor, nodeProps, { at: cellPath });
|
|
2164
|
-
}
|
|
2165
|
-
else if (finalProps === null || finalProps === void 0 ? void 0 : finalProps.pinned) {
|
|
2166
|
-
// 確保其他 title column 也有 pinned 屬性以保持一致性
|
|
2167
|
-
if (core.Element.isElement(cell) && cell.treatAsTitle) {
|
|
2168
|
-
core.Transforms.setNodes(editor, { pinned: true }, { at: cellPath });
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
});
|
|
2172
|
-
}
|
|
2173
|
-
});
|
|
2174
|
-
// 檢查是否需要移動位置
|
|
2175
|
-
const needsMove = columnIndex >= targetColumnIndex && columnIndex !== targetColumnIndex;
|
|
2176
|
-
if (needsMove) {
|
|
2177
|
-
for (let rowIndex = containerElement.children.length - 1; rowIndex >= 0; rowIndex--) {
|
|
2178
|
-
const row = containerElement.children[rowIndex];
|
|
2179
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2180
|
-
const fromPath = [...containerPath, rowIndex, columnIndex];
|
|
2181
|
-
const toPath = [...containerPath, rowIndex, targetColumnIndex];
|
|
2182
|
-
core.Transforms.moveNodes(editor, {
|
|
2183
|
-
at: fromPath,
|
|
2184
|
-
to: toPath,
|
|
2185
|
-
});
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
// 調整 columnWidths:將 columnIndex 的寬度移動到 targetColumnIndex
|
|
2189
|
-
const currentWidths = getColumnWidths(element);
|
|
2190
|
-
if (currentWidths.length > 0) {
|
|
2191
|
-
const movedWidths = moveOrSwapColumnWidth(currentWidths, columnIndex, targetColumnIndex, 'move');
|
|
2192
|
-
// 如果設定了 pinned,需要轉換為混合模式
|
|
2193
|
-
if ((finalProps === null || finalProps === void 0 ? void 0 : finalProps.pinned) && tableWidth > 0) {
|
|
2194
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(element);
|
|
2195
|
-
// 更新釘選欄位索引
|
|
2196
|
-
const updatedPinnedIndices = pinnedColumnIndices
|
|
2197
|
-
.map((idx) => {
|
|
2198
|
-
if (idx === columnIndex)
|
|
2199
|
-
return targetColumnIndex;
|
|
2200
|
-
if (idx >= targetColumnIndex && idx < columnIndex)
|
|
2201
|
-
return idx + 1;
|
|
2202
|
-
return idx;
|
|
2203
|
-
})
|
|
2204
|
-
.concat(targetColumnIndex)
|
|
2205
|
-
.filter((idx, i, arr) => arr.indexOf(idx) === i)
|
|
2206
|
-
.sort((a, b) => a - b);
|
|
2207
|
-
const mixedWidths = convertToMixedWidthMode(movedWidths, updatedPinnedIndices, tableWidth);
|
|
2208
|
-
setColumnWidths(editor, element, mixedWidths);
|
|
2209
|
-
}
|
|
2210
|
-
else {
|
|
2211
|
-
setColumnWidths(editor, element, movedWidths);
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
else if ((finalProps === null || finalProps === void 0 ? void 0 : finalProps.pinned) && tableWidth > 0) {
|
|
2216
|
-
// 即使沒有移動位置,如果設定了 pinned,也需要轉換為混合模式
|
|
2217
|
-
const currentWidths = getColumnWidths(element);
|
|
2218
|
-
if (currentWidths.length > 0) {
|
|
2219
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(element);
|
|
2220
|
-
// 找出所有已經是 title 的 columns
|
|
2221
|
-
const titleColumnIndices = new Set();
|
|
2222
|
-
const firstRow = containerElement.children[0];
|
|
2223
|
-
if (core.Element.isElement(firstRow) && firstRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2224
|
-
firstRow.children.forEach((cell, colIndex) => {
|
|
2225
|
-
if (core.Element.isElement(cell) &&
|
|
2226
|
-
cell.type.includes(table.TABLE_CELL_TYPE) &&
|
|
2227
|
-
cell.treatAsTitle) {
|
|
2228
|
-
titleColumnIndices.add(colIndex);
|
|
2229
|
-
}
|
|
2230
|
-
});
|
|
2231
|
-
}
|
|
2232
|
-
// 將當前 column 加入 title columns
|
|
2233
|
-
titleColumnIndices.add(columnIndex);
|
|
2234
|
-
// 合併所有 pinned columns 和 title columns
|
|
2235
|
-
const allPinnedIndices = new Set([...pinnedColumnIndices, ...Array.from(titleColumnIndices)]);
|
|
2236
|
-
const updatedPinnedIndices = Array.from(allPinnedIndices).sort((a, b) => a - b);
|
|
2237
|
-
const mixedWidths = convertToMixedWidthMode(currentWidths, updatedPinnedIndices, tableWidth);
|
|
2238
|
-
setColumnWidths(editor, element, mixedWidths);
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
};
|
|
2242
|
-
if (tableHeaderElement) {
|
|
2243
|
-
processContainer(tableHeaderElement);
|
|
2244
|
-
}
|
|
2245
|
-
if (tableBodyElement) {
|
|
2246
|
-
processContainer(tableBodyElement);
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
catch (error) {
|
|
2250
|
-
console.warn('Failed to set column as title:', error);
|
|
2251
|
-
}
|
|
2252
|
-
}, [editor, element]);
|
|
2253
|
-
const pinColumn = React.useCallback((columnIndex) => {
|
|
2254
|
-
try {
|
|
2255
|
-
setColumnAsTitle(columnIndex, { pinned: true });
|
|
2256
|
-
}
|
|
2257
|
-
catch (error) {
|
|
2258
|
-
console.warn('Failed to pin column:', error);
|
|
2259
|
-
}
|
|
2260
|
-
}, [setColumnAsTitle]);
|
|
2261
|
-
const unpinColumn = React.useCallback(() => {
|
|
2262
|
-
try {
|
|
2263
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2264
|
-
if (!tableStructure)
|
|
2265
|
-
return;
|
|
2266
|
-
const { tableHeaderElement, tableBodyElement } = tableStructure;
|
|
2267
|
-
// 檢查 column 與 row 之間是否有交叉 pinned 狀態的關係
|
|
2268
|
-
const shouldRowRemainPinned = (rowElement, excludeColumns) => {
|
|
2269
|
-
let hasNonExcludedCells = false;
|
|
2270
|
-
for (let colIndex = 0; colIndex < rowElement.children.length; colIndex++) {
|
|
2271
|
-
const cell = rowElement.children[colIndex];
|
|
2272
|
-
if (!core.Element.isElement(cell) || !cell.type.includes(table.TABLE_CELL_TYPE))
|
|
2273
|
-
continue;
|
|
2274
|
-
if (excludeColumns.has(colIndex))
|
|
2275
|
-
continue;
|
|
2276
|
-
hasNonExcludedCells = true;
|
|
2277
|
-
if (!cell.pinned) {
|
|
2278
|
-
return false;
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
return hasNonExcludedCells;
|
|
2282
|
-
};
|
|
2283
|
-
const processContainer = (containerElement) => {
|
|
2284
|
-
const containerPath = slateReact.ReactEditor.findPath(editor, containerElement);
|
|
2285
|
-
const treatAsTitleColumns = new Set();
|
|
2286
|
-
containerElement.children.forEach((row) => {
|
|
2287
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2288
|
-
row.children.forEach((cell, colIndex) => {
|
|
2289
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE) && cell.treatAsTitle) {
|
|
2290
|
-
treatAsTitleColumns.add(colIndex);
|
|
2291
|
-
}
|
|
2292
|
-
});
|
|
2293
|
-
}
|
|
2294
|
-
});
|
|
2295
|
-
containerElement.children.forEach((row, rowIndex) => {
|
|
2296
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2297
|
-
const rowShouldRemainPinned = shouldRowRemainPinned(row, treatAsTitleColumns);
|
|
2298
|
-
row.children.forEach((cell, colIndex) => {
|
|
2299
|
-
if (treatAsTitleColumns.has(colIndex) && core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2300
|
-
const cellPath = [...containerPath, rowIndex, colIndex];
|
|
2301
|
-
core.Transforms.unsetNodes(editor, 'treatAsTitle', { at: cellPath });
|
|
2302
|
-
if (!rowShouldRemainPinned) {
|
|
2303
|
-
core.Transforms.unsetNodes(editor, 'pinned', { at: cellPath });
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
});
|
|
2307
|
-
}
|
|
2308
|
-
});
|
|
2309
|
-
};
|
|
2310
|
-
if (tableHeaderElement) {
|
|
2311
|
-
processContainer(tableHeaderElement);
|
|
2312
|
-
}
|
|
2313
|
-
if (tableBodyElement) {
|
|
2314
|
-
processContainer(tableBodyElement);
|
|
2315
|
-
}
|
|
2316
|
-
// 轉換回純百分比模式
|
|
2317
|
-
const currentWidths = getColumnWidths(element);
|
|
2318
|
-
const percentageWidths = convertToPercentageMode(currentWidths);
|
|
2319
|
-
setColumnWidths(editor, element, percentageWidths);
|
|
2320
|
-
}
|
|
2321
|
-
catch (error) {
|
|
2322
|
-
console.warn('Failed to unpin column:', error);
|
|
2323
|
-
}
|
|
2324
|
-
}, [editor, element]);
|
|
2325
|
-
const setPinnedOnRowCells = React.useCallback((row, pinned) => {
|
|
2326
|
-
try {
|
|
2327
|
-
for (const [, cell] of row.children.entries()) {
|
|
2328
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2329
|
-
const cellPath = slateReact.ReactEditor.findPath(editor, cell);
|
|
2330
|
-
if (pinned) {
|
|
2331
|
-
core.Transforms.setNodes(editor, { pinned: true }, { at: cellPath });
|
|
2332
|
-
}
|
|
2333
|
-
else {
|
|
2334
|
-
core.Transforms.unsetNodes(editor, 'pinned', { at: cellPath });
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
catch (error) {
|
|
2340
|
-
console.warn('Failed to set pinned on row cells:', error);
|
|
2341
|
-
}
|
|
2342
|
-
}, [editor]);
|
|
2343
|
-
const setPinnedOnAllHeaderRows = React.useCallback((headerElement, pinned) => {
|
|
2344
|
-
try {
|
|
2345
|
-
for (const headerRow of headerElement.children) {
|
|
2346
|
-
if (core.Element.isElement(headerRow) && headerRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2347
|
-
setPinnedOnRowCells(headerRow, pinned);
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
catch (error) {
|
|
2352
|
-
console.warn('Failed to set pinned on all header rows:', error);
|
|
2353
|
-
}
|
|
2354
|
-
}, [setPinnedOnRowCells]);
|
|
2355
|
-
const pinRow = React.useCallback((rowIndex) => {
|
|
2356
|
-
try {
|
|
2357
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2358
|
-
if (!tableStructure)
|
|
2359
|
-
return;
|
|
2360
|
-
const { tableHeaderElement, headerRowCount } = tableStructure;
|
|
2361
|
-
// 先將目前所有的 header rows 都設為 pinned
|
|
2362
|
-
if (tableHeaderElement) {
|
|
2363
|
-
setPinnedOnAllHeaderRows(tableHeaderElement, true);
|
|
2364
|
-
}
|
|
2365
|
-
// 然後將目標 row 移動到 header 中並設為 pinned
|
|
2366
|
-
if (rowIndex >= headerRowCount) {
|
|
2367
|
-
moveRowToHeader(rowIndex, { pinned: true });
|
|
2368
|
-
}
|
|
2369
|
-
}
|
|
2370
|
-
catch (error) {
|
|
2371
|
-
console.warn('Failed to pin row:', error);
|
|
2372
|
-
}
|
|
2373
|
-
}, [editor, element, moveRowToHeader, setPinnedOnAllHeaderRows]);
|
|
2374
|
-
const unpinRow = React.useCallback(() => {
|
|
2375
|
-
try {
|
|
2376
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2377
|
-
if (!tableStructure)
|
|
2378
|
-
return;
|
|
2379
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath } = tableStructure;
|
|
2380
|
-
if (!tableHeaderElement || !tableBodyElement)
|
|
2381
|
-
return;
|
|
2382
|
-
// 檢查 column 與 row 之間是否有交叉 pinned 狀態的關係
|
|
2383
|
-
const shouldColumnRemainPinned = (columnIndex) => {
|
|
2384
|
-
const containers = [tableHeaderElement, tableBodyElement];
|
|
2385
|
-
for (const container of containers) {
|
|
2386
|
-
if (!core.Element.isElement(container))
|
|
2387
|
-
continue;
|
|
2388
|
-
for (const row of container.children) {
|
|
2389
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2390
|
-
const cell = row.children[columnIndex];
|
|
2391
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2392
|
-
// 如果這個 cell 在 body 中且有 pinned 屬性,則 column 應該保持 pinned
|
|
2393
|
-
if (container.type === tableBodyElement.type && cell.pinned) {
|
|
2394
|
-
return true;
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
return false;
|
|
2401
|
-
};
|
|
2402
|
-
tableHeaderElement.children.forEach((row, headerRowIndex) => {
|
|
2403
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2404
|
-
row.children.forEach((cell, colIndex) => {
|
|
2405
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2406
|
-
const cellPath = [...tableHeaderPath, headerRowIndex, colIndex];
|
|
2407
|
-
if (!shouldColumnRemainPinned(colIndex)) {
|
|
2408
|
-
core.Transforms.unsetNodes(editor, 'pinned', { at: cellPath });
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
});
|
|
2412
|
-
}
|
|
2413
|
-
});
|
|
2414
|
-
const tableBodyPath = slateReact.ReactEditor.findPath(editor, tableBodyElement);
|
|
2415
|
-
for (let i = tableHeaderElement.children.length - 1; i >= 0; i--) {
|
|
2416
|
-
const row = tableHeaderElement.children[i];
|
|
2417
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2418
|
-
const fromPath = [...tableHeaderPath, i];
|
|
2419
|
-
const toPath = [...tableBodyPath, 0];
|
|
2420
|
-
core.Transforms.moveNodes(editor, {
|
|
2421
|
-
at: fromPath,
|
|
2422
|
-
to: toPath,
|
|
2423
|
-
});
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
catch (error) {
|
|
2428
|
-
console.warn('Failed to unpin row:', error);
|
|
2429
|
-
}
|
|
2430
|
-
}, [editor, element]);
|
|
2431
|
-
/**
|
|
2432
|
-
* 內部函數:移動或交換列的位置
|
|
2433
|
-
* @param mode 'swap' 為交換相鄰位置(toolbar 按鈕),'move' 為移動到任意位置(拖曳)
|
|
2434
|
-
*/
|
|
2435
|
-
const moveOrSwapRow = React.useCallback((sourceRowIndex, targetRowIndex, mode = 'move') => {
|
|
2436
|
-
try {
|
|
2437
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2438
|
-
if (!tableStructure)
|
|
2439
|
-
return;
|
|
2440
|
-
const { tableHeaderElement, tableBodyElement, tableHeaderPath, tableBodyPath, headerRowCount } = tableStructure;
|
|
2441
|
-
// 確定當前列和目標列所屬的容器
|
|
2442
|
-
const sourceInHeader = sourceRowIndex < headerRowCount;
|
|
2443
|
-
const targetInHeader = targetRowIndex < headerRowCount;
|
|
2444
|
-
// 標題列只能與標題列互換/移動,一般列只能與一般列互換/移動
|
|
2445
|
-
if (sourceInHeader !== targetInHeader) {
|
|
2446
|
-
console.warn(`Cannot ${mode} row between header and body`);
|
|
2447
|
-
return;
|
|
2448
|
-
}
|
|
2449
|
-
// 檢查邊界
|
|
2450
|
-
if (sourceRowIndex === targetRowIndex) {
|
|
2451
|
-
return;
|
|
2452
|
-
}
|
|
2453
|
-
let containerPath;
|
|
2454
|
-
let sourceLocalIndex;
|
|
2455
|
-
let targetLocalIndex;
|
|
2456
|
-
if (sourceInHeader) {
|
|
2457
|
-
// 在 header 中
|
|
2458
|
-
if (!tableHeaderElement || !tableHeaderPath)
|
|
2459
|
-
return;
|
|
2460
|
-
containerPath = tableHeaderPath;
|
|
2461
|
-
sourceLocalIndex = sourceRowIndex;
|
|
2462
|
-
targetLocalIndex = targetRowIndex;
|
|
2463
|
-
}
|
|
2464
|
-
else {
|
|
2465
|
-
// 在 body 中
|
|
2466
|
-
if (!tableBodyElement)
|
|
2467
|
-
return;
|
|
2468
|
-
containerPath = tableBodyPath;
|
|
2469
|
-
sourceLocalIndex = sourceRowIndex - headerRowCount;
|
|
2470
|
-
targetLocalIndex = targetRowIndex - headerRowCount;
|
|
2471
|
-
}
|
|
2472
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
2473
|
-
if (mode === 'swap') {
|
|
2474
|
-
// swap 邏輯:交換兩個相鄰位置
|
|
2475
|
-
if (sourceRowIndex < targetRowIndex) {
|
|
2476
|
-
// 向下移動:先將源列移到目標位置之後
|
|
2477
|
-
const sourcePath = [...containerPath, sourceLocalIndex];
|
|
2478
|
-
const afterTargetPath = [...containerPath, targetLocalIndex];
|
|
2479
|
-
core.Transforms.moveNodes(editor, {
|
|
2480
|
-
at: sourcePath,
|
|
2481
|
-
to: afterTargetPath,
|
|
2482
|
-
});
|
|
2483
|
-
}
|
|
2484
|
-
else {
|
|
2485
|
-
// 向上移動:先將目標列移到源位置之後
|
|
2486
|
-
const targetPath = [...containerPath, targetLocalIndex];
|
|
2487
|
-
const afterSourcePath = [...containerPath, sourceLocalIndex];
|
|
2488
|
-
core.Transforms.moveNodes(editor, {
|
|
2489
|
-
at: targetPath,
|
|
2490
|
-
to: afterSourcePath,
|
|
2491
|
-
});
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
else {
|
|
2495
|
-
// move 邏輯:直接移動到目標位置
|
|
2496
|
-
const sourcePath = [...containerPath, sourceLocalIndex];
|
|
2497
|
-
const targetPath = [...containerPath, targetLocalIndex];
|
|
2498
|
-
core.Transforms.moveNodes(editor, {
|
|
2499
|
-
at: sourcePath,
|
|
2500
|
-
to: targetPath,
|
|
2501
|
-
});
|
|
2502
|
-
}
|
|
2503
|
-
});
|
|
2504
|
-
}
|
|
2505
|
-
catch (error) {
|
|
2506
|
-
console.warn(`Failed to ${mode} row:`, error);
|
|
2507
|
-
}
|
|
2508
|
-
}, [editor, element]);
|
|
2509
|
-
/**
|
|
2510
|
-
* 內部函數:移動或交換行的位置
|
|
2511
|
-
* @param mode 'swap' 為交換相鄰位置(toolbar 按鈕),'move' 為移動到任意位置(拖曳)
|
|
2512
|
-
*/
|
|
2513
|
-
const moveOrSwapColumn = React.useCallback((sourceColumnIndex, targetColumnIndex, mode = 'move') => {
|
|
2514
|
-
try {
|
|
2515
|
-
const tableStructure = getTableStructure(editor, element);
|
|
2516
|
-
if (!tableStructure)
|
|
2517
|
-
return;
|
|
2518
|
-
const { tableHeaderElement, tableBodyElement, columnCount } = tableStructure;
|
|
2519
|
-
// 檢查邊界
|
|
2520
|
-
if (targetColumnIndex < 0 || targetColumnIndex >= columnCount) {
|
|
2521
|
-
console.warn('Target column index out of bounds');
|
|
2522
|
-
return;
|
|
2523
|
-
}
|
|
2524
|
-
// 檢查是否為同一行
|
|
2525
|
-
if (sourceColumnIndex === targetColumnIndex) {
|
|
2526
|
-
return;
|
|
2527
|
-
}
|
|
2528
|
-
// 檢查當前行和目標行是否都是標題行或都是一般行
|
|
2529
|
-
// 透過檢查第一個 cell 的 treatAsTitle 屬性來判斷
|
|
2530
|
-
const checkIsTitleColumn = (container, colIndex) => {
|
|
2531
|
-
if (!core.Element.isElement(container))
|
|
2532
|
-
return false;
|
|
2533
|
-
for (const row of container.children) {
|
|
2534
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2535
|
-
const cell = row.children[colIndex];
|
|
2536
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2537
|
-
return !!cell.treatAsTitle;
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
return false;
|
|
2542
|
-
};
|
|
2543
|
-
// 檢查兩個 container 中的第一列來確定是否為標題行
|
|
2544
|
-
let sourceIsTitle = false;
|
|
2545
|
-
let targetIsTitle = false;
|
|
2546
|
-
if (tableHeaderElement) {
|
|
2547
|
-
sourceIsTitle = sourceIsTitle || checkIsTitleColumn(tableHeaderElement, sourceColumnIndex);
|
|
2548
|
-
targetIsTitle = targetIsTitle || checkIsTitleColumn(tableHeaderElement, targetColumnIndex);
|
|
2549
|
-
}
|
|
2550
|
-
if (tableBodyElement) {
|
|
2551
|
-
sourceIsTitle = sourceIsTitle || checkIsTitleColumn(tableBodyElement, sourceColumnIndex);
|
|
2552
|
-
targetIsTitle = targetIsTitle || checkIsTitleColumn(tableBodyElement, targetColumnIndex);
|
|
2553
|
-
}
|
|
2554
|
-
// 標題行只能與標題行互換/移動,一般行只能與一般行互換/移動
|
|
2555
|
-
if (sourceIsTitle !== targetIsTitle) {
|
|
2556
|
-
console.warn(`Cannot ${mode} column between title and normal columns`);
|
|
2557
|
-
return;
|
|
2558
|
-
}
|
|
2559
|
-
// 根據模式選擇不同的 columnWidths 處理方式
|
|
2560
|
-
const currentWidths = getColumnWidths(element);
|
|
2561
|
-
const newWidths = moveOrSwapColumnWidth(currentWidths, sourceColumnIndex, targetColumnIndex, mode);
|
|
2562
|
-
setColumnWidths(editor, element, newWidths);
|
|
2563
|
-
// 對 header 和 body 中的所有列進行操作
|
|
2564
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
2565
|
-
const containers = [tableHeaderElement, tableBodyElement].filter((c) => c && core.Element.isElement(c));
|
|
2566
|
-
for (const container of containers) {
|
|
2567
|
-
// 對每一列進行操作
|
|
2568
|
-
for (let rowIndex = 0; rowIndex < container.children.length; rowIndex++) {
|
|
2569
|
-
const row = container.children[rowIndex];
|
|
2570
|
-
if (!core.Element.isElement(row) || !row.type.includes(table.TABLE_ROW_TYPE))
|
|
2571
|
-
continue;
|
|
2572
|
-
const containerPath = slateReact.ReactEditor.findPath(editor, container);
|
|
2573
|
-
const rowPath = [...containerPath, rowIndex];
|
|
2574
|
-
if (mode === 'swap') {
|
|
2575
|
-
// swap 邏輯:交換兩個相鄰位置
|
|
2576
|
-
if (sourceColumnIndex < targetColumnIndex) {
|
|
2577
|
-
// 向右移動:將源 cell 移到目標位置之後
|
|
2578
|
-
const sourceCellPath = [...rowPath, sourceColumnIndex];
|
|
2579
|
-
const afterTargetCellPath = [...rowPath, targetColumnIndex];
|
|
2580
|
-
core.Transforms.moveNodes(editor, {
|
|
2581
|
-
at: sourceCellPath,
|
|
2582
|
-
to: afterTargetCellPath,
|
|
2583
|
-
});
|
|
2584
|
-
}
|
|
2585
|
-
else {
|
|
2586
|
-
// 向左移動:將目標 cell 移到源位置之後
|
|
2587
|
-
const targetCellPath = [...rowPath, targetColumnIndex];
|
|
2588
|
-
const afterSourceCellPath = [...rowPath, sourceColumnIndex];
|
|
2589
|
-
core.Transforms.moveNodes(editor, {
|
|
2590
|
-
at: targetCellPath,
|
|
2591
|
-
to: afterSourceCellPath,
|
|
2592
|
-
});
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
else {
|
|
2596
|
-
// move 邏輯:直接移動到目標位置
|
|
2597
|
-
const sourceCellPath = [...rowPath, sourceColumnIndex];
|
|
2598
|
-
const targetCellPath = [...rowPath, targetColumnIndex];
|
|
2599
|
-
core.Transforms.moveNodes(editor, {
|
|
2600
|
-
at: sourceCellPath,
|
|
2601
|
-
to: targetCellPath,
|
|
2602
|
-
});
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
}
|
|
2606
|
-
});
|
|
2607
|
-
}
|
|
2608
|
-
catch (error) {
|
|
2609
|
-
console.warn(`Failed to ${mode} column:`, error);
|
|
2610
|
-
}
|
|
2611
|
-
}, [editor, element]);
|
|
2612
|
-
return {
|
|
2613
|
-
addColumn,
|
|
2614
|
-
addRow,
|
|
2615
|
-
addColumnAndRow,
|
|
2616
|
-
deleteRow,
|
|
2617
|
-
deleteColumn,
|
|
2618
|
-
moveRowToBody,
|
|
2619
|
-
moveRowToHeader,
|
|
2620
|
-
unsetColumnAsTitle,
|
|
2621
|
-
setColumnAsTitle,
|
|
2622
|
-
pinColumn,
|
|
2623
|
-
unpinColumn,
|
|
2624
|
-
pinRow,
|
|
2625
|
-
unpinRow,
|
|
2626
|
-
isColumnPinned,
|
|
2627
|
-
isRowPinned,
|
|
2628
|
-
moveOrSwapRow,
|
|
2629
|
-
moveOrSwapColumn,
|
|
2630
|
-
};
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
function useTableStates() {
|
|
2634
|
-
const [tableSelectedOn, setTableSelectedOn] = React.useState();
|
|
2635
|
-
const [tableHoveredOn, setTableHoveredOn] = React.useState();
|
|
2636
|
-
return {
|
|
2637
|
-
tableSelectedOn,
|
|
2638
|
-
setTableSelectedOn,
|
|
2639
|
-
tableHoveredOn,
|
|
2640
|
-
setTableHoveredOn,
|
|
2641
|
-
};
|
|
2642
|
-
}
|
|
2643
|
-
|
|
2644
|
-
function Table({ attributes, children, element, }) {
|
|
2645
|
-
const { addColumn, addRow, addColumnAndRow, deleteRow, deleteColumn, moveRowToBody, moveRowToHeader, unsetColumnAsTitle, setColumnAsTitle, pinColumn, pinRow, unpinColumn, unpinRow, moveOrSwapRow, moveOrSwapColumn, } = useTableActions(element);
|
|
2646
|
-
const { tableSelectedOn, setTableSelectedOn, tableHoveredOn, setTableHoveredOn } = useTableStates();
|
|
2647
|
-
const portalContainerRef = React.useRef(null);
|
|
2648
|
-
const { columnCount, rowCount, normalCols, bodyCount, tableElements } = React.useMemo(() => {
|
|
2649
|
-
const elements = getTableElements(element);
|
|
2650
|
-
if (!elements.tableMainElement) {
|
|
2651
|
-
return {
|
|
2652
|
-
columnCount: 0,
|
|
2653
|
-
rowCount: 0,
|
|
2654
|
-
treatAsTitleCols: 0,
|
|
2655
|
-
normalCols: 0,
|
|
2656
|
-
bodyCount: 0,
|
|
2657
|
-
headerCount: 0,
|
|
2658
|
-
tableElements: elements,
|
|
2659
|
-
};
|
|
2660
|
-
}
|
|
2661
|
-
const headerRowElements = elements.tableHeaderElement
|
|
2662
|
-
? elements.tableHeaderElement.children.filter((child) => core.Element.isElement(child) && child.type.includes(table.TABLE_ROW_TYPE))
|
|
2663
|
-
: [];
|
|
2664
|
-
const bodyRowElements = elements.tableBodyElement
|
|
2665
|
-
? elements.tableBodyElement.children.filter((child) => core.Element.isElement(child) && child.type.includes(table.TABLE_ROW_TYPE))
|
|
2666
|
-
: [];
|
|
2667
|
-
const cols = bodyRowElements.length > 0 && core.Element.isElement(bodyRowElements[0]) ? bodyRowElements[0].children.length : 0;
|
|
2668
|
-
const treatAsTitleCols = bodyRowElements.length > 0 && core.Element.isElement(bodyRowElements[0])
|
|
2669
|
-
? bodyRowElements[0].children.filter((row) => (core.Element.isElement(row) ? row.treatAsTitle : false)).length
|
|
2670
|
-
: 0;
|
|
2671
|
-
const rows = headerRowElements.length + bodyRowElements.length;
|
|
2672
|
-
return {
|
|
2673
|
-
columnCount: cols,
|
|
2674
|
-
rowCount: rows,
|
|
2675
|
-
headerCount: headerRowElements.length,
|
|
2676
|
-
bodyCount: bodyRowElements.length,
|
|
2677
|
-
treatAsTitleCols,
|
|
2678
|
-
normalCols: cols - treatAsTitleCols,
|
|
2679
|
-
tableElements: elements,
|
|
2680
|
-
};
|
|
2681
|
-
}, [element]);
|
|
2682
|
-
// 預計算所有 cell positions
|
|
2683
|
-
const cellPositions = React.useMemo(() => {
|
|
2684
|
-
const positions = new Map();
|
|
2685
|
-
if (!tableElements.tableMainElement)
|
|
2686
|
-
return positions;
|
|
2687
|
-
const { tableHeaderElement, tableBodyElement } = tableElements;
|
|
2688
|
-
let globalRowIndex = 0;
|
|
2689
|
-
// Process header rows
|
|
2690
|
-
if (tableHeaderElement && core.Element.isElement(tableHeaderElement)) {
|
|
2691
|
-
for (const row of tableHeaderElement.children) {
|
|
2692
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2693
|
-
for (let colIndex = 0; colIndex < row.children.length; colIndex++) {
|
|
2694
|
-
const cell = row.children[colIndex];
|
|
2695
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2696
|
-
positions.set(cell, {
|
|
2697
|
-
columnIndex: colIndex,
|
|
2698
|
-
rowIndex: globalRowIndex,
|
|
2699
|
-
});
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
globalRowIndex++;
|
|
2703
|
-
}
|
|
2704
|
-
}
|
|
2705
|
-
}
|
|
2706
|
-
// Process body rows
|
|
2707
|
-
if (tableBodyElement && core.Element.isElement(tableBodyElement)) {
|
|
2708
|
-
for (const row of tableBodyElement.children) {
|
|
2709
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2710
|
-
for (let colIndex = 0; colIndex < row.children.length; colIndex++) {
|
|
2711
|
-
const cell = row.children[colIndex];
|
|
2712
|
-
if (core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE)) {
|
|
2713
|
-
positions.set(cell, {
|
|
2714
|
-
columnIndex: colIndex,
|
|
2715
|
-
rowIndex: globalRowIndex,
|
|
2716
|
-
});
|
|
2717
|
-
}
|
|
2718
|
-
}
|
|
2719
|
-
globalRowIndex++;
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2723
|
-
return positions;
|
|
2724
|
-
}, [tableElements]);
|
|
2725
|
-
// 預計算 pinned columns 和 rows
|
|
2726
|
-
const { pinnedColumns, pinnedRows } = React.useMemo(() => {
|
|
2727
|
-
const columns = new Set();
|
|
2728
|
-
const rows = new Set();
|
|
2729
|
-
if (!tableElements.tableBodyElement)
|
|
2730
|
-
return { pinnedColumns: columns, pinnedRows: rows };
|
|
2731
|
-
const { tableHeaderElement, tableBodyElement } = tableElements;
|
|
2732
|
-
// 檢查 pinned columns - 只需要檢查第一個 body row
|
|
2733
|
-
if (core.Element.isElement(tableBodyElement) && tableBodyElement.children.length > 0) {
|
|
2734
|
-
const firstBodyRow = tableBodyElement.children[0];
|
|
2735
|
-
if (core.Element.isElement(firstBodyRow) && firstBodyRow.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2736
|
-
for (let colIndex = 0; colIndex < firstBodyRow.children.length; colIndex++) {
|
|
2737
|
-
const cell = firstBodyRow.children[colIndex];
|
|
2738
|
-
if (core.Element.isElement(cell) &&
|
|
2739
|
-
cell.type.includes(table.TABLE_CELL_TYPE) &&
|
|
2740
|
-
cell.treatAsTitle &&
|
|
2741
|
-
cell.pinned) {
|
|
2742
|
-
columns.add(colIndex);
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
// 檢查 pinned rows - 只需要檢查 header rows
|
|
2748
|
-
if (tableHeaderElement && core.Element.isElement(tableHeaderElement)) {
|
|
2749
|
-
for (let rowIndex = 0; rowIndex < tableHeaderElement.children.length; rowIndex++) {
|
|
2750
|
-
const row = tableHeaderElement.children[rowIndex];
|
|
2751
|
-
if (core.Element.isElement(row) && row.type.includes(table.TABLE_ROW_TYPE)) {
|
|
2752
|
-
// 檢查這一行的所有 cells 是否都是 pinned
|
|
2753
|
-
const allCellsPinned = row.children.every((cell) => core.Element.isElement(cell) && cell.type.includes(table.TABLE_CELL_TYPE) && cell.pinned);
|
|
2754
|
-
if (allCellsPinned) {
|
|
2755
|
-
rows.add(rowIndex);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
return { pinnedColumns: columns, pinnedRows: rows };
|
|
2761
|
-
}, [tableElements]);
|
|
2762
|
-
const isReachMaximumColumns = React.useMemo(() => {
|
|
2763
|
-
return columnCount >= table.TABLE_DEFAULT_MAX_COLUMNS;
|
|
2764
|
-
}, [columnCount]);
|
|
2765
|
-
const isReachMaximumRows = React.useMemo(() => {
|
|
2766
|
-
return table.TABLE_DEFAULT_MAX_ROWS > 0 ? rowCount >= table.TABLE_DEFAULT_MAX_ROWS : false;
|
|
2767
|
-
}, [rowCount]);
|
|
2768
|
-
const isReachMinimumNormalColumns = React.useMemo(() => {
|
|
2769
|
-
return normalCols <= 1;
|
|
2770
|
-
}, [normalCols]);
|
|
2771
|
-
const isReachMinimumBodyRows = React.useMemo(() => {
|
|
2772
|
-
return bodyCount <= 1;
|
|
2773
|
-
}, [bodyCount]);
|
|
2774
|
-
const isColumnPinned = React.useCallback((columnIndex) => {
|
|
2775
|
-
return pinnedColumns.has(columnIndex);
|
|
2776
|
-
}, [pinnedColumns]);
|
|
2777
|
-
const isRowPinned = React.useCallback((rowIndex) => {
|
|
2778
|
-
return pinnedRows.has(rowIndex);
|
|
2779
|
-
}, [pinnedRows]);
|
|
2780
|
-
const actionsValue = React.useMemo(() => ({
|
|
2781
|
-
addColumn,
|
|
2782
|
-
addRow,
|
|
2783
|
-
addColumnAndRow,
|
|
2784
|
-
deleteRow,
|
|
2785
|
-
deleteColumn,
|
|
2786
|
-
moveRowToBody,
|
|
2787
|
-
moveRowToHeader,
|
|
2788
|
-
unsetColumnAsTitle,
|
|
2789
|
-
setColumnAsTitle,
|
|
2790
|
-
pinColumn,
|
|
2791
|
-
pinRow,
|
|
2792
|
-
unpinColumn,
|
|
2793
|
-
unpinRow,
|
|
2794
|
-
moveOrSwapRow,
|
|
2795
|
-
moveOrSwapColumn,
|
|
2796
|
-
}), [
|
|
2797
|
-
addColumn,
|
|
2798
|
-
addRow,
|
|
2799
|
-
addColumnAndRow,
|
|
2800
|
-
deleteRow,
|
|
2801
|
-
deleteColumn,
|
|
2802
|
-
moveRowToBody,
|
|
2803
|
-
moveRowToHeader,
|
|
2804
|
-
unsetColumnAsTitle,
|
|
2805
|
-
setColumnAsTitle,
|
|
2806
|
-
pinColumn,
|
|
2807
|
-
pinRow,
|
|
2808
|
-
unpinColumn,
|
|
2809
|
-
unpinRow,
|
|
2810
|
-
moveOrSwapRow,
|
|
2811
|
-
moveOrSwapColumn,
|
|
2812
|
-
]);
|
|
2813
|
-
const metadataValue = React.useMemo(() => ({
|
|
2814
|
-
tableElement: element,
|
|
2815
|
-
columnCount,
|
|
2816
|
-
rowCount,
|
|
2817
|
-
portalContainerRef,
|
|
2818
|
-
isReachMaximumColumns,
|
|
2819
|
-
isReachMaximumRows,
|
|
2820
|
-
isReachMinimumNormalColumns,
|
|
2821
|
-
isReachMinimumBodyRows,
|
|
2822
|
-
pinnedColumns,
|
|
2823
|
-
pinnedRows,
|
|
2824
|
-
cellPositions,
|
|
2825
|
-
isColumnPinned,
|
|
2826
|
-
isRowPinned,
|
|
2827
|
-
}), [
|
|
2828
|
-
element,
|
|
2829
|
-
columnCount,
|
|
2830
|
-
rowCount,
|
|
2831
|
-
portalContainerRef,
|
|
2832
|
-
isReachMaximumColumns,
|
|
2833
|
-
isReachMaximumRows,
|
|
2834
|
-
isReachMinimumNormalColumns,
|
|
2835
|
-
isReachMinimumBodyRows,
|
|
2836
|
-
pinnedColumns,
|
|
2837
|
-
pinnedRows,
|
|
2838
|
-
cellPositions,
|
|
2839
|
-
isColumnPinned,
|
|
2840
|
-
isRowPinned,
|
|
2841
|
-
]);
|
|
2842
|
-
const stateValue = React.useMemo(() => ({
|
|
2843
|
-
tableSelectedOn,
|
|
2844
|
-
setTableSelectedOn,
|
|
2845
|
-
tableHoveredOn,
|
|
2846
|
-
setTableHoveredOn,
|
|
2847
|
-
}), [tableSelectedOn, setTableSelectedOn, tableHoveredOn, setTableHoveredOn]);
|
|
2848
|
-
return (React.createElement(TableDragProvider, null,
|
|
2849
|
-
React.createElement(TableActionsContext.Provider, { value: actionsValue },
|
|
2850
|
-
React.createElement(TableMetadataContext.Provider, { value: metadataValue },
|
|
2851
|
-
React.createElement(TableStateContext.Provider, { value: stateValue },
|
|
2852
|
-
React.createElement("div", Object.assign({}, attributes, { className: "qdr-table" }),
|
|
2853
|
-
children,
|
|
2854
|
-
React.createElement("button", { type: "button", onClick: (evt) => {
|
|
2855
|
-
evt.preventDefault();
|
|
2856
|
-
evt.stopPropagation();
|
|
2857
|
-
setTableSelectedOn((prev) => ((prev === null || prev === void 0 ? void 0 : prev.region) === 'table' ? undefined : { region: 'table' }));
|
|
2858
|
-
}, onMouseDown: (evt) => {
|
|
2859
|
-
evt.preventDefault();
|
|
2860
|
-
evt.stopPropagation();
|
|
2861
|
-
}, className: "qdr-table__selection", title: (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'table' ? 'Deselect Table' : 'Select Table' },
|
|
2862
|
-
React.createElement(components.Icon, { icon: icons.Drag, width: 20, height: 20 })),
|
|
2863
|
-
React.createElement("div", { ref: portalContainerRef, className: "qdr-table__portal-container", "data-slate-editor": false, contentEditable: false })))))));
|
|
2864
|
-
}
|
|
2865
|
-
|
|
2866
|
-
function TableTitle(props) {
|
|
2867
|
-
const { compositionPath } = react.useComposition();
|
|
2868
|
-
const { attributes, children, element } = props;
|
|
2869
|
-
const editor = react.useQuadrats();
|
|
2870
|
-
const path = react.ReactEditor.findPath(editor, element);
|
|
2871
|
-
const text = core.Editor.string(editor, path);
|
|
2872
|
-
const isEmpty = !text;
|
|
2873
|
-
const composing = React.useMemo(() => core.Path.equals(compositionPath, path), [compositionPath, path]);
|
|
2874
|
-
return (React.createElement("h3", Object.assign({}, attributes, { className: "qdr-table__title" }),
|
|
2875
|
-
children,
|
|
2876
|
-
isEmpty && !composing && (React.createElement("span", { className: "qdr-table__title__placeholder", contentEditable: false }, "\u8ACB\u8F38\u5165\u8868\u683C\u6A19\u984C"))));
|
|
2877
|
-
}
|
|
2878
|
-
|
|
2879
|
-
const TableScrollContext = React.createContext({
|
|
2880
|
-
scrollRef: { current: null },
|
|
2881
|
-
scrollTop: 0,
|
|
2882
|
-
scrollLeft: 0,
|
|
2883
|
-
});
|
|
2884
|
-
|
|
2885
|
-
const TableDragLayer = ({ scrollRef }) => {
|
|
2886
|
-
const { dragState } = useTableDragContext();
|
|
2887
|
-
const [columnWidths, setColumnWidths] = React.useState([]);
|
|
2888
|
-
const [rowHeights, setRowHeights] = React.useState([]);
|
|
2889
|
-
const { isDragging, currentOffset } = reactDnd.useDragLayer((monitor) => ({
|
|
2890
|
-
isDragging: monitor.isDragging(),
|
|
2891
|
-
currentOffset: monitor.getClientOffset(),
|
|
2892
|
-
}));
|
|
2893
|
-
// 計算所有 column 的寬度和 row 的高度
|
|
2894
|
-
React.useEffect(() => {
|
|
2895
|
-
if (!scrollRef.current || !isDragging)
|
|
2896
|
-
return;
|
|
2897
|
-
const tableContainer = scrollRef.current;
|
|
2898
|
-
const cells = tableContainer.querySelectorAll('.qdr-table__cell');
|
|
2899
|
-
if ((dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'column') {
|
|
2900
|
-
// 計算每個 column 的寬度
|
|
2901
|
-
const widths = [];
|
|
2902
|
-
const columnCells = Array.from(cells).filter((cell) => {
|
|
2903
|
-
const cellElement = cell;
|
|
2904
|
-
return cellElement.dataset.columnIndex !== undefined;
|
|
2905
|
-
});
|
|
2906
|
-
const columnCount = Math.max(...columnCells.map((cell) => {
|
|
2907
|
-
const cellElement = cell;
|
|
2908
|
-
return parseInt(cellElement.dataset.columnIndex || '0', 10);
|
|
2909
|
-
})) + 1;
|
|
2910
|
-
for (let i = 0; i < columnCount; i++) {
|
|
2911
|
-
const cellsInColumn = columnCells.filter((cell) => {
|
|
2912
|
-
const cellElement = cell;
|
|
2913
|
-
return parseInt(cellElement.dataset.columnIndex || '0', 10) === i;
|
|
2914
|
-
});
|
|
2915
|
-
if (cellsInColumn.length > 0) {
|
|
2916
|
-
const firstCell = cellsInColumn[0];
|
|
2917
|
-
widths[i] = firstCell.getBoundingClientRect().width;
|
|
2918
|
-
}
|
|
2919
|
-
}
|
|
2920
|
-
setColumnWidths(widths);
|
|
2921
|
-
}
|
|
2922
|
-
else if ((dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'row') {
|
|
2923
|
-
// 計算每個 row 的高度
|
|
2924
|
-
const heights = [];
|
|
2925
|
-
const rowCells = Array.from(cells).filter((cell) => {
|
|
2926
|
-
const cellElement = cell;
|
|
2927
|
-
return cellElement.dataset.rowIndex !== undefined;
|
|
2928
|
-
});
|
|
2929
|
-
const rowCount = Math.max(...rowCells.map((cell) => {
|
|
2930
|
-
const cellElement = cell;
|
|
2931
|
-
return parseInt(cellElement.dataset.rowIndex || '0', 10);
|
|
2932
|
-
})) + 1;
|
|
2933
|
-
for (let i = 0; i < rowCount; i++) {
|
|
2934
|
-
const cellsInRow = rowCells.filter((cell) => {
|
|
2935
|
-
const cellElement = cell;
|
|
2936
|
-
return parseInt(cellElement.dataset.rowIndex || '0', 10) === i;
|
|
2937
|
-
});
|
|
2938
|
-
if (cellsInRow.length > 0) {
|
|
2939
|
-
const firstCell = cellsInRow[0];
|
|
2940
|
-
heights[i] = firstCell.getBoundingClientRect().height;
|
|
2941
|
-
}
|
|
2942
|
-
}
|
|
2943
|
-
setRowHeights(heights);
|
|
2944
|
-
}
|
|
2945
|
-
}, [isDragging, dragState, scrollRef]);
|
|
2946
|
-
if (!isDragging || !dragState || !currentOffset || !scrollRef.current) {
|
|
2947
|
-
return null;
|
|
2948
|
-
}
|
|
2949
|
-
const tableContainer = scrollRef.current;
|
|
2950
|
-
const tableRect = tableContainer.getBoundingClientRect();
|
|
2951
|
-
if (dragState.type) {
|
|
2952
|
-
const sourceIndex = dragState.type === 'column' ? dragState.columnIndex : dragState.rowIndex;
|
|
2953
|
-
const rowHeight = dragState.type === 'column' ? tableRect.height : rowHeights[sourceIndex];
|
|
2954
|
-
const columnWidth = dragState.type === 'column' ? columnWidths[sourceIndex] : tableRect.width;
|
|
2955
|
-
return (React.createElement("div", { className: "qdr-table__drag-overlay", style: {
|
|
2956
|
-
left: tableRect.left,
|
|
2957
|
-
top: tableRect.top,
|
|
2958
|
-
width: columnWidth,
|
|
2959
|
-
height: rowHeight,
|
|
2960
|
-
transform: dragState.type === 'column'
|
|
2961
|
-
? `translateX(${currentOffset.x - tableRect.left - columnWidth / 2}px)`
|
|
2962
|
-
: `translateY(${currentOffset.y - tableRect.top - rowHeight / 2}px)`,
|
|
2963
|
-
} },
|
|
2964
|
-
React.createElement("div", { className: "qdr-table__drag-overlay-content" })));
|
|
2965
|
-
}
|
|
2966
|
-
return null;
|
|
2967
|
-
};
|
|
2968
|
-
|
|
2969
|
-
function TableMain(props) {
|
|
2970
|
-
const { attributes, children } = props;
|
|
2971
|
-
const { setConfirmModalConfig } = react.useModal();
|
|
2972
|
-
const editor = react.useSlateStatic();
|
|
2973
|
-
const { addColumn, addRow, addColumnAndRow } = useTableActionsContext();
|
|
2974
|
-
const { isReachMaximumColumns, isReachMaximumRows, tableElement } = useTableMetadata();
|
|
2975
|
-
const { tableSelectedOn, setTableSelectedOn } = useTableStateContext();
|
|
2976
|
-
const { dragState } = useTableDragContext();
|
|
2977
|
-
// Table align functions
|
|
2978
|
-
const setAlign = useTableCellAlign(tableElement, editor);
|
|
2979
|
-
const getAlign = useTableCellAlignStatus(tableElement, editor);
|
|
2980
|
-
const tablePath = react.ReactEditor.findPath(editor, tableElement);
|
|
2981
|
-
const scrollRef = React.useRef(null);
|
|
2982
|
-
const tableRef = React.useRef(null);
|
|
2983
|
-
const [scrollTop, setScrollTop] = React.useState(0);
|
|
2984
|
-
const [scrollLeft, setScrollLeft] = React.useState(0);
|
|
2985
|
-
const [tableWidth, setTableWidth] = React.useState(0);
|
|
2986
|
-
const scrollUpdateTimerRef = React.useRef(null);
|
|
2987
|
-
const isUpdatingScrollRef = React.useRef(false); // 標記是否正在更新滾動位置
|
|
2988
|
-
const previousColumnWidthsRef = React.useRef(''); // 追蹤 columnWidths 的變化
|
|
2989
|
-
// sizing
|
|
2990
|
-
const { tableBodyElement } = getTableElements(tableElement);
|
|
2991
|
-
const firstRowCells = tableBodyElement === null || tableBodyElement === void 0 ? void 0 : tableBodyElement.children[0].children;
|
|
2992
|
-
// 獲取欄位寬度(傳入 tableWidth 以支援混合模式)
|
|
2993
|
-
const columnWidths = React.useMemo(() => getColumnWidths(tableElement, tableWidth), [tableElement, tableWidth]);
|
|
2994
|
-
// 計算 table 的最小寬度
|
|
2995
|
-
const tableMinWidth = React.useMemo(() => table.calculateTableMinWidth(columnWidths), [columnWidths]);
|
|
2996
|
-
// 監聽 table 寬度變化
|
|
2997
|
-
React.useEffect(() => {
|
|
2998
|
-
const { current: table } = tableRef;
|
|
2999
|
-
if (!table)
|
|
3000
|
-
return;
|
|
3001
|
-
const resizeObserver = new ResizeObserver((entries) => {
|
|
3002
|
-
for (const entry of entries) {
|
|
3003
|
-
setTableWidth(entry.contentRect.width);
|
|
3004
|
-
}
|
|
3005
|
-
});
|
|
3006
|
-
resizeObserver.observe(table);
|
|
3007
|
-
// 初始化寬度
|
|
3008
|
-
setTableWidth(table.getBoundingClientRect().width);
|
|
3009
|
-
return () => {
|
|
3010
|
-
resizeObserver.disconnect();
|
|
3011
|
-
};
|
|
3012
|
-
}, []);
|
|
3013
|
-
React.useEffect(() => {
|
|
3014
|
-
const { current: scrollContainer } = scrollRef;
|
|
3015
|
-
if (!scrollContainer)
|
|
3016
|
-
return;
|
|
3017
|
-
const handleScroll = () => {
|
|
3018
|
-
setScrollTop(scrollContainer.scrollTop);
|
|
3019
|
-
setScrollLeft(scrollContainer.scrollLeft);
|
|
3020
|
-
// 如果正在更新滾動位置,不要觸發 Slate 更新
|
|
3021
|
-
if (isUpdatingScrollRef.current) {
|
|
3022
|
-
return;
|
|
3023
|
-
}
|
|
3024
|
-
// 如果正在拖曳,不要觸發 Slate 更新(避免 transform 導致事件遺失)
|
|
3025
|
-
if (dragState) {
|
|
3026
|
-
return;
|
|
3027
|
-
}
|
|
3028
|
-
// 使用 debounce 來減少 Slate 更新頻率
|
|
3029
|
-
if (scrollUpdateTimerRef.current) {
|
|
3030
|
-
clearTimeout(scrollUpdateTimerRef.current);
|
|
3031
|
-
}
|
|
3032
|
-
scrollUpdateTimerRef.current = setTimeout(() => {
|
|
3033
|
-
// 更新 tableElement 的 scrollPosition
|
|
3034
|
-
const tablePath = react.ReactEditor.findPath(editor, tableElement);
|
|
3035
|
-
slate.Transforms.setNodes(editor, {
|
|
3036
|
-
scrollPosition: {
|
|
3037
|
-
scrollLeft: scrollContainer.scrollLeft,
|
|
3038
|
-
scrollTop: scrollContainer.scrollTop,
|
|
3039
|
-
},
|
|
3040
|
-
}, { at: tablePath });
|
|
3041
|
-
}, 300); // 300ms debounce
|
|
3042
|
-
};
|
|
3043
|
-
scrollContainer.addEventListener('scroll', handleScroll, false);
|
|
3044
|
-
return () => {
|
|
3045
|
-
scrollContainer.removeEventListener('scroll', handleScroll, false);
|
|
3046
|
-
if (scrollUpdateTimerRef.current) {
|
|
3047
|
-
clearTimeout(scrollUpdateTimerRef.current);
|
|
3048
|
-
}
|
|
3049
|
-
};
|
|
3050
|
-
}, [editor, tableElement, dragState]);
|
|
3051
|
-
// 只在 columnWidths 改變時恢復滾動位置
|
|
3052
|
-
React.useEffect(() => {
|
|
3053
|
-
const { current: scrollContainer } = scrollRef;
|
|
3054
|
-
if (!scrollContainer || !tableElement.scrollPosition)
|
|
3055
|
-
return;
|
|
3056
|
-
// 檢查 columnWidths 是否真的改變了
|
|
3057
|
-
const currentColumnWidthsStr = JSON.stringify(columnWidths);
|
|
3058
|
-
if (previousColumnWidthsRef.current !== currentColumnWidthsStr) {
|
|
3059
|
-
previousColumnWidthsRef.current = currentColumnWidthsStr;
|
|
3060
|
-
// 標記正在更新,避免觸發 handleScroll
|
|
3061
|
-
isUpdatingScrollRef.current = true;
|
|
3062
|
-
// 使用 requestAnimationFrame 確保 DOM 已更新
|
|
3063
|
-
requestAnimationFrame(() => {
|
|
3064
|
-
var _a, _b, _c, _d;
|
|
3065
|
-
scrollContainer.scrollLeft = (_b = (_a = tableElement.scrollPosition) === null || _a === void 0 ? void 0 : _a.scrollLeft) !== null && _b !== void 0 ? _b : 0;
|
|
3066
|
-
scrollContainer.scrollTop = (_d = (_c = tableElement.scrollPosition) === null || _c === void 0 ? void 0 : _c.scrollTop) !== null && _d !== void 0 ? _d : 0;
|
|
3067
|
-
// 重置標記
|
|
3068
|
-
setTimeout(() => {
|
|
3069
|
-
isUpdatingScrollRef.current = false;
|
|
3070
|
-
}, 100);
|
|
3071
|
-
});
|
|
3072
|
-
}
|
|
3073
|
-
}, [columnWidths, tableElement.scrollPosition]);
|
|
3074
|
-
const scrollContextValue = React.useMemo(() => ({ scrollTop, scrollLeft, scrollRef }), [scrollTop, scrollLeft, scrollRef]);
|
|
3075
|
-
// 複製 Table 功能
|
|
3076
|
-
const copyTable = React.useCallback(() => {
|
|
3077
|
-
try {
|
|
3078
|
-
const clonedTable = JSON.parse(JSON.stringify(tableElement));
|
|
3079
|
-
// 找到當前 table 的父節點路徑
|
|
3080
|
-
const tableParentPath = tablePath.slice(0, -1);
|
|
3081
|
-
const tableIndex = tablePath[tablePath.length - 1];
|
|
3082
|
-
// 在當前 table 之後插入複製的 table
|
|
3083
|
-
slate.Transforms.insertNodes(editor, clonedTable, {
|
|
3084
|
-
at: [...tableParentPath, tableIndex + 1],
|
|
3085
|
-
});
|
|
3086
|
-
}
|
|
3087
|
-
catch (error) {
|
|
3088
|
-
console.error('Failed to copy table:', error);
|
|
3089
|
-
}
|
|
3090
|
-
}, [editor, tableElement, tablePath]);
|
|
3091
|
-
// 獲取當前 table 的 align 狀態
|
|
3092
|
-
const currentTableAlign = getAlign('table');
|
|
3093
|
-
// 根據當前 table align 狀態選擇對應的 icon
|
|
3094
|
-
const getCurrentTableAlignIcon = React.useCallback(() => {
|
|
3095
|
-
switch (currentTableAlign) {
|
|
3096
|
-
case 'left':
|
|
3097
|
-
return icons.AlignLeft;
|
|
3098
|
-
case 'center':
|
|
3099
|
-
return icons.AlignCenter;
|
|
3100
|
-
case 'right':
|
|
3101
|
-
return icons.AlignRight;
|
|
3102
|
-
default:
|
|
3103
|
-
return icons.AlignLeft;
|
|
3104
|
-
}
|
|
3105
|
-
}, [currentTableAlign]);
|
|
3106
|
-
return (React.createElement("div", { className: clsx('qdr-table__mainWrapper', {
|
|
3107
|
-
'qdr-table__mainWrapper--selected': (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'table',
|
|
3108
|
-
}) },
|
|
3109
|
-
React.createElement(toolbar.InlineToolbar, { className: "qdr-table__table-toolbar", onClickAway: (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'table' ? () => setTableSelectedOn(undefined) : undefined, iconGroups: [
|
|
3110
|
-
{
|
|
3111
|
-
icons: [
|
|
3112
|
-
{
|
|
3113
|
-
icon: icons.Copy,
|
|
3114
|
-
onClick: () => {
|
|
3115
|
-
copyTable();
|
|
3116
|
-
},
|
|
3117
|
-
},
|
|
3118
|
-
React.createElement(toolbar.ToolbarGroupIcon, { key: "table-align-change", icon: getCurrentTableAlignIcon() },
|
|
3119
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.AlignLeft, onClick: () => {
|
|
3120
|
-
setAlign('left', 'table');
|
|
3121
|
-
} }),
|
|
3122
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.AlignCenter, onClick: () => {
|
|
3123
|
-
setAlign('center', 'table');
|
|
3124
|
-
} }),
|
|
3125
|
-
React.createElement(toolbar.ToolbarIcon, { icon: icons.AlignRight, onClick: () => {
|
|
3126
|
-
setAlign('right', 'table');
|
|
3127
|
-
} })),
|
|
3128
|
-
],
|
|
3129
|
-
},
|
|
3130
|
-
{
|
|
3131
|
-
icons: [
|
|
3132
|
-
{
|
|
3133
|
-
icon: icons.Trash,
|
|
3134
|
-
className: 'qdr-table__delete',
|
|
3135
|
-
onClick: () => {
|
|
3136
|
-
setConfirmModalConfig({
|
|
3137
|
-
title: '刪除表格',
|
|
3138
|
-
content: '是否確認刪除此表格?刪除後將立即移除,且此操作無法復原。',
|
|
3139
|
-
confirmText: '刪除表格',
|
|
3140
|
-
onConfirm: () => {
|
|
3141
|
-
slate.Transforms.removeNodes(editor, { at: tablePath });
|
|
3142
|
-
},
|
|
3143
|
-
});
|
|
3144
|
-
},
|
|
3145
|
-
},
|
|
3146
|
-
],
|
|
3147
|
-
},
|
|
3148
|
-
] }),
|
|
3149
|
-
React.createElement("div", { ref: scrollRef, className: "qdr-table__scrollContainer" },
|
|
3150
|
-
React.createElement(TableScrollContext.Provider, { value: scrollContextValue },
|
|
3151
|
-
React.createElement("table", Object.assign({}, attributes, { ref: (node) => {
|
|
3152
|
-
// 合併兩個 refs
|
|
3153
|
-
tableRef.current = node;
|
|
3154
|
-
if (typeof attributes.ref === 'function') {
|
|
3155
|
-
attributes.ref(node);
|
|
3156
|
-
}
|
|
3157
|
-
else if (attributes.ref) {
|
|
3158
|
-
attributes.ref.current = node;
|
|
3159
|
-
}
|
|
3160
|
-
}, className: "qdr-table__main", style: {
|
|
3161
|
-
minWidth: tableMinWidth,
|
|
3162
|
-
} }),
|
|
3163
|
-
React.createElement("colgroup", null, columnWidths.map((width, index) => (React.createElement("col", { key: index, style: {
|
|
3164
|
-
width: table.columnWidthToCSS(width),
|
|
3165
|
-
minWidth: table.columnWidthToCSS(width),
|
|
3166
|
-
} })))),
|
|
3167
|
-
children)),
|
|
3168
|
-
React.createElement(TableDragLayer, { scrollRef: scrollRef })),
|
|
3169
|
-
React.createElement("div", { className: "qdr-table__size-indicators" }, firstRowCells === null || firstRowCells === void 0 ? void 0 : firstRowCells.map((cell, colIndex) => (React.createElement("div", { key: colIndex, className: "qdr-table__size-indicator", style: {
|
|
3170
|
-
width: table.columnWidthToCSS(columnWidths[colIndex]),
|
|
3171
|
-
minWidth: table.columnWidthToCSS(columnWidths[colIndex]),
|
|
3172
|
-
transform: cell.pinned ? 'none' : `translateX(-${scrollLeft}px)`,
|
|
3173
|
-
zIndex: cell.pinned ? 2 : 1,
|
|
3174
|
-
} },
|
|
3175
|
-
React.createElement("div", { className: "qdr-table__size" }, table.columnWidthToCSS(columnWidths[colIndex])))))),
|
|
3176
|
-
isReachMaximumColumns ? null : (React.createElement("button", { type: "button", onClick: () => addColumn(), title: "Add Column", className: "qdr-table__add-column" },
|
|
3177
|
-
React.createElement(components.Icon, { icon: icons.Plus, width: 20, height: 20, className: "qdr-table__btn-icon" }))),
|
|
3178
|
-
isReachMaximumRows ? null : (React.createElement("button", { type: "button", onClick: () => addRow(), title: "Add Row", className: "qdr-table__add-row" },
|
|
3179
|
-
React.createElement(components.Icon, { icon: icons.Plus, width: 20, height: 20, className: "qdr-table__btn-icon" }))),
|
|
3180
|
-
isReachMaximumColumns || isReachMaximumRows ? null : (React.createElement("button", { type: "button", onClick: addColumnAndRow, title: "Add Column and Row", className: "qdr-table__add-both" },
|
|
3181
|
-
React.createElement(components.Icon, { icon: icons.Plus, width: 20, height: 20, className: "qdr-table__btn-icon" })))));
|
|
3182
|
-
}
|
|
3183
|
-
|
|
3184
|
-
const TableHeaderContext = React.createContext({
|
|
3185
|
-
isHeader: false,
|
|
3186
|
-
});
|
|
3187
|
-
|
|
3188
|
-
function TableHeader(props) {
|
|
3189
|
-
const { attributes, children, element } = props;
|
|
3190
|
-
const tableHeaderContextValue = React.useMemo(() => ({ isHeader: true }), []);
|
|
3191
|
-
const hasAnyRowPinned = element.children.some((child) => child.children.every((cell) => cell.pinned));
|
|
3192
|
-
return (React.createElement(TableHeaderContext.Provider, { value: tableHeaderContextValue },
|
|
3193
|
-
React.createElement("thead", Object.assign({}, attributes, { className: clsx('qdr-table__header', { 'qdr-table__header--pinned': hasAnyRowPinned }) }), children)));
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
function TableRow(props) {
|
|
3197
|
-
const { attributes, children } = props;
|
|
3198
|
-
return React.createElement("tr", Object.assign({}, attributes), children);
|
|
3199
|
-
}
|
|
3200
|
-
|
|
3201
|
-
function useColumnResize({ tableElement, columnIndex, cellRef }) {
|
|
3202
|
-
const editor = slateReact.useSlateStatic();
|
|
3203
|
-
const { setTableSelectedOn } = useTableStateContext();
|
|
3204
|
-
const [isResizing, setIsResizing] = React.useState(false);
|
|
3205
|
-
const resizeDataRef = React.useRef(null);
|
|
3206
|
-
const currentWidthsRef = React.useRef(null);
|
|
3207
|
-
const handleResizeStart = React.useCallback((e) => {
|
|
3208
|
-
e.preventDefault();
|
|
3209
|
-
e.stopPropagation();
|
|
3210
|
-
// 清除當前的 focus 狀態和 table selection
|
|
3211
|
-
core.Transforms.deselect(editor);
|
|
3212
|
-
setTableSelectedOn(undefined);
|
|
3213
|
-
const cell = cellRef.current;
|
|
3214
|
-
if (!cell)
|
|
3215
|
-
return;
|
|
3216
|
-
// 找到 table DOM 元素和 scroll container
|
|
3217
|
-
const tableDOMElement = cell.closest('table');
|
|
3218
|
-
const scrollContainer = tableDOMElement === null || tableDOMElement === void 0 ? void 0 : tableDOMElement.closest('.qdr-table__scrollContainer');
|
|
3219
|
-
if (!tableDOMElement || !scrollContainer)
|
|
3220
|
-
return;
|
|
3221
|
-
const tableRect = tableDOMElement.getBoundingClientRect();
|
|
3222
|
-
const containerRect = scrollContainer.getBoundingClientRect();
|
|
3223
|
-
// 獲取釘選欄位資訊
|
|
3224
|
-
const { pinnedColumnIndices } = getPinnedColumnsInfo(tableElement);
|
|
3225
|
-
// 儲存初始狀態
|
|
3226
|
-
resizeDataRef.current = {
|
|
3227
|
-
startX: e.clientX,
|
|
3228
|
-
startWidths: getColumnWidths(tableElement, tableRect.width),
|
|
3229
|
-
tableWidth: tableRect.width,
|
|
3230
|
-
containerWidth: containerRect.width,
|
|
3231
|
-
pinnedColumnIndices,
|
|
3232
|
-
tableDOMElement,
|
|
3233
|
-
};
|
|
3234
|
-
setIsResizing(true);
|
|
3235
|
-
// 顯示 size indicators 容器
|
|
3236
|
-
const mainWrapper = tableDOMElement.closest('.qdr-table__mainWrapper');
|
|
3237
|
-
const sizeIndicatorsContainer = mainWrapper === null || mainWrapper === void 0 ? void 0 : mainWrapper.querySelector('.qdr-table__size-indicators');
|
|
3238
|
-
if (sizeIndicatorsContainer) {
|
|
3239
|
-
sizeIndicatorsContainer.style.display = 'flex';
|
|
3240
|
-
}
|
|
3241
|
-
// 為當前 column 的所有 cell 添加 resizing class
|
|
3242
|
-
const allRows = tableDOMElement.querySelectorAll('tr');
|
|
3243
|
-
allRows.forEach((row) => {
|
|
3244
|
-
const cells = row.querySelectorAll('td, th');
|
|
3245
|
-
const targetCell = cells[columnIndex];
|
|
3246
|
-
if (targetCell) {
|
|
3247
|
-
targetCell.classList.add('qdr-table__cell--resizing');
|
|
3248
|
-
}
|
|
3249
|
-
});
|
|
3250
|
-
const handleMouseMove = (moveEvent) => {
|
|
3251
|
-
if (!resizeDataRef.current)
|
|
3252
|
-
return;
|
|
3253
|
-
const { startX, startWidths, tableWidth, pinnedColumnIndices, tableDOMElement } = resizeDataRef.current;
|
|
3254
|
-
const deltaX = moveEvent.clientX - startX;
|
|
3255
|
-
// 將位移轉換為百分比
|
|
3256
|
-
const deltaPercentage = (deltaX / tableWidth) * 100;
|
|
3257
|
-
// 計算新的欄位寬度
|
|
3258
|
-
const newWidths = calculateResizedColumnWidths(startWidths, columnIndex, deltaPercentage, deltaX, pinnedColumnIndices);
|
|
3259
|
-
// 儲存計算結果,但不更新 Slate
|
|
3260
|
-
currentWidthsRef.current = newWidths;
|
|
3261
|
-
// 更新 table 的最小寬度
|
|
3262
|
-
tableDOMElement.style.minWidth = table.calculateTableMinWidth(newWidths);
|
|
3263
|
-
// 直接更新 DOM 的 <col> 元素
|
|
3264
|
-
const colgroup = tableDOMElement.querySelector('colgroup');
|
|
3265
|
-
if (colgroup) {
|
|
3266
|
-
const cols = colgroup.querySelectorAll('col');
|
|
3267
|
-
newWidths.forEach((width, index) => {
|
|
3268
|
-
const col = cols[index];
|
|
3269
|
-
if (col) {
|
|
3270
|
-
const cssWidth = width.type === 'percentage' ? `${width.value.toFixed(1)}%` : `${width.value}px`;
|
|
3271
|
-
col.style.width = cssWidth;
|
|
3272
|
-
col.style.minWidth = cssWidth;
|
|
3273
|
-
}
|
|
3274
|
-
});
|
|
3275
|
-
}
|
|
3276
|
-
// 如果有 pinned columns,重新計算所有 pinned cells 的 left 位置
|
|
3277
|
-
if (pinnedColumnIndices.length > 0) {
|
|
3278
|
-
const allRows = tableDOMElement.querySelectorAll('tr');
|
|
3279
|
-
const scrollContainer = tableDOMElement.closest('.qdr-table__scrollContainer');
|
|
3280
|
-
const containerRect = scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.getBoundingClientRect();
|
|
3281
|
-
const containerWidth = (containerRect === null || containerRect === void 0 ? void 0 : containerRect.width) || tableWidth;
|
|
3282
|
-
// 為每個 pinned column 計算應該的絕對位置
|
|
3283
|
-
pinnedColumnIndices.forEach((pinnedIndex) => {
|
|
3284
|
-
// 計算此 column 之前所有 pinned columns 的累積寬度
|
|
3285
|
-
let accumulatedLeft = 0;
|
|
3286
|
-
for (let i = 0; i < pinnedIndex; i++) {
|
|
3287
|
-
if (pinnedColumnIndices.includes(i)) {
|
|
3288
|
-
const width = newWidths[i];
|
|
3289
|
-
if (width.type === 'percentage') {
|
|
3290
|
-
const pixelWidth = (containerWidth * width.value) / 100;
|
|
3291
|
-
accumulatedLeft += pixelWidth;
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
}
|
|
3295
|
-
// 更新該 column 所有 cells 的 left 位置
|
|
3296
|
-
allRows.forEach((row) => {
|
|
3297
|
-
const cells = row.querySelectorAll('td, th');
|
|
3298
|
-
const targetCell = cells[pinnedIndex];
|
|
3299
|
-
if (targetCell && targetCell.classList.contains('qdr-table__cell--pinned')) {
|
|
3300
|
-
targetCell.style.left = `${accumulatedLeft}px`;
|
|
3301
|
-
}
|
|
3302
|
-
});
|
|
3303
|
-
});
|
|
3304
|
-
}
|
|
3305
|
-
// 更新 size indicators
|
|
3306
|
-
const sizeIndicatorsContainer = mainWrapper === null || mainWrapper === void 0 ? void 0 : mainWrapper.querySelector('.qdr-table__size-indicators');
|
|
3307
|
-
if (sizeIndicatorsContainer) {
|
|
3308
|
-
const indicators = sizeIndicatorsContainer.querySelectorAll('.qdr-table__size-indicator');
|
|
3309
|
-
newWidths.forEach((width, index) => {
|
|
3310
|
-
const indicator = indicators[index];
|
|
3311
|
-
if (indicator) {
|
|
3312
|
-
const cssWidth = width.type === 'percentage' ? `${width.value.toFixed(1)}%` : `${width.value}px`;
|
|
3313
|
-
const displayWidth = width.type === 'percentage' ? `${width.value.toFixed(1)}%` : `${width.value}px`;
|
|
3314
|
-
indicator.style.width = cssWidth;
|
|
3315
|
-
indicator.style.minWidth = cssWidth;
|
|
3316
|
-
// 更新顯示文字
|
|
3317
|
-
const sizeText = indicator.querySelector('.qdr-table__size');
|
|
3318
|
-
if (sizeText) {
|
|
3319
|
-
sizeText.textContent = displayWidth;
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
});
|
|
3323
|
-
}
|
|
3324
|
-
};
|
|
3325
|
-
const handleMouseUp = () => {
|
|
3326
|
-
const allRows = tableDOMElement.querySelectorAll('tr');
|
|
3327
|
-
allRows.forEach((row) => {
|
|
3328
|
-
const cells = row.querySelectorAll('td, th');
|
|
3329
|
-
const targetCell = cells[columnIndex];
|
|
3330
|
-
if (targetCell) {
|
|
3331
|
-
targetCell.classList.remove('qdr-table__cell--resizing');
|
|
3332
|
-
}
|
|
3333
|
-
});
|
|
3334
|
-
// 隱藏 size indicators 容器
|
|
3335
|
-
const mainWrapper = tableDOMElement.closest('.qdr-table__mainWrapper');
|
|
3336
|
-
const sizeIndicatorsContainer = mainWrapper === null || mainWrapper === void 0 ? void 0 : mainWrapper.querySelector('.qdr-table__size-indicators');
|
|
3337
|
-
if (sizeIndicatorsContainer) {
|
|
3338
|
-
sizeIndicatorsContainer.style.display = 'none';
|
|
3339
|
-
}
|
|
3340
|
-
// 將最終寬度寫入 Slate
|
|
3341
|
-
if (currentWidthsRef.current) {
|
|
3342
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
3343
|
-
setColumnWidths(editor, tableElement, currentWidthsRef.current);
|
|
3344
|
-
});
|
|
3345
|
-
}
|
|
3346
|
-
setIsResizing(false);
|
|
3347
|
-
resizeDataRef.current = null;
|
|
3348
|
-
currentWidthsRef.current = null;
|
|
3349
|
-
document.removeEventListener('mousemove', handleMouseMove);
|
|
3350
|
-
document.removeEventListener('mouseup', handleMouseUp);
|
|
3351
|
-
};
|
|
3352
|
-
document.addEventListener('mousemove', handleMouseMove);
|
|
3353
|
-
document.addEventListener('mouseup', handleMouseUp);
|
|
3354
|
-
}, [editor, columnIndex, cellRef, tableElement, setTableSelectedOn]);
|
|
3355
|
-
return {
|
|
3356
|
-
isResizing,
|
|
3357
|
-
handleResizeStart,
|
|
3358
|
-
};
|
|
3359
|
-
}
|
|
3360
|
-
|
|
3361
|
-
const ROW_DRAG_TYPE = 'TABLE_ROW';
|
|
3362
|
-
const RowDragButton = ({ rowIndex, headerRowCount, style, onClick }) => {
|
|
3363
|
-
const buttonRef = React.useRef(null);
|
|
3364
|
-
const { setDragState, setDropTargetIndex, setDragDirection } = useTableDragContext();
|
|
3365
|
-
// 判斷當前 row 是否在 header 中
|
|
3366
|
-
const isInHeader = rowIndex < headerRowCount;
|
|
3367
|
-
const [{ isDragging }, drag, preview] = reactDnd.useDrag(() => ({
|
|
3368
|
-
type: ROW_DRAG_TYPE,
|
|
3369
|
-
item: () => {
|
|
3370
|
-
const dragItem = { rowIndex, isInHeader };
|
|
3371
|
-
setDragState({ type: 'row', rowIndex, isInHeader });
|
|
3372
|
-
return dragItem;
|
|
3373
|
-
},
|
|
3374
|
-
collect: (monitor) => ({
|
|
3375
|
-
isDragging: monitor.isDragging(),
|
|
3376
|
-
}),
|
|
3377
|
-
end: () => {
|
|
3378
|
-
setDragState(null);
|
|
3379
|
-
setDropTargetIndex(null);
|
|
3380
|
-
setDragDirection(null);
|
|
3381
|
-
},
|
|
3382
|
-
}), [rowIndex, isInHeader, setDragState, setDropTargetIndex, setDragDirection]);
|
|
3383
|
-
// 使用空白圖片作為 drag preview,避免顯示預設的拖曳圖像
|
|
3384
|
-
React.useEffect(() => {
|
|
3385
|
-
preview(reactDndHtml5Backend.getEmptyImage(), { captureDraggingState: true });
|
|
3386
|
-
}, [preview]);
|
|
3387
|
-
drag(buttonRef);
|
|
3388
|
-
return (React.createElement("button", { ref: buttonRef, type: "button", contentEditable: false, style: style, onClick: onClick, title: "\u9EDE\u64CA\u958B\u555F\u9078\u55AE\uFF0C\u62D6\u66F3\u4EE5\u79FB\u52D5", className: clsx('qdr-table__cell-row-action', {
|
|
3389
|
-
'qdr-table__cell-row-action--dragging': isDragging,
|
|
3390
|
-
}) },
|
|
3391
|
-
React.createElement(components.Icon, { icon: icons.Drag, width: 20, height: 20 })));
|
|
3392
|
-
};
|
|
3393
|
-
|
|
3394
|
-
const COLUMN_DRAG_TYPE = 'TABLE_COLUMN';
|
|
3395
|
-
const ColumnDragButton = ({ columnIndex, style, onClick, checkIsTitleColumn, }) => {
|
|
3396
|
-
const buttonRef = React.useRef(null);
|
|
3397
|
-
const { setDragState, setDropTargetIndex, setDragDirection } = useTableDragContext();
|
|
3398
|
-
const isTitle = checkIsTitleColumn(columnIndex);
|
|
3399
|
-
const [{ isDragging }, drag, preview] = reactDnd.useDrag(() => ({
|
|
3400
|
-
type: COLUMN_DRAG_TYPE,
|
|
3401
|
-
item: () => {
|
|
3402
|
-
const dragItem = { columnIndex, isTitle };
|
|
3403
|
-
setDragState({ type: 'column', columnIndex, isTitle });
|
|
3404
|
-
return dragItem;
|
|
3405
|
-
},
|
|
3406
|
-
collect: (monitor) => ({
|
|
3407
|
-
isDragging: monitor.isDragging(),
|
|
3408
|
-
}),
|
|
3409
|
-
end: () => {
|
|
3410
|
-
setDragState(null);
|
|
3411
|
-
setDropTargetIndex(null);
|
|
3412
|
-
setDragDirection(null);
|
|
3413
|
-
},
|
|
3414
|
-
}), [columnIndex, isTitle, setDragState, setDropTargetIndex, setDragDirection]);
|
|
3415
|
-
// 使用空白圖片作為 drag preview,避免顯示預設的拖曳圖像
|
|
3416
|
-
React.useEffect(() => {
|
|
3417
|
-
preview(reactDndHtml5Backend.getEmptyImage(), { captureDraggingState: true });
|
|
3418
|
-
}, [preview]);
|
|
3419
|
-
drag(buttonRef);
|
|
3420
|
-
return (React.createElement("button", { ref: buttonRef, type: "button", contentEditable: false, style: style, onClick: onClick, title: "\u9EDE\u64CA\u958B\u555F\u9078\u55AE\uFF0C\u62D6\u66F3\u4EE5\u79FB\u52D5", className: clsx('qdr-table__cell-column-action', {
|
|
3421
|
-
'qdr-table__cell-column-action--dragging': isDragging,
|
|
3422
|
-
}) },
|
|
3423
|
-
React.createElement(components.Icon, { icon: icons.Drag, width: 20, height: 20 })));
|
|
3424
|
-
};
|
|
3425
|
-
|
|
3426
|
-
function TableCell(props) {
|
|
3427
|
-
var _a, _b;
|
|
3428
|
-
const { attributes, children, element } = props;
|
|
3429
|
-
const { tableSelectedOn, setTableSelectedOn, tableHoveredOn, setTableHoveredOn } = useTableStateContext();
|
|
3430
|
-
const { columnCount, rowCount, portalContainerRef, isColumnPinned, tableElement } = useTableMetadata();
|
|
3431
|
-
const { moveOrSwapRow, moveOrSwapColumn } = useTableActionsContext();
|
|
3432
|
-
const { dragState, dropTargetIndex, setDropTargetIndex, dragDirection, setDragDirection } = useTableDragContext();
|
|
3433
|
-
// Component context
|
|
3434
|
-
const { isHeader } = React.useContext(TableHeaderContext);
|
|
3435
|
-
const { scrollTop, scrollLeft, scrollRef } = React.useContext(TableScrollContext);
|
|
3436
|
-
const editor = slateReact.useSlateStatic();
|
|
3437
|
-
// Cell-specific hooks
|
|
3438
|
-
const focused = useTableCellFocused(element, editor);
|
|
3439
|
-
const cellPosition = useTableCellPosition(element);
|
|
3440
|
-
const transformCellContent = useTableCellTransformContent(element, editor);
|
|
3441
|
-
// Get header row count from table structure
|
|
3442
|
-
const tableElements = getTableElements(tableElement);
|
|
3443
|
-
const headerRowCount = ((_a = tableElements.tableHeaderElement) === null || _a === void 0 ? void 0 : _a.children.length) || 0;
|
|
3444
|
-
// Helper to check if column is title column
|
|
3445
|
-
const checkIsTitleColumn = React.useCallback((colIndex) => {
|
|
3446
|
-
const elements = getTableElements(tableElement);
|
|
3447
|
-
if (!elements.tableBodyElement)
|
|
3448
|
-
return false;
|
|
3449
|
-
const firstRow = elements.tableBodyElement.children[0];
|
|
3450
|
-
if (!core.Element.isElement(firstRow))
|
|
3451
|
-
return false;
|
|
3452
|
-
const cell = firstRow.children[colIndex];
|
|
3453
|
-
return core.Element.isElement(cell) && !!cell.treatAsTitle;
|
|
3454
|
-
}, [tableElement]);
|
|
3455
|
-
// Toolbar actions
|
|
3456
|
-
const { focusToolbarIconGroups, inlineToolbarIconGroups } = useTableCellToolbarActions({
|
|
3457
|
-
element,
|
|
3458
|
-
cellPosition,
|
|
3459
|
-
isHeader,
|
|
3460
|
-
transformCellContent,
|
|
3461
|
-
});
|
|
3462
|
-
const isSelectedInSameRow = (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'row' && (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.index) === cellPosition.rowIndex;
|
|
3463
|
-
const isSelectedInSameColumn = (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.region) === 'column' && (tableSelectedOn === null || tableSelectedOn === void 0 ? void 0 : tableSelectedOn.index) === cellPosition.columnIndex;
|
|
3464
|
-
// 判斷是否為正在被拖曳的 row/column
|
|
3465
|
-
const isDraggingThisRow = (dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'row' && dragState.rowIndex === cellPosition.rowIndex;
|
|
3466
|
-
const isDraggingThisColumn = (dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'column' && dragState.columnIndex === cellPosition.columnIndex;
|
|
3467
|
-
const isSelectionTriggerByMe = (isSelectedInSameRow && cellPosition.columnIndex === 0) || (isSelectedInSameColumn && cellPosition.rowIndex === 0);
|
|
3468
|
-
const showColumnActionButton = React.useMemo(() => cellPosition.rowIndex === 0 &&
|
|
3469
|
-
(isSelectedInSameColumn || ((tableHoveredOn === null || tableHoveredOn === void 0 ? void 0 : tableHoveredOn.columnIndex) === cellPosition.columnIndex && !tableSelectedOn)), [cellPosition, isSelectedInSameColumn, tableHoveredOn, tableSelectedOn]);
|
|
3470
|
-
const showRowActionButton = React.useMemo(() => cellPosition.columnIndex === 0 &&
|
|
3471
|
-
(isSelectedInSameRow || ((tableHoveredOn === null || tableHoveredOn === void 0 ? void 0 : tableHoveredOn.rowIndex) === cellPosition.rowIndex && !tableSelectedOn)), [cellPosition, isSelectedInSameRow, tableHoveredOn, tableSelectedOn]);
|
|
3472
|
-
// 用於定位 InlineToolbar 的 ref
|
|
3473
|
-
const cellRef = React.useRef(null);
|
|
3474
|
-
const [toolbarPosition, setToolbarPosition] = React.useState(null);
|
|
3475
|
-
const [rowButtonPosition, setRowButtonPosition] = React.useState(null);
|
|
3476
|
-
const [columnButtonPosition, setColumnButtonPosition] = React.useState(null);
|
|
3477
|
-
const [cellStuckAtLeft, setCellStuckAtLeft] = React.useState(undefined);
|
|
3478
|
-
const [, dropRow] = reactDnd.useDrop(() => ({
|
|
3479
|
-
accept: ROW_DRAG_TYPE,
|
|
3480
|
-
canDrop: (item) => {
|
|
3481
|
-
if (!dragState || dragState.type !== 'row')
|
|
3482
|
-
return false;
|
|
3483
|
-
if (item.rowIndex === cellPosition.rowIndex)
|
|
3484
|
-
return false;
|
|
3485
|
-
const sourceInHeader = item.isInHeader;
|
|
3486
|
-
const targetInHeader = cellPosition.rowIndex < headerRowCount;
|
|
3487
|
-
return sourceInHeader === targetInHeader;
|
|
3488
|
-
},
|
|
3489
|
-
hover: (item, monitor) => {
|
|
3490
|
-
if (!monitor.canDrop()) {
|
|
3491
|
-
setDropTargetIndex(null);
|
|
3492
|
-
setDragDirection(null);
|
|
3493
|
-
return;
|
|
3494
|
-
}
|
|
3495
|
-
const direction = item.rowIndex < cellPosition.rowIndex ? 'down' : 'up';
|
|
3496
|
-
setDropTargetIndex(cellPosition.rowIndex);
|
|
3497
|
-
setDragDirection(direction);
|
|
3498
|
-
},
|
|
3499
|
-
drop: (item) => {
|
|
3500
|
-
moveOrSwapRow(item.rowIndex, cellPosition.rowIndex, 'move');
|
|
3501
|
-
setDropTargetIndex(null);
|
|
3502
|
-
setDragDirection(null);
|
|
3503
|
-
},
|
|
3504
|
-
}), [dragState, cellPosition.rowIndex, headerRowCount, moveOrSwapRow, setDropTargetIndex, setDragDirection]);
|
|
3505
|
-
// Drop target logic for columns
|
|
3506
|
-
const [, dropColumn] = reactDnd.useDrop(() => ({
|
|
3507
|
-
accept: COLUMN_DRAG_TYPE,
|
|
3508
|
-
canDrop: (item) => {
|
|
3509
|
-
if (!dragState || dragState.type !== 'column')
|
|
3510
|
-
return false;
|
|
3511
|
-
if (item.columnIndex === cellPosition.columnIndex)
|
|
3512
|
-
return false;
|
|
3513
|
-
const sourceIsTitle = item.isTitle;
|
|
3514
|
-
const targetIsTitle = checkIsTitleColumn(cellPosition.columnIndex);
|
|
3515
|
-
return sourceIsTitle === targetIsTitle;
|
|
3516
|
-
},
|
|
3517
|
-
hover: (item, monitor) => {
|
|
3518
|
-
if (!monitor.canDrop()) {
|
|
3519
|
-
setDropTargetIndex(null);
|
|
3520
|
-
setDragDirection(null);
|
|
3521
|
-
return;
|
|
3522
|
-
}
|
|
3523
|
-
// 計算拖曳方向
|
|
3524
|
-
const direction = item.columnIndex < cellPosition.columnIndex ? 'right' : 'left';
|
|
3525
|
-
setDropTargetIndex(cellPosition.columnIndex);
|
|
3526
|
-
setDragDirection(direction);
|
|
3527
|
-
},
|
|
3528
|
-
drop: (item) => {
|
|
3529
|
-
moveOrSwapColumn(item.columnIndex, cellPosition.columnIndex, 'move');
|
|
3530
|
-
setDropTargetIndex(null);
|
|
3531
|
-
setDragDirection(null);
|
|
3532
|
-
},
|
|
3533
|
-
}), [dragState, cellPosition.columnIndex, checkIsTitleColumn, moveOrSwapColumn, setDropTargetIndex, setDragDirection]);
|
|
3534
|
-
// Combine refs
|
|
3535
|
-
dropRow(dropColumn(cellRef));
|
|
3536
|
-
// Column resize
|
|
3537
|
-
const { isResizing, handleResizeStart } = useColumnResize({
|
|
3538
|
-
tableElement,
|
|
3539
|
-
columnIndex: cellPosition.columnIndex,
|
|
3540
|
-
cellRef,
|
|
3541
|
-
});
|
|
3542
|
-
React.useEffect(() => {
|
|
3543
|
-
const { current: cell } = cellRef;
|
|
3544
|
-
const { current: portalContainer } = portalContainerRef;
|
|
3545
|
-
if (!cell || !portalContainer) {
|
|
3546
|
-
setToolbarPosition(null);
|
|
3547
|
-
setRowButtonPosition(null);
|
|
3548
|
-
setColumnButtonPosition(null);
|
|
3549
|
-
return;
|
|
3550
|
-
}
|
|
3551
|
-
const rafId = requestAnimationFrame(() => {
|
|
3552
|
-
const cellRect = cell.getBoundingClientRect();
|
|
3553
|
-
const portalContainerRect = portalContainer.getBoundingClientRect();
|
|
3554
|
-
// 工具列位置 (針對 focused 狀態)
|
|
3555
|
-
if (focused || isSelectionTriggerByMe) {
|
|
3556
|
-
setToolbarPosition({
|
|
3557
|
-
top: cellRect.top - portalContainerRect.top - 4, // -4px offset
|
|
3558
|
-
left: cellRect.left - portalContainerRect.left,
|
|
3559
|
-
});
|
|
3560
|
-
}
|
|
3561
|
-
else {
|
|
3562
|
-
setToolbarPosition(null);
|
|
3563
|
-
}
|
|
3564
|
-
// 行按鈕位置 (顯示在第一列)
|
|
3565
|
-
if (cellPosition.columnIndex === 0) {
|
|
3566
|
-
setRowButtonPosition({
|
|
3567
|
-
top: Math.min(cellRect.top - portalContainerRect.top + cellRect.height / 2 - 10, 0), // 置中,按鈕高度約 20px
|
|
3568
|
-
left: cellRect.left - portalContainerRect.left - 10, // 向左偏移 10px
|
|
3569
|
-
});
|
|
3570
|
-
}
|
|
3571
|
-
else {
|
|
3572
|
-
setRowButtonPosition(null);
|
|
3573
|
-
}
|
|
3574
|
-
// 列按鈕位置 (顯示在第一行)
|
|
3575
|
-
if (cellPosition.rowIndex === 0) {
|
|
3576
|
-
setColumnButtonPosition({
|
|
3577
|
-
top: cellRect.top - portalContainerRect.top - 10 + scrollTop, // 向上偏移 10px
|
|
3578
|
-
left: cellRect.left - portalContainerRect.left + cellRect.width / 2 - 10, // 置中,按鈕寬度約 20px
|
|
3579
|
-
});
|
|
3580
|
-
}
|
|
3581
|
-
else {
|
|
3582
|
-
setColumnButtonPosition(null);
|
|
3583
|
-
}
|
|
3584
|
-
});
|
|
3585
|
-
return () => {
|
|
3586
|
-
cancelAnimationFrame(rafId);
|
|
3587
|
-
};
|
|
3588
|
-
}, [
|
|
3589
|
-
focused,
|
|
3590
|
-
isSelectionTriggerByMe,
|
|
3591
|
-
cellPosition.columnIndex,
|
|
3592
|
-
cellPosition.rowIndex,
|
|
3593
|
-
portalContainerRef,
|
|
3594
|
-
scrollTop,
|
|
3595
|
-
scrollLeft,
|
|
3596
|
-
]);
|
|
3597
|
-
React.useEffect(() => {
|
|
3598
|
-
const { current: cell } = cellRef;
|
|
3599
|
-
const { current: scrollContainer } = scrollRef;
|
|
3600
|
-
if (scrollContainer && cell) {
|
|
3601
|
-
const cellRect = cell.getBoundingClientRect();
|
|
3602
|
-
const containerRect = scrollContainer.getBoundingClientRect();
|
|
3603
|
-
setCellStuckAtLeft(Math.max(Math.round(cellRect.left - containerRect.left), 0));
|
|
3604
|
-
}
|
|
3605
|
-
}, [scrollRef]);
|
|
3606
|
-
const TagName = isHeader ? 'th' : 'td';
|
|
3607
|
-
const myColumnIsPinned = isColumnPinned(cellPosition.columnIndex) && element.treatAsTitle;
|
|
3608
|
-
return (React.createElement(TagName, Object.assign({}, attributes, { ref: cellRef, onMouseEnter: () => {
|
|
3609
|
-
setTableHoveredOn({ columnIndex: cellPosition.columnIndex, rowIndex: cellPosition.rowIndex });
|
|
3610
|
-
}, onMouseLeave: () => {
|
|
3611
|
-
setTableHoveredOn(undefined);
|
|
3612
|
-
}, className: clsx('qdr-table__cell', {
|
|
3613
|
-
'qdr-table__cell--header': isHeader || element.treatAsTitle,
|
|
3614
|
-
'qdr-table__cell--pinned': myColumnIsPinned,
|
|
3615
|
-
'qdr-table__cell--top-active': isSelectedInSameRow ||
|
|
3616
|
-
(isSelectedInSameColumn && cellPosition.rowIndex === 0) ||
|
|
3617
|
-
isDraggingThisRow ||
|
|
3618
|
-
(isDraggingThisColumn && cellPosition.rowIndex === 0),
|
|
3619
|
-
'qdr-table__cell--right-active': isSelectedInSameColumn ||
|
|
3620
|
-
(isSelectedInSameRow && cellPosition.columnIndex === columnCount - 1) ||
|
|
3621
|
-
isDraggingThisColumn ||
|
|
3622
|
-
(isDraggingThisRow && cellPosition.columnIndex === columnCount - 1),
|
|
3623
|
-
'qdr-table__cell--bottom-active': isSelectedInSameRow ||
|
|
3624
|
-
(isSelectedInSameColumn && cellPosition.rowIndex === rowCount - 1) ||
|
|
3625
|
-
isDraggingThisRow ||
|
|
3626
|
-
(isDraggingThisColumn && cellPosition.rowIndex === rowCount - 1),
|
|
3627
|
-
'qdr-table__cell--left-active': isSelectedInSameColumn ||
|
|
3628
|
-
(isSelectedInSameRow && cellPosition.columnIndex === 0) ||
|
|
3629
|
-
isDraggingThisColumn ||
|
|
3630
|
-
(isDraggingThisRow && cellPosition.columnIndex === 0),
|
|
3631
|
-
'qdr-table__cell--is-selection-trigger-by-me': isSelectionTriggerByMe,
|
|
3632
|
-
'qdr-table__cell--drag-row-target-top': (dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'row' &&
|
|
3633
|
-
dropTargetIndex === cellPosition.rowIndex &&
|
|
3634
|
-
dropTargetIndex !== dragState.rowIndex &&
|
|
3635
|
-
dragDirection === 'up',
|
|
3636
|
-
'qdr-table__cell--drag-row-target-bottom': (dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'row' &&
|
|
3637
|
-
dropTargetIndex === cellPosition.rowIndex &&
|
|
3638
|
-
dropTargetIndex !== dragState.rowIndex &&
|
|
3639
|
-
dragDirection === 'down',
|
|
3640
|
-
'qdr-table__cell--drag-column-target-left': (dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'column' &&
|
|
3641
|
-
dropTargetIndex === cellPosition.columnIndex &&
|
|
3642
|
-
dropTargetIndex !== dragState.columnIndex &&
|
|
3643
|
-
dragDirection === 'left',
|
|
3644
|
-
'qdr-table__cell--drag-column-target-right': (dragState === null || dragState === void 0 ? void 0 : dragState.type) === 'column' &&
|
|
3645
|
-
dropTargetIndex === cellPosition.columnIndex &&
|
|
3646
|
-
dropTargetIndex !== dragState.columnIndex &&
|
|
3647
|
-
dragDirection === 'right',
|
|
3648
|
-
}), "data-row-index": cellPosition.rowIndex, "data-column-index": cellPosition.columnIndex, style: myColumnIsPinned
|
|
3649
|
-
? {
|
|
3650
|
-
left: cellStuckAtLeft,
|
|
3651
|
-
}
|
|
3652
|
-
: undefined }),
|
|
3653
|
-
children,
|
|
3654
|
-
React.createElement("div", { contentEditable: false, "data-slate-editor": false, className: clsx('qdr-table__cell__resize-handle', {
|
|
3655
|
-
'qdr-table__cell__resize-handle--active': isResizing,
|
|
3656
|
-
}), onMouseDown: handleResizeStart }),
|
|
3657
|
-
focused && (React.createElement(components.Portal, { getContainer: () => portalContainerRef.current || document.body },
|
|
3658
|
-
React.createElement(toolbar.InlineToolbar, { className: 'qdr-table__cell__focus-toolbar', style: {
|
|
3659
|
-
top: toolbarPosition === null || toolbarPosition === void 0 ? void 0 : toolbarPosition.top,
|
|
3660
|
-
left: toolbarPosition === null || toolbarPosition === void 0 ? void 0 : toolbarPosition.left,
|
|
3661
|
-
}, iconGroups: focusToolbarIconGroups }))),
|
|
3662
|
-
showRowActionButton && (React.createElement(components.Portal, { getContainer: () => portalContainerRef.current || document.body },
|
|
3663
|
-
React.createElement(RowDragButton, { rowIndex: cellPosition.rowIndex, headerRowCount: headerRowCount, style: {
|
|
3664
|
-
top: rowButtonPosition === null || rowButtonPosition === void 0 ? void 0 : rowButtonPosition.top,
|
|
3665
|
-
left: rowButtonPosition === null || rowButtonPosition === void 0 ? void 0 : rowButtonPosition.left,
|
|
3666
|
-
}, onClick: (e) => {
|
|
3667
|
-
e.preventDefault();
|
|
3668
|
-
e.stopPropagation();
|
|
3669
|
-
core.Transforms.deselect(editor);
|
|
3670
|
-
setTableSelectedOn((prev) => {
|
|
3671
|
-
if ((prev === null || prev === void 0 ? void 0 : prev.region) === 'row' && prev.index === cellPosition.rowIndex) {
|
|
3672
|
-
return undefined;
|
|
3673
|
-
}
|
|
3674
|
-
return { region: 'row', index: cellPosition.rowIndex };
|
|
3675
|
-
});
|
|
3676
|
-
} }))),
|
|
3677
|
-
showColumnActionButton && (React.createElement(components.Portal, { getContainer: () => portalContainerRef.current || document.body },
|
|
3678
|
-
React.createElement(ColumnDragButton, { columnIndex: cellPosition.columnIndex, style: {
|
|
3679
|
-
// pinned 時因為 sticky 所以要扣掉 scrollTop
|
|
3680
|
-
top: ((_b = columnButtonPosition === null || columnButtonPosition === void 0 ? void 0 : columnButtonPosition.top) !== null && _b !== void 0 ? _b : 0) - (element.pinned ? scrollTop : 0),
|
|
3681
|
-
left: columnButtonPosition === null || columnButtonPosition === void 0 ? void 0 : columnButtonPosition.left,
|
|
3682
|
-
}, onClick: (e) => {
|
|
3683
|
-
e.preventDefault();
|
|
3684
|
-
e.stopPropagation();
|
|
3685
|
-
core.Transforms.deselect(editor);
|
|
3686
|
-
setTableSelectedOn((prev) => {
|
|
3687
|
-
if ((prev === null || prev === void 0 ? void 0 : prev.region) === 'column' && prev.index === cellPosition.columnIndex) {
|
|
3688
|
-
return undefined;
|
|
3689
|
-
}
|
|
3690
|
-
return { region: 'column', index: cellPosition.columnIndex };
|
|
3691
|
-
});
|
|
3692
|
-
}, checkIsTitleColumn: checkIsTitleColumn }))),
|
|
3693
|
-
isSelectionTriggerByMe && (cellPosition.columnIndex === 0 || cellPosition.rowIndex === 0) ? (React.createElement(components.Portal, { getContainer: () => portalContainerRef.current || document.body },
|
|
3694
|
-
React.createElement(toolbar.InlineToolbar, { className: "qdr-table__cell__inline-table-toolbar", style: {
|
|
3695
|
-
top: toolbarPosition === null || toolbarPosition === void 0 ? void 0 : toolbarPosition.top,
|
|
3696
|
-
left: toolbarPosition === null || toolbarPosition === void 0 ? void 0 : toolbarPosition.left,
|
|
3697
|
-
}, iconGroups: inlineToolbarIconGroups, onClickAway: () => {
|
|
3698
|
-
setTableSelectedOn(undefined);
|
|
3699
|
-
} }))) : null));
|
|
3700
|
-
}
|
|
3701
|
-
|
|
3702
|
-
function TableBody(props) {
|
|
3703
|
-
const { attributes, children } = props;
|
|
3704
|
-
return (React.createElement("tbody", Object.assign({}, attributes, { className: "qdr-table__body" }), children));
|
|
3705
|
-
}
|
|
3706
|
-
|
|
3707
|
-
const defaultRenderTableElements = {
|
|
3708
|
-
table: (props) => React.createElement(Table, Object.assign({ key: Math.random() }, props)),
|
|
3709
|
-
table_title: (props) => React.createElement(TableTitle, Object.assign({}, props)),
|
|
3710
|
-
table_main: (props) => React.createElement(TableMain, Object.assign({}, props)),
|
|
3711
|
-
table_header: (props) => React.createElement(TableHeader, Object.assign({}, props)),
|
|
3712
|
-
table_body: (props) => React.createElement(TableBody, Object.assign({}, props)),
|
|
3713
|
-
table_row: (props) => React.createElement(TableRow, Object.assign({}, props)),
|
|
3714
|
-
table_cell: (props) => React.createElement(TableCell, Object.assign({}, props)),
|
|
3715
|
-
};
|
|
3716
|
-
|
|
3717
|
-
function createReactTable(options = {}) {
|
|
3718
|
-
const core$1 = table.createTable(options);
|
|
3719
|
-
const { types } = core$1;
|
|
3720
|
-
return Object.assign(Object.assign({}, core$1), { createHandlers: () => ({
|
|
3721
|
-
onKeyDown(event, editor, next) {
|
|
3722
|
-
if (event.nativeEvent.isComposing) {
|
|
3723
|
-
return;
|
|
3724
|
-
}
|
|
3725
|
-
if (core$1.isSelectionInTableCell(editor)) {
|
|
3726
|
-
const checkCurrentCellHasContent = () => {
|
|
3727
|
-
if (!editor.selection)
|
|
3728
|
-
return false;
|
|
3729
|
-
const [cellNode] = core.Editor.node(editor, editor.selection);
|
|
3730
|
-
if (core.Text.isText(cellNode)) {
|
|
3731
|
-
return cellNode.text.trim() !== '';
|
|
3732
|
-
}
|
|
3733
|
-
if (!core.Element.isElement(cellNode))
|
|
3734
|
-
return false;
|
|
3735
|
-
const hasContent = cellNode.children.some((child) => {
|
|
3736
|
-
if (core.Text.isText(child)) {
|
|
3737
|
-
return child.text.trim() !== '';
|
|
3738
|
-
}
|
|
3739
|
-
return core.Element.isElement(child);
|
|
3740
|
-
});
|
|
3741
|
-
return hasContent;
|
|
3742
|
-
};
|
|
3743
|
-
if (event.key === 'Enter') {
|
|
3744
|
-
if (core$1.isSelectionInTableList(editor)) {
|
|
3745
|
-
return next();
|
|
3746
|
-
}
|
|
3747
|
-
event.preventDefault();
|
|
3748
|
-
const currentCellHasContent = checkCurrentCellHasContent();
|
|
3749
|
-
if (currentCellHasContent) {
|
|
3750
|
-
// Insert soft break
|
|
3751
|
-
core.Editor.insertText(editor, '\n');
|
|
3752
|
-
}
|
|
3753
|
-
else {
|
|
3754
|
-
// Move to next cell
|
|
3755
|
-
core$1.moveToNextCell(editor, types);
|
|
3756
|
-
}
|
|
3757
|
-
return;
|
|
3758
|
-
}
|
|
3759
|
-
if (event.key === 'Tab') {
|
|
3760
|
-
event.preventDefault();
|
|
3761
|
-
// shift+tab
|
|
3762
|
-
if (event.shiftKey)
|
|
3763
|
-
return;
|
|
3764
|
-
core$1.moveToNextCell(editor, types);
|
|
3765
|
-
return;
|
|
3766
|
-
}
|
|
3767
|
-
// 處理方向鍵上下移動
|
|
3768
|
-
if (event.key === 'ArrowUp') {
|
|
3769
|
-
event.preventDefault();
|
|
3770
|
-
if (event.shiftKey) {
|
|
3771
|
-
// Shift + 上:擴展選取範圍
|
|
3772
|
-
core$1.extendSelectionUp(editor, types);
|
|
3773
|
-
}
|
|
3774
|
-
else {
|
|
3775
|
-
// 只按上:移動到上一行
|
|
3776
|
-
core$1.moveToRowAbove(editor, types);
|
|
3777
|
-
}
|
|
3778
|
-
return;
|
|
3779
|
-
}
|
|
3780
|
-
if (event.key === 'ArrowDown') {
|
|
3781
|
-
event.preventDefault();
|
|
3782
|
-
if (event.shiftKey) {
|
|
3783
|
-
// Shift + 下:擴展選取範圍
|
|
3784
|
-
core$1.extendSelectionDown(editor, types);
|
|
3785
|
-
}
|
|
3786
|
-
else {
|
|
3787
|
-
// 只按下:移動到下一行
|
|
3788
|
-
core$1.moveToRowBelow(editor, types);
|
|
3789
|
-
}
|
|
3790
|
-
return;
|
|
3791
|
-
}
|
|
3792
|
-
if (event.key === 'ArrowLeft') {
|
|
3793
|
-
// Shift + 左:擴展選取範圍
|
|
3794
|
-
if (event.shiftKey) {
|
|
3795
|
-
event.preventDefault();
|
|
3796
|
-
core$1.extendSelectionLeft(editor, types);
|
|
3797
|
-
return;
|
|
3798
|
-
}
|
|
3799
|
-
}
|
|
3800
|
-
if (event.key === 'ArrowRight') {
|
|
3801
|
-
// Shift + 右:擴展選取範圍
|
|
3802
|
-
if (event.shiftKey) {
|
|
3803
|
-
event.preventDefault();
|
|
3804
|
-
core$1.extendSelectionRight(editor, types);
|
|
3805
|
-
return;
|
|
3806
|
-
}
|
|
3807
|
-
}
|
|
3808
|
-
}
|
|
3809
|
-
next();
|
|
3810
|
-
},
|
|
3811
|
-
}), createRenderElement: (options = {}) => {
|
|
3812
|
-
const renderTable = options.table || defaultRenderTableElements.table;
|
|
3813
|
-
const renderTableTitle = options.table_title || defaultRenderTableElements.table_title;
|
|
3814
|
-
const renderTableMain = options.table_main || defaultRenderTableElements.table_main;
|
|
3815
|
-
const renderTableHeader = options.table_header || defaultRenderTableElements.table_header;
|
|
3816
|
-
const renderTableBody = options.table_body || defaultRenderTableElements.table_body;
|
|
3817
|
-
const renderTableRow = options.table_row || defaultRenderTableElements.table_row;
|
|
3818
|
-
const renderTableCell = options.table_cell || defaultRenderTableElements.table_cell;
|
|
3819
|
-
return react.createRenderElements([
|
|
3820
|
-
{
|
|
3821
|
-
type: types.table,
|
|
3822
|
-
render: (props) => {
|
|
3823
|
-
const { attributes, children, element } = props;
|
|
3824
|
-
return renderTable({
|
|
3825
|
-
attributes,
|
|
3826
|
-
element,
|
|
3827
|
-
children,
|
|
3828
|
-
});
|
|
3829
|
-
},
|
|
3830
|
-
},
|
|
3831
|
-
{
|
|
3832
|
-
type: types.table_title,
|
|
3833
|
-
render: (props) => {
|
|
3834
|
-
const { attributes, children, element } = props;
|
|
3835
|
-
return renderTableTitle({
|
|
3836
|
-
attributes,
|
|
3837
|
-
element,
|
|
3838
|
-
children,
|
|
3839
|
-
});
|
|
3840
|
-
},
|
|
3841
|
-
},
|
|
3842
|
-
{
|
|
3843
|
-
type: types.table_main,
|
|
3844
|
-
render: (props) => {
|
|
3845
|
-
const { attributes, children, element } = props;
|
|
3846
|
-
return renderTableMain({
|
|
3847
|
-
attributes,
|
|
3848
|
-
element,
|
|
3849
|
-
children,
|
|
3850
|
-
});
|
|
3851
|
-
},
|
|
3852
|
-
},
|
|
3853
|
-
{
|
|
3854
|
-
type: types.table_header,
|
|
3855
|
-
render: (props) => {
|
|
3856
|
-
const { attributes, children, element } = props;
|
|
3857
|
-
return renderTableHeader({
|
|
3858
|
-
attributes,
|
|
3859
|
-
element,
|
|
3860
|
-
children,
|
|
3861
|
-
});
|
|
3862
|
-
},
|
|
3863
|
-
},
|
|
3864
|
-
{
|
|
3865
|
-
type: types.table_body,
|
|
3866
|
-
render: (props) => {
|
|
3867
|
-
const { attributes, children, element } = props;
|
|
3868
|
-
return renderTableBody({
|
|
3869
|
-
attributes,
|
|
3870
|
-
element,
|
|
3871
|
-
children,
|
|
3872
|
-
});
|
|
3873
|
-
},
|
|
3874
|
-
},
|
|
3875
|
-
{
|
|
3876
|
-
type: types.table_row,
|
|
3877
|
-
render: (props) => {
|
|
3878
|
-
const { attributes, children, element } = props;
|
|
3879
|
-
return renderTableRow({
|
|
3880
|
-
attributes,
|
|
3881
|
-
element,
|
|
3882
|
-
children,
|
|
3883
|
-
});
|
|
3884
|
-
},
|
|
3885
|
-
},
|
|
3886
|
-
{
|
|
3887
|
-
type: types.table_cell,
|
|
3888
|
-
render: (props) => {
|
|
3889
|
-
const { attributes, children, element } = props;
|
|
3890
|
-
return renderTableCell({
|
|
3891
|
-
attributes,
|
|
3892
|
-
element,
|
|
3893
|
-
children,
|
|
3894
|
-
});
|
|
3895
|
-
},
|
|
3896
|
-
},
|
|
3897
|
-
]);
|
|
3898
|
-
}, with(editor) {
|
|
3899
|
-
const { insertData } = editor;
|
|
3900
|
-
/** 從他處複製文字貼過來時觸發 */
|
|
3901
|
-
editor.insertData = (data) => {
|
|
3902
|
-
const { selection } = editor;
|
|
3903
|
-
if (!selection) {
|
|
3904
|
-
insertData(data);
|
|
3905
|
-
return;
|
|
3906
|
-
}
|
|
3907
|
-
// 先檢查選取的起點是否在 table cell 中
|
|
3908
|
-
const cellEntry = core.Editor.above(editor, {
|
|
3909
|
-
at: selection.anchor,
|
|
3910
|
-
match: (n) => core.Element.isElement(n) && n.type === types.table_cell,
|
|
3911
|
-
});
|
|
3912
|
-
if (cellEntry) {
|
|
3913
|
-
// 確認在 table 內,將選取範圍縮減到起點
|
|
3914
|
-
core.Transforms.collapse(editor, { edge: 'start' });
|
|
3915
|
-
// 重新取得 cell entry(使用當前游標位置)
|
|
3916
|
-
const targetCellEntry = core.Editor.above(editor, {
|
|
3917
|
-
match: (n) => core.Element.isElement(n) && n.type === types.table_cell,
|
|
3918
|
-
});
|
|
3919
|
-
if (!targetCellEntry) {
|
|
3920
|
-
insertData(data);
|
|
3921
|
-
return;
|
|
3922
|
-
}
|
|
3923
|
-
const [, cellPath] = targetCellEntry;
|
|
3924
|
-
// 從剪貼簿取得純文字
|
|
3925
|
-
const text = data.getData('text/plain');
|
|
3926
|
-
if (text) {
|
|
3927
|
-
// 檢測是否為表格格式(包含 Tab 或換行)
|
|
3928
|
-
const hasTableFormat = text.includes('\t') || text.includes('\n');
|
|
3929
|
-
if (hasTableFormat) {
|
|
3930
|
-
// 解析表格格式:行由 \n 分隔,欄位由 \t 分隔
|
|
3931
|
-
const rows = text.split('\n').filter((row) => row.length > 0);
|
|
3932
|
-
if (rows.length === 0) {
|
|
3933
|
-
return;
|
|
3934
|
-
}
|
|
3935
|
-
// 找到當前 cell 所在的 row 和 column index
|
|
3936
|
-
const rowEntry = core.Editor.above(editor, {
|
|
3937
|
-
at: cellPath,
|
|
3938
|
-
match: (n) => core.Element.isElement(n) && n.type === types.table_row,
|
|
3939
|
-
});
|
|
3940
|
-
if (!rowEntry) {
|
|
3941
|
-
// 如果找不到 row,降級為純文字插入
|
|
3942
|
-
core.Transforms.insertText(editor, text.replace(/\t/g, ' '));
|
|
3943
|
-
return;
|
|
3944
|
-
}
|
|
3945
|
-
const [, rowPath] = rowEntry;
|
|
3946
|
-
const currentColumnIndex = cellPath[cellPath.length - 1];
|
|
3947
|
-
// 找到 table body 或 header
|
|
3948
|
-
const containerEntry = core.Editor.above(editor, {
|
|
3949
|
-
at: rowPath,
|
|
3950
|
-
match: (n) => core.Element.isElement(n) &&
|
|
3951
|
-
(n.type === types.table_body || n.type === types.table_header),
|
|
3952
|
-
});
|
|
3953
|
-
if (!containerEntry) {
|
|
3954
|
-
// 降級為純文字插入
|
|
3955
|
-
core.Transforms.insertText(editor, text.replace(/\t/g, ' '));
|
|
3956
|
-
return;
|
|
3957
|
-
}
|
|
3958
|
-
const [container, containerPath] = containerEntry;
|
|
3959
|
-
const currentRowIndex = rowPath[rowPath.length - 1];
|
|
3960
|
-
// 使用 Editor.withoutNormalizing 批次處理
|
|
3961
|
-
core.Editor.withoutNormalizing(editor, () => {
|
|
3962
|
-
rows.forEach((rowText, rowOffset) => {
|
|
3963
|
-
const columns = rowText.split('\t');
|
|
3964
|
-
const targetRowIndex = currentRowIndex + rowOffset;
|
|
3965
|
-
// 檢查目標 row 是否存在
|
|
3966
|
-
if (targetRowIndex >= container.children.length) {
|
|
3967
|
-
return; // 超出範圍,跳過此行
|
|
3968
|
-
}
|
|
3969
|
-
const targetRow = container.children[targetRowIndex];
|
|
3970
|
-
if (!core.Element.isElement(targetRow) || targetRow.type !== types.table_row) {
|
|
3971
|
-
return;
|
|
3972
|
-
}
|
|
3973
|
-
// 貼上到各個 cell
|
|
3974
|
-
columns.forEach((columnText, columnOffset) => {
|
|
3975
|
-
const targetColumnIndex = currentColumnIndex + columnOffset;
|
|
3976
|
-
// 檢查目標 cell 是否存在
|
|
3977
|
-
if (targetColumnIndex >= targetRow.children.length) {
|
|
3978
|
-
return; // 超出範圍,跳過此欄
|
|
3979
|
-
}
|
|
3980
|
-
const targetCellPath = [...containerPath, targetRowIndex, targetColumnIndex];
|
|
3981
|
-
// 清空目標 cell 的內容
|
|
3982
|
-
const targetCell = targetRow.children[targetColumnIndex];
|
|
3983
|
-
if (core.Element.isElement(targetCell) && targetCell.type === types.table_cell) {
|
|
3984
|
-
// 移除所有子節點
|
|
3985
|
-
for (let i = targetCell.children.length - 1; i >= 0; i--) {
|
|
3986
|
-
core.Transforms.removeNodes(editor, { at: [...targetCellPath, i] });
|
|
3987
|
-
}
|
|
3988
|
-
// 插入新內容
|
|
3989
|
-
core.Transforms.insertNodes(editor, {
|
|
3990
|
-
type: core.PARAGRAPH_TYPE,
|
|
3991
|
-
children: [{ text: columnText }],
|
|
3992
|
-
}, { at: [...targetCellPath, 0] });
|
|
3993
|
-
}
|
|
3994
|
-
});
|
|
3995
|
-
});
|
|
3996
|
-
});
|
|
3997
|
-
return;
|
|
3998
|
-
}
|
|
3999
|
-
}
|
|
4000
|
-
}
|
|
4001
|
-
// 預設行為
|
|
4002
|
-
insertData(data);
|
|
4003
|
-
};
|
|
4004
|
-
return core$1.with(editor);
|
|
4005
|
-
} });
|
|
4006
|
-
}
|
|
4007
|
-
|
|
4008
|
-
exports.TableActionsContext = TableActionsContext;
|
|
4009
|
-
exports.TableMetadataContext = TableMetadataContext;
|
|
4010
|
-
exports.TableStateContext = TableStateContext;
|
|
4011
|
-
exports.createReactTable = createReactTable;
|
|
4012
|
-
exports.defaultRenderTableElements = defaultRenderTableElements;
|
|
4013
|
-
exports.useTableActionsContext = useTableActionsContext;
|
|
4014
|
-
exports.useTableCellToolbarActions = useTableCellToolbarActions;
|
|
4015
|
-
exports.useTableMetadata = useTableMetadata;
|
|
4016
|
-
exports.useTableStateContext = useTableStateContext;
|