@frontify/guideline-blocks-settings 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/.eslintrc.js +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +24 -5
  4. package/package.json +46 -10
  5. package/postcss.config.js +8 -0
  6. package/setupTests.ts +13 -0
  7. package/src/components/Attachments/AttachmentItem.tsx +257 -0
  8. package/src/components/Attachments/Attachments.spec.ct.tsx +151 -0
  9. package/src/components/Attachments/Attachments.tsx +221 -0
  10. package/src/components/Attachments/index.ts +4 -0
  11. package/src/components/Attachments/types.ts +30 -0
  12. package/src/components/BlockInjectButton/BlockInjectButton.spec.ct.tsx +48 -0
  13. package/src/components/BlockInjectButton/BlockInjectButton.tsx +212 -0
  14. package/src/components/BlockInjectButton/index.ts +4 -0
  15. package/src/components/BlockInjectButton/types.ts +18 -0
  16. package/src/components/BlockItemWrapper/BlockItemWrapper.spec.ct.tsx +146 -0
  17. package/src/components/BlockItemWrapper/BlockItemWrapper.tsx +76 -0
  18. package/src/components/BlockItemWrapper/Toolbar.tsx +128 -0
  19. package/src/components/BlockItemWrapper/constants.ts +4 -0
  20. package/src/components/BlockItemWrapper/index.ts +5 -0
  21. package/src/components/BlockItemWrapper/types.ts +46 -0
  22. package/src/components/DownloadButton/DownloadButton.spec.ct.tsx +20 -0
  23. package/src/components/DownloadButton/DownloadButton.tsx +36 -0
  24. package/src/components/DownloadButton/index.ts +3 -0
  25. package/src/components/DownloadButton/types.ts +5 -0
  26. package/src/components/RichTextEditor/RichTextEditor.spec.ct.tsx +204 -0
  27. package/src/components/RichTextEditor/RichTextEditor.tsx +62 -0
  28. package/src/components/RichTextEditor/SerializedText.tsx +25 -0
  29. package/src/components/RichTextEditor/constants.ts +3 -0
  30. package/src/components/RichTextEditor/index.ts +6 -0
  31. package/src/components/RichTextEditor/pluginPresets/defaultPluginsWithLinkChooser.tsx +53 -0
  32. package/src/components/RichTextEditor/pluginPresets/index.ts +3 -0
  33. package/src/components/RichTextEditor/plugins/ButtonPlugin/ButtonMarkupElement/ButtonMarkupElementNode.tsx +74 -0
  34. package/src/components/RichTextEditor/plugins/ButtonPlugin/ButtonMarkupElement/index.ts +11 -0
  35. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/ButtonButton.tsx +20 -0
  36. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/ButtonToolbarButton.tsx +56 -0
  37. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/CustomFloatingButton.tsx +19 -0
  38. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/EditButtonModal/EditModal.tsx +42 -0
  39. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/FloatingButton.tsx +37 -0
  40. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/FloatingButtonEditButton.tsx +22 -0
  41. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/FloatingButtonUrlInput.tsx +30 -0
  42. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/InsertButtonModal/InsertButtonModal.tsx +81 -0
  43. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/InsertButtonModal/types.ts +13 -0
  44. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/InsertButtonModal/useInsertModal.ts +143 -0
  45. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/UnlinkButton.tsx +31 -0
  46. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/floatingButtonStore.ts +46 -0
  47. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/index.ts +12 -0
  48. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonEdit.ts +113 -0
  49. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonEnter.ts +21 -0
  50. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonEscape.ts +30 -0
  51. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonInsert.ts +71 -0
  52. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useVirtualFloatingButton.ts +22 -0
  53. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/index.ts +3 -0
  54. package/src/components/RichTextEditor/plugins/ButtonPlugin/createButtonPlugin.ts +116 -0
  55. package/src/components/RichTextEditor/plugins/ButtonPlugin/index.ts +7 -0
  56. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/index.ts +8 -0
  57. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/insertButton.ts +17 -0
  58. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/submitFloatingButton.ts +40 -0
  59. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/unwrapButton.ts +68 -0
  60. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/upsertButton.ts +198 -0
  61. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/upsertButtonText.ts +40 -0
  62. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/wrapButton.ts +30 -0
  63. package/src/components/RichTextEditor/plugins/ButtonPlugin/types.ts +13 -0
  64. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/createButtonNode.ts +28 -0
  65. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/getButtonStyle.ts +14 -0
  66. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/getUrl.ts +18 -0
  67. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/index.ts +8 -0
  68. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/styles.ts +77 -0
  69. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/triggerFloatingButton.ts +23 -0
  70. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/triggerFloatingButtonEdit.ts +30 -0
  71. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/triggerFloatingButtonInsert.ts +45 -0
  72. package/src/components/RichTextEditor/plugins/ButtonPlugin/withButton.ts +106 -0
  73. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/CustomFloatingLink.tsx +26 -0
  74. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/EditLinkModal/EditModal.tsx +43 -0
  75. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/EditLinkModal/index.ts +4 -0
  76. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/EditLinkModal/useFloatingLinkEdit.ts +113 -0
  77. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/FloatingLink.tsx +45 -0
  78. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/InsertLinkModal.tsx +5 -0
  79. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/InsertModal.tsx +105 -0
  80. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/index.ts +4 -0
  81. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/types.ts +16 -0
  82. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/useFloatingLinkInsert.ts +73 -0
  83. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/useInsertModal.ts +136 -0
  84. package/src/components/RichTextEditor/plugins/LinkPlugin/LinkButton.tsx +38 -0
  85. package/src/components/RichTextEditor/plugins/LinkPlugin/LinkMarkupElement/LinkMarkupElementNode.tsx +36 -0
  86. package/src/components/RichTextEditor/plugins/LinkPlugin/LinkMarkupElement/index.ts +11 -0
  87. package/src/components/RichTextEditor/plugins/LinkPlugin/id.ts +3 -0
  88. package/src/components/RichTextEditor/plugins/LinkPlugin/index.ts +48 -0
  89. package/src/components/RichTextEditor/plugins/LinkPlugin/types.ts +12 -0
  90. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/getUrl.ts +30 -0
  91. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/index.ts +4 -0
  92. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/relativeUrlRegex.spec.ts +35 -0
  93. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/relativeUrlRegex.ts +3 -0
  94. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/url.spec.ts +75 -0
  95. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/url.ts +21 -0
  96. package/src/components/RichTextEditor/plugins/TextStylePlugins/custom1Plugin.tsx +61 -0
  97. package/src/components/RichTextEditor/plugins/TextStylePlugins/custom2Plugin.tsx +61 -0
  98. package/src/components/RichTextEditor/plugins/TextStylePlugins/custom3Plugin.tsx +62 -0
  99. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading1Plugin.tsx +61 -0
  100. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading2Plugin.tsx +58 -0
  101. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading3Plugin.tsx +58 -0
  102. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading4Plugin.tsx +59 -0
  103. package/src/components/RichTextEditor/plugins/TextStylePlugins/helpers.tsx +44 -0
  104. package/src/components/RichTextEditor/plugins/TextStylePlugins/imageCaptionPlugin.tsx +61 -0
  105. package/src/components/RichTextEditor/plugins/TextStylePlugins/imageTitlePlugin.tsx +61 -0
  106. package/src/components/RichTextEditor/plugins/TextStylePlugins/index.ts +15 -0
  107. package/src/components/RichTextEditor/plugins/TextStylePlugins/paragraphPlugin.tsx +58 -0
  108. package/src/components/RichTextEditor/plugins/TextStylePlugins/quotePlugin.tsx +62 -0
  109. package/src/components/RichTextEditor/plugins/index.ts +6 -0
  110. package/src/components/RichTextEditor/plugins/shared/LinkSelector/DocumentLink.tsx +80 -0
  111. package/src/components/RichTextEditor/plugins/shared/LinkSelector/DocumentLinks.tsx +97 -0
  112. package/src/components/RichTextEditor/plugins/shared/LinkSelector/LinkSelector.spec.ct.tsx +138 -0
  113. package/src/components/RichTextEditor/plugins/shared/LinkSelector/LinkSelector.tsx +80 -0
  114. package/src/components/RichTextEditor/plugins/shared/LinkSelector/PageLink.tsx +83 -0
  115. package/src/components/RichTextEditor/plugins/shared/LinkSelector/PageLinks.tsx +68 -0
  116. package/src/components/RichTextEditor/plugins/shared/LinkSelector/SectionLink.tsx +37 -0
  117. package/src/components/RichTextEditor/plugins/shared/LinkSelector/index.ts +3 -0
  118. package/src/components/RichTextEditor/plugins/styles.ts +179 -0
  119. package/src/components/RichTextEditor/serializer/index.ts +3 -0
  120. package/src/components/RichTextEditor/serializer/nodes/button.ts +25 -0
  121. package/src/components/RichTextEditor/serializer/nodes/checkItemNode.ts +29 -0
  122. package/src/components/RichTextEditor/serializer/nodes/default.ts +52 -0
  123. package/src/components/RichTextEditor/serializer/nodes/link.ts +25 -0
  124. package/src/components/RichTextEditor/serializer/nodes/mentionHtmlNode.ts +17 -0
  125. package/src/components/RichTextEditor/serializer/serializeNodesToHtmlRecursive.ts +134 -0
  126. package/src/components/RichTextEditor/serializer/serializeToHtml.ts +49 -0
  127. package/src/components/RichTextEditor/serializer/utlis/reactCssPropsToCss.ts +21 -0
  128. package/src/components/RichTextEditor/serializer/utlis/serializeLeafToHtml.ts +32 -0
  129. package/src/components/RichTextEditor/types.ts +23 -0
  130. package/src/components/index.ts +7 -0
  131. package/src/helpers/addHttps.spec.ts +42 -0
  132. package/src/helpers/addHttps.ts +15 -0
  133. package/src/helpers/convertToRichTextValue.spec.ts +32 -0
  134. package/src/helpers/convertToRichTextValue.ts +6 -0
  135. package/src/helpers/customCoordinatesGetterFactory.spec.ts +69 -0
  136. package/src/helpers/customCoordinatesGetterFactory.ts +39 -0
  137. package/src/helpers/hasRichTextValue.spec.ts +63 -0
  138. package/src/helpers/hasRichTextValue.ts +29 -0
  139. package/src/helpers/index.ts +8 -0
  140. package/src/helpers/isDownloadable.spec.ts +47 -0
  141. package/src/helpers/isDownloadable.ts +7 -0
  142. package/src/helpers/mapColorPalettes.spec.ts +146 -0
  143. package/src/helpers/mapColorPalettes.ts +22 -0
  144. package/src/hooks/index.ts +4 -0
  145. package/src/hooks/useAttachments.spec.ts +79 -0
  146. package/src/hooks/useAttachments.ts +46 -0
  147. package/src/hooks/useDndSensors.spec.ts +40 -0
  148. package/src/hooks/useDndSensors.ts +23 -0
  149. package/src/index.ts +8 -0
  150. package/src/settings/background.spec.ts +173 -0
  151. package/src/settings/background.ts +49 -0
  152. package/src/settings/border.spec.ts +76 -0
  153. package/src/settings/border.ts +90 -0
  154. package/src/settings/borderRadius.spec.ts +30 -0
  155. package/src/settings/borderRadius.ts +73 -0
  156. package/src/settings/borderRadiusExtended.spec.ts +52 -0
  157. package/src/settings/borderRadiusExtended.ts +84 -0
  158. package/src/settings/defaultValues.ts +21 -0
  159. package/src/settings/gutter.spec.ts +60 -0
  160. package/src/settings/gutter.ts +75 -0
  161. package/src/settings/index.ts +14 -0
  162. package/src/settings/margin.spec.ts +42 -0
  163. package/src/settings/margin.ts +72 -0
  164. package/src/settings/marginExtended.spec.ts +45 -0
  165. package/src/settings/marginExtended.ts +91 -0
  166. package/src/settings/padding.spec.ts +42 -0
  167. package/src/settings/padding.ts +73 -0
  168. package/src/settings/paddingExtended.spec.ts +45 -0
  169. package/src/settings/paddingExtended.ts +91 -0
  170. package/src/settings/security.spec.ts +87 -0
  171. package/src/settings/security.ts +61 -0
  172. package/src/settings/securityDownloadable.spec.ts +46 -0
  173. package/src/settings/securityDownloadable.ts +33 -0
  174. package/src/settings/securityGlobalControl.ts +42 -0
  175. package/src/settings/types.ts +128 -0
  176. package/src/utilities/color/getReadableColor.spec.ts +32 -0
  177. package/src/utilities/color/getReadableColor.ts +34 -0
  178. package/src/utilities/color/index.ts +10 -0
  179. package/src/utilities/color/isDark.spec.ts +33 -0
  180. package/src/utilities/color/isDark.ts +29 -0
  181. package/src/utilities/color/setAlpha.spec.ts +28 -0
  182. package/src/utilities/color/setAlpha.ts +14 -0
  183. package/src/utilities/color/toColorObject.spec.ts +19 -0
  184. package/src/utilities/color/toColorObject.ts +16 -0
  185. package/src/utilities/color/toHex8String.spec.ts +17 -0
  186. package/src/utilities/color/toHex8String.ts +14 -0
  187. package/src/utilities/color/toHexString.spec.ts +17 -0
  188. package/src/utilities/color/toHexString.ts +10 -0
  189. package/src/utilities/color/toRgbaString.spec.ts +12 -0
  190. package/src/utilities/color/toRgbaString.ts +14 -0
  191. package/src/utilities/color/toShortRgba.spec.ts +16 -0
  192. package/src/utilities/color/toShortRgba.ts +35 -0
  193. package/src/utilities/index.ts +5 -0
  194. package/src/utilities/moveItemInArray.spec.ts +17 -0
  195. package/src/utilities/moveItemInArray.ts +21 -0
  196. package/src/utilities/react/getBackgroundColorStyles.spec.ts +18 -0
  197. package/src/utilities/react/getBackgroundColorStyles.ts +11 -0
  198. package/src/utilities/react/getBorderStyles.spec.ts +39 -0
  199. package/src/utilities/react/getBorderStyles.ts +21 -0
  200. package/src/utilities/react/getRadiusStyles.spec.ts +25 -0
  201. package/src/utilities/react/getRadiusStyles.ts +8 -0
  202. package/src/utilities/react/index.ts +6 -0
  203. package/src/utilities/react/joinClassNames.spec.ts +18 -0
  204. package/src/utilities/react/joinClassNames.ts +10 -0
  205. package/tailwind.config.js +27 -0
  206. package/tsconfig.json +3 -1
  207. package/vite.config.ts +11 -1
  208. package/dist/index.cjs.js +0 -2
  209. package/dist/index.cjs.js.map +0 -1
  210. package/dist/index.d.ts +0 -147
  211. package/dist/index.es.js +0 -9
  212. package/dist/index.es.js.map +0 -1
  213. package/dist/index.umd.js +0 -2
  214. package/dist/index.umd.js.map +0 -1
@@ -0,0 +1,116 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { AppBridgeBlock } from '@frontify/app-bridge';
4
+ import { Plugin, PluginProps } from '@frontify/fondue';
5
+ import { RangeBeforeOptions, createPluginFactory } from '@udecode/plate';
6
+ import { CSSProperties } from 'react';
7
+ import { isValidUrl } from '../LinkPlugin/utils/url';
8
+ import { ButtonMarkupElement } from './ButtonMarkupElement';
9
+ import { ButtonButton } from './components/ButtonButton';
10
+ import { CustomFloatingButton } from './components/FloatingButton/CustomFloatingButton';
11
+ import { BlockButtonStyles } from './utils';
12
+ import { withButton } from './withButton';
13
+
14
+ export const ELEMENT_BUTTON = 'button';
15
+ export const BUTTON_PLUGIN = 'button-plugin';
16
+
17
+ export interface ButtonPlugin {
18
+ forceSubmit?: boolean;
19
+
20
+ /**
21
+ * Allow custom config for rangeBeforeOptions.
22
+ * @example default
23
+ * {
24
+ * matchString: ' ',
25
+ * skipInvalid: true,
26
+ * afterMatch: true,
27
+ * }
28
+ */
29
+ rangeBeforeOptions?: RangeBeforeOptions;
30
+
31
+ /**
32
+ * Hotkeys to trigger floating button.
33
+ * @default 'command+k, ctrl+k'
34
+ */
35
+ triggerFloatingButtonHotkeys: string;
36
+
37
+ /**
38
+ * Callback to validate an url.
39
+ * @default isUrl
40
+ */
41
+ isUrl: (text: string) => boolean;
42
+
43
+ /**
44
+ * Callback to optionally get the href for a url
45
+ * @returns href: an optional link to be used that is different from the text content (example https://google.com for google.com)
46
+ */
47
+ getUrlHref?: (url: string) => string | undefined;
48
+
49
+ /**
50
+ * On keyboard shortcut or toolbar mousedown, get the link url by calling this promise. The
51
+ * default behavior is to use the browser's native `prompt`.
52
+ */
53
+ getLinkUrl?: (prevUrl: string | null) => Promise<string | null>;
54
+ }
55
+
56
+ /**
57
+ * Enables support for hyperlinks.
58
+ */
59
+ export const createButtonPlugin = (appBridge: AppBridgeBlock) =>
60
+ createPluginFactory({
61
+ key: ELEMENT_BUTTON,
62
+ isElement: true,
63
+ isInline: true,
64
+ props: ({ element }) => ({
65
+ nodeProps: { href: element?.url, target: element?.target },
66
+ }),
67
+ withOverrides: withButton,
68
+ renderAfterEditable: CustomFloatingButton,
69
+ options: {
70
+ isUrl: isValidUrl,
71
+ rangeBeforeOptions: {
72
+ matchString: ' ',
73
+ skipInvalid: true,
74
+ afterMatch: true,
75
+ },
76
+ triggerFloatingButtonHotkeys: 'command+shift+k, ctrl+shift+k',
77
+ appBridge,
78
+ },
79
+ then: (editor, { type }) => ({
80
+ deserializeHtml: {
81
+ rules: [
82
+ {
83
+ validNodeName: 'A',
84
+ validClassName: 'btn',
85
+ },
86
+ ],
87
+ getNode: (el) => ({
88
+ type,
89
+ url: el.getAttribute('href'),
90
+ target: el.getAttribute('target') || '_blank',
91
+ }),
92
+ },
93
+ }),
94
+ })();
95
+
96
+ export type ButtonPluginProps = Omit<PluginProps, 'styles'> & {
97
+ styles?: Record<string, CSSProperties & { hover?: CSSProperties }>;
98
+ } & { appBridge: AppBridgeBlock };
99
+
100
+ export class ButtonPlugin extends Plugin {
101
+ public styles: CSSProperties = {};
102
+ private appBridge: AppBridgeBlock;
103
+ constructor({ styles = BlockButtonStyles, ...props }: ButtonPluginProps) {
104
+ super(BUTTON_PLUGIN, {
105
+ button: ButtonButton,
106
+ markupElement: new ButtonMarkupElement(),
107
+ ...props,
108
+ });
109
+ this.styles = styles;
110
+ this.appBridge = props?.appBridge as AppBridgeBlock;
111
+ }
112
+
113
+ plugins() {
114
+ return [createButtonPlugin(this.appBridge)];
115
+ }
116
+ }
@@ -0,0 +1,7 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './createButtonPlugin';
4
+ export * from './types';
5
+ export * from './withButton';
6
+ export * from './transforms/index';
7
+ export * from './utils/index';
@@ -0,0 +1,8 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './insertButton';
4
+ export * from './submitFloatingButton';
5
+ export * from './unwrapButton';
6
+ export * from './upsertButton';
7
+ export * from './upsertButtonText';
8
+ export * from './wrapButton';
@@ -0,0 +1,17 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { InsertNodesOptions, PlateEditor, TText, Value, insertNodes } from '@udecode/plate';
4
+ import { TButtonElement } from '../types';
5
+ import { CreateButtonNodeOptions, createButtonNode } from '../utils/index';
6
+
7
+ export const insertButton = <V extends Value>(
8
+ editor: PlateEditor<V>,
9
+ createButtonNodeOptions: CreateButtonNodeOptions,
10
+ options?: InsertNodesOptions<V>
11
+ ) => {
12
+ insertNodes<TButtonElement | TText>(
13
+ editor,
14
+ [createButtonNode(editor, createButtonNodeOptions)],
15
+ options as InsertNodesOptions
16
+ );
17
+ };
@@ -0,0 +1,40 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { PlateEditor, Value, focusEditor, getPluginOptions } from '@udecode/plate';
4
+ import { floatingButtonActions, floatingButtonSelectors } from '../components/FloatingButton/floatingButtonStore';
5
+ import { ButtonPlugin, ELEMENT_BUTTON } from '../createButtonPlugin';
6
+ import { upsertButton } from '.';
7
+
8
+ export const submitFloatingButton = <V extends Value>(editor: PlateEditor<V>) => {
9
+ if (!editor.selection) {
10
+ return;
11
+ }
12
+
13
+ const { isUrl, forceSubmit } = getPluginOptions<ButtonPlugin, V>(editor, ELEMENT_BUTTON);
14
+ const url = floatingButtonSelectors.url();
15
+
16
+ const isValid = isUrl?.(url) || forceSubmit;
17
+ if (!isValid) {
18
+ return;
19
+ }
20
+
21
+ const text = floatingButtonSelectors.text();
22
+ const buttonStyle = floatingButtonSelectors.buttonStyle();
23
+ const target = floatingButtonSelectors.newTab() ? undefined : '_self';
24
+
25
+ floatingButtonActions.hide();
26
+
27
+ upsertButton(editor, {
28
+ url,
29
+ text,
30
+ buttonStyle,
31
+ target,
32
+ isUrl: (_url) => (forceSubmit || !isUrl ? true : isUrl(_url)),
33
+ });
34
+
35
+ setTimeout(() => {
36
+ focusEditor(editor, editor.selection ?? undefined);
37
+ }, 0);
38
+
39
+ return true;
40
+ };
@@ -0,0 +1,68 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import {
4
+ PlateEditor,
5
+ UnwrapNodesOptions,
6
+ Value,
7
+ getAboveNode,
8
+ getPluginType,
9
+ isElement,
10
+ splitNodes,
11
+ unwrapNodes,
12
+ withoutNormalizing,
13
+ } from '@udecode/plate';
14
+ import { ELEMENT_BUTTON } from '../createButtonPlugin';
15
+
16
+ /**
17
+ * Unwrap button node.
18
+ */
19
+ export const unwrapButton = <V extends Value>(
20
+ editor: PlateEditor<V>,
21
+ options?: UnwrapNodesOptions & {
22
+ split?: boolean;
23
+ }
24
+ ) => {
25
+ return withoutNormalizing(editor, () => {
26
+ if (options?.split) {
27
+ const buttonAboveAnchor = getAboveNode(editor, {
28
+ at: editor.selection?.anchor,
29
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
30
+ });
31
+
32
+ // anchor in button
33
+ if (buttonAboveAnchor) {
34
+ splitNodes(editor, {
35
+ at: editor.selection?.anchor,
36
+ match: (n) => isElement(n) && n.type === getPluginType(editor, ELEMENT_BUTTON),
37
+ });
38
+ unwrapButton(editor, {
39
+ at: editor.selection?.anchor,
40
+ });
41
+ return true;
42
+ }
43
+ const buttonAboveFocus = getAboveNode(editor, {
44
+ at: editor.selection?.focus,
45
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
46
+ });
47
+
48
+ // focus in button
49
+ if (buttonAboveFocus) {
50
+ splitNodes(editor, {
51
+ at: editor.selection?.focus,
52
+ match: (n) => isElement(n) && n.type === getPluginType(editor, ELEMENT_BUTTON),
53
+ });
54
+ unwrapButton(editor, {
55
+ at: editor.selection?.focus,
56
+ });
57
+ return true;
58
+ }
59
+ }
60
+
61
+ unwrapNodes(editor, {
62
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
63
+ ...options,
64
+ });
65
+
66
+ return;
67
+ });
68
+ };
@@ -0,0 +1,198 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import {
4
+ InsertNodesOptions,
5
+ PlateEditor,
6
+ TNode,
7
+ TNodeEntry,
8
+ UnwrapNodesOptions,
9
+ Value,
10
+ WrapNodesOptions,
11
+ findNode,
12
+ getAboveNode,
13
+ getEditorString,
14
+ getNodeLeaf,
15
+ getNodeProps,
16
+ getPluginOptions,
17
+ getPluginType,
18
+ isDefined,
19
+ isExpanded,
20
+ removeNodes,
21
+ setNodes,
22
+ } from '@udecode/plate';
23
+ import { Path } from 'slate';
24
+ import { ButtonPlugin, ELEMENT_BUTTON } from '../createButtonPlugin';
25
+ import { RichTextButtonStyle, TButtonElement } from '../types';
26
+ import { CreateButtonNodeOptions } from '../utils/index';
27
+ import { insertButton, unwrapButton, upsertButtonText, wrapButton } from '.';
28
+
29
+ export type UpsertButtonOptions<V extends Value = Value> = CreateButtonNodeOptions & {
30
+ /**
31
+ * If true, insert text when selection is in url.
32
+ */
33
+ insertTextInButton?: boolean;
34
+ insertNodesOptions?: InsertNodesOptions<V>;
35
+ unwrapNodesOptions?: UnwrapNodesOptions<V>;
36
+ wrapNodesOptions?: WrapNodesOptions<V>;
37
+ isUrl?: (url: string) => boolean;
38
+ };
39
+
40
+ /**
41
+ * If selection in a button or is not url:
42
+ * - insert text with url, exit
43
+ * If selection is expanded or `update` in a button:
44
+ * - remove button node, get button text
45
+ * Then:
46
+ * - insert button node
47
+ */
48
+ export const upsertButton = <V extends Value>(
49
+ editor: PlateEditor<V>,
50
+ {
51
+ url,
52
+ text,
53
+ buttonStyle,
54
+ target,
55
+ insertTextInButton,
56
+ insertNodesOptions,
57
+ isUrl = getPluginOptions<ButtonPlugin, V>(editor, ELEMENT_BUTTON).isUrl,
58
+ }: UpsertButtonOptions<V>
59
+ ) => {
60
+ const at = editor.selection;
61
+ if (!at) {
62
+ return;
63
+ }
64
+
65
+ const buttonAbove = getAboveNode<TButtonElement>(editor, {
66
+ at,
67
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
68
+ });
69
+
70
+ // anchor and focus in button -> insert text
71
+ if (insertTextInButton && buttonAbove) {
72
+ // we don't want to insert marks in buttons
73
+ editor.insertText(url);
74
+ return true;
75
+ }
76
+
77
+ if (!isUrl?.(url)) {
78
+ return;
79
+ }
80
+
81
+ if (isDefined(text) && text.length === 0) {
82
+ text = url;
83
+ }
84
+
85
+ if (buttonAbove) {
86
+ editButtonUrlAndTarget<V>(url, editor, buttonAbove, target, buttonStyle, text);
87
+ return true;
88
+ }
89
+
90
+ // selection contains at one edge edge or between the edges
91
+ const buttonEntry = findNode<TButtonElement>(editor, {
92
+ at,
93
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
94
+ });
95
+
96
+ const [buttonNode, buttonPath] = buttonEntry ?? [];
97
+
98
+ const shouldReplaceText = shouldReplaceButtonText<V>(editor, buttonPath, text);
99
+
100
+ if (isExpanded(at)) {
101
+ anchorAndFocusInButton<V>(buttonAbove, editor, url, buttonStyle, target, text);
102
+ return true;
103
+ }
104
+
105
+ if (shouldReplaceText) {
106
+ removeNodes(editor, {
107
+ at: buttonPath,
108
+ });
109
+ }
110
+
111
+ const props = getNodeProps(buttonNode ?? ({} as TNode));
112
+
113
+ const path = editor.selection?.focus.path;
114
+ if (!path) {
115
+ return;
116
+ }
117
+
118
+ // button text should have the focused leaf marks
119
+ const leaf = getNodeLeaf(editor, path);
120
+
121
+ // if text is empty, text is url
122
+ if (!text?.length) {
123
+ text = url;
124
+ }
125
+
126
+ insertButton(
127
+ editor,
128
+ {
129
+ ...props,
130
+ url,
131
+ target,
132
+ children: [
133
+ {
134
+ ...leaf,
135
+ text,
136
+ },
137
+ ],
138
+ },
139
+ insertNodesOptions
140
+ );
141
+ return true;
142
+ };
143
+
144
+ function shouldReplaceButtonText<V extends Value>(editor: PlateEditor<V>, buttonPath?: Path, text?: string) {
145
+ return buttonPath && text?.length && text !== getEditorString(editor, buttonPath);
146
+ }
147
+
148
+ function anchorAndFocusInButton<V extends Value>(
149
+ buttonAbove: undefined,
150
+ editor: PlateEditor<V>,
151
+ url: string,
152
+ buttonStyle?: RichTextButtonStyle,
153
+ target?: string,
154
+ text?: string
155
+ ) {
156
+ if (buttonAbove) {
157
+ unwrapButton(editor, {
158
+ at: buttonAbove[1],
159
+ });
160
+ } else {
161
+ unwrapButton(editor, {
162
+ split: true,
163
+ });
164
+ }
165
+
166
+ wrapButton(editor, {
167
+ url,
168
+ buttonStyle,
169
+ target,
170
+ });
171
+
172
+ upsertButtonText(editor, { url, target, text });
173
+ }
174
+
175
+ function editButtonUrlAndTarget<V extends Value>(
176
+ url: string,
177
+ editor: PlateEditor<V>,
178
+ buttonAbove: TNodeEntry<TButtonElement>,
179
+ target?: string,
180
+ buttonStyle?: string,
181
+ text?: string
182
+ ) {
183
+ if (
184
+ url !== buttonAbove[0]?.url ||
185
+ target !== buttonAbove[0]?.target ||
186
+ buttonStyle !== buttonAbove[0]?.buttonStyle
187
+ ) {
188
+ setNodes<TButtonElement>(
189
+ editor,
190
+ { url, target, buttonStyle },
191
+ {
192
+ at: buttonAbove[1],
193
+ }
194
+ );
195
+ }
196
+
197
+ upsertButtonText(editor, { url, text, target });
198
+ }
@@ -0,0 +1,40 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import {
4
+ PlateEditor,
5
+ TText,
6
+ Value,
7
+ getAboveNode,
8
+ getEditorString,
9
+ getPluginType,
10
+ replaceNodeChildren,
11
+ } from '@udecode/plate';
12
+ import { ELEMENT_BUTTON, TButtonElement } from '..';
13
+ import { UpsertButtonOptions } from './upsertButton';
14
+
15
+ /**
16
+ * If the text is different than the button above text, replace button children by a new text.
17
+ * The new text has the same marks than the first text replaced.
18
+ */
19
+ export const upsertButtonText = <V extends Value>(editor: PlateEditor<V>, { text }: UpsertButtonOptions<V>) => {
20
+ const newButton = getAboveNode<TButtonElement>(editor, {
21
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
22
+ });
23
+
24
+ if (newButton) {
25
+ const [newButtonNode, newButtonPath] = newButton;
26
+
27
+ if (text?.length && text !== getEditorString(editor, newButtonPath)) {
28
+ const firstText = newButtonNode.children[0];
29
+
30
+ // remove button children
31
+ replaceNodeChildren<TText>(editor, {
32
+ at: newButtonPath,
33
+ nodes: { ...firstText, text },
34
+ insertOptions: {
35
+ select: true,
36
+ },
37
+ });
38
+ }
39
+ }
40
+ };
@@ -0,0 +1,30 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { PlateEditor, Value, WrapNodesOptions, getPluginType, wrapNodes } from '@udecode/plate';
4
+ import { ELEMENT_BUTTON, RichTextButtonStyle, TButtonElement } from '..';
5
+
6
+ export interface WrapButtonOptions<V extends Value = Value> extends WrapNodesOptions<V> {
7
+ url: string;
8
+ buttonStyle?: RichTextButtonStyle;
9
+ target?: string;
10
+ }
11
+
12
+ /**
13
+ * Wrap a button node with split.
14
+ */
15
+ export const wrapButton = <V extends Value>(
16
+ editor: PlateEditor<V>,
17
+ { url, buttonStyle, target, ...options }: WrapButtonOptions<V>
18
+ ) => {
19
+ wrapNodes<TButtonElement, Value>(
20
+ editor,
21
+ {
22
+ type: getPluginType(editor, ELEMENT_BUTTON),
23
+ url,
24
+ buttonStyle,
25
+ target,
26
+ children: [],
27
+ },
28
+ { split: true, ...options } as WrapNodesOptions
29
+ );
30
+ };
@@ -0,0 +1,13 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { TLinkElement } from '@udecode/plate';
4
+
5
+ export type TButtonElement = TLinkElement & {
6
+ chosenLink?: {
7
+ searchResult?: {
8
+ link?: string;
9
+ };
10
+ };
11
+ };
12
+
13
+ export type RichTextButtonStyle = 'primary' | 'secondary' | 'tertiary';
@@ -0,0 +1,28 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { PlateEditor, TText, Value, getPluginType } from '@udecode/plate';
4
+ import { RichTextButtonStyle, TButtonElement } from '..';
5
+ import { ELEMENT_BUTTON } from '../createButtonPlugin';
6
+
7
+ export interface CreateButtonNodeOptions {
8
+ url: string;
9
+ text?: string;
10
+ buttonStyle?: RichTextButtonStyle;
11
+ target?: string;
12
+ children?: TText[];
13
+ }
14
+
15
+ export const createButtonNode = <V extends Value>(
16
+ editor: PlateEditor<V>,
17
+ { url, text = '', buttonStyle = 'primary', target, children }: CreateButtonNodeOptions
18
+ ): TButtonElement => {
19
+ const type = getPluginType(editor, ELEMENT_BUTTON);
20
+
21
+ return {
22
+ type,
23
+ url,
24
+ target,
25
+ buttonStyle,
26
+ children: children ?? [{ text }],
27
+ };
28
+ };
@@ -0,0 +1,14 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { PlateEditor, getAboveNode } from '@udecode/plate';
4
+ import { ELEMENT_BUTTON, RichTextButtonStyle, TButtonElement } from '..';
5
+
6
+ export const getButtonStyle = (editor: PlateEditor): RichTextButtonStyle => {
7
+ const linkNode = getAboveNode<TButtonElement>(editor, { match: { type: ELEMENT_BUTTON } });
8
+
9
+ if (!Array.isArray(linkNode)) {
10
+ return 'primary';
11
+ }
12
+
13
+ return (linkNode[0]?.buttonStyle as RichTextButtonStyle) || 'primary';
14
+ };
@@ -0,0 +1,18 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { PlateEditor, getAboveNode } from '@udecode/plate';
4
+ import { ELEMENT_BUTTON, TButtonElement } from '..';
5
+
6
+ const getLinkNode = (editor: PlateEditor, cb: (link: TButtonElement) => string): string => {
7
+ const linkNode = getAboveNode<TButtonElement>(editor, { match: { type: ELEMENT_BUTTON } });
8
+
9
+ if (!Array.isArray(linkNode)) {
10
+ return '';
11
+ }
12
+
13
+ return cb(linkNode[0]);
14
+ };
15
+
16
+ export const getUrlFromEditor = (editor: PlateEditor) => {
17
+ return getLinkNode(editor, (link) => link.url ?? '');
18
+ };
@@ -0,0 +1,8 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './getUrl';
4
+ export * from './createButtonNode';
5
+ export * from './triggerFloatingButton';
6
+ export * from './triggerFloatingButtonEdit';
7
+ export * from './triggerFloatingButtonInsert';
8
+ export * from './styles';