x4js 2.0.13 → 2.0.14

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.
Files changed (258) hide show
  1. package/.vscode/launch.json +14 -0
  2. package/README.md +5 -0
  3. package/{lib/src/demo → demo}/main.scss +3 -1
  4. package/{lib/src/demo/main.tsx → demo/main.ts} +37 -36
  5. package/demo/package.json +26 -0
  6. package/demo/scss.d.ts +4 -0
  7. package/demo/svg.d.ts +1 -0
  8. package/demo/tsconfig.json +14 -0
  9. package/lib/README.txt +5 -0
  10. package/lib/cjs/x4.css +1 -1
  11. package/lib/cjs/x4.js +2 -1
  12. package/lib/esm/x4.css +1 -1
  13. package/lib/esm/x4.mjs +2 -1
  14. package/lib/src/components/boxes/boxes.module.scss +17 -0
  15. package/lib/src/components/boxes/boxes.ts +162 -13
  16. package/lib/src/components/breadcrumb/breadcrumb.scss +56 -28
  17. package/lib/src/components/breadcrumb/breadcrumb.ts +93 -84
  18. package/lib/src/components/btngroup/btngroup.module.scss +12 -0
  19. package/lib/src/components/btngroup/btngroup.ts +41 -8
  20. package/lib/src/components/button/button.module.scss +23 -5
  21. package/lib/src/components/button/button.ts +72 -4
  22. package/lib/src/components/canvas/canvas.module.scss +25 -0
  23. package/lib/src/components/canvas/canvas.ts +189 -0
  24. package/lib/src/components/canvas/canvas_ex.ts +269 -0
  25. package/lib/src/components/checkbox/checkbox.ts +18 -4
  26. package/lib/src/components/combobox/combobox.module.scss +24 -15
  27. package/lib/src/components/combobox/combobox.ts +107 -24
  28. package/lib/src/components/components.ts +7 -0
  29. package/lib/src/components/dialog/dialog.module.scss +37 -3
  30. package/lib/src/components/dialog/dialog.ts +149 -31
  31. package/lib/src/components/filedrop/cloud-arrow-up.svg +1 -0
  32. package/lib/src/components/filedrop/filedrop.module.scss +70 -0
  33. package/lib/src/components/filedrop/filedrop.ts +131 -0
  34. package/lib/src/components/form/form.module.scss +4 -0
  35. package/lib/src/components/form/form.ts +137 -6
  36. package/lib/src/components/gridview/arrow-down-light.svg +1 -0
  37. package/lib/src/components/gridview/arrow-up-light.svg +1 -0
  38. package/lib/src/components/gridview/gridview.module.scss +324 -0
  39. package/lib/src/components/gridview/gridview.ts +1175 -0
  40. package/lib/src/components/icon/icon.module.scss +1 -1
  41. package/lib/src/components/icon/icon.ts +4 -1
  42. package/lib/src/components/image/image.module.scss +8 -1
  43. package/lib/src/components/image/image.ts +105 -6
  44. package/lib/src/components/input/input.module.scss +8 -3
  45. package/lib/src/components/input/input.ts +137 -14
  46. package/lib/src/components/keyboard/arrow-up.svg +1 -0
  47. package/lib/src/components/keyboard/delete-left.svg +1 -0
  48. package/lib/src/components/keyboard/eye-slash.svg +1 -0
  49. package/lib/src/components/keyboard/keyboard.module.scss +134 -0
  50. package/lib/src/components/keyboard/keyboard.ts +525 -0
  51. package/lib/src/components/label/label.module.scss +22 -4
  52. package/lib/src/components/label/label.ts +33 -0
  53. package/lib/src/components/link/link.ts +81 -78
  54. package/lib/src/components/listbox/listbox.module.scss +61 -3
  55. package/lib/src/components/listbox/listbox.ts +164 -56
  56. package/lib/src/components/menu/menu.module.scss +10 -1
  57. package/lib/src/components/menu/menu.ts +4 -1
  58. package/lib/src/components/messages/messages.module.scss +44 -0
  59. package/lib/src/components/messages/messages.ts +164 -18
  60. package/lib/src/components/messages/pen-field.svg +1 -0
  61. package/lib/src/components/normalize.scss +5 -0
  62. package/lib/src/components/notification/notification.module.scss +4 -2
  63. package/lib/src/components/notification/notification.ts +2 -4
  64. package/lib/src/components/panel/panel.module.scss +12 -0
  65. package/lib/src/components/popup/popup.module.scss +4 -2
  66. package/lib/src/components/popup/popup.ts +136 -92
  67. package/lib/src/components/propgrid/folder-closed.svg +1 -0
  68. package/lib/src/components/propgrid/folder-open.svg +1 -0
  69. package/lib/src/components/propgrid/progrid.module.scss +108 -0
  70. package/lib/src/components/propgrid/propgrid.ts +271 -0
  71. package/lib/src/components/propgrid/updown.svg +4 -0
  72. package/lib/src/components/radio/radio.module.scss +147 -0
  73. package/lib/src/components/radio/radio.svg +4 -0
  74. package/lib/src/components/radio/radio.ts +142 -0
  75. package/lib/src/components/select/select.module.scss +9 -0
  76. package/lib/src/components/select/select.ts +134 -0
  77. package/lib/src/components/shared.scss +47 -0
  78. package/lib/src/components/sizers/sizer.ts +9 -2
  79. package/lib/src/components/slider/slider.module.scss +77 -30
  80. package/lib/src/components/slider/slider.ts +72 -22
  81. package/lib/src/components/tabs/tabs.module.scss +1 -2
  82. package/lib/src/components/tabs/tabs.ts +43 -12
  83. package/lib/src/components/textarea/textarea.module.scss +6 -2
  84. package/lib/src/components/textarea/textarea.ts +73 -8
  85. package/lib/src/components/textedit/textedit.module.scss +3 -1
  86. package/lib/src/components/textedit/textedit.ts +31 -4
  87. package/lib/src/components/themes.scss +7 -0
  88. package/lib/src/components/tickline/tickline.module.scss +26 -0
  89. package/lib/src/components/tickline/tickline.ts +82 -0
  90. package/lib/src/components/tooltips/comments-question.svg +1 -0
  91. package/lib/src/components/tooltips/tooltips.scss +30 -9
  92. package/lib/src/components/tooltips/tooltips.ts +10 -5
  93. package/lib/src/components/treeview/treeview.module.scss +129 -60
  94. package/lib/src/components/treeview/treeview.ts +47 -12
  95. package/lib/src/components/viewport/viewport.module.scss +7 -0
  96. package/lib/src/core/component.ts +102 -32
  97. package/lib/src/core/core_application.ts +222 -2
  98. package/lib/src/core/core_colors.ts +2 -2
  99. package/lib/src/{components/grid/datastore.ts → core/core_data.ts} +261 -250
  100. package/lib/src/core/core_dragdrop.ts +3 -3
  101. package/lib/src/core/core_element.ts +13 -1
  102. package/lib/src/core/core_events.ts +28 -0
  103. package/lib/src/core/core_i18n.ts +18 -2
  104. package/lib/src/core/core_react.ts +79 -0
  105. package/lib/src/core/core_router.ts +23 -7
  106. package/lib/src/core/core_styles.ts +5 -5
  107. package/lib/src/core/core_svg.ts +173 -12
  108. package/lib/src/core/core_tools.ts +305 -87
  109. package/lib/src/x4tsx.d.ts +25 -0
  110. package/lib/styles/x4.css +1 -1
  111. package/lib/types/x4js.d.ts +767 -92
  112. package/package.json +4 -4
  113. package/scripts/build.mjs +378 -0
  114. package/scripts/prepack.mjs +346 -0
  115. package/src/components/base.scss +25 -0
  116. package/src/components/boxes/boxes.module.scss +54 -0
  117. package/src/components/boxes/boxes.ts +278 -0
  118. package/src/components/breadcrumb/breadcrumb.scss +56 -0
  119. package/src/components/breadcrumb/breadcrumb.ts +93 -0
  120. package/src/components/breadcrumb/chevron-right.svg +1 -0
  121. package/src/components/btngroup/btngroup.module.scss +41 -0
  122. package/src/components/btngroup/btngroup.ts +153 -0
  123. package/src/components/button/button.module.scss +173 -0
  124. package/src/components/button/button.ts +185 -0
  125. package/src/components/calendar/calendar-check-sharp-light.svg +1 -0
  126. package/src/components/calendar/calendar.module.scss +163 -0
  127. package/src/components/calendar/calendar.ts +327 -0
  128. package/src/components/calendar/chevron-left-sharp-light.svg +1 -0
  129. package/src/components/calendar/chevron-right-sharp-light.svg +1 -0
  130. package/src/components/canvas/canvas.module.scss +25 -0
  131. package/src/components/canvas/canvas.ts +189 -0
  132. package/src/components/canvas/canvas_ex.ts +269 -0
  133. package/src/components/checkbox/check.svg +4 -0
  134. package/src/components/checkbox/checkbox.module.scss +142 -0
  135. package/src/components/checkbox/checkbox.ts +140 -0
  136. package/src/components/colorinput/colorinput.module.scss +65 -0
  137. package/src/components/colorinput/colorinput.ts +91 -0
  138. package/src/components/colorinput/crosshairs-simple-sharp-light.svg +1 -0
  139. package/src/components/colorpicker/colorpicker.module.scss +133 -0
  140. package/src/components/colorpicker/colorpicker.ts +482 -0
  141. package/src/components/combobox/combobox.module.scss +133 -0
  142. package/src/components/combobox/combobox.ts +275 -0
  143. package/src/components/combobox/updown.svg +4 -0
  144. package/src/components/components.ts +41 -0
  145. package/src/components/dialog/dialog.module.scss +105 -0
  146. package/src/components/dialog/dialog.ts +212 -0
  147. package/src/components/dialog/xmark-sharp-light.svg +1 -0
  148. package/src/components/filedrop/cloud-arrow-up.svg +1 -0
  149. package/src/components/filedrop/filedrop.module.scss +70 -0
  150. package/src/components/filedrop/filedrop.ts +131 -0
  151. package/src/components/form/form.module.scss +38 -0
  152. package/src/components/form/form.ts +172 -0
  153. package/src/components/gridview/arrow-down-light.svg +1 -0
  154. package/src/components/gridview/arrow-up-light.svg +1 -0
  155. package/src/components/gridview/gridview.module.scss +324 -0
  156. package/src/components/gridview/gridview.ts +1175 -0
  157. package/src/components/header/header.module.scss +40 -0
  158. package/src/components/header/header.ts +130 -0
  159. package/src/components/icon/icon.module.scss +30 -0
  160. package/src/components/icon/icon.ts +139 -0
  161. package/src/components/image/image.module.scss +28 -0
  162. package/src/components/image/image.ts +168 -0
  163. package/src/components/input/input.module.scss +74 -0
  164. package/src/components/input/input.ts +398 -0
  165. package/src/components/keyboard/arrow-up.svg +1 -0
  166. package/src/components/keyboard/delete-left.svg +1 -0
  167. package/src/components/keyboard/eye-slash.svg +1 -0
  168. package/src/components/keyboard/keyboard.module.scss +134 -0
  169. package/src/components/keyboard/keyboard.ts +525 -0
  170. package/src/components/label/label.module.scss +76 -0
  171. package/src/components/label/label.ts +97 -0
  172. package/src/components/link/link.ts +81 -0
  173. package/src/components/listbox/listbox.module.scss +161 -0
  174. package/src/components/listbox/listbox.ts +539 -0
  175. package/src/components/menu/caret-right-solid.svg +1 -0
  176. package/src/components/menu/menu.module.scss +117 -0
  177. package/src/components/menu/menu.ts +174 -0
  178. package/src/components/messages/circle-exclamation.svg +1 -0
  179. package/src/components/messages/messages.module.scss +92 -0
  180. package/src/components/messages/messages.ts +215 -0
  181. package/src/components/messages/pen-field.svg +1 -0
  182. package/src/components/normalize.scss +391 -0
  183. package/src/components/notification/circle-check-solid.svg +1 -0
  184. package/src/components/notification/circle-exclamation-solid.svg +1 -0
  185. package/src/components/notification/circle-notch-light.svg +1 -0
  186. package/src/components/notification/notification.module.scss +84 -0
  187. package/src/components/notification/notification.ts +107 -0
  188. package/src/components/notification/xmark-sharp-light.svg +1 -0
  189. package/src/components/panel/panel.module.scss +60 -0
  190. package/src/components/panel/panel.ts +58 -0
  191. package/src/components/popup/popup.module.scss +45 -0
  192. package/src/components/popup/popup.ts +440 -0
  193. package/src/components/progress/progress.module.scss +57 -0
  194. package/src/components/progress/progress.ts +44 -0
  195. package/src/components/propgrid/folder-closed.svg +1 -0
  196. package/src/components/propgrid/folder-open.svg +1 -0
  197. package/src/components/propgrid/progrid.module.scss +108 -0
  198. package/src/components/propgrid/propgrid.ts +271 -0
  199. package/src/components/propgrid/updown.svg +4 -0
  200. package/src/components/radio/radio.module.scss +147 -0
  201. package/src/components/radio/radio.svg +4 -0
  202. package/src/components/radio/radio.ts +142 -0
  203. package/src/components/rating/rating.module.scss +23 -0
  204. package/src/components/rating/rating.ts +131 -0
  205. package/src/components/rating/star-sharp-light.svg +1 -0
  206. package/src/components/rating/star-sharp-solid.svg +1 -0
  207. package/src/components/select/select.module.scss +9 -0
  208. package/src/components/select/select.ts +134 -0
  209. package/src/components/shared.scss +137 -0
  210. package/src/components/sizers/sizer.module.scss +90 -0
  211. package/src/components/sizers/sizer.ts +131 -0
  212. package/src/components/slider/slider.module.scss +118 -0
  213. package/src/components/slider/slider.ts +198 -0
  214. package/src/components/switch/switch.module.scss +127 -0
  215. package/src/components/switch/switch.ts +62 -0
  216. package/src/components/tabs/tabs.module.scss +45 -0
  217. package/src/components/tabs/tabs.ts +199 -0
  218. package/src/components/textarea/textarea.module.scss +63 -0
  219. package/src/components/textarea/textarea.ts +125 -0
  220. package/src/components/textedit/textedit.module.scss +116 -0
  221. package/src/components/textedit/textedit.ts +110 -0
  222. package/src/components/themes.scss +88 -0
  223. package/src/components/tickline/tickline.module.scss +26 -0
  224. package/src/components/tickline/tickline.ts +82 -0
  225. package/src/components/tooltips/circle-info-sharp-light.svg +1 -0
  226. package/src/components/tooltips/comments-question.svg +1 -0
  227. package/src/components/tooltips/tooltips.scss +72 -0
  228. package/src/components/tooltips/tooltips.ts +109 -0
  229. package/src/components/treeview/chevron-down-light.svg +1 -0
  230. package/src/components/treeview/treeview.module.scss +185 -0
  231. package/src/components/treeview/treeview.ts +445 -0
  232. package/src/components/viewport/viewport.module.scss +32 -0
  233. package/src/components/viewport/viewport.ts +41 -0
  234. package/src/core/component.ts +1072 -0
  235. package/src/core/core_application.ts +264 -0
  236. package/src/core/core_colors.ts +250 -0
  237. package/src/core/core_data.ts +1309 -0
  238. package/src/core/core_dom.ts +471 -0
  239. package/src/core/core_dragdrop.ts +201 -0
  240. package/src/core/core_element.ts +110 -0
  241. package/src/core/core_events.ts +177 -0
  242. package/src/core/core_i18n.ts +393 -0
  243. package/src/core/core_react.ts +79 -0
  244. package/src/core/core_router.ts +237 -0
  245. package/src/core/core_styles.ts +214 -0
  246. package/src/core/core_svg.ts +711 -0
  247. package/src/core/core_tools.ts +906 -0
  248. package/src/types/scss.d.ts +4 -0
  249. package/src/types/svg.d.ts +1 -0
  250. package/src/types/x4react.d.ts +9 -0
  251. package/src/x4.scss +19 -0
  252. package/src/x4tsx.d.ts +25 -0
  253. package/tsconfig.json +14 -0
  254. package/lib/src/components/grid/gridview.ts +0 -1108
  255. package/lib/src/components/grid/memdb.ts +0 -325
  256. /package/{lib/src/demo → demo}/assets/house-light.svg +0 -0
  257. /package/{lib/src/demo → demo}/assets/radio.svg +0 -0
  258. /package/{lib/src/demo → demo}/index.html +0 -0
@@ -25,7 +25,7 @@ interface DropInfo {
25
25
  }
26
26
 
27
27
  type DropCallback = ( command: 'enter' | 'leave' | 'drag' | 'drop', el: Component, infos: DropInfo ) => void;
28
- type FilterCallback = ( el: Component ) => boolean;
28
+ type FilterCallback = ( el: Component, data: DataTransfer ) => boolean;
29
29
 
30
30
  /**
31
31
  *
@@ -84,7 +84,7 @@ class DragManager {
84
84
  registerDropTarget(el: Component, cb: DropCallback, filterCB?: FilterCallback ) {
85
85
 
86
86
  const dragEnter = (ev: DragEvent) => {
87
- if( filterCB && !filterCB(this.dragSource) ) {
87
+ if( filterCB && !filterCB(this.dragSource,ev.dataTransfer) ) {
88
88
  console.log( 'reject ', el );
89
89
  ev.dataTransfer.dropEffect = 'none';
90
90
  return;
@@ -98,7 +98,7 @@ class DragManager {
98
98
  const dragOver = (ev: DragEvent) => {
99
99
  //console.log( "dragover", ev.target );
100
100
 
101
- if( filterCB && !filterCB(this.dragSource) ) {
101
+ if( filterCB && !filterCB(this.dragSource,ev.dataTransfer) ) {
102
102
  console.log( 'reject ', el );
103
103
  ev.dataTransfer.dropEffect = 'none';
104
104
  return;
@@ -42,7 +42,7 @@ export class CoreElement<E extends EventMap = EventMap> {
42
42
  }
43
43
 
44
44
  private __stopTimer( name: string ) {
45
- const clear = this.#timers.get(name);
45
+ const clear = this.#timers?.get(name);
46
46
  if (clear) { clear(); }
47
47
  }
48
48
 
@@ -86,6 +86,18 @@ export class CoreElement<E extends EventMap = EventMap> {
86
86
  this.#events.addListener( name, listener );
87
87
  }
88
88
 
89
+ /**
90
+ * detach
91
+ */
92
+
93
+ off<K extends keyof E>( name: K, listener: ( ev: E[K] ) => void ) {
94
+ console.assert( listener!==undefined && listener!==null );
95
+
96
+ if( this.#events ) {
97
+ this.#events.removeListener( name, listener );
98
+ }
99
+ }
100
+
89
101
  /**
90
102
  *
91
103
  */
@@ -91,6 +91,34 @@ export class EventSource<E extends EventMap = EventMap > {
91
91
  listeners.push(cb);
92
92
  }
93
93
  }
94
+
95
+ return ( ) => {
96
+ this.removeListener( name, callback );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * stop listening to an event
102
+ * @param eventName - event name
103
+ * @param callback - callback to remove (must be the same as in on )
104
+ */
105
+
106
+ removeListener<K extends keyof E>(name: K, callback: (ev: E[K]) => any) {
107
+
108
+ if (!this._registry ) {
109
+ return;
110
+ }
111
+
112
+ let listeners = this._registry.get(name as string);
113
+ if (!listeners) {
114
+ return;
115
+ }
116
+
117
+ const cb = callback as EventCallback;
118
+ const idx = listeners.indexOf(cb);
119
+ if (idx !== -1) {
120
+ listeners.splice(idx, 1);
121
+ }
94
122
  }
95
123
 
96
124
  fire<K extends keyof E>(name: K, evx: E[K]) {
@@ -303,7 +303,15 @@ let fr = {
303
303
 
304
304
  copy: 'Copier',
305
305
  cut: 'Couper',
306
- paste: 'Coller'
306
+ paste: 'Coller',
307
+
308
+ filedrop: 'Déposez un fichier',
309
+
310
+ keyboard: {
311
+ next: 'Suivant',
312
+ numeric: '123',
313
+ alpha: 'Abc'
314
+ },
307
315
  }
308
316
  };
309
317
 
@@ -359,7 +367,15 @@ let en = {
359
367
 
360
368
  copy: 'Copy',
361
369
  cut: 'Cut',
362
- paste: 'Paste'
370
+ paste: 'Paste',
371
+
372
+ filedrop: 'Drop a file',
373
+
374
+ keyboard: {
375
+ next: 'Next',
376
+ numeric: '123',
377
+ alpha: 'Abc'
378
+ },
363
379
  }
364
380
  };
365
381
 
@@ -0,0 +1,79 @@
1
+ /**
2
+ * ___ ___ __
3
+ * \ \/ / / _
4
+ * \ / /_| |_
5
+ * / \____ _|
6
+ * /__/\__\ |_|.2
7
+ *
8
+ * @file core_react.ts
9
+ * @author Etienne Cochard
10
+ *
11
+ * @copyright (c) 2025 R-libre ingenierie
12
+ *
13
+ * Use of this source code is governed by an MIT-style license
14
+ * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
+ *
16
+ * to use you must:
17
+ *
18
+ * 1. add this to your tsconfig.json
19
+ * "compilerOptions": {
20
+ * ...
21
+ * "jsx": "preserve",
22
+ * ...
23
+ * }
24
+ *
25
+ * 2. be sure that esbuild has this:
26
+ * jsxFactory: "x4create_element",
27
+ * loader: { ".ts": "tsx" }
28
+ *
29
+ *
30
+ * after that, all things like that will be ok
31
+ *
32
+ * import { x4_create_element } from "x4react"
33
+ * const xx = <h1>This is a title</h1>
34
+ *
35
+ */
36
+
37
+ import { Component } from './component.js';
38
+ import { Constructor, isString } from './core_tools.js';
39
+
40
+
41
+ /**
42
+ * x4 is reactive ?
43
+ * hard jsx :)
44
+ */
45
+
46
+ export class x4_react {
47
+
48
+ static create_element( tag: string, props: any, ...content: any[] ): Component;
49
+ static create_element<X extends Component>( tag: string | Constructor<X>, props: X["props"], ...content: any[] ) {
50
+
51
+ props = props || {};
52
+
53
+ let el: Component;
54
+
55
+ // --- simple div ------------------------
56
+ if( isString(tag) ) {
57
+ el = new Component( { tag } );
58
+ Object.entries( props )
59
+ .forEach(([name, value]) => {
60
+ if (name.startsWith('on') && name.toLowerCase() in window) {
61
+ el.dom.addEventListener(name.toLowerCase().substring(2), value)
62
+ }
63
+ else {
64
+ el.setAttribute(name, value )
65
+ }
66
+ });
67
+ }
68
+ // --- Component ------------------------
69
+ else {
70
+ el = new tag( props );
71
+ }
72
+
73
+ if( content && content.length ) {
74
+ el.appendContent( content );
75
+ }
76
+
77
+ return el;
78
+ }
79
+ }
@@ -14,7 +14,7 @@
14
14
  * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
15
  **/
16
16
 
17
- import { EvError } from './component.js';
17
+ import { EvChange, EvError } from './component.js';
18
18
  import { EventMap, EventSource } from './core_events.js';
19
19
 
20
20
  type RouteHandler = ( params: any, path: string ) => void;
@@ -30,7 +30,7 @@ interface Route {
30
30
  handler: RouteHandler;
31
31
  }
32
32
 
33
- function parseRoute(str: string | RegExp, loose = false): Segment {
33
+ export function parseRoute(str: string | RegExp, loose = false): Segment {
34
34
 
35
35
  if (str instanceof RegExp) {
36
36
  return {
@@ -77,6 +77,7 @@ function parseRoute(str: string | RegExp, loose = false): Segment {
77
77
  }
78
78
 
79
79
  interface RouterEvents extends EventMap {
80
+ change: EvChange;
80
81
  error: EvError;
81
82
  }
82
83
 
@@ -116,7 +117,7 @@ export class Router extends EventSource< RouterEvents > {
116
117
 
117
118
  this.m_routes = [];
118
119
  this.m_useHash = useHash;
119
-
120
+
120
121
  window.addEventListener('popstate', (event) => {
121
122
  const url = this._getLocation( );
122
123
  const found = this._find(url);
@@ -124,6 +125,8 @@ export class Router extends EventSource< RouterEvents > {
124
125
  found.handlers.forEach(h => {
125
126
  h(found.params,url);
126
127
  });
128
+
129
+ this.fire( "change", { value: this._getLocation() } );
127
130
  });
128
131
  }
129
132
 
@@ -140,6 +143,10 @@ export class Router extends EventSource< RouterEvents > {
140
143
  return this.m_useHash ? '/'+document.location.hash.substring(1) : document.location.pathname;
141
144
  }
142
145
 
146
+ /**
147
+ *
148
+ */
149
+
143
150
  navigate( uri: string, notify = true, replace = false ) {
144
151
 
145
152
  if( !uri.startsWith('/') ) {
@@ -152,7 +159,7 @@ export class Router extends EventSource< RouterEvents > {
152
159
  //window.history.pushState({}, '', 'error')
153
160
  console.log( 'route not found: '+uri );
154
161
  this.fire( "error", {code: 404, message: "route not found" } );
155
- return;
162
+ return false;
156
163
  }
157
164
 
158
165
  if( this.m_useHash ) {
@@ -171,12 +178,21 @@ export class Router extends EventSource< RouterEvents > {
171
178
  }
172
179
 
173
180
  if( notify ) {
174
- found.handlers.forEach( h => {
175
- h( found.params, uri );
176
- } );
181
+ //found.handlers.forEach( h => {
182
+ // h( found.params, uri );
183
+ //} );
184
+
185
+ found.handlers[0]( found.params, uri );
177
186
  }
187
+
188
+ this.fire( "change", { value: this._getLocation() } );
189
+ return true;
178
190
  }
179
191
 
192
+ /**
193
+ *
194
+ */
195
+
180
196
  private _find( url: string ): { params: Record<string,any>, handlers: RouteHandler[] } {
181
197
 
182
198
  let matches = [];
@@ -14,7 +14,7 @@
14
14
  * that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
15
15
  **/
16
16
 
17
- import { pascalCase, isString } from './core_tools.js';
17
+ import { isString } from './core_tools.js';
18
18
 
19
19
  export const unitless: Record<string,1> = {
20
20
  animationIterationCount: 1,
@@ -82,8 +82,8 @@ export function isUnitLess( name: string ) {
82
82
 
83
83
  export class Stylesheet {
84
84
 
85
- private m_sheet: CSSStyleSheet;
86
- private m_rules: Map<string, number> = new Map( );
85
+ readonly m_sheet: CSSStyleSheet;
86
+ readonly m_rules: Map<string, number> = new Map( );
87
87
 
88
88
  constructor() {
89
89
 
@@ -91,7 +91,7 @@ export class Stylesheet {
91
91
  for(let i=0; i<document.styleSheets.length; i++) {
92
92
  let sheet = document.styleSheets[i];
93
93
  if(sheet.title === name ) {
94
- return <CSSStyleSheet>sheet;
94
+ return sheet;
95
95
  }
96
96
  }
97
97
  }
@@ -101,7 +101,7 @@ export class Stylesheet {
101
101
  const dom = document.createElement( 'style' );
102
102
  dom.setAttribute('id', 'x4-dynamic-css' );
103
103
  document.head.appendChild(dom);
104
- this.m_sheet = <CSSStyleSheet>dom.sheet
104
+ this.m_sheet = dom.sheet
105
105
  }
106
106
  }
107
107
 
@@ -69,10 +69,28 @@ class SvgItem {
69
69
  this._dom = document.createElementNS("http://www.w3.org/2000/svg", tag );
70
70
  }
71
71
 
72
+ /**
73
+ * @returns the svh element dom
74
+ */
75
+
72
76
  getDom( ) {
73
77
  return this._dom;
74
78
  }
75
79
 
80
+ /**
81
+ *
82
+ */
83
+
84
+ reset( ) {
85
+ const attrs = this._dom.attributes;
86
+ for (let i = attrs.length - 1; i >= 0; i--) {
87
+ this._dom.removeAttribute(attrs[i].name);
88
+ }
89
+
90
+ return this;
91
+ }
92
+
93
+
76
94
  /**
77
95
  * change the stroke color
78
96
  * @param color
@@ -126,6 +144,18 @@ class SvgItem {
126
144
  return this;
127
145
  }
128
146
 
147
+ /**
148
+ * return the given attribute if any
149
+ */
150
+
151
+ getAttr( name: string ) : string {
152
+ return this._dom.getAttribute( name );
153
+ }
154
+
155
+ getNumAttr( name: string ) {
156
+ return parseInt( this._dom.getAttribute( name ) );
157
+ }
158
+
129
159
  /**
130
160
  * define a new attribute
131
161
  * @param name attibute name
@@ -165,7 +195,7 @@ class SvgItem {
165
195
  * @param name class name to add
166
196
  */
167
197
 
168
- addClass( cls: string ) {
198
+ addClass( cls: string ): this {
169
199
  if( !cls ) return;
170
200
 
171
201
  if( cls.indexOf(' ')>=0 ) {
@@ -175,6 +205,27 @@ class SvgItem {
175
205
  else {
176
206
  this._dom.classList.add(cls);
177
207
  }
208
+
209
+ return this;
210
+ }
211
+
212
+ /**
213
+ * remove a class
214
+ * @param name class name to remove
215
+ */
216
+
217
+ removeClass( cls: string ): this {
218
+ if( !cls ) return;
219
+
220
+ if( cls.indexOf(' ')>=0 ) {
221
+ const ccs = cls.split( " " );
222
+ this._dom.classList.remove(...ccs);
223
+ }
224
+ else {
225
+ this._dom.classList.remove(cls);
226
+ }
227
+
228
+ return this;
178
229
  }
179
230
 
180
231
  /**
@@ -191,7 +242,13 @@ class SvgItem {
191
242
  */
192
243
 
193
244
  transform( tr: string ): this {
194
- this.setAttr( "transform", tr );
245
+ const t = this.getAttr( "transform" ) ?? "";
246
+ this.setAttr( "transform", t+' '+tr );
247
+ return this;
248
+ }
249
+
250
+ clear_transform( ) {
251
+ this.setAttr( "transform", null );
195
252
  return this;
196
253
  }
197
254
 
@@ -243,6 +300,12 @@ export class SvgPath extends SvgItem {
243
300
  return this;
244
301
  }
245
302
 
303
+ reset( ) {
304
+ this._path = "";
305
+ super.reset( );
306
+ return this;
307
+ }
308
+
246
309
  /**
247
310
  * move the current pos
248
311
  * @param x new pos x
@@ -267,6 +330,16 @@ export class SvgPath extends SvgItem {
267
330
  return this._update( );
268
331
  }
269
332
 
333
+ /**
334
+ * draw a curve
335
+ */
336
+
337
+ curveTo( x1: number, y1: number, x2: number, y2: number, x3: number, y3: number ) {
338
+ this._path += clean`C${x1},${y1} ${x2},${y2} ${x3},${y3}`;
339
+ return this._update( );
340
+ }
341
+
342
+
270
343
  /**
271
344
  * close the currentPath
272
345
  */
@@ -286,13 +359,13 @@ export class SvgPath extends SvgItem {
286
359
  * @returns this
287
360
  */
288
361
 
289
- arc( x: number, y: number, r: number, start: number, end: number ): this {
362
+ arc( x: number, y: number, r: number, start: number, end: number, clockwise= true ): this {
290
363
 
291
364
  const st = p2c( x, y, r, start-90 );
292
365
  const en = p2c( x, y, r, end-90 );
293
366
 
294
- const flag = end - start <= 180 ? "0" : "1";
295
- this._path += clean`M${st.x},${st.y}A${r},${r} 0 ${flag} 1 ${en.x},${en.y}`;
367
+ const flag = ((end-start) <= 180 ? "0" : "1");
368
+ this._path += clean`M${st.x},${st.y}A${r},${r} 0 ${flag} ${(clockwise ? '1' : '0')} ${en.x},${en.y}`;
296
369
 
297
370
  return this._update( );
298
371
  }
@@ -353,6 +426,42 @@ export class SvgText extends SvgItem {
353
426
  }
354
427
  }
355
428
 
429
+ /**
430
+ *
431
+ */
432
+
433
+ export class SvgIcon extends SvgItem {
434
+ constructor( svg: string ) {
435
+ super( "svg" );
436
+
437
+ if( svg.startsWith("data:image/svg+xml,") ) {
438
+ svg = svg.substring( 19 );
439
+ }
440
+
441
+ const parser = new DOMParser();
442
+ const doc = parser.parseFromString( decodeURIComponent(svg), "image/svg+xml");
443
+
444
+ const parserErrorElement = doc.querySelector("parsererror");
445
+ if( parserErrorElement ) {
446
+ console.error( "error while parsing svg:\n"+ parserErrorElement.textContent );
447
+ }
448
+
449
+ const svgRoot = doc.documentElement; // The <svg> element from the string
450
+ for( let i=0; i<svgRoot.attributes.length; i++) {
451
+ this._dom.setAttribute( svgRoot.attributes[i].name, svgRoot.attributes[i].value );
452
+ }
453
+
454
+ for( let i=0; i<svgRoot.childNodes.length; i++) {
455
+ const child = svgRoot.childNodes[i];
456
+ if (child.nodeType === 1) {
457
+ this._dom.appendChild(child);
458
+ }
459
+ }
460
+ }
461
+ }
462
+
463
+
464
+
356
465
  /**
357
466
  *
358
467
  */
@@ -420,6 +529,12 @@ export class SvgGroup extends SvgItem {
420
529
  return item;
421
530
  }
422
531
 
532
+ appendItems<K extends SvgItem>( items: K[] ) {
533
+ items.forEach( item => {
534
+ this._dom.appendChild( item.getDom() );
535
+ } );
536
+ }
537
+
423
538
  /**
424
539
  *
425
540
  */
@@ -434,7 +549,7 @@ export class SvgGroup extends SvgItem {
434
549
  return this.append( text );
435
550
  }
436
551
 
437
- ellipse( x: number, y: number, r1: number, r2 = r1 ): SvgShape {
552
+ ellipse( x: number, y: number, r1: number, r2: number ): SvgShape {
438
553
  const shape = new SvgShape( 'ellipse' );
439
554
  shape.setAttr( 'cx', num(x)+'' );
440
555
  shape.setAttr( 'cy', num(y)+'' );
@@ -443,6 +558,24 @@ export class SvgGroup extends SvgItem {
443
558
  return this.append( shape );
444
559
  }
445
560
 
561
+ circle( x: number, y: number, r1: number ): SvgShape {
562
+ const shape = new SvgShape( 'ellipse' );
563
+ shape.setAttr( 'cx', num(x)+'' );
564
+ shape.setAttr( 'cy', num(y)+'' );
565
+ shape.setAttr( 'rx', num(r1)+'' );
566
+ shape.setAttr( 'ry', num(r1)+'' );
567
+ return this.append( shape );
568
+ }
569
+
570
+ icon( svg: string, x: number, y: number, w: number, h: number ): SvgIcon {
571
+ const icon = new SvgIcon( svg );
572
+ icon.setAttr( 'x', num(x)+'' );
573
+ icon.setAttr( 'y', num(y)+'' );
574
+ icon.setAttr( 'width', num(w)+'' );
575
+ icon.setAttr( 'height', num(h)+'' );
576
+ return this.append( icon );
577
+ }
578
+
446
579
  rect( x: number, y: number, w: number, h: number ): SvgShape {
447
580
 
448
581
  if( h<0 ) {
@@ -458,8 +591,12 @@ export class SvgGroup extends SvgItem {
458
591
  return this.append( shape );
459
592
  }
460
593
 
461
- group( ) {
594
+ group( id?: string ) {
462
595
  const group = new SvgGroup( );
596
+ if( id ) {
597
+ group.setAttr( 'id', id );
598
+ }
599
+
463
600
  return this.append( group );
464
601
  }
465
602
 
@@ -500,20 +637,35 @@ export class SvgGroup extends SvgItem {
500
637
 
501
638
  export class SvgBuilder extends SvgGroup {
502
639
  private static g_clip_id = 1;
640
+ private static g_pat_id = 1;
503
641
 
504
642
  constructor( ) {
505
643
  super( );
506
644
  }
507
645
 
508
646
  addClip( x: number, y: number, w: number, h: number ) {
509
-
510
- const id = 'c-'+SvgBuilder.g_clip_id++;
647
+ const id = 'clip-'+SvgBuilder.g_clip_id++;
511
648
  const clip = new SvgGroup( 'clipPath' );
512
649
  clip.setAttr('id', id );
513
650
  clip.rect( x, y, w, h );
514
651
 
515
652
  this.append(clip);
516
- return id;
653
+ return {id,clip};
654
+ }
655
+
656
+ addPattern( x: number, y: number, w: number, h: number ) {
657
+ const id = 'pat-'+SvgBuilder.g_pat_id++;
658
+
659
+ const pat = new SvgGroup( 'pattern' );
660
+ pat.setAttr( 'id', id );
661
+ pat.setAttr( 'x', num(x)+'' );
662
+ pat.setAttr( 'y', num(y)+'' );
663
+ pat.setAttr( 'width', num(w)+'' );
664
+ pat.setAttr( 'height', num(h)+'' );
665
+ pat.setAttr( 'patternUnits', "userSpaceOnUse" );
666
+
667
+ this.append(pat);
668
+ return {id,pat};
517
669
  }
518
670
  }
519
671
 
@@ -525,7 +677,7 @@ export class SvgBuilder extends SvgGroup {
525
677
 
526
678
  interface SvgProps extends ComponentProps {
527
679
  viewbox?: string;
528
- svg?: SvgBuilder;
680
+ svg: SvgBuilder;
529
681
  }
530
682
 
531
683
  /**
@@ -540,11 +692,20 @@ export class SvgComponent<P extends SvgProps = SvgProps> extends Component<P> {
540
692
  this.setAttribute( 'xmlns', SVG_NS );
541
693
 
542
694
  if( props.viewbox ) {
543
- this.setAttribute( "viewbox", props.viewbox );
695
+ this.setAttribute( "viewBox", props.viewbox );
544
696
  }
545
697
 
546
698
  if( props.svg ) {
547
699
  this.dom.appendChild( props.svg.getDom() );
548
700
  }
549
701
  }
702
+
703
+ setSvg( bld: SvgBuilder ) {
704
+ this.clearContent( );
705
+ this.dom.appendChild( bld.getDom() );
706
+ }
707
+
708
+ addItems( ...items: SvgItem[] ) {
709
+ items.forEach( item => this.dom.appendChild( item.getDom() ) );
710
+ }
550
711
  }