(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react"), require("@abc-editor/core"), require("abcjs"), require("tonal")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react", "@abc-editor/core", "abcjs", "tonal"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.ABCEditorReact = {}, global["react/jsx-runtime"], global.React, global.ABCEditorCore, global.ABCJS, global.Tonal)); })(this, function(exports2, jsxRuntime, react, core, abcjs, tonal) { "use strict"; function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d2 = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d2.get ? d2 : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const abcjs__namespace = /* @__PURE__ */ _interopNamespaceDefault(abcjs); var u = Object.getOwnPropertySymbols; var x = Object.prototype.hasOwnProperty, y = Object.prototype.propertyIsEnumerable; var d = (e, o) => { var P = {}; for (var r2 in e) x.call(e, r2) && o.indexOf(r2) < 0 && (P[r2] = e[r2]); if (e != null && u) for (var r2 of u(e)) o.indexOf(r2) < 0 && y.call(e, r2) && (P[r2] = e[r2]); return P; }; function C(e) { let o = react.createContext(null); return { BaseContext: o, Provider: (v) => { var t = v, { children: r2 = null } = t, a = d(t, ["children"]); let n = e(a); return jsxRuntime.jsx(o.Provider, { value: n, children: r2 }); } }; } const { renderAbc } = abcjs__namespace; const useEditor = ({ initialAbc, abcjsOptions, enableKbdShortcuts, chordTemplate, onChange = () => { } }) => { const editorState = react.useRef( new core.EditorState(initialAbc, { chordTemplate }) ); const [abc, setAbc] = react.useState(() => editorState.current.abc); const renderDiv = react.useRef(null); const setRenderDiv = react.useCallback( (div) => renderDiv.current = div, [] ); const [editorCommands, dispatchEditorCommand] = react.useReducer( core.editorCommandReducer, { rhythm: core.Rhythm.Eighth, rest: false, accidental: core.Accidental.None, dotted: false, triplet: false, showKeyboard: false, beamed: false, midiEnabled: false } ); const restRef = react.useRef(editorCommands.rest); react.useEffect(() => { restRef.current = editorCommands.rest; }, [editorCommands.rest]); const rhythmRef = react.useRef(editorCommands.rhythm); react.useEffect(() => { rhythmRef.current = editorCommands.rhythm; }, [editorCommands.rhythm]); const dottedRef = react.useRef(editorCommands.dotted); react.useEffect(() => { dottedRef.current = editorCommands.dotted; }, [editorCommands.dotted]); const accidentalRef = react.useRef(editorCommands.accidental); react.useEffect(() => { accidentalRef.current = editorCommands.accidental; }, [editorCommands.accidental]); const beamedRef = react.useRef(editorCommands.beamed); react.useEffect(() => { beamedRef.current = editorCommands.beamed; }, [editorCommands.beamed]); const tripletRef = react.useRef(editorCommands.triplet); react.useEffect(() => { tripletRef.current = editorCommands.triplet; }, [editorCommands.triplet]); const onAddNote = react.useCallback((noteName) => { editorState.current.addNote(noteName, rhythmRef.current, { beamed: beamedRef.current, dotted: dottedRef.current, rest: restRef.current, accidental: accidentalRef.current, triplet: tripletRef.current }); setAbc(editorState.current.abc); }, []); react.useEffect(() => { if (!renderDiv.current) return; const [tuneObject] = renderAbc(renderDiv.current, abc + "yy", { ...abcjsOptions, add_classes: true }); editorState.current.updateTuneData(tuneObject.lines); onChange(abc, tuneObject); dispatchEditorCommand({ type: "setBeamed", beamed: editorState.current.shouldBeamNextNote(editorCommands.rhythm) }); if (editorState.current.isEndOfTriplet && tripletRef.current === true) dispatchEditorCommand({ type: "toggleTriplet" }); }, [abc, abcjsOptions, editorCommands.rhythm, onChange]); react.useEffect(() => { dispatchEditorCommand({ type: "setAccidental", accidental: core.Accidental.None }); dispatchEditorCommand({ type: "setDotted", dotted: false }); if (restRef.current === true) dispatchEditorCommand({ type: "toggleRest" }); }, [abc]); const onBackspace = react.useCallback(() => { editorState.current.backspace(); setAbc(editorState.current.abc); }, []); const onNewLine = react.useCallback(() => { editorState.current.newLine(); setAbc(editorState.current.abc); }, []); const lastKnownMousePos = react.useRef(null); react.useEffect(() => { if (!renderDiv.current) return; const cleanUpStaffListeners = core.setupStaffMouseListeners({ renderDiv: renderDiv.current, numTuneLines: editorState.current.tuneLines.length, rhythm: editorCommands.rhythm, rest: editorCommands.rest, accidental: editorCommands.accidental, dotted: editorCommands.dotted, onAddNote, lastMousePos: lastKnownMousePos.current, updateLastMousePos: (pos) => lastKnownMousePos.current = pos }); return () => cleanUpStaffListeners(); }, [ abc, abcjsOptions, editorCommands.accidental, editorCommands.dotted, editorCommands.rest, editorCommands.rhythm, onAddNote ]); react.useEffect(() => { if (!enableKbdShortcuts) return; const cleanUpKbdListener = core.setupKeyboardListener( dispatchEditorCommand, onBackspace, onNewLine ); return () => cleanUpKbdListener(); }, [enableKbdShortcuts, onBackspace, onNewLine]); react.useEffect(() => { if (!editorCommands.midiEnabled) return; const cleanUpMidiListener = core.setupMIDIListener(onAddNote); return () => cleanUpMidiListener(); }, [editorCommands.midiEnabled, onAddNote]); return { currentCommands: editorCommands, dispatchCommand: dispatchEditorCommand, setRenderDiv, onAddNote, onBackspace, onNewLine }; }; const { BaseContext, Provider } = C(useEditor); const useEditorContext = () => react.useContext(BaseContext); const EditorProvider = Provider; function r(e) { var t, f, n = ""; if ("string" == typeof e || "number" == typeof e) n += e; else if ("object" == typeof e) if (Array.isArray(e)) { var o = e.length; for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f); } else for (f in e) e[f] && (n && (n += " "), n += f); return n; } function clsx() { for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t); return n; } const key = "_key_1aqm4_1"; const keyLabel = "_keyLabel_1aqm4_14"; const black = "_black_1aqm4_18"; const left = "_left_1aqm4_30"; const right = "_right_1aqm4_33"; const played = "_played_1aqm4_36"; const white = "_white_1aqm4_40"; const styles$3 = { key, keyLabel, black, left, right, played, white }; const BLACK_KEY_HALF_STEPS = [1, 3, 6, 8, 10]; const BLACK_KEY_SHIFT_LEFT = [1, 6]; const BLACK_KEY_SHIFT_RIGHT = [3, 10]; function Key({ midiNum, played: played2, onKeyPlayed, onKeyReleased }) { const halfSteps = midiNum % 12; const isBlackKey = BLACK_KEY_HALF_STEPS.includes(halfSteps); let adjustPosition = "none"; if (isBlackKey) { if (BLACK_KEY_SHIFT_LEFT.includes(halfSteps)) adjustPosition = "left"; else if (BLACK_KEY_SHIFT_RIGHT.includes(halfSteps)) adjustPosition = "right"; } const className = clsx(styles$3.key, { [styles$3.white]: !isBlackKey, [styles$3.black]: isBlackKey, [styles$3.left]: adjustPosition === "left", [styles$3.right]: adjustPosition === "right", [styles$3.played]: played2 }); const onMouseDown = react.useCallback( (e) => { e.preventDefault(); onKeyPlayed(); }, [onKeyPlayed] ); const onMouseUp = react.useCallback( (e) => { e.preventDefault(); onKeyReleased(); }, [onKeyReleased] ); return /* @__PURE__ */ jsxRuntime.jsx( "div", { className, onMouseDown, onMouseUp, onMouseLeave: onMouseUp, onTouchStart: onMouseDown, onTouchEnd: onMouseUp, draggable: false, children: halfSteps === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$3.keyLabel, children: tonal.Midi.midiToNoteName(midiNum) }) } ); } const keyboard = "_keyboard_ihmqj_1"; const styles$2 = { keyboard }; function Keyboard({ startKey, endKey }) { const { onAddNote } = useEditorContext(); const [keys, setKeys] = react.useState( new Array(endKey - startKey).fill(false) ); return /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$2.keyboard, children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: keys.map((isPlayed, idx) => { const midiNum = startKey + idx; return /* @__PURE__ */ jsxRuntime.jsx( Key, { midiNum, played: isPlayed, onKeyPlayed: () => { setKeys((prev) => prev.with(idx, true)); onAddNote(midiNum); }, onKeyReleased: () => { setKeys((prev) => prev.with(idx, false)); } }, midiNum ); }) }) }); } function Score() { const { setRenderDiv } = useEditorContext(); return /* @__PURE__ */ jsxRuntime.jsx( "div", { style: { padding: 20, overflow: "auto", width: "100%", cursor: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setRenderDiv }) } ); } const EditorControlIcon = ({ icon, size }) => { const rawSvg = react.useMemo(() => core.getIcon(icon), [icon]); const refCallback = react.useCallback( (ref) => { if (!ref || !rawSvg) return; const svg = ref.querySelector("svg"); if (svg && size) { svg.setAttribute("height", size.toString()); svg.setAttribute("width", size.toString()); } }, [rawSvg, size] ); return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: refCallback, dangerouslySetInnerHTML: { __html: rawSvg } }); }; const controls = "_controls_cmfdp_1"; const controlGroup = "_controlGroup_cmfdp_8"; const iconButton = "_iconButton_cmfdp_16"; const selected = "_selected_cmfdp_40"; const spacer = "_spacer_cmfdp_44"; const styles$1 = { controls, controlGroup, iconButton, selected, spacer }; function EditorControls() { const { currentCommands, dispatchCommand, onBackspace, onNewLine } = useEditorContext(); const iconSize = 18; return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles$1.controls, children: [ /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: styles$1.controlGroup, children: [ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Accidental" }), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.accidental === core.Accidental.Flat }), onClick: () => dispatchCommand({ type: "toggleAccidental", accidental: core.Accidental.Flat }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Flat, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.accidental === core.Accidental.Sharp }), onClick: () => dispatchCommand({ type: "toggleAccidental", accidental: core.Accidental.Sharp }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Sharp, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.accidental === core.Accidental.Natural }), onClick: () => dispatchCommand({ type: "toggleAccidental", accidental: core.Accidental.Natural }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Natural, size: iconSize }) } ) ] }), /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: styles$1.controlGroup, children: [ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Rhythm" }), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.rhythm === core.Rhythm.Whole }), onClick: () => dispatchCommand({ type: "setRhythm", rhythm: core.Rhythm.Whole }), children: currentCommands.rest ? /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.WholeRest, size: iconSize }) : /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.WholeNote, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.rhythm === core.Rhythm.Half }), onClick: () => dispatchCommand({ type: "setRhythm", rhythm: core.Rhythm.Half }), children: currentCommands.rest ? /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.HalfRest, size: iconSize }) : /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.HalfNote, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.rhythm === core.Rhythm.Quarter }), onClick: () => dispatchCommand({ type: "setRhythm", rhythm: core.Rhythm.Quarter }), children: currentCommands.rest ? /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.QuarterRest, size: iconSize }) : /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.QuarterNote, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.rhythm === core.Rhythm.Eighth }), onClick: () => dispatchCommand({ type: "setRhythm", rhythm: core.Rhythm.Eighth }), children: currentCommands.rest ? /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.EighthRest, size: iconSize }) : /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.EighthNote, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.rhythm === core.Rhythm.Sixteenth }), onClick: () => dispatchCommand({ type: "setRhythm", rhythm: core.Rhythm.Sixteenth }), children: currentCommands.rest ? /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.SixteenthRest, size: iconSize }) : /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.SixteenthNote, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$1.spacer }), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.dotted }), onClick: () => dispatchCommand({ type: "toggleDotted" }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Dot, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.rest }), onClick: () => dispatchCommand({ type: "toggleRest" }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.QuarterRest, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.triplet }), onClick: () => dispatchCommand({ type: "toggleTriplet" }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Triplet, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.beamed }), onClick: () => dispatchCommand({ type: "toggleBeamed" }), disabled: ![8, 16].includes(currentCommands.rhythm), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Beaming, size: iconSize }) } ) ] }), /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: styles$1.controlGroup, children: [ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Edit" }), /* @__PURE__ */ jsxRuntime.jsx("button", { className: clsx(styles$1.iconButton), onClick: onBackspace, children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Backspace, size: iconSize }) }), /* @__PURE__ */ jsxRuntime.jsx("button", { className: clsx(styles$1.iconButton), onClick: onNewLine, children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.NewLine, size: iconSize }) }) ] }), /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: styles$1.controlGroup, children: [ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Input" }), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.showKeyboard }), onClick: () => dispatchCommand({ type: "toggleShowKeyboard" }), children: /* @__PURE__ */ jsxRuntime.jsx(EditorControlIcon, { icon: core.Icon.Piano, size: iconSize }) } ), /* @__PURE__ */ jsxRuntime.jsx( "button", { className: clsx(styles$1.iconButton, { [styles$1.selected]: currentCommands.midiEnabled }), onClick: () => dispatchCommand({ type: "toggleMidi" }), children: "MIDI" } ) ] }) ] }); } const editor = "_editor_uytz2_1"; const styles = { editor }; function Editor({ initialAbc, chordTemplate, jazzChords, format, selectTypes, visualTranspose, autoLineBreaks, responsive = false, scale = 1, enableKbdShortcuts = false, onChange = () => { } }) { const abcjsOptions = react.useMemo( () => ({ scale, responsive: responsive ? "resize" : void 0, jazzchords: jazzChords, format, selectTypes, visualTranspose, ...(autoLineBreaks == null ? void 0 : autoLineBreaks.staffWidth) ? { staffwidth: autoLineBreaks.staffWidth, wrap: { minSpacing: autoLineBreaks.minSpacing || 1.8, maxSpacing: autoLineBreaks.maxSpacing || 2.2, preferredMeasuresPerLine: autoLineBreaks.preferredMeasuresPerLine } } : {} }), [ scale, responsive, jazzChords, format, visualTranspose, selectTypes, autoLineBreaks == null ? void 0 : autoLineBreaks.staffWidth, autoLineBreaks == null ? void 0 : autoLineBreaks.minSpacing, autoLineBreaks == null ? void 0 : autoLineBreaks.maxSpacing, autoLineBreaks == null ? void 0 : autoLineBreaks.preferredMeasuresPerLine ] ); return /* @__PURE__ */ jsxRuntime.jsx( EditorProvider, { initialAbc, chordTemplate, abcjsOptions, enableKbdShortcuts, onChange, children: /* @__PURE__ */ jsxRuntime.jsx(InnerEditor, {}) } ); } function InnerEditor() { const { currentCommands } = useEditorContext(); return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.editor, children: [ /* @__PURE__ */ jsxRuntime.jsx(EditorControls, {}), currentCommands.showKeyboard && /* @__PURE__ */ jsxRuntime.jsx(Keyboard, { startKey: 60, endKey: 84 }), /* @__PURE__ */ jsxRuntime.jsx(Score, {}) ] }); } exports2.ABCEditor = Editor; exports2.EditorControls = EditorControls; exports2.EditorProvider = EditorProvider; exports2.Keyboard = Keyboard; exports2.Score = Score; exports2.useEditorContext = useEditorContext; Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" }); });