text-guitar-chart 0.1.0 → 0.1.1

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.
@@ -840,45 +840,86 @@ export class EditableSVGuitarChord {
840
840
  }
841
841
 
842
842
  /**
843
- * Position dialog relative to the clicked element
843
+ * Calculate absolute position for a dialog relative to a reference element
844
+ * @param {HTMLElement} dialog - The dialog element to position
845
+ * @param {Element} referenceElement - The element to position relative to
846
+ * @param {object} options - Positioning options
847
+ * @param {'beside'|'below'} [options.placement] - Whether to place beside or below the reference
848
+ * @param {number} [options.offset] - Distance from reference element
849
+ * @returns {{x: number, y: number, arrowSide: string, elementCenterY: number}}
844
850
  */
845
- positionDialog() {
846
- if (!this.currentEditElement || !this.dialog) return;
847
-
848
- // Get the bounding rect of the clicked element
849
- const elementRect = this.currentEditElement.getBoundingClientRect();
850
- const dialogRect = this.dialog.getBoundingClientRect();
851
+ calculateDialogPosition(dialog, referenceElement, options = {}) {
852
+ const { placement = 'beside', offset = 20 } = options;
853
+
854
+ const elementRect = referenceElement.getBoundingClientRect();
855
+ const dialogRect = dialog.getBoundingClientRect();
851
856
 
852
- // Calculate position
853
857
  const elementCenterX = elementRect.left + elementRect.width / 2;
854
858
  const elementCenterY = elementRect.top + elementRect.height / 2;
855
859
 
856
- // Position dialog to the right and slightly above the dot
857
- let dialogX = elementCenterX + 20;
858
- let dialogY = elementCenterY - dialogRect.height / 2;
860
+ let dialogX, dialogY;
861
+ let arrowSide = 'left';
859
862
 
860
- // Ensure dialog stays within viewport bounds
861
863
  const padding = 10;
862
864
  const maxX = window.innerWidth - dialogRect.width - padding;
863
865
  const maxY = window.innerHeight - dialogRect.height - padding;
864
866
 
865
- let arrowSide = 'left'; // Default: arrow points right (dot is to the left of dialog)
866
-
867
- if (dialogX > maxX) {
868
- // Position to the left of the dot instead
869
- dialogX = elementCenterX - dialogRect.width - 20;
870
- arrowSide = 'right'; // Arrow points left (dot is to the right of dialog)
867
+ if (placement === 'beside') {
868
+ // Position to the right and vertically centered
869
+ dialogX = elementCenterX + offset;
870
+ dialogY = elementCenterY - dialogRect.height / 2;
871
+
872
+ // Check if dialog fits on the right
873
+ if (dialogX > maxX) {
874
+ // Position to the left instead
875
+ dialogX = elementCenterX - dialogRect.width - offset;
876
+ arrowSide = 'right';
877
+ }
878
+ } else if (placement === 'below') {
879
+ // Position below the reference element
880
+ dialogX = elementRect.left;
881
+ dialogY = elementRect.bottom + offset;
882
+
883
+ // Check if dialog fits below
884
+ if (dialogY > maxY) {
885
+ // Position above instead
886
+ dialogY = elementRect.top - dialogRect.height - offset;
887
+ }
871
888
  }
889
+
890
+ // Ensure dialog stays within viewport bounds
872
891
  if (dialogX < padding) dialogX = padding;
892
+ if (dialogX > maxX) dialogX = maxX;
873
893
  if (dialogY < padding) dialogY = padding;
874
894
  if (dialogY > maxY) dialogY = maxY;
875
895
 
896
+ // Add scroll offsets to convert from viewport coordinates to absolute page coordinates
897
+ return {
898
+ x: dialogX + window.scrollX,
899
+ y: dialogY + window.scrollY,
900
+ arrowSide,
901
+ elementCenterY
902
+ };
903
+ }
904
+
905
+ /**
906
+ * Position dialog relative to the clicked element
907
+ */
908
+ positionDialog() {
909
+ if (!this.currentEditElement || !this.dialog) return;
910
+
911
+ const position = this.calculateDialogPosition(this.dialog, this.currentEditElement, {
912
+ placement: 'beside',
913
+ offset: 20
914
+ });
915
+
876
916
  // Apply positioning
877
- this.dialog.style.left = `${dialogX}px`;
878
- this.dialog.style.top = `${dialogY}px`;
917
+ this.dialog.style.left = `${position.x}px`;
918
+ this.dialog.style.top = `${position.y}px`;
879
919
 
880
920
  // Add arrow CSS class and calculate arrow position
881
- this.addArrowCSS(arrowSide, elementCenterY, dialogY, dialogRect.height);
921
+ const dialogRect = this.dialog.getBoundingClientRect();
922
+ this.addArrowCSS(position.arrowSide, position.elementCenterY, position.y - window.scrollY, dialogRect.height);
882
923
  }
883
924
 
884
925
  /**
@@ -887,40 +928,18 @@ export class EditableSVGuitarChord {
887
928
  positionOpenStringDialog() {
888
929
  if (!this.currentEditElement || !this.openStringDialog) return;
889
930
 
890
- // Get the bounding rect of the clicked element
891
- const elementRect = this.currentEditElement.getBoundingClientRect();
892
- const dialogRect = this.openStringDialog.getBoundingClientRect();
893
-
894
- // Calculate position
895
- const elementCenterX = elementRect.left + elementRect.width / 2;
896
- const elementCenterY = elementRect.top + elementRect.height / 2;
897
-
898
- // Position dialog to the right and slightly above the open string
899
- let dialogX = elementCenterX + 20;
900
- let dialogY = elementCenterY - dialogRect.height / 2;
901
-
902
- // Ensure dialog stays within viewport bounds
903
- const padding = 10;
904
- const maxX = window.innerWidth - dialogRect.width - padding;
905
- const maxY = window.innerHeight - dialogRect.height - padding;
906
-
907
- let arrowSide = 'left'; // Default: arrow points right (open string is to the left of dialog)
908
-
909
- if (dialogX > maxX) {
910
- // Position to the left of the open string instead
911
- dialogX = elementCenterX - dialogRect.width - 20;
912
- arrowSide = 'right'; // Arrow points left (open string is to the right of dialog)
913
- }
914
- if (dialogX < padding) dialogX = padding;
915
- if (dialogY < padding) dialogY = padding;
916
- if (dialogY > maxY) dialogY = maxY;
931
+ const position = this.calculateDialogPosition(this.openStringDialog, this.currentEditElement, {
932
+ placement: 'beside',
933
+ offset: 20
934
+ });
917
935
 
918
936
  // Apply positioning
919
- this.openStringDialog.style.left = `${dialogX}px`;
920
- this.openStringDialog.style.top = `${dialogY}px`;
937
+ this.openStringDialog.style.left = `${position.x}px`;
938
+ this.openStringDialog.style.top = `${position.y}px`;
921
939
 
922
940
  // Add arrow CSS class and calculate arrow position
923
- this.addOpenStringArrowCSS(arrowSide, elementCenterY, dialogY, dialogRect.height);
941
+ const dialogRect = this.openStringDialog.getBoundingClientRect();
942
+ this.addOpenStringArrowCSS(position.arrowSide, position.elementCenterY, position.y - window.scrollY, dialogRect.height);
924
943
  }
925
944
 
926
945
  /**
@@ -1273,27 +1292,14 @@ export class EditableSVGuitarChord {
1273
1292
  positionSettingsDialog() {
1274
1293
  if (!this.settingsButton || !this.settingsDialog) return;
1275
1294
 
1276
- // Get the bounding rect of the settings button
1277
- const buttonRect = this.settingsButton.getBoundingClientRect();
1278
- const dialogRect = this.settingsDialog.getBoundingClientRect();
1279
-
1280
- // Position dialog below and to the right of the button
1281
- let dialogX = buttonRect.left;
1282
- let dialogY = buttonRect.bottom + 5;
1283
-
1284
- // Ensure dialog stays within viewport bounds
1285
- const padding = 10;
1286
- const maxX = window.innerWidth - dialogRect.width - padding;
1287
- const maxY = window.innerHeight - dialogRect.height - padding;
1288
-
1289
- if (dialogX > maxX) dialogX = maxX;
1290
- if (dialogX < padding) dialogX = padding;
1291
- if (dialogY > maxY) dialogY = buttonRect.top - dialogRect.height - 5;
1292
- if (dialogY < padding) dialogY = padding;
1295
+ const position = this.calculateDialogPosition(this.settingsDialog, this.settingsButton, {
1296
+ placement: 'below',
1297
+ offset: 5
1298
+ });
1293
1299
 
1294
1300
  // Apply positioning
1295
- this.settingsDialog.style.left = `${dialogX}px`;
1296
- this.settingsDialog.style.top = `${dialogY}px`;
1301
+ this.settingsDialog.style.left = `${position.x}px`;
1302
+ this.settingsDialog.style.top = `${position.y}px`;
1297
1303
  }
1298
1304
 
1299
1305
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "text-guitar-chart",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A JavaScript library to write text based guitar chord charts and convert them to SVG.",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",