Skip to content

Commit 2283619

Browse files
gjulivangjulivan
authored andcommitted
chore: allow list indent and fix code view flow
1 parent 37bfc73 commit 2283619

14 files changed

Lines changed: 102 additions & 43 deletions

File tree

packages/pluggableWidgets/rich-text-web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"@tiptap/starter-kit": "^3.23.4",
7373
"@uiw/react-color-compact": "^2.10.1",
7474
"classnames": "^2.5.1",
75+
"highlight.js": "^11.11.1",
7576
"js-beautify": "^2.0.3",
7677
"katex": "^0.16.22",
7778
"linkifyjs": "^4.3.2",

packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
21
import type { Editor as TipTapEditor } from "@tiptap/core";
32
import { Subscript } from "@tiptap/extension-subscript";
43
import { Superscript } from "@tiptap/extension-superscript";
@@ -10,6 +9,10 @@ import { TextStyle } from "@tiptap/extension-text-style";
109
import { EditorContent, useEditor } from "@tiptap/react";
1110
import { StarterKit } from "@tiptap/starter-kit";
1211
import { forwardRef, ReactElement, useImperativeHandle, useMemo } from "react";
12+
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
13+
import { EditorContextProvider, useCurrentEditor } from "./EditorContext";
14+
import { HighlightedCodeEditor } from "./HighlightedCodeEditor";
15+
import { Toolbar } from "./toolbars";
1316
import { RichTextContainerProps } from "../../typings/RichTextProps";
1417
import { FontFamilyClass } from "../extensions/FontFamilyClass";
1518
import { FontSize } from "../extensions/FontSize";
@@ -26,9 +29,6 @@ import { TextColorClass } from "../extensions/TextColorClass";
2629
import { TextDirection } from "../extensions/TextDirection";
2730
import { TextHighlightClass } from "../extensions/TextHighlightClass";
2831
import { YouTubeResize } from "../extensions/YouTubeResize";
29-
import { EditorContextProvider, useCurrentEditor } from "./EditorContext";
30-
import { HighlightedCodeEditor } from "./HighlightedCodeEditor";
31-
import { Toolbar } from "./toolbars";
3232
import { ConfirmDialog } from "./toolbars/components/ConfirmDialog";
3333
import { ToolbarGroupsConfig } from "./toolbars/ToolbarConfig";
3434

@@ -84,13 +84,12 @@ function EditorInner({
8484
customFonts
8585
}: EditorInnerProps): ReactElement {
8686
const { editor, codeViewState, codeViewDispatch } = useCurrentEditor();
87-
8887
const handleSaveCode = (): void => {
8988
if (!editor) return;
9089

9190
// Update editor content with modified HTML
9291
editor.commands.setContent(codeViewState.htmlCode);
93-
codeViewDispatch({ type: "SAVE_CODE_CHANGES" });
92+
codeViewDispatch({ type: "SAVE_CODE_CHANGES", isExiting: true });
9493
};
9594

9695
const handleCancelCode = (): void => {
@@ -112,7 +111,7 @@ function EditorInner({
112111
toolbarGroups={toolbarGroups}
113112
advancedConfig={advancedConfig}
114113
customFonts={customFonts}
115-
/>
114+
></Toolbar>
116115
)}
117116
{codeViewState.isCodeView ? (
118117
<HighlightedCodeEditor
@@ -126,8 +125,7 @@ function EditorInner({
126125
</div>
127126
{codeViewState.showConfirm && (
128127
<ConfirmDialog
129-
title="Save Code Changes?"
130-
message="Do you want to save the HTML code changes and apply them to the editor?"
128+
message="Save changes?"
131129
confirmLabel="Save"
132130
cancelLabel="Cancel"
133131
onConfirm={handleSaveCode}

packages/pluggableWidgets/rich-text-web/src/components/EditorContext.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import { createContext, useContext, useReducer, ReactNode, ReactElement, Dispatc
66
export interface CodeViewState {
77
isCodeView: boolean;
88
htmlCode: string;
9+
lastSavedHtml?: string;
910
showConfirm: boolean;
1011
}
1112

1213
// Code view actions
1314
export type CodeViewAction =
1415
| { type: "ENTER_CODE_VIEW"; html: string }
1516
| { type: "EXIT_CODE_VIEW_REQUEST" }
16-
| { type: "SAVE_CODE_CHANGES" }
17+
| { type: "SAVE_CODE_CHANGES"; isExiting?: boolean }
1718
| { type: "CANCEL_CODE_CHANGES" }
1819
| { type: "UPDATE_HTML_CODE"; html: string };
1920

@@ -33,23 +34,28 @@ function codeViewReducer(state: CodeViewState, action: CodeViewAction): CodeView
3334
return {
3435
isCodeView: true,
3536
htmlCode: beautifiedHtml,
37+
lastSavedHtml: beautifiedHtml,
3638
showConfirm: false
3739
};
3840
case "EXIT_CODE_VIEW_REQUEST":
41+
const isLatestHtmlSaved = state.lastSavedHtml === state.htmlCode;
3942
return {
4043
...state,
41-
showConfirm: true
44+
showConfirm: !isLatestHtmlSaved,
45+
isCodeView: !isLatestHtmlSaved
4246
};
4347
case "SAVE_CODE_CHANGES":
4448
return {
45-
isCodeView: false,
46-
htmlCode: "",
49+
...state,
50+
lastSavedHtml: state.htmlCode,
51+
isCodeView: action.isExiting ? false : state.isCodeView,
4752
showConfirm: false
4853
};
4954
case "CANCEL_CODE_CHANGES":
5055
return {
5156
isCodeView: false,
5257
htmlCode: "",
58+
lastSavedHtml: "",
5359
showConfirm: false
5460
};
5561
case "UPDATE_HTML_CODE":

packages/pluggableWidgets/rich-text-web/src/components/toolbars/Toolbar.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ $rte-shadow-color: #00000014 !default;
5151
}
5252
}
5353

54+
.code-view-actions {
55+
width: 100%;
56+
justify-content: flex-end;
57+
}
58+
5459
.toolbar-group {
5560
display: flex;
5661
gap: 2px;

packages/pluggableWidgets/rich-text-web/src/components/toolbars/Toolbar.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactElement, ReactNode, useState, useEffect, useMemo, useRef } from "react";
1+
import { ReactElement, ReactNode, useState, useEffect, useMemo, PropsWithChildren } from "react";
22
import { useCurrentEditor } from "../EditorContext";
33
import { CodeViewToolbarButton } from "./components/CodeView";
44
import { ColorPickerToolbarButton } from "./components/ColorPicker";
@@ -23,7 +23,7 @@ import { ToolbarDropdown } from "./components/ToolbarDropdown";
2323
import { ToolbarSplitButton } from "./components/ToolbarSplitButton";
2424
import classNames from "classnames";
2525

26-
interface ToolbarProps {
26+
interface ToolbarProps extends PropsWithChildren {
2727
imageSourceContent?: ReactNode;
2828
preset?: PresetEnum;
2929
toolbarConfig?: ToolbarConfigEnum;
@@ -118,31 +118,29 @@ function ToolbarRow(props: ToolbarRowProps): ReactElement {
118118

119119
function ToolbarRowCode(): ReactElement {
120120
const { editor, codeViewState, codeViewDispatch } = useCurrentEditor();
121-
const initialHtmlCode = useRef<string | null>(codeViewState.htmlCode);
122121

123-
const isDisabled = initialHtmlCode.current === codeViewState.htmlCode;
122+
const isDisabled = codeViewState.htmlCode === codeViewState.lastSavedHtml;
124123
const extraProps = isDisabled ? { "aria-disabled": true } : {};
125124

126125
const handleSaveCode = (): void => {
127126
if (!editor) return;
128127

129128
// Update editor content with modified HTML
130129
editor.commands.setContent(codeViewState.htmlCode);
131-
initialHtmlCode.current = codeViewState.htmlCode;
132-
// codeViewDispatch({ type: "SAVE_CODE_CHANGES" });
130+
codeViewDispatch({ type: "SAVE_CODE_CHANGES" });
133131
};
134132

135133
const handleCancelCode = (): void => {
136134
codeViewDispatch({ type: "CANCEL_CODE_CHANGES" });
137135
};
138136
return (
139-
<div className="toolbar-row">
137+
<div className="toolbar-row code-view-actions">
140138
<div className="mx-button button btn btn-default" onClick={handleCancelCode}>
141-
Cancel
139+
{isDisabled ? "Close" : "Cancel"}
142140
</div>
143141
<div
144142
{...extraProps}
145-
className={classNames("mx-button", "button", "btn", "btn-primary", {
143+
className={classNames("mx-button", "button", "btn", "btn-success", {
146144
disabled: isDisabled
147145
})}
148146
onClick={handleSaveCode}

packages/pluggableWidgets/rich-text-web/src/components/toolbars/components/CodeView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ReactElement } from "react";
2+
import { ToolbarDefaultButton } from "./ToolbarDefaultButton";
23
import { useCurrentEditor } from "../../EditorContext";
34
import { BaseToolbarButtonProps } from "../helpers/toolbarTypes";
4-
import { ToolbarDefaultButton } from "./ToolbarDefaultButton";
55

66
export function CodeViewToolbarButton({ config }: BaseToolbarButtonProps): ReactElement {
77
const { editor, codeViewState, codeViewDispatch } = useCurrentEditor();
@@ -23,6 +23,7 @@ export function CodeViewToolbarButton({ config }: BaseToolbarButtonProps): React
2323
<ToolbarDefaultButton
2424
onClick={handleToggle}
2525
isActive={codeViewState.isCodeView}
26+
allowInCodeView
2627
icon={config.icon}
2728
title={config.title}
2829
/>

packages/pluggableWidgets/rich-text-web/src/components/toolbars/components/ConfirmDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ export function ConfirmDialog({
2626
return (
2727
<div className="confirm-dialog-overlay">
2828
<div ref={dialogRef} className="toolbar-dialog confirm-dialog">
29-
<h3>{title}</h3>
30-
<p className="confirm-message">{message}</p>
29+
{title && <h3>{title}</h3>}
30+
{message && <p className="confirm-message">{message}</p>}
3131
<div className="dialog-actions">
3232
<button type="button" onClick={onCancel}>
3333
{cancelLabel}

packages/pluggableWidgets/rich-text-web/src/components/toolbars/components/Dialog.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@
253253
}
254254

255255
.confirm-message {
256-
margin: 0 0 16px 0;
256+
padding: 16px;
257257
font-size: 14px;
258258
color: #666;
259259
line-height: 1.5;

packages/pluggableWidgets/rich-text-web/src/components/toolbars/components/ToolbarDefaultButton.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import classNames from "classnames";
22
import { ButtonHTMLAttributes, forwardRef, ReactElement } from "react";
3+
import { useCurrentEditor } from "../../EditorContext";
34

45
export interface ToolbarDefaultButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
56
icon?: string;
67
activeIcon?: string;
78
isActive?: boolean;
9+
/** When true, the button stays enabled while the editor is in code view (used by the code view toggle). */
10+
allowInCodeView?: boolean;
811
}
912

1013
/**
@@ -15,16 +18,20 @@ export interface ToolbarDefaultButtonProps extends ButtonHTMLAttributes<HTMLButt
1518
*/
1619
export const ToolbarDefaultButton = forwardRef<HTMLButtonElement, ToolbarDefaultButtonProps>(
1720
function ToolbarDefaultButton(
18-
{ icon, activeIcon, isActive, className, type, children, ...rest },
21+
{ icon, activeIcon, isActive, allowInCodeView, className, type, disabled, children, ...rest },
1922
ref
2023
): ReactElement {
2124
const currentIcon = isActive && activeIcon ? activeIcon : icon;
25+
const { codeViewState } = useCurrentEditor();
26+
27+
const lockedByCodeView = !allowInCodeView && codeViewState.isCodeView === true;
2228

2329
return (
2430
<button
2531
{...rest}
2632
ref={ref}
2733
type={type ?? "button"}
34+
disabled={disabled || lockedByCodeView}
2835
className={classNames(className || "icon-button", { "is-active": isActive })}
2936
>
3037
{children ?? <span className={`icons icon-${currentIcon}`} />}

packages/pluggableWidgets/rich-text-web/src/components/toolbars/components/ToolbarDropdown.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,16 @@ export function ToolbarDropdown({ config }: BaseToolbarButtonProps): ReactElemen
8181
{isOpen && (
8282
<div ref={refs.setFloating} style={floatingStyles} className="toolbar-dropdown-menu">
8383
{options?.map(option => (
84-
<button
84+
<ToolbarDefaultButton
8585
key={option.value}
8686
onClick={() => handleSelect(option)}
8787
data-value={option.value}
88-
className={`toolbar-dropdown-item ${currentValue === option.value ? "is-active" : ""}`}
88+
className="toolbar-dropdown-item"
89+
isActive={currentValue === option.value}
8990
>
9091
{option.icon && <span className={`icons icon-${option.icon}`} />}
9192
<span>{option.label}</span>
92-
</button>
93+
</ToolbarDefaultButton>
9394
))}
9495
</div>
9596
)}

0 commit comments

Comments
 (0)