x4js 2.3.19 → 2.3.21

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.
@@ -121,33 +121,42 @@ export function isUnitLess( name: string ) {
121
121
  return unitless[name] ? true : false;
122
122
  }
123
123
 
124
+ // one sheet for the page, shared by all the Stylesheet: a name is one rule,
125
+ // whatever instance sets it
126
+ let page_sheet: CSSStyleSheet = null;
127
+ const page_rules = new Map<string, CSSRule>( );
128
+
129
+ function pageSheet( ): CSSStyleSheet {
130
+ if( !page_sheet ) {
131
+ // found by its id: a title would make it a "preferred" style sheet,
132
+ // of which a page applies only one
133
+ let dom = document.getElementById( 'x4-dynamic-css' ) as HTMLStyleElement;
134
+ if( !dom ) {
135
+ dom = document.createElement( 'style' );
136
+ dom.id = 'x4-dynamic-css';
137
+ document.head.appendChild( dom );
138
+ }
139
+
140
+ page_sheet = dom.sheet;
141
+ }
142
+
143
+ return page_sheet;
144
+ }
145
+
124
146
  /**
125
- *
147
+ * Rules added to the page while it runs, in one <style id="x4-dynamic-css">.
148
+ * @deprecated the CSS variables do it: setStyleVariable( ) on a component,
149
+ * var( --name ) in its stylesheet
126
150
  */
127
151
 
128
152
  export class Stylesheet {
129
153
 
130
154
  readonly _sheet: CSSStyleSheet;
131
- readonly _rules: Map<string, number> = new Map( );
155
+ readonly _rules: Map<string, CSSRule>;
132
156
 
133
157
  constructor() {
134
-
135
- function getStyleSheet( name: string ) : CSSStyleSheet {
136
- for(let i=0; i<document.styleSheets.length; i++) {
137
- let sheet = document.styleSheets[i];
138
- if(sheet.title === name ) {
139
- return sheet;
140
- }
141
- }
142
- }
143
-
144
- this._sheet = getStyleSheet( 'x4-dynamic-css' );
145
- if( !this._sheet ) {
146
- const dom = document.createElement( 'style' );
147
- dom.setAttribute('id', 'x4-dynamic-css' );
148
- document.head.appendChild(dom);
149
- this._sheet = dom.sheet
150
- }
158
+ this._sheet = pageSheet( );
159
+ this._rules = page_rules;
151
160
  }
152
161
 
153
162
  /**
@@ -156,20 +165,31 @@ export class Stylesheet {
156
165
  * @param {string} definition - css definition of the rule
157
166
  * @example
158
167
  * setRule('xbody', "body { background-color: #ff0000; }" );
168
+ * @deprecated a CSS variable: setStyleVariable( ) on a component, var( --name ) in its stylesheet
159
169
  */
160
170
 
161
171
  public setRule(name: string, definition: any ) {
162
172
 
163
173
  if( isString(definition) ) {
164
- let index = this._rules.get( name );
165
- if (index !== undefined) {
166
- this._sheet.deleteRule(index);
174
+ const sheet = this._sheet;
175
+ const old = this._rules.get( name );
176
+
177
+ // in the place of the rule of that name, else at the end
178
+ let index = sheet.cssRules.length;
179
+ if( old ) {
180
+ const at = Array.prototype.indexOf.call( sheet.cssRules, old );
181
+ if( at>=0 ) {
182
+ index = at;
183
+ }
167
184
  }
168
- else {
169
- index = this._sheet.cssRules.length;
185
+
186
+ // inserted first: a definition that is not valid throws, the old rule stays
187
+ sheet.insertRule( definition, index );
188
+ if( old && sheet.cssRules[index+1]===old ) {
189
+ sheet.deleteRule( index+1 );
170
190
  }
171
191
 
172
- this._rules.set( name, this._sheet.insertRule( definition, index) );
192
+ this._rules.set( name, sheet.cssRules[index] );
173
193
  }
174
194
  else {
175
195
  let idx = 1;
@@ -203,6 +223,7 @@ export class Stylesheet {
203
223
  * ```
204
224
  * let color = Component.getCss( ).getVar( 'button-color' );
205
225
  * ```
226
+ * @deprecated getStyleVariable( ) of a component, or getComputedStyle( document.documentElement ).getPropertyValue( "--name" )
206
227
  */
207
228
 
208
229
  public static getVar( name: string ) : any {
@@ -156,7 +156,7 @@ class SvgItem {
156
156
  * @param cap
157
157
  */
158
158
 
159
- strokeCap( cap: "butt" | "round" | "sqaure" ) {
159
+ strokeCap( cap: "butt" | "round" | "square" ) {
160
160
  return this.setAttr( "stroke-linecap", cap );
161
161
  }
162
162
 
@@ -218,12 +218,13 @@ class SvgItem {
218
218
  */
219
219
 
220
220
  getNumAttr( name: string ) {
221
- const a = this._dom.getAttribute( name )
222
- if( a=='' ) {
221
+ // not set or empty: 0; "1.5" is 1.5
222
+ const a = this._dom.getAttribute( name );
223
+ if( !a ) {
223
224
  return 0;
224
225
  }
225
226
 
226
- return parseInt( a );
227
+ return parseFloat( a );
227
228
  }
228
229
 
229
230
  /**
@@ -271,7 +272,9 @@ class SvgItem {
271
272
  */
272
273
 
273
274
  addClass( cls: string ): this {
274
- if( !cls ) return;
275
+ if( !cls ) {
276
+ return this;
277
+ }
275
278
 
276
279
  cls = cls.trim();
277
280
  if( cls.indexOf(' ')>=0 ) {
@@ -484,12 +487,13 @@ export class SvgPath extends SvgItem {
484
487
 
485
488
  /**
486
489
  * draw an arc
487
- * Draws an elliptical arc from the current point to a new point.
490
+ * A circular arc around (x, y), from the angle start to end in degrees: 0 is the top,
491
+ * 90 the right (clockwise). It starts a new subpath at its first point.
488
492
  *
489
- * @param x - The x-coordinate of the center of the ellipse.
490
- * @param y - The y-coordinate of the center of the ellipse.
493
+ * @param x - The x-coordinate of the center.
494
+ * @param y - The y-coordinate of the center.
491
495
  * @param r - The radius of the arc.
492
- * @param start - The start angle of the arc in degrees (0 is right, 90 is down).
496
+ * @param start - The start angle of the arc in degrees (0 is the top, 90 is the right).
493
497
  * @param end - The end angle of the arc in degrees.
494
498
  * @param clockwise - If `true`, the arc is drawn clockwise; otherwise, counter-clockwise. Defaults to `true`.
495
499
  *
@@ -612,12 +616,16 @@ export class SvgIcon extends SvgItem {
612
616
  constructor( svg: string ) {
613
617
  super( "svg" );
614
618
 
619
+ // a data url is decoded when it is encoded ("data:image/svg+xml,%3Csvg..."), a raw svg never
615
620
  if( svg.startsWith("data:image/svg+xml,") ) {
616
621
  svg = svg.substring( 19 );
622
+ if( !svg.startsWith( "<" ) ) {
623
+ svg = decodeURIComponent( svg );
624
+ }
617
625
  }
618
626
 
619
627
  const parser = new DOMParser();
620
- const doc = parser.parseFromString( decodeURIComponent(svg), "image/svg+xml");
628
+ const doc = parser.parseFromString( svg, "image/svg+xml");
621
629
 
622
630
  const parserErrorElement = doc.querySelector("parsererror");
623
631
  if( parserErrorElement ) {
@@ -629,11 +637,9 @@ export class SvgIcon extends SvgItem {
629
637
  this._dom.setAttribute( svgRoot.attributes[i].name, svgRoot.attributes[i].value );
630
638
  }
631
639
 
632
- for( let i=0; i<svgRoot.childNodes.length; i++) {
633
- const child = svgRoot.childNodes[i];
634
- if (child.nodeType === 1) {
635
- this._dom.appendChild(child);
636
- }
640
+ // the elements, all of them: each one moved leaves the list
641
+ for( const child of Array.from( svgRoot.children ) ) {
642
+ this._dom.appendChild( child );
637
643
  }
638
644
  }
639
645
  }
@@ -707,7 +713,9 @@ export class SvgGradient extends SvgItem {
707
713
  * @returns The current `SvgGradient` instance for chaining.
708
714
  */
709
715
  addStop( offset: number_or_perc, color: string ): this {
710
- this._dom.insertAdjacentHTML( "beforeend", `<stop offset="${offset}%" stop-color="${color}"></stop>`);
716
+ // a number is a percentage: 20 is "20%"
717
+ const o = isString( offset ) ? offset : offset + "%";
718
+ this._dom.insertAdjacentHTML( "beforeend", `<stop offset="${o}" stop-color="${color}"></stop>`);
711
719
  return this;
712
720
  }
713
721
  }
@@ -124,8 +124,8 @@ export function unsafeHtml(x: string): UnsafeHtml {
124
124
  export function unsafe(strings: TemplateStringsArray, ...values: any[]): UnsafeHtml {
125
125
 
126
126
  const resolve = ( value: any ) : string | UnsafeHtml => {
127
- let v: string;
128
- if( !value ) {
127
+ // nothing to write: null, undefined (after the last part of the template)
128
+ if( value===undefined || value===null ) {
129
129
  return "";
130
130
  }
131
131
 
@@ -137,7 +137,8 @@ export function unsafe(strings: TemplateStringsArray, ...values: any[]): UnsafeH
137
137
  return value.toString();
138
138
  }
139
139
 
140
- return sanitizeHtml(value);
140
+ // a number, a boolean...: its text, escaped
141
+ return sanitizeHtml( String( value ) );
141
142
  }
142
143
 
143
144
  const result = strings.reduce((acc, str, i) => {
@@ -561,7 +562,8 @@ export function date_format(date: Date, options?: any): string {
561
562
 
562
563
  export function date_diff(date1: Date, date2: Date, options?: any): string {
563
564
 
564
- let dt = (date1.getTime() - date2.getTime()) / 1000;
565
+ // the gap, whichever date comes first
566
+ let dt = Math.abs(date1.getTime() - date2.getTime()) / 1000;
565
567
 
566
568
  // seconds
567
569
  let sec = dt;
@@ -575,7 +577,7 @@ export function date_diff(date1: Date, date2: Date, options?: any): string {
575
577
  return sprintf(_tr.global.diff_date_minutes, Math.round(min));
576
578
  }
577
579
 
578
- // hours
580
+ // hours, and the minutes left
579
581
  let hrs = Math.floor(min / 60);
580
582
  return sprintf(_tr.global.diff_date_hours, hrs, min % 60);
581
583
  }
@@ -626,13 +628,20 @@ export function date_clone(date: Date): Date {
626
628
  }
627
629
 
628
630
  /**
629
- * return the week number of a date
631
+ * return the week number of a date, ISO 8601: weeks from monday,
632
+ * the week 1 holds the first thursday of the year (the 29/12/2025 is in the week 1 of 2026)
630
633
  */
631
634
 
632
635
  export function date_calc_weeknum(date: Date): number {
633
- const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
634
- const pastDaysOfYear = (date.valueOf() - firstDayOfYear.valueOf()) / 86400000;
635
- return Math.floor((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
636
+ // in UTC: a day of 23 or 25 hours (summer time) does not move the weeks
637
+ const d = new Date( Date.UTC( date.getFullYear( ), date.getMonth( ), date.getDate( ) ) );
638
+
639
+ // the thursday of its week gives the year of the week
640
+ const day = d.getUTCDay( ) || 7; // monday 1 .. sunday 7
641
+ d.setUTCDate( d.getUTCDate( ) + 4 - day );
642
+
643
+ const jan1 = Date.UTC( d.getUTCFullYear( ), 0, 1 );
644
+ return Math.ceil( ( ( d.getTime( ) - jan1 ) / 86400000 + 1 ) / 7 );
636
645
  }
637
646
 
638
647