@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,30 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { ChangeEventHandler, useCallback, useEffect, useRef } from 'react';
4
+ import { HTMLPropsAs, mergeProps, useComposedRef } from '@udecode/plate';
5
+ import { floatingButtonActions, floatingButtonSelectors, useFloatingButtonSelectors } from './floatingButtonStore';
6
+
7
+ export const useFloatingButtonUrlInput = (props: HTMLPropsAs<'input'>): HTMLPropsAs<'input'> => {
8
+ const updated = useFloatingButtonSelectors().updated();
9
+ const ref = useRef<HTMLInputElement>(null);
10
+
11
+ useEffect(() => {
12
+ if (ref.current && updated) {
13
+ setTimeout(() => {
14
+ ref.current?.focus();
15
+ }, 0);
16
+ }
17
+ }, [updated]);
18
+
19
+ const onChange: ChangeEventHandler<HTMLInputElement> = useCallback((e) => {
20
+ floatingButtonActions.url(e.target.value);
21
+ }, []);
22
+
23
+ return mergeProps(
24
+ {
25
+ onChange,
26
+ defaultValue: floatingButtonSelectors.url(),
27
+ },
28
+ { ...props, ref: useComposedRef<HTMLInputElement>(props.ref, ref) }
29
+ );
30
+ };
@@ -0,0 +1,81 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { FormControl } from '@frontify/fondue';
4
+ import { CSSProperties, ReactElement, ReactNode, useState } from 'react';
5
+ import { InsertModal } from '../../../../LinkPlugin/FloatingLink/InsertLinkModal/InsertModal';
6
+ import { useInsertModal } from './useInsertModal';
7
+ import { BlockStyles } from '../../../../styles';
8
+
9
+ export const InsertButtonModal = () => {
10
+ const modalProps = useInsertModal();
11
+ const { state, onButtonStyleChange } = modalProps;
12
+
13
+ return (
14
+ <InsertModal {...modalProps} testId="floating-button-insert">
15
+ <div className="tw-pt-5">
16
+ <FormControl
17
+ label={{
18
+ children: 'Button Style',
19
+ htmlFor: 'buttonStyle',
20
+ required: true,
21
+ }}
22
+ >
23
+ <HoverableButton
24
+ id="primary"
25
+ styles={BlockStyles.buttonPrimary}
26
+ isActive={state.buttonStyle === 'primary'}
27
+ onClick={() => onButtonStyleChange('primary')}
28
+ >
29
+ {state.text || 'Primary Button'}
30
+ </HoverableButton>
31
+
32
+ <HoverableButton
33
+ id="secondary"
34
+ styles={BlockStyles.buttonSecondary}
35
+ isActive={state.buttonStyle === 'secondary'}
36
+ onClick={() => onButtonStyleChange('secondary')}
37
+ >
38
+ {state.text || 'Secondary Button'}
39
+ </HoverableButton>
40
+
41
+ <HoverableButton
42
+ id="tertiary"
43
+ styles={BlockStyles.buttonTertiary}
44
+ isActive={state.buttonStyle === 'tertiary'}
45
+ onClick={() => onButtonStyleChange('tertiary')}
46
+ >
47
+ {state.text || 'Tertiary Button'}
48
+ </HoverableButton>
49
+ </FormControl>
50
+ </div>
51
+ </InsertModal>
52
+ );
53
+ };
54
+
55
+ type Props = {
56
+ id: string;
57
+ styles?: CSSProperties & { hover?: CSSProperties };
58
+ isActive: boolean;
59
+ onClick: () => void;
60
+ children: ReactNode;
61
+ };
62
+
63
+ const HoverableButton = ({ id, styles, isActive, onClick, children }: Props): ReactElement => {
64
+ const [hovered, setHovered] = useState(false);
65
+ const getStyles = () => (styles && styles.hover && hovered ? { ...styles, ...styles.hover } : styles);
66
+
67
+ return (
68
+ <button
69
+ data-test-id={`floating-button-insert-${id}`}
70
+ onMouseEnter={() => setHovered(true)}
71
+ onMouseLeave={() => setHovered(false)}
72
+ onClick={onClick}
73
+ style={getStyles()}
74
+ className={
75
+ isActive ? 'tw-outline tw-outline-1 tw-outline-violet-60 tw-outline-offset-2 tw-w-fit' : 'tw-w-fit'
76
+ }
77
+ >
78
+ {children}
79
+ </button>
80
+ );
81
+ };
@@ -0,0 +1,13 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { CheckboxState } from '@frontify/fondue';
4
+ import { RichTextButtonStyle } from '../../../types';
5
+
6
+ export type InsertModalDispatchType = { type: string; payload?: Partial<InsertModalStateProps> };
7
+
8
+ export type InsertModalStateProps = {
9
+ url: string;
10
+ text: string;
11
+ buttonStyle: RichTextButtonStyle;
12
+ newTab: CheckboxState;
13
+ };
@@ -0,0 +1,143 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { Dispatch, Reducer, useEffect, useReducer } from 'react';
4
+ import { getPluginOptions, useEditorRef, useHotkeys } from '@udecode/plate';
5
+ import { InsertModalDispatchType, InsertModalStateProps } from './types';
6
+ import { floatingButtonActions, floatingButtonSelectors } from '../floatingButtonStore';
7
+ import { ELEMENT_BUTTON } from '../../../createButtonPlugin';
8
+ import { submitFloatingButton } from '../../../transforms/submitFloatingButton';
9
+ import { RichTextButtonStyle } from '../../../types';
10
+ import { getButtonStyle } from '../../../utils/getButtonStyle';
11
+ import { AppBridgeBlock } from '@frontify/app-bridge';
12
+ import { CheckboxState } from '@frontify/fondue';
13
+ import { addHttps } from '../../../../../../../helpers';
14
+ import { isValidUrlOrEmpty } from '../../../../LinkPlugin/utils/url';
15
+
16
+ const initialState: InsertModalStateProps = {
17
+ url: '',
18
+ text: '',
19
+ buttonStyle: 'primary',
20
+ newTab: CheckboxState.Unchecked,
21
+ };
22
+
23
+ export const InsertModalState = (): [InsertModalStateProps, Dispatch<InsertModalDispatchType>] => {
24
+ const [state, dispatch] = useReducer<Reducer<InsertModalStateProps, InsertModalDispatchType>>((state, action) => {
25
+ const { type, payload } = action;
26
+
27
+ switch (type) {
28
+ case 'NEW_TAB':
29
+ return {
30
+ ...state,
31
+ newTab: CheckboxState.Checked,
32
+ };
33
+ case 'SAME_TAB':
34
+ return {
35
+ ...state,
36
+ newTab: CheckboxState.Unchecked,
37
+ };
38
+ case 'URL':
39
+ case 'TEXT':
40
+ case 'BUTTON_STYLE':
41
+ case 'INIT':
42
+ return {
43
+ ...state,
44
+ ...payload,
45
+ };
46
+ default:
47
+ return state;
48
+ }
49
+ }, initialState);
50
+
51
+ return [state, dispatch];
52
+ };
53
+
54
+ export const useInsertModal = () => {
55
+ const editor = useEditorRef();
56
+ const [state, dispatch] = InsertModalState();
57
+
58
+ useEffect(() => {
59
+ const buttonStyle = getButtonStyle(editor);
60
+
61
+ dispatch({
62
+ type: 'INIT',
63
+ payload: {
64
+ text: floatingButtonSelectors.text(),
65
+ buttonStyle,
66
+ newTab: floatingButtonSelectors.newTab() ? CheckboxState.Checked : CheckboxState.Unchecked,
67
+ url: floatingButtonSelectors.url(),
68
+ },
69
+ });
70
+ }, [dispatch, editor]);
71
+
72
+ const onTextChange = (value: string) => {
73
+ dispatch({
74
+ type: 'TEXT',
75
+ payload: { text: value },
76
+ });
77
+ };
78
+
79
+ const onButtonStyleChange = (value: RichTextButtonStyle) => {
80
+ dispatch({
81
+ type: 'BUTTON_STYLE',
82
+ payload: { buttonStyle: value },
83
+ });
84
+ };
85
+
86
+ const onUrlChange = (value: string) => {
87
+ dispatch({
88
+ type: 'URL',
89
+ payload: { url: value },
90
+ });
91
+ };
92
+
93
+ const onToggleTab = (checked: boolean) => {
94
+ checked ? dispatch({ type: 'NEW_TAB' }) : dispatch({ type: 'SAME_TAB' });
95
+ };
96
+
97
+ const onCancel = () => {
98
+ floatingButtonActions.hide();
99
+ };
100
+
101
+ const onSave = (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | KeyboardEvent | undefined) => {
102
+ if (!isValidUrlOrEmpty(state.url) || !hasValues) {
103
+ return;
104
+ }
105
+
106
+ const urlToSave = addHttps(state.url);
107
+
108
+ floatingButtonActions.text(state.text);
109
+ floatingButtonActions.url(urlToSave);
110
+ floatingButtonActions.buttonStyle(state.buttonStyle);
111
+ floatingButtonActions.newTab(state.newTab === CheckboxState.Checked);
112
+
113
+ if (submitFloatingButton(editor)) {
114
+ event?.preventDefault();
115
+ }
116
+ };
117
+
118
+ const hasValues = state.url !== '' && state.text !== '';
119
+
120
+ const { appBridge } = getPluginOptions<{ appBridge: AppBridgeBlock }>(editor, ELEMENT_BUTTON);
121
+
122
+ useHotkeys(
123
+ 'enter',
124
+ onSave,
125
+ {
126
+ enableOnFormTags: ['INPUT'],
127
+ },
128
+ []
129
+ );
130
+
131
+ return {
132
+ state,
133
+ onTextChange,
134
+ onButtonStyleChange,
135
+ onUrlChange,
136
+ onToggleTab,
137
+ onCancel,
138
+ onSave,
139
+ hasValues,
140
+ isValidUrlOrEmpty,
141
+ appBridge,
142
+ };
143
+ };
@@ -0,0 +1,31 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { useCallback } from 'react';
4
+ import {
5
+ AsProps,
6
+ Button,
7
+ HTMLPropsAs,
8
+ createComponentAs,
9
+ createElementAs,
10
+ focusEditor,
11
+ useEditorRef,
12
+ } from '@udecode/plate';
13
+ import { unwrapButton } from '../../transforms/index';
14
+
15
+ export const useUnlinkButton = (props: HTMLPropsAs<'button'>): HTMLPropsAs<'button'> => {
16
+ const editor = useEditorRef();
17
+
18
+ return {
19
+ onClick: useCallback(() => {
20
+ unwrapButton(editor);
21
+ focusEditor(editor, editor.selection ?? undefined);
22
+ }, [editor]),
23
+ ...props,
24
+ };
25
+ };
26
+
27
+ export const UnlinkButton = createComponentAs<AsProps<'button'>>((props) => {
28
+ const htmlProps = useUnlinkButton(props);
29
+
30
+ return createElementAs(Button, htmlProps);
31
+ });
@@ -0,0 +1,46 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { createStore } from '@udecode/plate';
4
+ import { RichTextButtonStyle } from '../../types';
5
+
6
+ export type FloatingButtonMode = '' | 'insert' | 'edit';
7
+
8
+ export const floatingButtonStore: any = createStore('floatingButton')({
9
+ openEditorId: null as null | string,
10
+ mouseDown: false,
11
+ updated: false,
12
+ url: '',
13
+ text: '',
14
+ buttonStyle: 'primary' as RichTextButtonStyle,
15
+ newTab: false,
16
+ mode: '' as FloatingButtonMode,
17
+ isEditing: false,
18
+ })
19
+ .extendActions((set) => ({
20
+ reset: () => {
21
+ set.url('');
22
+ set.text('');
23
+ set.buttonStyle('primary');
24
+ set.newTab(false);
25
+ set.mode('');
26
+ set.isEditing(false);
27
+ },
28
+ }))
29
+ .extendActions((set) => ({
30
+ show: (mode: FloatingButtonMode, editorId: string) => {
31
+ set.mode(mode);
32
+ set.isEditing(false);
33
+ set.openEditorId(editorId);
34
+ },
35
+ hide: () => {
36
+ set.reset();
37
+ set.openEditorId(null);
38
+ },
39
+ }))
40
+ .extendSelectors((state) => ({
41
+ isOpen: (editorId: string) => state.openEditorId === editorId,
42
+ }));
43
+
44
+ export const floatingButtonActions = floatingButtonStore.set;
45
+ export const floatingButtonSelectors = floatingButtonStore.get;
46
+ export const useFloatingButtonSelectors = () => floatingButtonStore.use;
@@ -0,0 +1,12 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './FloatingButton';
4
+ export * from './FloatingButtonEditButton';
5
+ export * from './FloatingButtonUrlInput';
6
+ export * from './UnlinkButton';
7
+ export * from './floatingButtonStore';
8
+ export * from './useFloatingButtonEdit';
9
+ export * from './useFloatingButtonEnter';
10
+ export * from './useFloatingButtonEscape';
11
+ export * from './useFloatingButtonInsert';
12
+ export * from './useVirtualFloatingButton';
@@ -0,0 +1,113 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import {
4
+ HTMLPropsAs,
5
+ getAboveNode,
6
+ getDefaultBoundingClientRect,
7
+ getEndPoint,
8
+ getPluginOptions,
9
+ getPluginType,
10
+ getRangeBoundingClientRect,
11
+ getStartPoint,
12
+ someNode,
13
+ useComposedRef,
14
+ useEditorRef,
15
+ useHotkeys,
16
+ usePlateSelectors,
17
+ } from '@udecode/plate';
18
+ import { useCallback, useEffect } from 'react';
19
+ import { ButtonPlugin, ELEMENT_BUTTON } from '../../createButtonPlugin';
20
+ import { getUrlFromEditor } from '../../utils';
21
+ import { triggerFloatingButtonEdit } from '../../utils/triggerFloatingButtonEdit';
22
+ import {
23
+ FloatingButtonProps,
24
+ floatingButtonActions,
25
+ floatingButtonSelectors,
26
+ useFloatingButtonEnter,
27
+ useFloatingButtonEscape,
28
+ useFloatingButtonSelectors,
29
+ useVirtualFloatingButton,
30
+ } from '.';
31
+
32
+ export const useFloatingButtonEdit = ({ floatingOptions, ...props }: FloatingButtonProps): HTMLPropsAs<'div'> => {
33
+ const editor = useEditorRef();
34
+ const keyEditor = usePlateSelectors(editor.id).keyEditor();
35
+ const mode = useFloatingButtonSelectors().mode();
36
+ const open = useFloatingButtonSelectors().isOpen(editor.id);
37
+
38
+ const { triggerFloatingButtonHotkeys } = getPluginOptions<ButtonPlugin>(editor, ELEMENT_BUTTON);
39
+
40
+ const getBoundingClientRect = useCallback(() => {
41
+ const entry = getAboveNode(editor, {
42
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
43
+ });
44
+
45
+ if (entry) {
46
+ const [, path] = entry;
47
+ return getRangeBoundingClientRect(editor, {
48
+ anchor: getStartPoint(editor, path),
49
+ focus: getEndPoint(editor, path),
50
+ });
51
+ }
52
+
53
+ return getDefaultBoundingClientRect();
54
+ }, [editor]);
55
+
56
+ const isOpen = open && mode === 'edit';
57
+
58
+ const { update, style, floating } = useVirtualFloatingButton({
59
+ open: isOpen,
60
+ getBoundingClientRect,
61
+ ...floatingOptions,
62
+ });
63
+
64
+ useEffect(() => {
65
+ const url = getUrlFromEditor(editor);
66
+ if (url) {
67
+ floatingButtonActions.url(url);
68
+ }
69
+
70
+ if (
71
+ editor.selection &&
72
+ someNode(editor, {
73
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
74
+ })
75
+ ) {
76
+ floatingButtonActions.show('edit', editor.id);
77
+ update();
78
+ return;
79
+ }
80
+
81
+ if (floatingButtonSelectors.mode() === 'edit') {
82
+ floatingButtonActions.hide();
83
+ }
84
+ }, [editor, keyEditor, update]);
85
+
86
+ useHotkeys(
87
+ triggerFloatingButtonHotkeys,
88
+ (e) => {
89
+ e.preventDefault();
90
+
91
+ if (floatingButtonSelectors.mode() === 'edit') {
92
+ triggerFloatingButtonEdit(editor);
93
+ }
94
+ },
95
+ {
96
+ enableOnContentEditable: true,
97
+ },
98
+ []
99
+ );
100
+
101
+ useFloatingButtonEnter();
102
+
103
+ useFloatingButtonEscape();
104
+
105
+ return {
106
+ style: {
107
+ ...style,
108
+ zIndex: 1000,
109
+ },
110
+ ...props,
111
+ ref: useComposedRef<HTMLElement | null>(props.ref, floating),
112
+ };
113
+ };
@@ -0,0 +1,21 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { useEditorRef, useHotkeys } from '@udecode/plate';
4
+ import { submitFloatingButton } from '../../transforms/submitFloatingButton';
5
+
6
+ export const useFloatingButtonEnter = () => {
7
+ const editor = useEditorRef();
8
+
9
+ useHotkeys(
10
+ '*',
11
+ (e) => {
12
+ if (e.key === 'Enter' && submitFloatingButton(editor)) {
13
+ e.preventDefault();
14
+ }
15
+ },
16
+ {
17
+ enableOnFormTags: ['INPUT'],
18
+ },
19
+ []
20
+ );
21
+ };
@@ -0,0 +1,30 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { focusEditor, useEditorRef, useHotkeys } from '@udecode/plate';
4
+ import { floatingButtonActions, floatingButtonSelectors } from './floatingButtonStore';
5
+
6
+ export const useFloatingButtonEscape = () => {
7
+ const editor = useEditorRef();
8
+
9
+ useHotkeys(
10
+ 'escape',
11
+ () => {
12
+ if (floatingButtonSelectors.mode() !== 'edit') {
13
+ return;
14
+ }
15
+
16
+ if (floatingButtonSelectors.isEditing()) {
17
+ floatingButtonActions.show('edit', editor.id);
18
+ focusEditor(editor, editor.selection ?? undefined);
19
+ return;
20
+ }
21
+
22
+ floatingButtonActions.hide();
23
+ },
24
+ {
25
+ enableOnFormTags: ['INPUT'],
26
+ enableOnContentEditable: true,
27
+ },
28
+ []
29
+ );
30
+ };
@@ -0,0 +1,71 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import {
4
+ HTMLPropsAs,
5
+ getPluginOptions,
6
+ getSelectionBoundingClientRect,
7
+ useComposedRef,
8
+ useEditorRef,
9
+ useHotkeys,
10
+ } from '@udecode/plate';
11
+ import { useEffect } from 'react';
12
+ import { useFocused } from 'slate-react';
13
+ import { ButtonPlugin, ELEMENT_BUTTON } from '../../createButtonPlugin';
14
+ import { triggerFloatingButtonInsert } from '../../utils/triggerFloatingButtonInsert';
15
+ import {
16
+ FloatingButtonProps,
17
+ floatingButtonActions,
18
+ useFloatingButtonEscape,
19
+ useFloatingButtonSelectors,
20
+ useVirtualFloatingButton,
21
+ } from '.';
22
+
23
+ export const useFloatingButtonInsert = ({ floatingOptions, ...props }: FloatingButtonProps): HTMLPropsAs<'div'> => {
24
+ const editor = useEditorRef();
25
+ const focused = useFocused();
26
+ const mode = useFloatingButtonSelectors().mode();
27
+ const open = useFloatingButtonSelectors().isOpen(editor.id);
28
+
29
+ const { triggerFloatingButtonHotkeys } = getPluginOptions<ButtonPlugin>(editor, ELEMENT_BUTTON);
30
+
31
+ useHotkeys(
32
+ triggerFloatingButtonHotkeys,
33
+ (e) => {
34
+ e.preventDefault();
35
+
36
+ triggerFloatingButtonInsert(editor, {
37
+ focused,
38
+ });
39
+ },
40
+ {
41
+ enableOnContentEditable: true,
42
+ },
43
+ [focused]
44
+ );
45
+
46
+ const { update, style, floating } = useVirtualFloatingButton({
47
+ open: open && mode === 'insert',
48
+ getBoundingClientRect: getSelectionBoundingClientRect,
49
+ whileElementsMounted: undefined,
50
+ ...floatingOptions,
51
+ });
52
+
53
+ // wait for update before focusing input
54
+ useEffect(() => {
55
+ if (open) {
56
+ update();
57
+ }
58
+ floatingButtonActions.updated(open);
59
+ }, [open, update]);
60
+
61
+ useFloatingButtonEscape();
62
+
63
+ return {
64
+ style: {
65
+ ...style,
66
+ zIndex: 1000,
67
+ },
68
+ ...props,
69
+ ref: useComposedRef<HTMLElement | null>(props.ref, floating),
70
+ };
71
+ };
@@ -0,0 +1,22 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { UseVirtualFloatingOptions, flip, offset, useVirtualFloating } from '@udecode/plate';
4
+
5
+ const OFFSET_Y = 12;
6
+ const OFFSET_X = -22;
7
+ const PADDING = 96;
8
+
9
+ export const useVirtualFloatingButton = (floatingOptions?: UseVirtualFloatingOptions) =>
10
+ useVirtualFloating({
11
+ placement: 'bottom-start',
12
+ middleware: [
13
+ offset({
14
+ mainAxis: OFFSET_Y,
15
+ alignmentAxis: OFFSET_X,
16
+ }),
17
+ flip({
18
+ padding: PADDING,
19
+ }),
20
+ ],
21
+ ...floatingOptions,
22
+ });
@@ -0,0 +1,3 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './FloatingButton/index';