astro-abcjs 1.0.0 → 2.0.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/README.md CHANGED
@@ -1,11 +1,51 @@
1
+
1
2
  # astro-abcjs
2
3
 
3
4
  An abcjs component for Astro projects.
4
5
 
5
- ## Credits & Inspiration
6
+ ## Usage
7
+
8
+ ### In .astro files
9
+
10
+ Import the component and use it in your Astro page or component:
11
+
12
+ ```astro
13
+ ---
14
+ import AbcjsPlayer from 'astro-abcjs/AbcjsPlayer.astro';
15
+ ---
16
+
17
+ <AbcjsPlayer
18
+ notation={`X:1\nT:Scale\nM:4/4\nK:C\nC D E F | G A B c |`}
19
+ showControls={true}
20
+ responsive={true}
21
+ abcjsVersion="6.6.0"
22
+ />
23
+ ```
24
+
25
+ ### In MDX files
6
26
 
7
- This component was inspired by the article:
27
+ First, make sure your project supports MDX and components in MDX.
28
+
29
+ Import the component at the top of your MDX file:
30
+
31
+ ```mdx
32
+ import AbcjsPlayer from 'astro-abcjs/AbcjsPlayer.astro';
33
+
34
+ <AbcjsPlayer
35
+ notation={`X:1\nT:Scale\nM:4/4\nK:C\nC D E F | G A B c |`}
36
+ showControls={true}
37
+ responsive={true}
38
+ abcjsVersion="6.6.0"
39
+ />
40
+ ```
41
+
42
+ #### Props
43
+
44
+ - `notation` (string, required): The ABC notation string to render.
45
+ - `showControls` (boolean, default: true): Show audio playback controls.
46
+ - `responsive` (boolean, default: true): Make the notation responsive to container size.
47
+ - `abcjsVersion` (string, optional, default: "6.6.0"): The abcjs version to load from CDN. Override to pin or upgrade/downgrade.
48
+
49
+ ## Credits & Inspiration
8
50
 
9
- **Building a Reusable Music Component in Astro using ABCjs**
10
- by [Pavlin Gunov](https://www.pavlinbg.com/)
11
- <https://www.pavlinbg.com/posts/building-a-reusable-music-component-in-astro-using-abcjs>
51
+ This component was inspired by the article [Building a Reusable Music Component in Astro using ABCjs](https://www.pavlinbg.com/posts/building-a-reusable-music-component-in-astro-using-abcjs) by [Pavlin Gunov](https://www.pavlinbg.com/).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-abcjs",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "An Astro component to embed abcjs in content",
5
5
  "keywords": [
6
6
  "astro",
@@ -1,74 +1,101 @@
1
1
  ---
2
2
  // AbcjsPlayer.astro
3
3
  // Reusable Astro component for rendering ABCjs music notation and audio playback
4
+
4
5
  import "abcjs/abcjs-audio.css";
5
6
 
7
+ // Use official abcjs types
8
+ import type { AbcVisualParams } from "abcjs";
9
+
10
+ // Props interface using official abcjs types
6
11
  interface Props {
7
12
  notation: string;
13
+ /**
14
+ * Options for abcjs.renderAbc (see AbcVisualParams in abcjs types)
15
+ * Note: Function callbacks (e.g., clickListener) are not supported as options
16
+ * are serialized to JSON for client-side use. For advanced interactivity, use
17
+ * custom events or request a callback/event API.
18
+ */
19
+ options?: AbcVisualParams;
20
+ /**
21
+ * Show audio playback controls (default: true)
22
+ */
8
23
  showControls?: boolean;
9
- responsive?: boolean;
24
+ /**
25
+ * abcjs version to load from CDN (default: "6.6.0")
26
+ */
27
+ abcjsVersion?: string;
10
28
  }
11
29
 
12
- const { notation, showControls = true, responsive = true } = Astro.props;
30
+ const {
31
+ notation,
32
+ options = {},
33
+ showControls = true,
34
+ abcjsVersion = "6.6.0",
35
+ } = Astro.props;
13
36
  ---
14
37
 
15
38
  <div
16
39
  class="abcjs-container"
17
40
  data-notation={notation}
18
- data-responsive={responsive}
41
+ data-options={JSON.stringify(options)}
19
42
  >
20
- <div id="paper" class="abcjs-paper"></div>
21
- {showControls && <div id="audio" class="abcjs-audio" />}
43
+ <div class="abcjs-paper"></div>
44
+ {showControls && <div class="abcjs-audio" />}
22
45
  </div>
23
46
 
24
- <script>
47
+ <script define:vars={{ abcjsVersion, options }}>
25
48
  if (typeof window !== "undefined") {
26
- import("abcjs").then((abcjs) => {
27
- function initializeAbcjs() {
28
- const containers = document.querySelectorAll(".abcjs-container");
29
- containers.forEach((container) => {
30
- if (container.dataset.abcjsInitialized === "true") return;
31
- const notation = container.getAttribute("data-notation");
32
- if (!notation) return;
33
- const isResponsive =
34
- container.getAttribute("data-responsive") === "true";
35
- const paperDiv = container.querySelector("#paper");
36
- const audioDiv = container.querySelector("#audio");
37
- // Render music notation
38
- const visualObj = abcjs.default.renderAbc(paperDiv, notation, {
39
- responsive: isResponsive ? "resize" : undefined,
40
- add_classes: true,
41
- paddingleft: 0,
42
- paddingright: 0,
43
- paddingbottom: 10,
44
- paddingtop: 10,
49
+ // Load abcjs browser build from CDN if not already loaded
50
+ if (!window.ABCJS) {
51
+ const script = document.createElement("script");
52
+ script.src = `https://cdn.jsdelivr.net/npm/abcjs@${abcjsVersion}/dist/abcjs-basic.js`;
53
+ script.onload = initializeAbcjs;
54
+ document.head.appendChild(script);
55
+ } else {
56
+ initializeAbcjs();
57
+ }
58
+
59
+ function initializeAbcjs() {
60
+ const abcjs = window.ABCJS;
61
+ if (!abcjs) return;
62
+ const containers = document.querySelectorAll(".abcjs-container");
63
+ containers.forEach((container) => {
64
+ if (container.dataset.abcjsInitialized === "true") return;
65
+ const notation = container.getAttribute("data-notation");
66
+ if (!notation) return;
67
+ const paperDiv = container.querySelector(".abcjs-paper");
68
+ const audioDiv = container.querySelector(".abcjs-audio");
69
+ // Render music notation using options prop (passed via define:vars)
70
+ // Render music notation using per-container options
71
+ const containerOptions = JSON.parse(
72
+ container.getAttribute("data-options") || "{}",
73
+ );
74
+ const visualObj = abcjs.renderAbc(paperDiv, notation, containerOptions);
75
+ // Audio playback controls
76
+ if (audioDiv && abcjs.synth) {
77
+ const synthControl = new abcjs.synth.SynthController();
78
+ synthControl.load(audioDiv, null, {
79
+ displayLoop: true,
80
+ displayRestart: true,
81
+ displayPlay: true,
82
+ displayProgress: true,
83
+ displayWarp: true,
45
84
  });
46
- // Audio playback controls
47
- if (audioDiv) {
48
- const synthControl = new abcjs.default.synth.SynthController();
49
- synthControl.load(audioDiv, null, {
50
- displayLoop: true,
51
- displayRestart: true,
52
- displayPlay: true,
53
- displayProgress: true,
54
- displayWarp: true,
85
+ const createSynth = new abcjs.synth.CreateSynth();
86
+ createSynth
87
+ .init({ visualObj: visualObj[0] })
88
+ .then(() => {
89
+ synthControl.setTune(visualObj[0], false);
90
+ })
91
+ .catch((error) => {
92
+ console.warn("Audio synthesis initialization failed:", error);
55
93
  });
56
- const createSynth = new abcjs.default.synth.CreateSynth();
57
- createSynth
58
- .init({ visualObj: visualObj[0] })
59
- .then(() => {
60
- synthControl.setTune(visualObj[0], false);
61
- })
62
- .catch((error) => {
63
- console.warn("Audio synthesis initialization failed:", error);
64
- });
65
- }
66
- container.dataset.abcjsInitialized = "true";
67
- });
68
- }
69
- initializeAbcjs();
70
- document.addEventListener("astro:page-load", initializeAbcjs);
71
- });
94
+ }
95
+ container.dataset.abcjsInitialized = "true";
96
+ });
97
+ }
98
+ document.addEventListener("astro:page-load", initializeAbcjs);
72
99
  }
73
100
  </script>
74
101