feat: 优化输入体验
This commit is contained in:
@@ -104,7 +104,7 @@ const EditableContent = function (
|
|||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}, [initialValue, options, handleConfirm]);
|
}, [initialValue, options, handleConfirm, handleHideContextMenu, overlay]);
|
||||||
let renderContent, renderPopover;
|
let renderContent, renderPopover;
|
||||||
switch (inputType) {
|
switch (inputType) {
|
||||||
case "select":
|
case "select":
|
||||||
|
|||||||
+28
-15
@@ -1,4 +1,3 @@
|
|||||||
import { Button, Menu, Upload } from "antd";
|
|
||||||
import {
|
import {
|
||||||
FolderOpenOutlined,
|
FolderOpenOutlined,
|
||||||
HighlightOutlined,
|
HighlightOutlined,
|
||||||
@@ -8,10 +7,14 @@ import {
|
|||||||
SaveOutlined,
|
SaveOutlined,
|
||||||
UndoOutlined,
|
UndoOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
|
import { Menu } from "antd";
|
||||||
import state from "../store/state";
|
import state from "../store/state";
|
||||||
import store from "../store/global";
|
import store from "../store/global";
|
||||||
import { createNotation, isNote, notations as N } from "../util/notation";
|
import { createNotation, isNote, notations as N } from "../util/notation";
|
||||||
import { createParagraph } from "../util/paragraph";
|
import {
|
||||||
|
createParagraph,
|
||||||
|
createParagraphWithNotations,
|
||||||
|
} from "../util/paragraph";
|
||||||
import { exportFile, loadFile, saveFile } from "../util/file";
|
import { exportFile, loadFile, saveFile } from "../util/file";
|
||||||
import { findParagraphAndNotation } from "../util/editor";
|
import { findParagraphAndNotation } from "../util/editor";
|
||||||
import {
|
import {
|
||||||
@@ -55,19 +58,11 @@ const handleEditMenu = wrappedAction(({ key }) => {
|
|||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case "add-paragraph": {
|
case "add-paragraph": {
|
||||||
store.paragraphs.push(
|
store.paragraphs.push(createParagraphWithNotations());
|
||||||
createParagraph({
|
|
||||||
notations: [
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: N.separator }),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const handleConvertMenu = wrappedAction(() => {
|
const handleConvertMenu = wrappedAction(() => {
|
||||||
@@ -176,8 +171,19 @@ const handleKeyPress = wrappedAction((ev) => {
|
|||||||
}
|
}
|
||||||
case inputKey === "enter" && ctrl && !shift: {
|
case inputKey === "enter" && ctrl && !shift: {
|
||||||
const newNotation = createNotation();
|
const newNotation = createNotation();
|
||||||
paragraph.notations.splice(notationIndex + 1, 0, newNotation);
|
if (
|
||||||
state.selectedNotationKey = newNotation.key;
|
notationIndex === paragraph.notations.length - 1 &&
|
||||||
|
paragraphIndex === store.paragraphs.length - 1
|
||||||
|
) {
|
||||||
|
// 在最后一个音符时ctrl+enter插入新段落并创建符号
|
||||||
|
store.paragraphs.push(createParagraph({ notations: [newNotation] }));
|
||||||
|
state.selectedNotationKey = store.paragraphs
|
||||||
|
.at(-1)
|
||||||
|
.notations.at(0).key;
|
||||||
|
} else {
|
||||||
|
paragraph.notations.splice(notationIndex + 1, 0, newNotation);
|
||||||
|
state.selectedNotationKey = newNotation.key;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case inputKey === "j" && !ctrl && !shift: {
|
case inputKey === "j" && !ctrl && !shift: {
|
||||||
@@ -218,6 +224,8 @@ const handleKeyPress = wrappedAction((ev) => {
|
|||||||
paragraph.alignJustify = !paragraph.alignJustify;
|
paragraph.alignJustify = !paragraph.alignJustify;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 仅未选中符号时
|
// 仅未选中符号时
|
||||||
@@ -226,7 +234,10 @@ const handleKeyPress = wrappedAction((ev) => {
|
|||||||
state.selectedNotationKey =
|
state.selectedNotationKey =
|
||||||
state.lastSelectedNotationKey ||
|
state.lastSelectedNotationKey ||
|
||||||
store.paragraphs?.at(0)?.notations?.at(0).key;
|
store.paragraphs?.at(0)?.notations?.at(0).key;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 全局
|
// 全局
|
||||||
@@ -261,6 +272,8 @@ const handleKeyPress = wrappedAction((ev) => {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+15
-61
@@ -1,25 +1,19 @@
|
|||||||
import {
|
import {
|
||||||
ArrowDownOutlined,
|
|
||||||
ArrowUpOutlined,
|
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
FontColorsOutlined,
|
|
||||||
MinusOutlined,
|
|
||||||
PauseOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
RadiusSettingOutlined,
|
RadiusSettingOutlined,
|
||||||
StopOutlined,
|
StopOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Menu, message } from "antd";
|
import { Menu } from "antd";
|
||||||
import state from "../store/state";
|
import state from "../store/state";
|
||||||
import { findParagraphAndNotation } from "../util/editor";
|
import { findParagraphAndNotation } from "../util/editor";
|
||||||
import { isNote, isSeparator } from "../util/notation";
|
import { isNote } from "../util/notation";
|
||||||
import { runInWrappedAction, wrappedAction } from "../store/history";
|
import { runInWrappedAction, wrappedAction } from "../store/history";
|
||||||
|
|
||||||
const getNotationContextMenu = (notation, paragraph) => {
|
const getNotationContextMenu = (notation, paragraph) => {
|
||||||
const hasTie = notation.tieTo;
|
const hasTie = notation.tieTo;
|
||||||
const handleMenu = wrappedAction(function ({ key }) {
|
const handleMenu = wrappedAction(function ({ key }) {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case key === "tie":
|
case key === "tie": {
|
||||||
if (!hasTie) {
|
if (!hasTie) {
|
||||||
state.tieSourceKey = notation.key;
|
state.tieSourceKey = notation.key;
|
||||||
return;
|
return;
|
||||||
@@ -30,6 +24,7 @@ const getNotationContextMenu = (notation, paragraph) => {
|
|||||||
}
|
}
|
||||||
notation.tieTo = null;
|
notation.tieTo = null;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case key === "break-underline":
|
case key === "break-underline":
|
||||||
notation.breakUnderline = !notation.breakUnderline;
|
notation.breakUnderline = !notation.breakUnderline;
|
||||||
break;
|
break;
|
||||||
@@ -38,76 +33,35 @@ const getNotationContextMenu = (notation, paragraph) => {
|
|||||||
notation.note = separator;
|
notation.note = separator;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// ENHANCE: 分音符类型显示菜单项
|
// ENHANCE: 分音符类型显示菜单项
|
||||||
<Menu onClick={handleMenu} style={{ minWidth: "80px" }}>
|
<Menu onClick={handleMenu} style={{ minWidth: "80px" }}>
|
||||||
{
|
{isNote(notation) && (
|
||||||
<Menu.Item key="break-underline" icon={<StopOutlined />}>
|
<Menu.Item key="break-underline" icon={<StopOutlined />}>
|
||||||
{notation.breakUnderline
|
{notation.breakUnderline
|
||||||
? "在此处延续增减时线"
|
? "在此处延续增减时线"
|
||||||
: "在此处打断增减时线"}
|
: "在此处打断增减时线"}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
}
|
)}
|
||||||
{
|
{isNote(notation) && (
|
||||||
<Menu.Item key="tie" icon={<RadiusSettingOutlined />}>
|
<Menu.Item
|
||||||
|
key="tie"
|
||||||
|
icon={<RadiusSettingOutlined />}
|
||||||
|
notation={notation}
|
||||||
|
>
|
||||||
{notation.tieTo ? "删除连音线" : "从此处添加连音线到..."}
|
{notation.tieTo ? "删除连音线" : "从此处添加连音线到..."}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
}
|
)}
|
||||||
<Menu.Item key="delete" icon={<DeleteOutlined />}>
|
<Menu.Item key="delete" icon={<DeleteOutlined />}>
|
||||||
删除
|
删除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: "octaveUp",
|
|
||||||
text: "升高一个八度(8)",
|
|
||||||
visible: isNote(notation),
|
|
||||||
onClick: wrappedAction(() => {
|
|
||||||
notation.octave += 1;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "octaveDown",
|
|
||||||
text: "降低一个八度(Shift + 8)",
|
|
||||||
visible: isNote(notation),
|
|
||||||
onClick: wrappedAction(() => {
|
|
||||||
notation.octave -= 1;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "tie",
|
|
||||||
text: notation.tieTo ? "删除连音线" : "从此处添加连音线到...",
|
|
||||||
visible: isNote(notation),
|
|
||||||
onClick: wrappedAction(() => {}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "break-underline",
|
|
||||||
text: notation.breakUnderline
|
|
||||||
? "在此处延续增减时线"
|
|
||||||
: "在此处打断增减时线",
|
|
||||||
visible: isNote(notation),
|
|
||||||
onClick: wrappedAction(() => {
|
|
||||||
notation.breakUnderline = !notation.breakUnderline;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "delete",
|
|
||||||
text: "删除",
|
|
||||||
onClick: wrappedAction(() => {
|
|
||||||
const index = paragraph.notations.indexOf(notation);
|
|
||||||
if (index !== -1) {
|
|
||||||
paragraph.notations.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
message.error("符号不存在");
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ENHANCE: 使用EditableContent组件的overlay属性传入菜单
|
// ENHANCE: 使用EditableContent组件的overlay属性传入菜单
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ function getParagraphMenuOptions(paragraph) {
|
|||||||
store.paragraphs.splice(idx, 1);
|
store.paragraphs.splice(idx, 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { action, intercept, keys, runInAction, toJS } from "mobx";
|
import { action, runInAction, toJS } from "mobx";
|
||||||
import globalStore from "./global";
|
import globalStore from "./global";
|
||||||
|
|
||||||
const storeHistory = [];
|
const storeHistory = [];
|
||||||
@@ -43,12 +43,10 @@ function go(span) {
|
|||||||
Math.max(0, cursor + span),
|
Math.max(0, cursor + span),
|
||||||
storeHistory.length - 1
|
storeHistory.length - 1
|
||||||
);
|
);
|
||||||
console.log("go ", span);
|
|
||||||
if (newCursor === cursor) {
|
if (newCursor === cursor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cursor = newCursor;
|
cursor = newCursor;
|
||||||
console.log("gone to ", newCursor);
|
|
||||||
Object.assign(globalStore, storeHistory[newCursor]);
|
Object.assign(globalStore, storeHistory[newCursor]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +62,6 @@ function executeAction(actionFunc, ...args) {
|
|||||||
if (!_isEqual(storeHistory[cursor], current)) {
|
if (!_isEqual(storeHistory[cursor], current)) {
|
||||||
storeHistory[++cursor] = current;
|
storeHistory[++cursor] = current;
|
||||||
storeHistory.length = cursor + 1;
|
storeHistory.length = cursor + 1;
|
||||||
console.log(
|
|
||||||
"pushed history, length & cursor: ",
|
|
||||||
storeHistory.length,
|
|
||||||
cursor
|
|
||||||
);
|
|
||||||
if (storeHistory.length > 256) {
|
if (storeHistory.length > 256) {
|
||||||
storeHistory.splice(0, 256 - storeHistory.length);
|
storeHistory.splice(0, 256 - storeHistory.length);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-14
@@ -1,6 +1,5 @@
|
|||||||
import store from "../store/global";
|
import store from "../store/global";
|
||||||
import { createNotation, notations } from "../util/notation";
|
import { createParagraphWithNotations } from "../util/paragraph";
|
||||||
import { createParagraph } from "../util/paragraph";
|
|
||||||
import { VERSION } from "./version";
|
import { VERSION } from "./version";
|
||||||
|
|
||||||
const initialData = {
|
const initialData = {
|
||||||
@@ -16,7 +15,7 @@ const initialData = {
|
|||||||
gapAfterTitle: 16,
|
gapAfterTitle: 16,
|
||||||
gapAfterHeader: 32,
|
gapAfterHeader: 32,
|
||||||
gapBetweenParagraph: 32,
|
gapBetweenParagraph: 32,
|
||||||
gapBetweenNotation: 8,
|
gapBetweenNotation: 16,
|
||||||
beat: [4, 4],
|
beat: [4, 4],
|
||||||
speed: 75,
|
speed: 75,
|
||||||
authors: ["佚名 作词", "简谱编辑器 制谱"],
|
authors: ["佚名 作词", "简谱编辑器 制谱"],
|
||||||
@@ -28,17 +27,7 @@ function getDefaultGlobalData() {
|
|||||||
}
|
}
|
||||||
function getDefaultGlobalDataWidthNotations() {
|
function getDefaultGlobalDataWidthNotations() {
|
||||||
const d = getDefaultGlobalData();
|
const d = getDefaultGlobalData();
|
||||||
d.paragraphs = [
|
d.paragraphs = [createParagraphWithNotations()];
|
||||||
createParagraph({
|
|
||||||
notations: [
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: "0" }),
|
|
||||||
createNotation({ note: notations.separator }),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +44,7 @@ function findParagraphAndNotation(key) {
|
|||||||
res.paragraphIndex = i;
|
res.paragraphIndex = i;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-7
@@ -1,6 +1,7 @@
|
|||||||
import { message } from "antd";
|
import { message } from "antd";
|
||||||
import { remove, transaction } from "mobx";
|
import { remove, transaction } from "mobx";
|
||||||
import { toJS } from "mobx";
|
import { toJS } from "mobx";
|
||||||
|
import globalState from "../store/state";
|
||||||
import store from "../store/global";
|
import store from "../store/global";
|
||||||
import { clearHistory } from "../store/history";
|
import { clearHistory } from "../store/history";
|
||||||
import domtoimage from "./dom-to-image";
|
import domtoimage from "./dom-to-image";
|
||||||
@@ -20,9 +21,9 @@ async function loadFile(file) {
|
|||||||
const data = await read(file);
|
const data = await read(file);
|
||||||
if (!(data.version <= VERSION)) {
|
if (!(data.version <= VERSION)) {
|
||||||
message.error("不支持的文件格式,请使用最新版本");
|
message.error("不支持的文件格式,请使用最新版本");
|
||||||
console.log(data);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
globalState.lastSelectedNotationKey = globalState.selectedNotationKey = null;
|
||||||
clearHistory();
|
clearHistory();
|
||||||
transaction(() => {
|
transaction(() => {
|
||||||
for (const key of Object.keys(store)) {
|
for (const key of Object.keys(store)) {
|
||||||
@@ -44,12 +45,6 @@ function saveFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exportFile() {
|
function exportFile() {
|
||||||
// const canvas = document.createElement("canvas");
|
|
||||||
// const ctx = canvas.getContext("2d");
|
|
||||||
// const dpi = window.devicePixelRatio || 1;
|
|
||||||
// canvas.style.width = store.canvasWidth;
|
|
||||||
// canvas.style.height = store.canvasHeight;
|
|
||||||
// ctx.scale(dpi, dpi);
|
|
||||||
domtoimage.toPng(document.querySelector("#temp_svg")).then((dataUrl) => {
|
domtoimage.toPng(document.querySelector("#temp_svg")).then((dataUrl) => {
|
||||||
const downloadLink = document.createElement("a");
|
const downloadLink = document.createElement("a");
|
||||||
downloadLink.download = (store.title || "未标题") + ".png";
|
downloadLink.download = (store.title || "未标题") + ".png";
|
||||||
|
|||||||
+14
-1
@@ -1,3 +1,5 @@
|
|||||||
|
import { createNotation, notations as N } from "./notation";
|
||||||
|
|
||||||
// 新建段落
|
// 新建段落
|
||||||
function createParagraph(initial) {
|
function createParagraph(initial) {
|
||||||
const p = {
|
const p = {
|
||||||
@@ -12,5 +14,16 @@ function createParagraph(initial) {
|
|||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
function createParagraphWithNotations() {
|
||||||
|
return createParagraph({
|
||||||
|
notations: [
|
||||||
|
createNotation({ note: "0" }),
|
||||||
|
createNotation({ note: "0" }),
|
||||||
|
createNotation({ note: "0" }),
|
||||||
|
createNotation({ note: "0" }),
|
||||||
|
createNotation({ note: N.separator }),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export { createParagraph };
|
export { createParagraph, createParagraphWithNotations };
|
||||||
|
|||||||
+4
-18
@@ -6,22 +6,6 @@ import store from "../store/global";
|
|||||||
let canvas, context2D, defaultFontFamily;
|
let canvas, context2D, defaultFontFamily;
|
||||||
let measureSvgEl, measureTextEl;
|
let measureSvgEl, measureTextEl;
|
||||||
|
|
||||||
function _calcFontData(fontSize) {
|
|
||||||
const span = document.createElement("span");
|
|
||||||
span.style.fontFamily = window.getComputedStyle(document.body).fontFamily;
|
|
||||||
span.style.fontSize = `${fontSize}px`;
|
|
||||||
span.style.visibility = "hidden";
|
|
||||||
span.style.position = "absolute";
|
|
||||||
span.style.display = "inline-block";
|
|
||||||
span.textContent = "x";
|
|
||||||
document.body.appendChild(span);
|
|
||||||
const data = span.getBoundingClientRect();
|
|
||||||
return {
|
|
||||||
xWidth: data.width,
|
|
||||||
xHeight: data.height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initializeCanvas(fontSize) {
|
function _initializeCanvas(fontSize) {
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
canvas = document.createElement("canvas");
|
canvas = document.createElement("canvas");
|
||||||
@@ -151,7 +135,8 @@ function calcParagraphContentHeight(paragraph) {
|
|||||||
}
|
}
|
||||||
let tieHeight = 0;
|
let tieHeight = 0;
|
||||||
if (_hasTie(paragraph)) {
|
if (_hasTie(paragraph)) {
|
||||||
tieHeight = placement.maxTieHeight;
|
// FIXME: 应该计算连音线高度
|
||||||
|
// tieHeight = placement.maxTieHeight;
|
||||||
}
|
}
|
||||||
// 段落高度不是最高的音符的高度,而是上边偏移最大的音符的上部分偏移量+下边偏移最
|
// 段落高度不是最高的音符的高度,而是上边偏移最大的音符的上部分偏移量+下边偏移最
|
||||||
// 大的音符的下部分偏移量
|
// 大的音符的下部分偏移量
|
||||||
@@ -184,7 +169,8 @@ function calcParagraphAboveOffset(paragraph) {
|
|||||||
}
|
}
|
||||||
let tieHeight = 0;
|
let tieHeight = 0;
|
||||||
if (_hasTie(paragraph)) {
|
if (_hasTie(paragraph)) {
|
||||||
tieHeight = placement.maxTieHeight;
|
// FIXME: 应该计算连音线高度
|
||||||
|
// tieHeight = placement.maxTieHeight;
|
||||||
}
|
}
|
||||||
const notationOffset = notations
|
const notationOffset = notations
|
||||||
.map((n) => calcNotationAboveOffset(n))
|
.map((n) => calcNotationAboveOffset(n))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Form, Input, InputNumber, Modal } from "antd";
|
import { Button, Form, Input, Modal } from "antd";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import store from "../../store/global";
|
import store from "../../store/global";
|
||||||
@@ -26,25 +26,28 @@ function ConfigModal({ visible, onVisibleChange }) {
|
|||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
onVisibleChange?.call(null, false);
|
onVisibleChange?.call(null, false);
|
||||||
}, [onVisibleChange]);
|
}, [onVisibleChange]);
|
||||||
const resetConfig = useCallback(
|
const resetConfig = unwrappedAction(() => {
|
||||||
unwrappedAction(() => {
|
const {
|
||||||
const {
|
canvasWidth,
|
||||||
canvasWidth,
|
canvasHeight,
|
||||||
canvasHeight,
|
marginTop,
|
||||||
marginTop,
|
marginHorizontal,
|
||||||
marginHorizontal,
|
gapBetweenParagraph,
|
||||||
} = getDefaultGlobalData();
|
gapBetweenNotation,
|
||||||
Object.assign(store, {
|
} = getDefaultGlobalData();
|
||||||
canvasWidth,
|
Object.assign(store, {
|
||||||
canvasHeight,
|
canvasWidth,
|
||||||
marginTop,
|
canvasHeight,
|
||||||
marginHorizontal,
|
marginTop,
|
||||||
});
|
marginHorizontal,
|
||||||
})
|
gapBetweenParagraph,
|
||||||
);
|
gapBetweenNotation,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
style={{ top: 20 }}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
title="配置"
|
title="配置"
|
||||||
onCancel={handleClose}
|
onCancel={handleClose}
|
||||||
@@ -88,6 +91,14 @@ function ConfigModal({ visible, onVisibleChange }) {
|
|||||||
label="左右边距"
|
label="左右边距"
|
||||||
propertyName="marginHorizontal"
|
propertyName="marginHorizontal"
|
||||||
/>
|
/>
|
||||||
|
<ImmediateNumberConfigItem
|
||||||
|
label="段间距"
|
||||||
|
propertyName="gapBetweenParagraph"
|
||||||
|
/>
|
||||||
|
<ImmediateNumberConfigItem
|
||||||
|
label="音符间距"
|
||||||
|
propertyName="gapBetweenNotation"
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
+11
-17
@@ -7,7 +7,7 @@ import {
|
|||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useEffect } from "react";
|
||||||
import state from "../../store/state";
|
import state from "../../store/state";
|
||||||
import store from "../../store/global";
|
import store from "../../store/global";
|
||||||
import P, {
|
import P, {
|
||||||
@@ -31,7 +31,6 @@ import Row from "../Row";
|
|||||||
import Styles from "./index.module.css";
|
import Styles from "./index.module.css";
|
||||||
|
|
||||||
function Editor() {
|
function Editor() {
|
||||||
const [isHelpModalVisible, setIsHelpModalVisible] = useState(false);
|
|
||||||
const heightCache = [];
|
const heightCache = [];
|
||||||
function accumulate(index) {
|
function accumulate(index) {
|
||||||
const paragraphs = store.paragraphs || [];
|
const paragraphs = store.paragraphs || [];
|
||||||
@@ -45,23 +44,18 @@ function Editor() {
|
|||||||
height += calcParagraphAboveOffset(paragraphs[index]);
|
height += calcParagraphAboveOffset(paragraphs[index]);
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
const handleChangeConfigModalVisibility = useCallback(
|
const handleChangeConfigModalVisibility = unwrappedAction((visible) => {
|
||||||
unwrappedAction((visible) => {
|
state.configDialogVisible = visible;
|
||||||
state.configDialogVisible = visible;
|
});
|
||||||
}, [])
|
const handleChangeHelpModalVisibility = unwrappedAction((visible) => {
|
||||||
);
|
state.helpDialogVisible = visible;
|
||||||
const handleChangeHelpModalVisibility = useCallback(
|
});
|
||||||
unwrappedAction((visible) => {
|
const handleShowConfigDialog = () => {
|
||||||
state.helpDialogVisible = visible;
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
const handleShowConfigDialog = useCallback(() => {
|
|
||||||
handleChangeConfigModalVisibility(true);
|
handleChangeConfigModalVisibility(true);
|
||||||
}, []);
|
};
|
||||||
const handleShowHelpDialog = useCallback(() => {
|
const handleShowHelpDialog = () => {
|
||||||
handleChangeHelpModalVisibility(true);
|
handleChangeHelpModalVisibility(true);
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener("keydown", handleKeyPress);
|
document.addEventListener("keydown", handleKeyPress);
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { action } from "mobx";
|
|
||||||
import { message } from "antd";
|
import { message } from "antd";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import EditableContent from "../../../component/EditableContent";
|
import EditableContent from "../../../component/EditableContent";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
import { Modal } from "antd";
|
import { Modal } from "antd";
|
||||||
import { action } from "mobx";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import EditableContent from "../../../component/EditableContent";
|
import EditableContent from "../../../component/EditableContent";
|
||||||
import P from "../../../util/placement";
|
import P from "../../../util/placement";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { action } from "mobx";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useEffect, useRef } from "react";
|
import { useRef } from "react";
|
||||||
import EditableContent from "../../../component/EditableContent";
|
import EditableContent from "../../../component/EditableContent";
|
||||||
import store from "../../../store/global";
|
import store from "../../../store/global";
|
||||||
import { wrappedAction } from "../../../store/history";
|
import { wrappedAction } from "../../../store/history";
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Button, Modal } from "antd";
|
import { Button, Modal } from "antd";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import state from "../../store/state";
|
|
||||||
import { unwrappedAction } from "../../store/history";
|
|
||||||
import Styles from "./index.module.css";
|
import Styles from "./index.module.css";
|
||||||
|
|
||||||
function KeyboardDescItem({ kbd, desc }) {
|
function KeyboardDescItem({ kbd, desc }) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { action } from "mobx";
|
|
||||||
import { message } from "antd";
|
import { message } from "antd";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import EditableContent from "../../component/EditableContent";
|
import EditableContent from "../../component/EditableContent";
|
||||||
@@ -7,7 +6,7 @@ import store from "../../store/global";
|
|||||||
import P, { calcSubTextWidth } from "../../util/placement";
|
import P, { calcSubTextWidth } from "../../util/placement";
|
||||||
import { findParagraphAndNotation } from "../../util/editor";
|
import { findParagraphAndNotation } from "../../util/editor";
|
||||||
import { getNotationContextMenu } from "../../menu/notation";
|
import { getNotationContextMenu } from "../../menu/notation";
|
||||||
import { notations } from "../../util/notation";
|
import { isNote, notations } from "../../util/notation";
|
||||||
import { wrappedAction } from "../../store/history";
|
import { wrappedAction } from "../../store/history";
|
||||||
import Row from "../Row";
|
import Row from "../Row";
|
||||||
import Text from "../Text";
|
import Text from "../Text";
|
||||||
@@ -40,11 +39,13 @@ function Notation({ offsetX, notation, paragraph }) {
|
|||||||
state.tieSourceKey = null;
|
state.tieSourceKey = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sourceParagraph.notations.includes(notation)) {
|
if (!sourceParagraph.notations.includes(notation)) {
|
||||||
|
message.warn("只允许相同段落的音符相连!");
|
||||||
|
} else if (!isNote(notation)) {
|
||||||
|
message.warn("只允许在音符上添加连音线!");
|
||||||
|
} else {
|
||||||
sourceNotation.tieTo = notation.key;
|
sourceNotation.tieTo = notation.key;
|
||||||
notation.tieTo = sourceNotation.key;
|
notation.tieTo = sourceNotation.key;
|
||||||
} else {
|
|
||||||
message.warn("只允许相同段落的音符相连!");
|
|
||||||
}
|
}
|
||||||
state.tieSourceKey = null;
|
state.tieSourceKey = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,11 @@ import P, {
|
|||||||
calcParagraphContentHeight,
|
calcParagraphContentHeight,
|
||||||
calcParagraphWidth,
|
calcParagraphWidth,
|
||||||
} from "../../util/placement";
|
} from "../../util/placement";
|
||||||
import {
|
import { getParagraphMenuOptions } from "../../menu/paragraph";
|
||||||
getParagraphMenuOptions,
|
|
||||||
handleSelectParagraphMenu,
|
|
||||||
} from "../../menu/paragraph";
|
|
||||||
import Notation from "../Notation";
|
import Notation from "../Notation";
|
||||||
import Row from "../Row";
|
import Row from "../Row";
|
||||||
|
|
||||||
function Paragraph({ paragraph, offsetY, alignJustify }) {
|
function Paragraph({ paragraph, offsetY, alignJustify }) {
|
||||||
console.log("render paragraph");
|
|
||||||
const notations = paragraph.notations || [];
|
const notations = paragraph.notations || [];
|
||||||
const widthCache = [];
|
const widthCache = [];
|
||||||
let itemFlexOffset = 0;
|
let itemFlexOffset = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user