@platecms/delta-smart-text 0.6.0 → 0.7.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.
- package/.env.example +7 -0
- package/__generated__/fragment-masking.ts +87 -0
- package/__generated__/gql.ts +238 -0
- package/__generated__/graphql.ts +3441 -0
- package/__generated__/index.ts +2 -0
- package/codegen.config.ts +24 -0
- package/eslint.config.mjs +43 -0
- package/i18n.js +28 -0
- package/package.json +5 -5
- package/project.json +54 -0
- package/src/components/DeltaSlateEditor.vue +74 -0
- package/src/components/icon/FontAwesomeIcon.vue +21 -0
- package/src/graphql/apiTokens/apiTokens.fragments.gql +8 -0
- package/src/graphql/assets/assets.fragments.gql +10 -0
- package/src/graphql/blueprints/blueprints.fragments.gql +52 -0
- package/src/graphql/buildingBlockFieldFulfillments/buildingBlockFieldFullfillment.fragements.gql +6 -0
- package/src/graphql/buildingBlockFields/buildingBlockField.fragments.gql +8 -0
- package/src/graphql/buildingBlocks/buildingBlocks.fragments.gql +11 -0
- package/src/graphql/channels/channels.fragments.gql +9 -0
- package/src/graphql/contentExperiences/allContentExperiences.query.gql +24 -0
- package/src/graphql/contentExperiences/contentExperience.query.gql +20 -0
- package/src/graphql/contentExperiences/contentExperiences.fragments.gql +14 -0
- package/src/graphql/contentFields/contentFields.fragments.gql +7 -0
- package/src/graphql/contentItems/allContentItems.query.gql +48 -0
- package/src/graphql/contentItems/contentItems.fragments.gql +11 -0
- package/src/graphql/contentTypes/allContentTypes.query.gql +26 -0
- package/src/graphql/contentTypes/contentTypes.fragments.gql +11 -0
- package/src/graphql/contentValidations/contentValidationRule.fragments.gql +34 -0
- package/src/graphql/contentValues/allContentValues.query.gql +41 -0
- package/src/graphql/contentValues/contentValues.fragments.gql +9 -0
- package/src/graphql/contentValues/createContentValue.mutation.gql +17 -0
- package/src/graphql/experienceComponents/experienceComponent.fragments.gql +13 -0
- package/src/graphql/fragments.gql +6 -0
- package/src/graphql/gridDefinition/gridDefinition.fragments.gql +5 -0
- package/src/graphql/gridPlacements/gridPlacement.fragments.gql +7 -0
- package/src/graphql/grids/grid.fragments.gql +7 -0
- package/src/graphql/invitations/invitations.fragments.gql +7 -0
- package/src/graphql/organizations/organizations.fragments.gql +13 -0
- package/src/graphql/pathParts/pathParts.fragments.gql +19 -0
- package/src/graphql/plateMaintainers/plateMaintainer.fragements.gql +10 -0
- package/src/graphql/roleAssignments/roleAssignment.fragments.gql +9 -0
- package/src/graphql/roles/roles.fragments.gql +7 -0
- package/src/graphql/subject/subject.fragments.gql +8 -0
- package/src/graphql/tags/tags.fragments.gql +17 -0
- package/src/graphql/themes/themes.fragments.gql +8 -0
- package/src/index.css +1 -0
- package/src/index.ts +21 -0
- package/src/locales/en.json +52 -0
- package/src/locales/nl.json +52 -0
- package/src/react/components/DeltaSlateEditor.tsx +243 -0
- package/src/react/components/DeltaSlateEditorConnector.tsx +50 -0
- package/src/react/components/Element.spec.tsx +244 -0
- package/src/react/components/Element.tsx +151 -0
- package/src/react/components/FontAwesomeIcon.tsx +17 -0
- package/src/react/components/Leaf.spec.tsx +61 -0
- package/src/react/components/Leaf.tsx +22 -0
- package/src/react/components/elements/CodeElement.tsx +16 -0
- package/src/react/components/elements/ContentValueElement.tsx +33 -0
- package/src/react/components/elements/LinkElement.tsx +44 -0
- package/src/react/components/inputs/SearchInput.tsx +22 -0
- package/src/react/components/inputs/TextInput.tsx +30 -0
- package/src/react/components/menus/ContentAndFormatMenu.tsx +272 -0
- package/src/react/components/menus/ContentLibraryMenu.tsx +48 -0
- package/src/react/components/menus/ReusableContentMenu.tsx +190 -0
- package/src/react/components/menus/content/ContentItemsMenu.tsx +215 -0
- package/src/react/components/menus/content/ContentTypesMenu.tsx +129 -0
- package/src/react/components/menus/content/partials/ContentFieldMenuItem.tsx +11 -0
- package/src/react/components/menus/content/partials/ContentValueMenuItem.tsx +58 -0
- package/src/react/components/menus/link/AnchorInput.tsx +123 -0
- package/src/react/components/menus/link/LinkInput.tsx +195 -0
- package/src/react/components/menus/link/LinkMenu.spec.tsx +145 -0
- package/src/react/components/menus/link/LinkMenu.tsx +289 -0
- package/src/react/components/menus/partials/MenuButton.tsx +52 -0
- package/src/react/components/menus/partials/MenuContainer.tsx +9 -0
- package/src/react/components/menus/partials/MenuHeader.tsx +11 -0
- package/src/react/components/toolbar/Toolbar.tsx +249 -0
- package/src/react/components/toolbar/ToolbarBlockButton.tsx +31 -0
- package/src/react/components/toolbar/ToolbarHeadingDropdownButton.tsx +76 -0
- package/src/react/components/toolbar/ToolbarLinkButton.tsx +33 -0
- package/src/react/components/toolbar/ToolbarMarkButton.tsx +25 -0
- package/src/react/components/toolbar/content/ContentExtractToolbarButton.tsx +68 -0
- package/src/react/components/toolbar/content/ContentLibraryToolbarButton.tsx +43 -0
- package/src/react/components/toolbar/content/ContentToolbar.tsx +37 -0
- package/src/react/components/toolbar/link/ToolbarDisplayLink.tsx +36 -0
- package/src/react/components/toolbar/link/UnlinkButton.tsx +25 -0
- package/src/react/config/hotkeys.ts +8 -0
- package/src/react/plugins/index.ts +59 -0
- package/src/react/store/editorSlice.ts +124 -0
- package/src/react/store/store.ts +12 -0
- package/src/react/types.ts +87 -0
- package/src/react/utils/decorator.ts +61 -0
- package/src/react/utils/index.ts +110 -0
- package/src/vue-shims.d.ts +5 -0
- package/tsconfig.json +26 -0
- package/tsconfig.lib.json +25 -0
- package/tsconfig.spec.json +22 -0
- package/vite.config.ts +67 -0
- package/components/DeltaSlateEditor.vue.d.ts +0 -26
- package/index.cjs +0 -381
- package/index.css +0 -1
- package/index.d.ts +0 -12
- package/index.js +0 -49254
- package/react/components/DeltaSlateEditor.d.ts +0 -7
- package/react/components/DeltaSlateEditorConnector.d.ts +0 -12
- package/react/components/Element.d.ts +0 -8
- package/react/components/FontAwesomeIcon.d.ts +0 -6
- package/react/components/Leaf.d.ts +0 -3
- package/react/components/elements/CodeElement.d.ts +0 -8
- package/react/components/elements/ContentValueElement.d.ts +0 -8
- package/react/components/elements/LinkElement.d.ts +0 -8
- package/react/components/inputs/SearchInput.d.ts +0 -5
- package/react/components/inputs/TextInput.d.ts +0 -7
- package/react/components/menus/ContentAndFormatMenu.d.ts +0 -10
- package/react/components/menus/ContentLibraryMenu.d.ts +0 -4
- package/react/components/menus/ReusableContentMenu.d.ts +0 -3
- package/react/components/menus/content/ContentItemsMenu.d.ts +0 -5
- package/react/components/menus/content/ContentTypesMenu.d.ts +0 -6
- package/react/components/menus/content/partials/ContentFieldMenuItem.d.ts +0 -6
- package/react/components/menus/content/partials/ContentValueMenuItem.d.ts +0 -7
- package/react/components/menus/link/AnchorInput.d.ts +0 -8
- package/react/components/menus/link/LinkInput.d.ts +0 -11
- package/react/components/menus/link/LinkMenu.d.ts +0 -18
- package/react/components/menus/partials/MenuButton.d.ts +0 -7
- package/react/components/menus/partials/MenuContainer.d.ts +0 -4
- package/react/components/menus/partials/MenuHeader.d.ts +0 -5
- package/react/components/toolbar/Toolbar.d.ts +0 -6
- package/react/components/toolbar/ToolbarBlockButton.d.ts +0 -12
- package/react/components/toolbar/ToolbarHeadingDropdownButton.d.ts +0 -2
- package/react/components/toolbar/ToolbarLinkButton.d.ts +0 -6
- package/react/components/toolbar/ToolbarMarkButton.d.ts +0 -6
- package/react/components/toolbar/content/ContentExtractToolbarButton.d.ts +0 -2
- package/react/components/toolbar/content/ContentLibraryToolbarButton.d.ts +0 -5
- package/react/components/toolbar/content/ContentToolbar.d.ts +0 -4
- package/react/components/toolbar/link/ToolbarDisplayLink.d.ts +0 -2
- package/react/components/toolbar/link/UnlinkButton.d.ts +0 -2
- package/react/config/hotkeys.d.ts +0 -2
- package/react/plugins/index.d.ts +0 -3
- package/react/store/editorSlice.d.ts +0 -169
- package/react/store/store.d.ts +0 -5
- package/react/types.d.ts +0 -65
- package/react/utils/decorator.d.ts +0 -15
- package/react/utils/index.d.ts +0 -17
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Editor, NodeEntry, Range, Text } from "slate";
|
|
2
|
+
import Prism, { Token } from "prismjs";
|
|
3
|
+
|
|
4
|
+
export function withContentValues(editor: Editor): Editor {
|
|
5
|
+
const { isInline, isVoid, markableVoid } = editor;
|
|
6
|
+
|
|
7
|
+
editor.isInline = (element): boolean =>
|
|
8
|
+
element.type === "contentValue" || element.type === "link" ? true : isInline(element);
|
|
9
|
+
|
|
10
|
+
editor.isVoid = (element): boolean => (element.type === "contentValue" ? true : isVoid(element));
|
|
11
|
+
|
|
12
|
+
editor.markableVoid = (element): boolean => (element.type === "contentValue" ? true : markableVoid(element));
|
|
13
|
+
|
|
14
|
+
return editor;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function decorate([node, path]: NodeEntry): Range[] {
|
|
18
|
+
const ranges: Range[] = [];
|
|
19
|
+
|
|
20
|
+
if (!Text.isText(node)) {
|
|
21
|
+
return ranges;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getLength(token: Token | string): number {
|
|
25
|
+
if (typeof token === "string") {
|
|
26
|
+
return token.length;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof token.content === "string") {
|
|
30
|
+
return token.content.length;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (Array.isArray(token.content)) {
|
|
34
|
+
return token.content.reduce((length, tokenContent): number => length + getLength(tokenContent), 0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const tokens = Prism.tokenize(node.text, Prism.languages.markdown);
|
|
41
|
+
let start = 0;
|
|
42
|
+
|
|
43
|
+
for (const token of tokens) {
|
|
44
|
+
const length = getLength(token);
|
|
45
|
+
const end = start + length;
|
|
46
|
+
|
|
47
|
+
if (typeof token !== "string") {
|
|
48
|
+
ranges.push({
|
|
49
|
+
[token.type]: true,
|
|
50
|
+
anchor: { path, offset: start },
|
|
51
|
+
focus: { path, offset: end },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
start = end;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return ranges;
|
|
59
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { BaseSelection, Descendant } from "slate";
|
|
2
|
+
import { createSelector, createSlice } from "@reduxjs/toolkit";
|
|
3
|
+
import { ContentItem, ContentType, Organization } from "../../../__generated__/graphql";
|
|
4
|
+
|
|
5
|
+
interface EditorSlice {
|
|
6
|
+
organization: Organization | undefined;
|
|
7
|
+
states: EditorState[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface EditorState {
|
|
11
|
+
uuid: string;
|
|
12
|
+
search: string;
|
|
13
|
+
target: BaseSelection | undefined;
|
|
14
|
+
initialValue: Descendant[];
|
|
15
|
+
value: Descendant[];
|
|
16
|
+
selected: {
|
|
17
|
+
contentType: string | undefined;
|
|
18
|
+
contentItem: string | undefined;
|
|
19
|
+
};
|
|
20
|
+
options: {
|
|
21
|
+
contentTypes: ContentType[];
|
|
22
|
+
contentItems: Record<string, ContentItem[]>;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const defaultEditorFieldState: Omit<EditorState, "uuid"> = {
|
|
27
|
+
search: "",
|
|
28
|
+
initialValue: [],
|
|
29
|
+
value: [],
|
|
30
|
+
target: undefined,
|
|
31
|
+
selected: {
|
|
32
|
+
contentType: undefined,
|
|
33
|
+
contentItem: undefined,
|
|
34
|
+
},
|
|
35
|
+
options: {
|
|
36
|
+
contentTypes: [],
|
|
37
|
+
contentItems: {},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const initialState: EditorSlice = {
|
|
42
|
+
organization: undefined,
|
|
43
|
+
states: [],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function selectStates(state: EditorSlice): EditorState[] {
|
|
47
|
+
return state.states;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function selectStateIndex(state: EditorSlice, uuid: string): number {
|
|
51
|
+
return state.states.findIndex((stateToFilter) => stateToFilter.uuid === uuid);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function selectCurrentOrganization(state: EditorSlice): Organization | undefined {
|
|
55
|
+
return state.organization;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const selectStateById = createSelector([selectStates, selectStateIndex], (states, index) => states[index]);
|
|
59
|
+
|
|
60
|
+
const editorSlice = createSlice({
|
|
61
|
+
name: "editor",
|
|
62
|
+
initialState,
|
|
63
|
+
reducers: {
|
|
64
|
+
setOrganization: (state, action: { payload: { organization: Organization | undefined } }) => {
|
|
65
|
+
state.organization = action.payload.organization;
|
|
66
|
+
},
|
|
67
|
+
setState: (
|
|
68
|
+
state,
|
|
69
|
+
action: { payload: { uuid: string; state?: Partial<Omit<EditorState, "uuid">>; context?: string } },
|
|
70
|
+
) => {
|
|
71
|
+
const currentState = state.states.find((stateToFilter) => stateToFilter.uuid === action.payload.uuid);
|
|
72
|
+
|
|
73
|
+
if (!currentState) {
|
|
74
|
+
state.states.push({
|
|
75
|
+
uuid: action.payload.uuid,
|
|
76
|
+
...defaultEditorFieldState,
|
|
77
|
+
...action.payload.state,
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (action.payload.state?.search !== undefined) {
|
|
83
|
+
currentState.search = action.payload.state.search;
|
|
84
|
+
}
|
|
85
|
+
if (action.payload.state?.selected) {
|
|
86
|
+
currentState.selected = action.payload.state.selected;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (action.payload.state && "target" in action.payload.state) {
|
|
90
|
+
currentState.target = action.payload.state.target;
|
|
91
|
+
}
|
|
92
|
+
currentState.value = action.payload.state?.value ?? currentState.value;
|
|
93
|
+
currentState.initialValue = action.payload.state?.initialValue ?? currentState.initialValue;
|
|
94
|
+
},
|
|
95
|
+
setInitialValue: (state, action: { payload: { uuid: string; initialValue: Descendant[] } }) => {
|
|
96
|
+
const currentState = state.states.find((stateToFilter) => stateToFilter.uuid === action.payload.uuid);
|
|
97
|
+
|
|
98
|
+
if (!currentState) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
currentState.initialValue = action.payload.initialValue;
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
selectors: {
|
|
106
|
+
selectStates,
|
|
107
|
+
selectStateIndex,
|
|
108
|
+
selectStateById,
|
|
109
|
+
selectCurrentOrganization,
|
|
110
|
+
selectStateByIdTarget: createSelector([selectStateById], (state) => state.target),
|
|
111
|
+
selectStateByIdSelected: createSelector([selectStateById], (state) => state.selected),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
export const {
|
|
116
|
+
selectStateById: selectById,
|
|
117
|
+
selectStateByIdTarget: selectByIdTarget,
|
|
118
|
+
selectStateByIdSelected: selectByIdSelected,
|
|
119
|
+
selectCurrentOrganization: selectOrganization,
|
|
120
|
+
} = editorSlice.selectors;
|
|
121
|
+
|
|
122
|
+
export const { setState, setOrganization, setInitialValue } = editorSlice.actions;
|
|
123
|
+
|
|
124
|
+
export default editorSlice.reducer;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Action, Store, ThunkAction, configureStore } from "@reduxjs/toolkit";
|
|
2
|
+
import editorReducer from "./editorSlice";
|
|
3
|
+
|
|
4
|
+
export const store: Store = configureStore({
|
|
5
|
+
reducer: {
|
|
6
|
+
editor: editorReducer,
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
11
|
+
export type AppDispatch = typeof store.dispatch;
|
|
12
|
+
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action<string>>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { BaseEditor } from "slate";
|
|
2
|
+
import { ReactEditor } from "slate-react";
|
|
3
|
+
import { Root } from "@platecms/delta-cast";
|
|
4
|
+
|
|
5
|
+
declare module "slate" {
|
|
6
|
+
interface CustomTypes {
|
|
7
|
+
Editor: BaseEditor & ReactEditor;
|
|
8
|
+
Element: DeltaElement;
|
|
9
|
+
Text: DeltaLeaf;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DeltaLeafMarkdown {
|
|
14
|
+
title?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type DeltaElement =
|
|
18
|
+
| BlockquoteElement
|
|
19
|
+
| CodeElement
|
|
20
|
+
| ContentValueElement
|
|
21
|
+
| HeadingElement
|
|
22
|
+
| LinkElement
|
|
23
|
+
| ListElement
|
|
24
|
+
| ListItemElement
|
|
25
|
+
| ParagraphElement;
|
|
26
|
+
|
|
27
|
+
export type DeltaLeaf = DeltaLeafMarkdown & {
|
|
28
|
+
text: string;
|
|
29
|
+
bold?: true;
|
|
30
|
+
italic?: true;
|
|
31
|
+
underline?: true;
|
|
32
|
+
strikethrough?: true;
|
|
33
|
+
inlineCode?: true;
|
|
34
|
+
highlight?: true;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface ParagraphElement {
|
|
38
|
+
type: "paragraph";
|
|
39
|
+
children: (ContentValueElement | DeltaLeaf | LinkElement)[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface HeadingElement {
|
|
43
|
+
type: "heading";
|
|
44
|
+
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
45
|
+
children: DeltaLeaf[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ListElement {
|
|
49
|
+
type: "list";
|
|
50
|
+
ordered: boolean;
|
|
51
|
+
children: ListItemElement[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ListItemElement {
|
|
55
|
+
type: "listItem";
|
|
56
|
+
children: DeltaLeaf[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface BlockquoteElement {
|
|
60
|
+
type: "blockquote";
|
|
61
|
+
children: DeltaLeaf[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CodeElement {
|
|
65
|
+
type: "code";
|
|
66
|
+
children: DeltaLeaf[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ContentValueElement {
|
|
70
|
+
type: "contentValue";
|
|
71
|
+
prn: string;
|
|
72
|
+
root: Root | undefined;
|
|
73
|
+
children: DeltaLeaf[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface LinkElement {
|
|
77
|
+
type: "link";
|
|
78
|
+
url: string;
|
|
79
|
+
children: DeltaLeaf[];
|
|
80
|
+
|
|
81
|
+
target?: "_parent" | "_top" | "blank" | "self";
|
|
82
|
+
|
|
83
|
+
internal?: {
|
|
84
|
+
prn: string;
|
|
85
|
+
anchor?: boolean;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { DeltaLeaf } from "../types";
|
|
2
|
+
|
|
3
|
+
export type Decorator<T> = (element: T, decorations: string[]) => string[];
|
|
4
|
+
export type LeafDecorator = Decorator<DeltaLeaf>;
|
|
5
|
+
|
|
6
|
+
function fontWeight(leaf: DeltaLeaf, decorations: string[]): string[] {
|
|
7
|
+
if (leaf.bold) {
|
|
8
|
+
decorations.push("font-bold");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return decorations;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function fontSize(leaf: DeltaLeaf, decorations: string[]): string[] {
|
|
15
|
+
if (leaf.title) {
|
|
16
|
+
decorations.push(`text-2xl`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return decorations;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function fontStyle(leaf: DeltaLeaf, decorations: string[]): string[] {
|
|
23
|
+
if (leaf.italic) {
|
|
24
|
+
decorations.push("italic");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return decorations;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function textDecoration(leaf: DeltaLeaf, decorations: string[]): string[] {
|
|
31
|
+
if (leaf.underline) {
|
|
32
|
+
decorations.push("underline");
|
|
33
|
+
}
|
|
34
|
+
if (leaf.strikethrough) {
|
|
35
|
+
decorations.push("line-through");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return decorations;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class DecoratorBuilder<T extends object> {
|
|
42
|
+
private readonly decorators: Decorator<T>[] = [];
|
|
43
|
+
|
|
44
|
+
public constructor(private readonly element: T) {}
|
|
45
|
+
|
|
46
|
+
public add(decorator: Decorator<T>): DecoratorBuilder<T> {
|
|
47
|
+
this.decorators.push(decorator);
|
|
48
|
+
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public build(): (decorations: string[]) => string[] {
|
|
53
|
+
return (decorations: string[]): string[] => {
|
|
54
|
+
this.decorators.forEach((decorator) => decorator(this.element, decorations));
|
|
55
|
+
|
|
56
|
+
return decorations;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { fontSize, fontWeight, fontStyle, textDecoration };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BaseEditor, Editor, Element, Node, Transforms } from "slate";
|
|
2
|
+
import { DeltaElement, DeltaLeaf, HeadingElement, ListElement } from "../types";
|
|
3
|
+
import { ReactEditor } from "slate-react";
|
|
4
|
+
import React, { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
export function hasMark(editor: Editor, format: keyof Omit<DeltaLeaf, "text">): boolean {
|
|
7
|
+
const marks: Omit<DeltaLeaf, "text"> | null = Editor.marks(editor);
|
|
8
|
+
|
|
9
|
+
return marks ? marks[format] === true : false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function toggleMark(editor: Editor, format: keyof Omit<DeltaLeaf, "text">): void {
|
|
13
|
+
const isActive = hasMark(editor, format);
|
|
14
|
+
|
|
15
|
+
if (isActive) {
|
|
16
|
+
Editor.removeMark(editor, format);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Editor.addMark(editor, format, true);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isBlockActive(
|
|
24
|
+
editor: BaseEditor & ReactEditor,
|
|
25
|
+
format: "code" | "heading" | "list",
|
|
26
|
+
properties?: { level?: HeadingElement["level"]; ordered?: ListElement["ordered"] },
|
|
27
|
+
): boolean {
|
|
28
|
+
const { selection } = editor;
|
|
29
|
+
if (!selection) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const [match] = Array.from(
|
|
34
|
+
Editor.nodes(editor, {
|
|
35
|
+
at: Editor.unhangRange(editor, selection),
|
|
36
|
+
match: (node: Node) =>
|
|
37
|
+
!Editor.isEditor(node) &&
|
|
38
|
+
Element.isElement(node) &&
|
|
39
|
+
node.type === format &&
|
|
40
|
+
(node.type === "heading" ? node.level === properties?.level : true) &&
|
|
41
|
+
(node.type === "list" ? node.ordered === properties?.ordered : true),
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return Boolean(match);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function toggleBlock(
|
|
49
|
+
editor: BaseEditor & ReactEditor,
|
|
50
|
+
format: "code" | "heading" | "list",
|
|
51
|
+
properties?: { level?: HeadingElement["level"]; ordered?: ListElement["ordered"] },
|
|
52
|
+
): void {
|
|
53
|
+
const isActive = isBlockActive(editor, format, properties);
|
|
54
|
+
|
|
55
|
+
Transforms.unwrapNodes(editor, {
|
|
56
|
+
match: (node) => Element.isElement(node) && node.type === "list",
|
|
57
|
+
split: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
let newProperties: Partial<DeltaElement>;
|
|
61
|
+
|
|
62
|
+
if (!isActive) {
|
|
63
|
+
newProperties = { type: format === "list" ? "listItem" : format, level: properties?.level };
|
|
64
|
+
} else {
|
|
65
|
+
newProperties = { type: "paragraph" };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Transforms.setNodes<DeltaElement>(editor, newProperties);
|
|
69
|
+
|
|
70
|
+
if (!isActive && format === "list") {
|
|
71
|
+
const block = { type: format, ordered: properties?.ordered, children: [] };
|
|
72
|
+
Transforms.wrapNodes(editor, block as DeltaElement);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function useClickOutside(ref: React.RefObject<HTMLElement | null>, callback: () => void): void {
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
function handleClickOutside(event: MouseEvent): void {
|
|
79
|
+
if (ref.current && !ref.current.contains(event.target as HTMLElement)) {
|
|
80
|
+
callback();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Bind the event listener
|
|
84
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
85
|
+
return (): void => {
|
|
86
|
+
// Unbind the event listener on clean up
|
|
87
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
88
|
+
};
|
|
89
|
+
}, [ref]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Finds the element of the given type at the selection.
|
|
94
|
+
* @param editor - The editor instance.
|
|
95
|
+
* @param elementType - The type of element to find.
|
|
96
|
+
* @returns The element and its path if found, otherwise undefined.
|
|
97
|
+
*/
|
|
98
|
+
export function findElement(editor: Editor, elementType: string): [Element, number[]] | undefined {
|
|
99
|
+
if (!editor.selection) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return Editor.above(editor, {
|
|
104
|
+
match: (node) => !Editor.isEditor(node) && Element.isElement(node) && node.type === elementType,
|
|
105
|
+
}) as [Element, number[]] | undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function hasElementSelected(editor: Editor, elementType: string): boolean {
|
|
109
|
+
return findElement(editor, elementType) !== undefined;
|
|
110
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"esModuleInterop": false,
|
|
5
|
+
"allowImportingTsExtensions": true,
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"module": "esnext",
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"jsxImportSource": "react",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"lib": ["es2022", "dom"]
|
|
14
|
+
},
|
|
15
|
+
"files": [],
|
|
16
|
+
"include": [],
|
|
17
|
+
"references": [
|
|
18
|
+
{
|
|
19
|
+
"path": "./tsconfig.lib.json"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "./tsconfig.spec.json"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"extends": "../../tsconfig.base.json"
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../generated/dist/packages/delta-smart-text",
|
|
5
|
+
"types": ["vite/client"]
|
|
6
|
+
},
|
|
7
|
+
"exclude": [
|
|
8
|
+
"src/**/__tests__/*",
|
|
9
|
+
"src/**/*.spec.vue",
|
|
10
|
+
"src/**/*.test.vue",
|
|
11
|
+
"vite.config.ts",
|
|
12
|
+
"vite.config.mts",
|
|
13
|
+
"vitest.config.ts",
|
|
14
|
+
"vitest.config.mts",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"src/**/*.spec.ts",
|
|
17
|
+
"src/**/*.test.tsx",
|
|
18
|
+
"src/**/*.spec.tsx",
|
|
19
|
+
"src/**/*.test.js",
|
|
20
|
+
"src/**/*.spec.js",
|
|
21
|
+
"src/**/*.test.jsx",
|
|
22
|
+
"src/**/*.spec.jsx"
|
|
23
|
+
],
|
|
24
|
+
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "codegen.config.ts"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../generated/dist/packages/delta-smart-text",
|
|
5
|
+
"types": ["vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest"]
|
|
6
|
+
},
|
|
7
|
+
"include": [
|
|
8
|
+
"vite.config.ts",
|
|
9
|
+
"vite.config.mts",
|
|
10
|
+
"vitest.config.ts",
|
|
11
|
+
"vitest.config.mts",
|
|
12
|
+
"src/**/*.test.ts",
|
|
13
|
+
"src/**/*.spec.ts",
|
|
14
|
+
"src/**/*.test.tsx",
|
|
15
|
+
"src/**/*.spec.tsx",
|
|
16
|
+
"src/**/*.test.js",
|
|
17
|
+
"src/**/*.spec.js",
|
|
18
|
+
"src/**/*.test.jsx",
|
|
19
|
+
"src/**/*.spec.jsx",
|
|
20
|
+
"src/**/*.d.ts"
|
|
21
|
+
]
|
|
22
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/// <reference types='vitest' />
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import vue from "@vitejs/plugin-vue";
|
|
4
|
+
import dts from "vite-plugin-dts";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
|
|
7
|
+
import { nxCopyAssetsPlugin } from "@nx/vite/plugins/nx-copy-assets.plugin";
|
|
8
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
9
|
+
import react from "@vitejs/plugin-react";
|
|
10
|
+
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
root: __dirname,
|
|
13
|
+
cacheDir: "../../node_modules/.vite/packages/delta-smart-text",
|
|
14
|
+
plugins: [
|
|
15
|
+
vue(),
|
|
16
|
+
react(),
|
|
17
|
+
tailwindcss(),
|
|
18
|
+
nxViteTsPaths(),
|
|
19
|
+
nxCopyAssetsPlugin(["*.md"]),
|
|
20
|
+
dts({ entryRoot: "src", tsconfigPath: path.join(__dirname, "tsconfig.lib.json") }),
|
|
21
|
+
],
|
|
22
|
+
// Uncomment this if you are using workers.
|
|
23
|
+
// worker: {
|
|
24
|
+
// plugins: [ nxViteTsPaths() ],
|
|
25
|
+
// },
|
|
26
|
+
// Configuration for building your library.
|
|
27
|
+
// See: https://vitejs.dev/guide/build.html#library-mode
|
|
28
|
+
build: {
|
|
29
|
+
outDir: "../../dist/packages/delta-smart-text",
|
|
30
|
+
emptyOutDir: true,
|
|
31
|
+
reportCompressedSize: true,
|
|
32
|
+
commonjsOptions: {
|
|
33
|
+
transformMixedEsModules: true,
|
|
34
|
+
},
|
|
35
|
+
lib: {
|
|
36
|
+
// Could also be a dictionary or array of multiple entry points.
|
|
37
|
+
entry: "src/index.ts",
|
|
38
|
+
name: "delta-smart-text",
|
|
39
|
+
fileName: "index",
|
|
40
|
+
// Change this to the formats you want to support.
|
|
41
|
+
// Don't forget to update your package.json as well.
|
|
42
|
+
formats: ["es", "cjs"],
|
|
43
|
+
},
|
|
44
|
+
rollupOptions: {
|
|
45
|
+
// External packages that should not be bundled into your library.
|
|
46
|
+
external: ["vue", "react", "react-dom"],
|
|
47
|
+
output: {
|
|
48
|
+
globals: {
|
|
49
|
+
vue: "Vue",
|
|
50
|
+
react: "React",
|
|
51
|
+
"react-dom": "ReactDOM",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
test: {
|
|
57
|
+
watch: false,
|
|
58
|
+
globals: true,
|
|
59
|
+
environment: "jsdom",
|
|
60
|
+
passWithNoTests: true,
|
|
61
|
+
reporters: ["default"],
|
|
62
|
+
coverage: {
|
|
63
|
+
reportsDirectory: "../../generated/coverage/packages/delta-smart-text",
|
|
64
|
+
clean: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Descendant } from 'slate';
|
|
2
|
-
import { ApolloClient, NormalizedCacheObject } from '@apollo/client';
|
|
3
|
-
import { Organization } from '../../__generated__/graphql';
|
|
4
|
-
type __VLS_Props = {
|
|
5
|
-
uuid?: string;
|
|
6
|
-
initialValue?: Descendant[];
|
|
7
|
-
context: HTMLElement | null;
|
|
8
|
-
client: ApolloClient<NormalizedCacheObject>;
|
|
9
|
-
organization: Organization | undefined;
|
|
10
|
-
isDisabled?: boolean;
|
|
11
|
-
};
|
|
12
|
-
type __VLS_PublicProps = {
|
|
13
|
-
'tree'?: any;
|
|
14
|
-
} & __VLS_Props;
|
|
15
|
-
declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
16
|
-
setValue: (value: Descendant[]) => void;
|
|
17
|
-
setInitialValue: (value: Descendant[]) => void;
|
|
18
|
-
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
19
|
-
"update:tree": (value: any) => any;
|
|
20
|
-
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
21
|
-
"onUpdate:tree"?: ((value: any) => any) | undefined;
|
|
22
|
-
}>, {
|
|
23
|
-
uuid: string;
|
|
24
|
-
initialValue: Descendant[];
|
|
25
|
-
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
26
|
-
export default _default;
|