@teachinglab/omd 0.7.13 → 0.7.15

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/jsvg/jsvg.js CHANGED
@@ -725,84 +725,320 @@ export class jsvgTextLine extends jsvgObject
725
725
  }
726
726
 
727
727
 
728
- // ================ jsvgTextBox (Div) ================================= //
728
+ // ================ jsvgTextBox ================================= //
729
729
 
730
730
  export class jsvgTextBox extends jsvgGroup
731
731
  {
732
- constructor()
732
+ constructor( options = {} )
733
733
  {
734
734
  super();
735
735
 
736
736
  this.text = "";
737
-
738
- var ns = 'http://www.w3.org/2000/svg';
739
- this.foreignObject = document.createElementNS( ns, 'foreignObject');
740
-
741
- this.foreignObject.style.backgroundColor = 'none';
742
- this.svgObject.appendChild( this.foreignObject );
737
+ this._useForeignObject = options.useForeignObject === true;
738
+ this._textAlign = "left";
739
+ this._verticalCenter = false;
740
+ this._lineHeight = null;
741
+ this._fontSize = 16;
742
+ this._fontFamily = "Arial, Helvetica, sans-serif";
743
+ this._fontWeight = "normal";
744
+ this._fontColor = "black";
745
+
746
+ const ns = 'http://www.w3.org/2000/svg';
747
+
748
+ if ( this._useForeignObject )
749
+ {
750
+ this.foreignObject = document.createElementNS( ns, 'foreignObject');
751
+ this.foreignObject.style.backgroundColor = 'none';
752
+ this.svgObject.appendChild( this.foreignObject );
743
753
 
744
- this.div = document.createElement('div');
745
- this.foreignObject.appendChild( this.div );
754
+ this.div = document.createElement('div');
755
+ this.foreignObject.appendChild( this.div );
756
+ }
757
+ else
758
+ {
759
+ this.textElement = document.createElementNS( ns, 'text');
760
+ this.textElement.setAttribute('x', '0');
761
+ this.textElement.setAttribute('y', '0');
762
+ this.textElement.setAttribute('fill', this._fontColor);
763
+ this.textElement.setAttribute('xml:space', 'preserve');
764
+ this.textElement.style.fontFamily = this._fontFamily;
765
+ this.textElement.style.fontSize = this._fontSize.toString() + "px";
766
+ this.svgObject.appendChild( this.textElement );
767
+ }
746
768
 
747
769
  this.setWidthAndHeight( 200,100 );
748
770
  }
749
771
 
750
- setText( T ) { this.text = T; this.div.innerHTML = T; }
772
+ _getLineHeight()
773
+ {
774
+ return this._lineHeight || this._fontSize;
775
+ }
776
+
777
+ _getTextX()
778
+ {
779
+ if ( this._textAlign === "center" )
780
+ return this.width / 2;
781
+ if ( this._textAlign === "right" || this._textAlign === "end" )
782
+ return this.width;
783
+ return 0;
784
+ }
785
+
786
+ _applyTextAnchor()
787
+ {
788
+ if ( !this.textElement ) return;
789
+ if ( this._textAlign === "center" )
790
+ this.textElement.setAttribute("text-anchor", "middle");
791
+ else if ( this._textAlign === "right" || this._textAlign === "end" )
792
+ this.textElement.setAttribute("text-anchor", "end");
793
+ else
794
+ this.textElement.setAttribute("text-anchor", "start");
795
+ }
796
+
797
+ _parseHtmlToLines( html )
798
+ {
799
+ const hasMarkup = /<\s*(sup|sub|br)\b/i.test( html || "" );
800
+ if ( !hasMarkup )
801
+ return [ [ { text: html || "", mode: null } ] ];
802
+
803
+ const container = document.createElement('div');
804
+ container.innerHTML = html || "";
805
+ const lines = [ [] ];
806
+
807
+ const walk = (node, mode) => {
808
+ if ( node.nodeType === 3 )
809
+ {
810
+ const value = node.nodeValue || "";
811
+ if ( value.length > 0 )
812
+ lines[ lines.length - 1 ].push({ text: value, mode: mode });
813
+ return;
814
+ }
815
+ if ( node.nodeType !== 1 ) return;
816
+ const tag = node.tagName.toLowerCase();
817
+ if ( tag === "br" )
818
+ {
819
+ lines.push([]);
820
+ return;
821
+ }
822
+ let nextMode = mode;
823
+ if ( tag === "sup" ) nextMode = "sup";
824
+ if ( tag === "sub" ) nextMode = "sub";
825
+ for ( let i = 0; i < node.childNodes.length; i++ )
826
+ walk( node.childNodes[i], nextMode );
827
+ };
828
+
829
+ for ( let i = 0; i < container.childNodes.length; i++ )
830
+ walk( container.childNodes[i], null );
831
+
832
+ if ( lines.length > 1 && lines[ lines.length - 1 ].length === 0 )
833
+ lines.pop();
834
+
835
+ return lines;
836
+ }
837
+
838
+ _renderSvgText( html )
839
+ {
840
+ if ( !this.textElement ) return;
841
+
842
+ const ns = 'http://www.w3.org/2000/svg';
843
+ const lines = this._parseHtmlToLines( html );
844
+ const lineHeight = this._getLineHeight();
845
+ const blockHeight = lineHeight * lines.length;
846
+ const startY = this._verticalCenter ? Math.max(0, (this.height - blockHeight) / 2) : 0;
847
+ const startX = this._getTextX();
848
+
849
+ while ( this.textElement.firstChild )
850
+ this.textElement.removeChild( this.textElement.firstChild );
851
+
852
+ this._applyTextAnchor();
853
+ this.textElement.setAttribute('dominant-baseline', 'hanging');
854
+
855
+ for ( let lineIndex = 0; lineIndex < lines.length; lineIndex++ )
856
+ {
857
+ const line = lines[lineIndex];
858
+ if ( line.length === 0 )
859
+ line.push({ text: "", mode: null });
860
+
861
+ for ( let segIndex = 0; segIndex < line.length; segIndex++ )
862
+ {
863
+ const seg = line[segIndex];
864
+ const tspan = document.createElementNS( ns, 'tspan' );
865
+ tspan.textContent = seg.text;
866
+
867
+ if ( seg.mode === "sup" )
868
+ {
869
+ tspan.setAttribute("baseline-shift", "super");
870
+ tspan.setAttribute("font-size", "0.7em");
871
+ }
872
+ else if ( seg.mode === "sub" )
873
+ {
874
+ tspan.setAttribute("baseline-shift", "sub");
875
+ tspan.setAttribute("font-size", "0.7em");
876
+ }
877
+
878
+ if ( segIndex === 0 )
879
+ {
880
+ tspan.setAttribute("x", startX);
881
+ if ( lineIndex === 0 )
882
+ tspan.setAttribute("y", startY);
883
+ else
884
+ tspan.setAttribute("dy", lineHeight);
885
+ }
886
+
887
+ this.textElement.appendChild( tspan );
888
+ }
889
+ }
890
+ }
891
+
892
+ _updateSvgLayout()
893
+ {
894
+ if ( this.textElement )
895
+ this._renderSvgText( this.text );
896
+ }
897
+
898
+ setText( T )
899
+ {
900
+ this.text = T || "";
901
+ if ( this._useForeignObject )
902
+ this.div.innerHTML = this.text;
903
+ else
904
+ this._renderSvgText( this.text );
905
+ }
751
906
 
752
907
  getText() { return this.text; }
753
908
 
754
909
  setStyle( S ) // for style setting with CSS
755
910
  {
756
- this.div.className = S;
757
- this.div.style.position = 'fixed'; // for safari
911
+ if ( this._useForeignObject )
912
+ {
913
+ this.div.className = S;
914
+ this.div.style.position = 'fixed'; // for safari
915
+ }
916
+ else if ( this.textElement )
917
+ {
918
+ this.textElement.setAttribute('class', S);
919
+ }
758
920
  };
759
921
 
760
- setFontFamily( F ) { this.div.style.fontFamily = F; }
922
+ setFontFamily( F )
923
+ {
924
+ this._fontFamily = F;
925
+ if ( this._useForeignObject )
926
+ this.div.style.fontFamily = F;
927
+ else if ( this.textElement )
928
+ this.textElement.style.fontFamily = F;
929
+ }
761
930
 
762
- setFontSize( S ) { this.div.style.fontSize = S.toString() + "px"; }
931
+ setFontSize( S )
932
+ {
933
+ this._fontSize = S;
934
+ if ( this._useForeignObject )
935
+ this.div.style.fontSize = S.toString() + "px";
936
+ else if ( this.textElement )
937
+ {
938
+ this.textElement.style.fontSize = S.toString() + "px";
939
+ this._updateSvgLayout();
940
+ }
941
+ }
763
942
 
764
- setLineHeight( H ) { this.div.style.lineHeight = H + 'px'; }
943
+ setLineHeight( H )
944
+ {
945
+ this._lineHeight = H;
946
+ if ( this._useForeignObject )
947
+ this.div.style.lineHeight = H + 'px';
948
+ else
949
+ this._updateSvgLayout();
950
+ }
765
951
 
766
- setFontWeight( W ) { this.div.style.fontWeight = W; }
952
+ setFontWeight( W )
953
+ {
954
+ this._fontWeight = W;
955
+ if ( this._useForeignObject )
956
+ this.div.style.fontWeight = W;
957
+ else if ( this.textElement )
958
+ this.textElement.style.fontWeight = W;
959
+ }
767
960
 
768
- setFontColor( C ) { this.div.style.color = C; }
961
+ setFontColor( C )
962
+ {
963
+ this._fontColor = C;
964
+ if ( this._useForeignObject )
965
+ this.div.style.color = C;
966
+ else if ( this.textElement )
967
+ this.textElement.setAttribute('fill', C);
968
+ }
769
969
 
770
- setAlignment( A ) { this.div.style.textAlign = A; }
970
+ setAlignment( A )
971
+ {
972
+ this._textAlign = A;
973
+ if ( this._useForeignObject )
974
+ this.div.style.textAlign = A;
975
+ else
976
+ this._updateSvgLayout();
977
+ }
771
978
 
772
979
  setVerticalCentering()
773
980
  {
774
- this.div.style.display = "flex";
775
- this.div.style.flexFlow = "column"
776
- this.div.style.justifyContent = "center";
777
- this.div.style.alignItems = "center";
981
+ if ( this._useForeignObject )
982
+ {
983
+ this.div.style.display = "flex";
984
+ this.div.style.flexFlow = "column"
985
+ this.div.style.justifyContent = "center";
986
+ this.div.style.alignItems = "center";
987
+ }
988
+ else
989
+ {
990
+ this._verticalCenter = true;
991
+ this._updateSvgLayout();
992
+ }
778
993
  }
779
994
 
780
995
  makeEditable()
781
996
  {
782
- this.div.style.contentEditable = 'true';
783
- this.div.onclick = function() { console.log("here"); this.focus(); };
997
+ if ( this._useForeignObject )
998
+ {
999
+ this.div.style.contentEditable = 'true';
1000
+ this.div.onclick = function() { console.log("here"); this.focus(); };
1001
+ }
784
1002
  }
785
1003
 
786
- setBorderWidthAndColor( width, color ) { this.div.style.border = width + "px solid " + color; }
1004
+ setBorderWidthAndColor( width, color )
1005
+ {
1006
+ if ( this._useForeignObject )
1007
+ this.div.style.border = width + "px solid " + color;
1008
+ }
787
1009
 
788
1010
  setWidth( W )
789
1011
  {
790
1012
  this.width = W;
791
- if ( this.div )
792
- this.div.style.width = W + 'px'
1013
+ if ( this._useForeignObject )
1014
+ {
1015
+ if ( this.div )
1016
+ this.div.style.width = W + 'px';
793
1017
 
794
- if ( this.foreignObject )
795
- this.foreignObject.setAttribute('width', W );
1018
+ if ( this.foreignObject )
1019
+ this.foreignObject.setAttribute('width', W );
1020
+ }
1021
+ else
1022
+ {
1023
+ this._updateSvgLayout();
1024
+ }
796
1025
  }
797
1026
 
798
1027
  setHeight( H )
799
1028
  {
800
1029
  this.height = H;
801
- if ( this.div )
802
- this.div.style.height = H + 'px'
1030
+ if ( this._useForeignObject )
1031
+ {
1032
+ if ( this.div )
1033
+ this.div.style.height = H + 'px';
803
1034
 
804
- if ( this.foreignObject )
805
- this.foreignObject.setAttribute('height', H );
1035
+ if ( this.foreignObject )
1036
+ this.foreignObject.setAttribute('height', H );
1037
+ }
1038
+ else
1039
+ {
1040
+ this._updateSvgLayout();
1041
+ }
806
1042
  }
807
1043
  }
808
1044
 
@@ -814,7 +1050,7 @@ export class jsvgTextInput extends jsvgTextBox
814
1050
  {
815
1051
  constructor()
816
1052
  {
817
- super();
1053
+ super({ useForeignObject: true });
818
1054
 
819
1055
  this.callback = null;
820
1056
 
@@ -861,7 +1097,7 @@ export class jsvgTextArea extends jsvgTextBox
861
1097
  {
862
1098
  constructor()
863
1099
  {
864
- super();
1100
+ super({ useForeignObject: true });
865
1101
 
866
1102
  this.callback = null;
867
1103
 
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@teachinglab/jsvg",
3
+ "version": "0.1.2",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./index.js"
8
+ },
9
+ "./jsvg.js": "./jsvg.js",
10
+ "./jsvgComponents.js": "./jsvgComponents.js"
11
+ },
12
+ "module": "./index.js"
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.7.13",
3
+ "version": "0.7.15",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",