x4js 1.6.4 → 2.0.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.
Files changed (282) hide show
  1. package/README.md +3 -14
  2. package/lib/README.txt +3 -14
  3. package/lib/src/assets/house-light.svg +1 -0
  4. package/lib/src/assets/radio.svg +4 -0
  5. package/lib/src/components/base.scss +26 -0
  6. package/lib/src/components/boxes/boxes.module.scss +37 -0
  7. package/lib/src/components/boxes/boxes.ts +125 -0
  8. package/lib/src/components/btngroup/btngroup.module.scss +29 -0
  9. package/lib/src/components/btngroup/btngroup.ts +106 -0
  10. package/lib/src/components/button/button.module.scss +154 -0
  11. package/lib/src/components/button/button.ts +117 -0
  12. package/lib/src/components/calendar/calendar-check-sharp-light.svg +1 -0
  13. package/lib/src/components/calendar/calendar.module.scss +163 -0
  14. package/lib/src/{calendar.ts → components/calendar/calendar.ts} +81 -83
  15. package/lib/src/components/calendar/chevron-left-sharp-light.svg +1 -0
  16. package/lib/src/components/calendar/chevron-right-sharp-light.svg +1 -0
  17. package/lib/src/components/checkbox/check.svg +4 -0
  18. package/lib/src/components/checkbox/checkbox.module.scss +142 -0
  19. package/lib/src/components/checkbox/checkbox.ts +125 -0
  20. package/lib/src/components/colorinput/colorinput.module.scss +65 -0
  21. package/lib/src/components/colorinput/colorinput.ts +88 -0
  22. package/lib/src/components/colorinput/crosshairs-simple-sharp-light.svg +1 -0
  23. package/lib/src/components/colorpicker/colorpicker.module.scss +133 -0
  24. package/lib/src/components/colorpicker/colorpicker.ts +477 -0
  25. package/lib/src/components/combobox/combobox.module.scss +121 -0
  26. package/lib/src/components/combobox/combobox.ts +190 -0
  27. package/lib/src/components/combobox/updown.svg +4 -0
  28. package/lib/src/components/dialog/dialog.module.scss +71 -0
  29. package/lib/src/components/dialog/dialog.ts +91 -0
  30. package/lib/src/components/dialog/xmark-sharp-light.svg +1 -0
  31. package/lib/src/components/form/form.module.scss +34 -0
  32. package/lib/src/components/form/form.ts +36 -0
  33. package/lib/src/components/header/header.module.scss +40 -0
  34. package/lib/src/components/header/header.ts +124 -0
  35. package/lib/src/components/icon/icon.module.scss +30 -0
  36. package/lib/src/components/icon/icon.ts +134 -0
  37. package/lib/src/components/image/image.module.scss +21 -0
  38. package/lib/src/components/image/image.ts +67 -0
  39. package/lib/src/components/input/input.module.scss +69 -0
  40. package/lib/src/components/input/input.ts +274 -0
  41. package/lib/src/components/label/label.module.scss +52 -0
  42. package/lib/src/components/label/label.ts +55 -0
  43. package/lib/src/components/listbox/listbox.module.scss +103 -0
  44. package/lib/src/components/listbox/listbox.ts +427 -0
  45. package/lib/src/components/menu/caret-right-solid.svg +1 -0
  46. package/lib/src/components/menu/menu.module.scss +108 -0
  47. package/lib/src/components/menu/menu.ts +168 -0
  48. package/lib/src/components/messages/circle-exclamation.svg +1 -0
  49. package/lib/src/components/messages/messages.module.scss +47 -0
  50. package/lib/src/components/messages/messages.ts +64 -0
  51. package/lib/src/components/normalize.scss +386 -0
  52. package/lib/src/components/notification/circle-check-solid.svg +1 -0
  53. package/lib/src/components/notification/circle-exclamation-solid.svg +1 -0
  54. package/lib/src/components/notification/circle-notch-light.svg +1 -0
  55. package/lib/src/components/notification/notification.module.scss +82 -0
  56. package/lib/src/components/notification/notification.ts +108 -0
  57. package/lib/src/components/notification/xmark-sharp-light.svg +1 -0
  58. package/lib/src/components/panel/panel.module.scss +48 -0
  59. package/lib/src/components/panel/panel.ts +57 -0
  60. package/lib/src/components/popup/popup.module.scss +43 -0
  61. package/lib/src/components/popup/popup.ts +395 -0
  62. package/lib/src/components/progress/progress.module.scss +57 -0
  63. package/lib/src/components/progress/progress.ts +43 -0
  64. package/lib/src/components/rating/rating.module.scss +23 -0
  65. package/lib/src/components/rating/rating.ts +125 -0
  66. package/lib/src/components/rating/star-sharp-light.svg +1 -0
  67. package/lib/src/components/rating/star-sharp-solid.svg +1 -0
  68. package/lib/src/components/shared.scss +76 -0
  69. package/lib/src/components/sizers/sizer.module.scss +90 -0
  70. package/lib/src/components/sizers/sizer.ts +120 -0
  71. package/lib/src/components/slider/slider.module.scss +71 -0
  72. package/lib/src/components/slider/slider.ts +143 -0
  73. package/lib/src/components/switch/switch.module.scss +127 -0
  74. package/lib/src/components/switch/switch.ts +56 -0
  75. package/lib/src/components/tabs/tabs.module.scss +46 -0
  76. package/lib/src/components/tabs/tabs.ts +157 -0
  77. package/lib/src/components/textarea/textarea.module.scss +59 -0
  78. package/lib/src/components/textarea/textarea.ts +54 -0
  79. package/lib/src/components/textedit/textedit.module.scss +114 -0
  80. package/lib/src/components/textedit/textedit.ts +82 -0
  81. package/lib/src/components/themes.scss +77 -0
  82. package/lib/src/components/tooltips/circle-info-sharp-light.svg +1 -0
  83. package/lib/src/components/tooltips/tooltips.scss +51 -0
  84. package/lib/src/components/tooltips/tooltips.ts +103 -0
  85. package/lib/src/components/treeview/chevron-down-light.svg +1 -0
  86. package/lib/src/components/treeview/treeview.module.scss +116 -0
  87. package/lib/src/components/treeview/treeview.ts +403 -0
  88. package/lib/src/components/viewport/viewport.module.scss +25 -0
  89. package/lib/src/components/viewport/viewport.ts +38 -0
  90. package/lib/src/core/component.ts +979 -0
  91. package/lib/src/core/core_colors.ts +250 -0
  92. package/lib/src/{dom_events.ts → core/core_dom.ts} +195 -39
  93. package/lib/src/{drag_manager.ts → core/core_dragdrop.ts} +29 -44
  94. package/lib/src/core/core_element.ts +98 -0
  95. package/lib/src/core/core_events.ts +149 -0
  96. package/lib/src/{i18n.ts → core/core_i18n.ts} +43 -42
  97. package/lib/src/{router.ts → core/core_router.ts} +27 -40
  98. package/lib/src/core/core_styles.ts +215 -0
  99. package/lib/src/core/core_svg.ts +550 -0
  100. package/lib/src/core/core_tools.ts +673 -0
  101. package/lib/src/main.scss +21 -0
  102. package/lib/src/main.tsx +323 -0
  103. package/lib/src/x4.scss +19 -0
  104. package/lib/types/x4.d.ts +2624 -0
  105. package/package.json +67 -59
  106. package/scripts/build.mjs +351 -0
  107. package/scripts/prepack.mjs +15 -0
  108. package/src/assets/house-light.svg +1 -0
  109. package/src/assets/radio.svg +4 -0
  110. package/src/components/base.scss +26 -0
  111. package/src/components/boxes/boxes.module.scss +37 -0
  112. package/src/components/boxes/boxes.ts +125 -0
  113. package/src/components/btngroup/btngroup.module.scss +29 -0
  114. package/src/components/btngroup/btngroup.ts +106 -0
  115. package/src/components/button/button.module.scss +154 -0
  116. package/src/components/button/button.ts +117 -0
  117. package/src/components/calendar/calendar-check-sharp-light.svg +1 -0
  118. package/src/components/calendar/calendar.module.scss +163 -0
  119. package/src/components/calendar/calendar.ts +326 -0
  120. package/src/components/calendar/chevron-left-sharp-light.svg +1 -0
  121. package/src/components/calendar/chevron-right-sharp-light.svg +1 -0
  122. package/src/components/checkbox/check.svg +4 -0
  123. package/src/components/checkbox/checkbox.module.scss +142 -0
  124. package/src/components/checkbox/checkbox.ts +125 -0
  125. package/src/components/colorinput/colorinput.module.scss +65 -0
  126. package/src/components/colorinput/colorinput.ts +88 -0
  127. package/src/components/colorinput/crosshairs-simple-sharp-light.svg +1 -0
  128. package/src/components/colorpicker/colorpicker.module.scss +133 -0
  129. package/src/components/colorpicker/colorpicker.ts +477 -0
  130. package/src/components/combobox/combobox.module.scss +121 -0
  131. package/src/components/combobox/combobox.ts +190 -0
  132. package/src/components/combobox/updown.svg +4 -0
  133. package/src/components/dialog/dialog.module.scss +71 -0
  134. package/src/components/dialog/dialog.ts +91 -0
  135. package/src/components/dialog/xmark-sharp-light.svg +1 -0
  136. package/src/components/form/form.module.scss +34 -0
  137. package/src/components/form/form.ts +36 -0
  138. package/src/components/header/header.module.scss +40 -0
  139. package/src/components/header/header.ts +124 -0
  140. package/src/components/icon/icon.module.scss +30 -0
  141. package/src/components/icon/icon.ts +134 -0
  142. package/src/components/image/image.module.scss +21 -0
  143. package/src/components/image/image.ts +67 -0
  144. package/src/components/input/input.module.scss +69 -0
  145. package/src/components/input/input.ts +274 -0
  146. package/src/components/label/label.module.scss +52 -0
  147. package/src/components/label/label.ts +55 -0
  148. package/src/components/listbox/listbox.module.scss +103 -0
  149. package/src/components/listbox/listbox.ts +427 -0
  150. package/src/components/menu/caret-right-solid.svg +1 -0
  151. package/src/components/menu/menu.module.scss +108 -0
  152. package/src/components/menu/menu.ts +168 -0
  153. package/src/components/messages/circle-exclamation.svg +1 -0
  154. package/src/components/messages/messages.module.scss +47 -0
  155. package/src/components/messages/messages.ts +64 -0
  156. package/src/components/normalize.scss +386 -0
  157. package/src/components/notification/circle-check-solid.svg +1 -0
  158. package/src/components/notification/circle-exclamation-solid.svg +1 -0
  159. package/src/components/notification/circle-notch-light.svg +1 -0
  160. package/src/components/notification/notification.module.scss +82 -0
  161. package/src/components/notification/notification.ts +108 -0
  162. package/src/components/notification/xmark-sharp-light.svg +1 -0
  163. package/src/components/panel/panel.module.scss +48 -0
  164. package/src/components/panel/panel.ts +57 -0
  165. package/src/components/popup/popup.module.scss +43 -0
  166. package/src/components/popup/popup.ts +395 -0
  167. package/src/components/progress/progress.module.scss +57 -0
  168. package/src/components/progress/progress.ts +43 -0
  169. package/src/components/rating/rating.module.scss +23 -0
  170. package/src/components/rating/rating.ts +125 -0
  171. package/src/components/rating/star-sharp-light.svg +1 -0
  172. package/src/components/rating/star-sharp-solid.svg +1 -0
  173. package/src/components/shared.scss +76 -0
  174. package/src/components/sizers/sizer.module.scss +90 -0
  175. package/src/components/sizers/sizer.ts +120 -0
  176. package/src/components/slider/slider.module.scss +71 -0
  177. package/src/components/slider/slider.ts +143 -0
  178. package/src/components/switch/switch.module.scss +127 -0
  179. package/src/components/switch/switch.ts +56 -0
  180. package/src/components/tabs/tabs.module.scss +46 -0
  181. package/src/components/tabs/tabs.ts +157 -0
  182. package/src/components/textarea/textarea.module.scss +59 -0
  183. package/src/components/textarea/textarea.ts +54 -0
  184. package/src/components/textedit/textedit.module.scss +114 -0
  185. package/src/components/textedit/textedit.ts +82 -0
  186. package/src/components/themes.scss +77 -0
  187. package/src/components/tooltips/circle-info-sharp-light.svg +1 -0
  188. package/src/components/tooltips/tooltips.scss +51 -0
  189. package/src/components/tooltips/tooltips.ts +103 -0
  190. package/src/components/treeview/chevron-down-light.svg +1 -0
  191. package/src/components/treeview/treeview.module.scss +116 -0
  192. package/src/components/treeview/treeview.ts +403 -0
  193. package/src/components/viewport/viewport.module.scss +25 -0
  194. package/src/components/viewport/viewport.ts +38 -0
  195. package/src/core/component.ts +979 -0
  196. package/src/core/core_colors.ts +250 -0
  197. package/src/core/core_dom.ts +471 -0
  198. package/src/core/core_dragdrop.ts +201 -0
  199. package/src/core/core_element.ts +98 -0
  200. package/src/core/core_events.ts +149 -0
  201. package/src/core/core_i18n.ts +377 -0
  202. package/src/core/core_router.ts +221 -0
  203. package/src/core/core_styles.ts +215 -0
  204. package/src/core/core_svg.ts +550 -0
  205. package/src/core/core_tools.ts +673 -0
  206. package/src/main.scss +21 -0
  207. package/src/main.tsx +323 -0
  208. package/src/x4.scss +19 -0
  209. package/tsconfig.json +14 -0
  210. package/types/scss.d.ts +4 -0
  211. package/types/svg.d.ts +4 -0
  212. package/types/x4react.d.ts +9 -0
  213. package/lib/changelog.txt +0 -23
  214. package/lib/cjs/x4js.js +0 -39
  215. package/lib/cjs/x4js.js.map +0 -7
  216. package/lib/esm/x4js.mjs +0 -15972
  217. package/lib/esm/x4js.mjs.map +0 -7
  218. package/lib/licence.md +0 -21
  219. package/lib/src/MIT-license.md +0 -14
  220. package/lib/src/action.ts +0 -88
  221. package/lib/src/alpha.jpg +0 -0
  222. package/lib/src/app_sockets.ts +0 -81
  223. package/lib/src/application.ts +0 -262
  224. package/lib/src/autocomplete.ts +0 -232
  225. package/lib/src/base64.ts +0 -166
  226. package/lib/src/base_component.ts +0 -152
  227. package/lib/src/button.ts +0 -355
  228. package/lib/src/canvas.ts +0 -510
  229. package/lib/src/cardview.ts +0 -228
  230. package/lib/src/checkbox.ts +0 -188
  231. package/lib/src/color.ts +0 -752
  232. package/lib/src/colorpicker.ts +0 -1649
  233. package/lib/src/combobox.ts +0 -512
  234. package/lib/src/component.ts +0 -2367
  235. package/lib/src/copyright.txt +0 -27
  236. package/lib/src/datastore.ts +0 -1302
  237. package/lib/src/dialog.ts +0 -656
  238. package/lib/src/drawtext.ts +0 -355
  239. package/lib/src/fileupload.ts +0 -213
  240. package/lib/src/form.ts +0 -413
  241. package/lib/src/formatters.ts +0 -105
  242. package/lib/src/gridview.ts +0 -1185
  243. package/lib/src/icon.ts +0 -362
  244. package/lib/src/image.ts +0 -225
  245. package/lib/src/index.ts +0 -89
  246. package/lib/src/input.ts +0 -297
  247. package/lib/src/label.ts +0 -153
  248. package/lib/src/layout.ts +0 -442
  249. package/lib/src/link.ts +0 -86
  250. package/lib/src/listview.ts +0 -765
  251. package/lib/src/md5.ts +0 -438
  252. package/lib/src/menu.ts +0 -425
  253. package/lib/src/messagebox.ts +0 -224
  254. package/lib/src/panel.ts +0 -86
  255. package/lib/src/popup.ts +0 -494
  256. package/lib/src/property_editor.ts +0 -337
  257. package/lib/src/radiobtn.ts +0 -197
  258. package/lib/src/rating.ts +0 -135
  259. package/lib/src/request.ts +0 -300
  260. package/lib/src/settings.ts +0 -77
  261. package/lib/src/sidebarview.ts +0 -108
  262. package/lib/src/spreadsheet.ts +0 -1449
  263. package/lib/src/styles.ts +0 -343
  264. package/lib/src/svgcomponent.ts +0 -592
  265. package/lib/src/tabbar.ts +0 -151
  266. package/lib/src/tabview.ts +0 -110
  267. package/lib/src/textarea.ts +0 -235
  268. package/lib/src/textedit.ts +0 -533
  269. package/lib/src/toaster.ts +0 -80
  270. package/lib/src/tools.ts +0 -1473
  271. package/lib/src/tooltips.ts +0 -191
  272. package/lib/src/treeview.ts +0 -716
  273. package/lib/src/version.ts +0 -30
  274. package/lib/src/x4.less +0 -2242
  275. package/lib/src/x4dom.ts +0 -57
  276. package/lib/src/x4events.ts +0 -585
  277. package/lib/src/x4js.ts +0 -89
  278. package/lib/src/x4react.ts +0 -90
  279. package/lib/styles/x4.css +0 -1785
  280. package/lib/styles/x4.less +0 -2242
  281. package/lib/types/x4js.d.ts +0 -6728
  282. package/license.md +0 -21
@@ -1,2367 +0,0 @@
1
- /**
2
- * ___ ___ __
3
- * \ \/ / / _
4
- * \ / /_| |_
5
- * / \____ _|
6
- * /__/\__\ |_|
7
- *
8
- * @file components.ts
9
- * @author Etienne Cochard
10
- *
11
- * Copyright (c) 2019-2023 R-libre ingenierie
12
- *
13
- * Permission is hereby granted, free of charge, to any person obtaining a copy
14
- * of this software and associated documentation files (the "Software"), to deal
15
- * in the Software without restriction, including without limitation the rights
16
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17
- * of the Software, and to permit persons to whom the Software is furnished to do so,
18
- * subject to the following conditions:
19
- * The above copyright notice and this permission notice shall be included in all copies
20
- * or substantial portions of the Software.
21
- *
22
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
23
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
24
- * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
- */
29
-
30
-
31
- /**
32
- * @todo
33
- * create Container class
34
- */
35
-
36
- import { pascalCase, Rect, isString, isArray, Size, isNumber, asap, HtmlString, isHtmlString, Constructor, getMousePos } from './tools';
37
- import { x4document } from './x4dom';
38
-
39
- import { Stylesheet, ComputedStyle } from './styles';
40
- import { _tr } from './i18n';
41
- import { BasicEvent, EventCallback } from './x4events';
42
- import { BaseComponent, BaseComponentProps, BaseComponentEventMap } from './base_component';
43
- import { IDOMEvents, X4ElementEventMap } from './dom_events';
44
-
45
- export { HtmlString, isHtmlString, html } from './tools'
46
-
47
- export interface EventHandler<T> {
48
- (ev: T): any;
49
- }
50
-
51
- export interface ICaptureInfo {
52
- initiator: Component;
53
- handler: EventHandler<UIEvent>;
54
- iframes: NodeListOf<HTMLIFrameElement>;
55
- }
56
-
57
- type CSSPropertyName = {
58
- -readonly [K in keyof CSSStyleDeclaration & string]: CSSStyleDeclaration[K];
59
- };
60
-
61
- /** @ignore classname prefix for system classes */
62
- const _x4_ns_prefix = 'x-';
63
-
64
- // -- elements -----------
65
-
66
- /** @ignore where event handlers are stored */
67
- const _x4_el_store = Symbol();
68
-
69
- /** @ignore where Component is stored in dom */
70
- const _x4_el_sym = Symbol();
71
-
72
- /** @ignore properties without 'px' unit */
73
- export const _x4_unitless: Record<string,1> = {
74
- animationIterationCount: 1, borderImageOutset: 1, borderImageSlice: 1, borderImageWidth: 1, boxFlex: 1, boxFlexGroup: 1,
75
- boxOrdinalGroup: 1, columnCount: 1, flex: 1, flexGrow: 1, flexPositive: 1, flexShrink: 1, flexNegative: 1, flexOrder: 1,
76
- gridRow: 1, gridColumn: 1, fontWeight: 1, lineClamp: 1, lineHeight: 1, opacity: 1, order: 1, orphans: 1, tabSize: 1, widows: 1,
77
- zIndex: 1, zoom: 1,
78
-
79
- // SVG-related _properties
80
- fillOpacity: 1, floodOpacity: 1, stopOpacity: 1, strokeDasharray: 1, strokeDashoffset: 1, strokeMiterlimit: 1, strokeOpacity: 1,
81
- strokeWidth: 1,
82
- };
83
-
84
- /** @ignore this events must be defined on domNode (do not bubble) */
85
- const unbubbleEvents = {
86
- mouseleave: 1, mouseenter: 1, load: 1, unload: 1, scroll: 1, focus: 1, blur: 1, rowexit: 1, beforeunload: 1, stop: 1,
87
- dragdrop: 1, dragenter: 1, dragexit: 1, draggesture: 1, dragover: 1, contextmenu: 1, create: 2, sizechange: 2
88
- };
89
-
90
- /** @ignore */
91
- const passiveEvents = {
92
- touchstart: 1, touchmove: 1, touchend: 1,
93
- //pointerdown: 1, pointermove: 1, pointerup: 1,
94
- };
95
-
96
- /** ignore */
97
- const reNumber = /^-?\d+(\.\d+)?$/;
98
-
99
-
100
- /**
101
- *
102
- */
103
-
104
- export type VoidCallback = () => void;
105
-
106
- export type ComponentOrString = Component | string | HtmlString;
107
- export type ComponentContent = ComponentOrString | ComponentOrString[];
108
-
109
- interface IMap<T> {
110
- [key: string]: T;
111
- }
112
-
113
- /**
114
- * DblClick Event
115
- * double click event
116
- */
117
-
118
- export interface EvDblClick extends BasicEvent {
119
- }
120
-
121
- export function EvDblClick(context: any = null) {
122
- return BasicEvent<EvDblClick>({ context });
123
- }
124
-
125
- /**
126
- * DblFocus Event
127
- * double click event
128
- */
129
-
130
- export interface EvFocus extends BasicEvent {
131
- readonly focus: boolean;
132
- }
133
-
134
- export function EvFocus( focus = true, context: any = null) {
135
- return BasicEvent<EvFocus>({ focus, context });
136
- }
137
-
138
- /**
139
- * basic event map of Component
140
- */
141
-
142
- export interface CEventMap extends BaseComponentEventMap {
143
- }
144
-
145
-
146
-
147
-
148
- /**
149
- * Basic properties of every Component
150
- */
151
-
152
- export interface CProps<T extends CEventMap = CEventMap> extends BaseComponentProps<T> {
153
- tag?: string; // dom Tag <div> if not specified
154
- ns?: string; // namespace (usefull for svg)
155
- cls?: string; // elements classes (space separated), prefix class name with @ to make it system wide
156
- id?: string; // element id
157
- style?: IMap<string | number>; // element style
158
- attrs?: Record<string,string|number|boolean>; // element attributes
159
-
160
- dom_events?: IDOMEvents; // DOM event handlers
161
- data?: any; // element user data (you can store everything you need here)
162
- content?: ComponentContent; // array of sub components
163
- tooltip?: string; // tooltip text
164
- ref?: string; // internal reference for itemWithRef
165
-
166
- // shortcuts for element style
167
- left?: number;
168
- top?: number;
169
- width?: number | string;
170
- height?: number | string;
171
- tabIndex?: number | boolean;
172
-
173
- flex?: number | string; // add @flex class to the element
174
- enabled?: boolean; // add @disabled to the element if false
175
- }
176
-
177
- interface DomListener {
178
- listener: EventListener,
179
- passive?: boolean
180
- }
181
-
182
- interface CInternalProps {
183
- dom_events: Record<string,DomListener[]>;
184
- classes: IMap<boolean>;
185
- uid: number;
186
- created: boolean;
187
- inrender: boolean;
188
- }
189
-
190
- /**
191
- *
192
- */
193
-
194
- export class Component<P extends CProps<CEventMap> = CProps<CEventMap>, E extends CEventMap = CEventMap> extends BaseComponent<P, E> {
195
- private m_dom: HTMLElement;
196
- private m_iprops: CInternalProps;
197
-
198
- private static __sb_width: number; // scrollbar width
199
- private static __comp_guid = 1000; // component global unique id
200
- private static __privateEvents: any = {};
201
- private static __sizeObserver: ResizeObserver; // resize observer
202
- private static __createObserver: MutationObserver; // creation observer
203
- //private static __intersectionObserver: IntersectionObserver; // visibility observer
204
-
205
- private static __capture: ICaptureInfo = null;
206
- private static __capture_mask: HTMLElement = null;
207
- private static __css: Stylesheet = null;
208
-
209
- constructor(props: P = null ) {
210
- super(props ?? {} as P );
211
-
212
- this.m_iprops = {
213
- classes: {},
214
- dom_events: {},
215
- uid: Component.__comp_guid++,
216
- inrender: false,
217
- created: false,
218
- };
219
-
220
- // prepare iprops
221
- if( this.m_props.cls ) {
222
- this.addClass( this.m_props.cls );
223
- }
224
- }
225
-
226
- /**
227
- *
228
- */
229
-
230
- get uid() {
231
- return this.m_iprops.uid;
232
- }
233
-
234
- /**
235
- * change the component content
236
- * @param content new content or null
237
- */
238
-
239
- setContent(content: ComponentContent, refreshAll = false ) {
240
- this.m_props.content = content ?? [];
241
-
242
- if( this.m_iprops.inrender || !this.m_dom ) {
243
- return;
244
- }
245
-
246
- if (refreshAll) {
247
- this.update();
248
- }
249
- else {
250
- this._updateContent();
251
- }
252
- }
253
-
254
- getContent( ) {
255
- return this.m_props.content;
256
- }
257
-
258
- /**
259
- * add a new child to the component content
260
- * @param content
261
- */
262
-
263
- appendChild(content: ComponentContent) {
264
-
265
- if (!content) {
266
- return;
267
- }
268
-
269
- const append = (c: ComponentOrString) => {
270
-
271
- if (!this.m_props.content) {
272
- this.m_props.content = [];
273
- }
274
- else if (!isArray(this.m_props.content)) {
275
- this.m_props.content = [this.m_props.content];
276
- }
277
-
278
- this.m_props.content.push(c);
279
- if (this.m_dom) {
280
- this._appendChild(c);
281
- }
282
- }
283
-
284
- if (isArray(content)) {
285
- content.forEach(append);
286
- }
287
- else {
288
- append(content);
289
- }
290
- }
291
-
292
- removeChild( item: Component ) {
293
- if( this.m_props.content ) {
294
- if (!isArray(this.m_props.content)) {
295
- this.m_props.content = [this.m_props.content];
296
- }
297
-
298
- this.m_props.content = this.m_props.content.filter( x => x!==item );
299
- }
300
-
301
- if( this.dom && item.dom ) {
302
- this.dom.removeChild( item.dom );
303
- }
304
- }
305
-
306
- /**
307
- *
308
- */
309
-
310
- setTag( tag: string, namespace?: string ) {
311
- this.m_props.tag = tag;
312
- if( namespace ) {
313
- this.m_props.ns = namespace;
314
- }
315
- }
316
-
317
- /**
318
- * get the Component data value
319
- * @param name name to get
320
- */
321
-
322
- getData(name: string | Symbol): any {
323
- if (this.m_props.data !== undefined) {
324
- return this.m_props.data[name.toString()];
325
- }
326
-
327
- return undefined;
328
- }
329
-
330
- /**
331
- * set the Component data value
332
- * @param name name to get
333
- * @param value
334
- */
335
-
336
- setData(name: string | Symbol , value: any) {
337
-
338
- let data = this.m_props.data;
339
- if (data === undefined) {
340
- data = this.m_props.data = {};
341
- }
342
-
343
- data[name.toString()] = value;
344
- }
345
-
346
-
347
- /**
348
- * return the DOM associated with the Component (if any)
349
- */
350
-
351
- get dom(): HTMLElement {
352
- return this.m_dom;
353
- }
354
-
355
- /**
356
- * shows the element
357
- * @param show
358
- */
359
-
360
- show(show?: boolean) {
361
- if (show === undefined || show === true) {
362
- this.removeClass('@hidden');
363
- }
364
- else {
365
- this.addClass('@hidden');
366
- }
367
- }
368
-
369
- /**
370
- * hides the element
371
- */
372
- hide() {
373
- this.addClass('@hidden');
374
- }
375
-
376
- /**
377
- * enable or disable the element
378
- * @param enable
379
- */
380
-
381
- enable(enable?: boolean) {
382
- if (enable === undefined || enable === true) {
383
- this.removeClass('@disable');
384
- this.removeAttribute('disabled');
385
- }
386
- else {
387
- this.disable();
388
- }
389
- }
390
-
391
- /**
392
- * disable the element
393
- */
394
-
395
- disable() {
396
- this.addClass('@disable');
397
- this.setAttribute('disabled', '');
398
- }
399
-
400
- /**
401
- * set the focus on the element
402
- */
403
-
404
- focus() {
405
- console.assert(!!this.m_dom);
406
- this.m_dom.focus();
407
- }
408
-
409
- /**
410
- * change the object style
411
- * @param style - style to add
412
- * @example ```typescript
413
- * el.setStyle( {left:100} );
414
- */
415
-
416
- public setStyle(style: Record<string,any>) {
417
- for (let s in style) {
418
- this.setStyleValue(s, style[s]);
419
- }
420
- }
421
-
422
- /**
423
- * change a style value
424
- * @param name string style nale
425
- * @param value any style value or null to remove style
426
- */
427
-
428
- setStyleValue(name: string, value: any) {
429
- let style = this.m_props.style;
430
- if (!style) {
431
- style = this.m_props.style = {};
432
- }
433
-
434
- style[name] = value;
435
- this._setDomStyleValue(name, value);
436
- }
437
-
438
- private _setDomStyleValue(name: string, value: string | number) {
439
-
440
- if (this.m_dom) {
441
- if (value === undefined) {
442
- value = null;
443
- }
444
- else if (!_x4_unitless[name as string] && (isNumber(value) || reNumber.test(value))) {
445
- value = value + 'px';
446
- }
447
-
448
- (this.m_dom.style as any)[name] = value;
449
- }
450
- }
451
-
452
- /**
453
- * compute the element style
454
- * @return all styles computed
455
- */
456
-
457
- public getComputedStyle(pseudoElt?: string): ComputedStyle {
458
- if (this.dom) {
459
- return new ComputedStyle(getComputedStyle(this.dom, pseudoElt ?? null));
460
- }
461
-
462
- return new ComputedStyle(this.m_props.style as any);
463
- }
464
-
465
- /**
466
- * return a single stype value
467
- * @param name - value to get
468
- */
469
-
470
- public getStyleValue(name: string) {
471
- return (this.getComputedStyle() as any)[name];
472
- }
473
-
474
- /**
475
- * define the elements attributes
476
- * @param attrs
477
- */
478
-
479
- public setAttributes(attrs: any) {
480
- for (let a in attrs) {
481
- this.setAttribute(a, attrs[a]);
482
- }
483
- }
484
-
485
- /**
486
- * change a single attribute
487
- * @param name attribute name
488
- * @param value new value
489
- */
490
-
491
- public setAttribute(name: string, value: any) {
492
-
493
- if (value === false || value === undefined) {
494
- this.removeAttribute(name);
495
- }
496
- else {
497
- if (value === true) {
498
- value = '';
499
- }
500
- else if (isNumber(value)) {
501
- value = '' + value;
502
- }
503
-
504
- let attrs = this.m_props.attrs;
505
- if (!attrs) {
506
- attrs = this.m_props.attrs = {};
507
- }
508
-
509
- attrs[name] = value;
510
- this._setDomAttribute(name, value);
511
- }
512
- }
513
-
514
- private _setDomAttribute(name: string, value: string) {
515
- if (this.m_dom) {
516
- this.m_dom.setAttribute(name, value);
517
- }
518
- }
519
-
520
- /**
521
- * remove an atrribute
522
- * @param name name of the attribute
523
- */
524
- public removeAttribute(name: string) {
525
- let attrs = this.m_props.attrs;
526
- if (!attrs) {
527
- return;
528
- }
529
-
530
- delete attrs[name];
531
-
532
- if (this.m_dom) {
533
- this.m_dom.removeAttribute(name);
534
- }
535
- }
536
-
537
- /**
538
- * get an attribute value
539
- * @param {string} name - attribute name
540
- * @return {string} attribute value
541
- * @example ```typescript
542
- * let chk = el.getAttribute( 'checked' );
543
- * @review double cache
544
- */
545
-
546
- public getAttribute(name: string): string {
547
- if (this.m_dom) {
548
- return this.m_dom.getAttribute(name);
549
- }
550
- else {
551
- //todo move to attrs
552
- if( name=='id' ) {
553
- return this.m_props.id;
554
- }
555
-
556
- return this.m_props.attrs ? this.m_props.attrs[name]+'' : undefined;
557
- }
558
- }
559
-
560
- /**
561
- * check if the element has an attribute
562
- * @param name attribute name
563
- * @return true is attribute is present
564
- * @example ```typescript
565
- * if( el.hasAttribute('checked') ) {
566
- * }
567
- */
568
-
569
- public hasAttribute(name: string): boolean {
570
- if (this.m_dom) {
571
- return this.m_dom.hasAttribute(name);
572
- }
573
- else {
574
- return this.m_props.attrs.hasOwnProperty(name);
575
- }
576
- }
577
-
578
-
579
- /**
580
- * a some classnames to the component
581
- * classes can be separated by a space
582
- * @param cls class to add
583
- * @example ```typescript
584
- * addClass( 'my class name @flex' );
585
- */
586
-
587
- public addClass(name: string) {
588
-
589
- if (name === null || name === undefined) {
590
- return;
591
- }
592
-
593
- name = name.trim();
594
- if (name === '') {
595
- return;
596
- }
597
-
598
- let add = (c: string) => {
599
-
600
- if (c === undefined || c === null || c === '') {
601
- return;
602
- }
603
-
604
- c = this._makeCls(c);
605
-
606
- // update vdom
607
- classes[c] = true;
608
-
609
- // update dom
610
- if (this.m_dom) {
611
- this.m_dom.classList.add(c);
612
- }
613
- }
614
-
615
- let classes = this.m_iprops.classes;
616
- if (name.indexOf(' ') < 0) {
617
- add(name);
618
- }
619
- else {
620
- let names = name.split(' ');
621
- names.forEach((n) => add(n));
622
- }
623
- }
624
-
625
- /**
626
- * Remove a class from the element
627
- * @param {string|array} name - classes in string form can be space separated
628
- *
629
- * @example ```typescript
630
- * el.removeClass( 'myclass' );
631
- * el.removeClass( 'myclass1 myclass2' );
632
- */
633
-
634
- public removeClass(name: string): void {
635
-
636
- if (name === undefined) {
637
- return;
638
- }
639
-
640
- let remove = (c: string) => {
641
- if (c === undefined || c === null || c === '') {
642
- return;
643
- }
644
-
645
- c = this._makeCls(c);
646
-
647
- delete this.m_iprops.classes[c];
648
- if (this.m_dom) {
649
- this.m_dom.classList.remove(c);
650
- }
651
- }
652
-
653
- // faster
654
- if (name.indexOf(' ') < 0) {
655
- remove(name);
656
- }
657
- else {
658
- // build class list
659
- let classes = name.trim().split(' ');
660
- for (let c of classes) {
661
- if (c !== undefined && c !== null && c !== '') {
662
- remove(c);
663
- }
664
- }
665
- }
666
- }
667
-
668
- /**
669
- *
670
- * @param cls
671
- * @param set
672
- */
673
-
674
- public setClass(cls: string, set: boolean) {
675
- if (set) { this.addClass(cls); }
676
- else { this.removeClass(cls); }
677
- return this;
678
- }
679
-
680
- /**
681
- * Toggle a class from the element (if present remove, if absent add)
682
- * @param {string|string[]} name - classes in string form can be space separated
683
- * @example ```typescript
684
- * el.toggleClass( 'myclass' );
685
- * el.toggleClass( 'myclass1 myclass2');
686
- * el.toggleClass( ['myclass1','myclass2']);
687
- */
688
-
689
- public toggleClass(name: string): void {
690
-
691
- let toggle = (c: string) => {
692
- if (c === undefined && c === null && c === '') {
693
- return;
694
- }
695
-
696
- c = this._makeCls(c);
697
- if (this.m_iprops.classes[c]) {
698
- delete this.m_iprops.classes[c]
699
- }
700
- else {
701
- this.m_iprops.classes[c] = true;
702
- }
703
-
704
- if (this.m_dom) {
705
- this.m_dom.classList.toggle(c);
706
- }
707
- }
708
-
709
- // faster
710
- if (name.indexOf(' ') < 0) {
711
- toggle(name);
712
- }
713
- else {
714
-
715
- // build class list
716
- let classes = name.trim().split(' ');
717
- for (let c of classes) {
718
- toggle(c);
719
- }
720
- }
721
- }
722
-
723
- /**
724
- * check if the object has the class
725
- * @param cls
726
- */
727
-
728
- public hasClass(cls: string): boolean {
729
-
730
- let c = this._makeCls(cls);
731
- if (this.m_dom) {
732
- return this.dom.classList.contains(c);
733
- }
734
- else {
735
- return !!this.m_iprops.classes[c];
736
- }
737
- }
738
-
739
- /**
740
- * remove all classes from the object
741
- * this is usefull for component recycling & reusing
742
- */
743
-
744
- public clearClasses() {
745
- this.m_iprops.classes = {};
746
- if (this.m_dom) {
747
- return this.m_dom.classList.value = '';
748
- }
749
- }
750
-
751
- public _build(): HTMLElement {
752
- if (this.m_dom) {
753
- return this.m_dom;
754
- }
755
-
756
- this._createDOM();
757
- return this.m_dom;
758
- }
759
-
760
- public render(props: P) {
761
- }
762
-
763
- public _createDOM(): HTMLElement {
764
-
765
- if (this.m_dom) {
766
- return this.m_dom;
767
- }
768
-
769
- // setup props
770
- const props = this.m_props;
771
-
772
- if( props.tabIndex!==undefined ) {
773
- this._setTabIndex( props.tabIndex );
774
- }
775
- this.render(props);
776
-
777
- // shortcuts ---------
778
- if (props.left !== undefined) { this.setStyleValue('left', props.left); }
779
- if (props.top !== undefined) { this.setStyleValue('top', props.top); }
780
- if (props.width !== undefined) { this.setStyleValue('width', props.width); }
781
- if (props.height !== undefined) { this.setStyleValue('height', props.height); }
782
-
783
- if (props.flex !== undefined) {
784
- this.addClass('@flex');
785
- if (props.flex != 1) {
786
- this.setStyleValue('flex', props.flex);
787
- }
788
- }
789
-
790
- if (props.enabled === false) {
791
- this.disable();
792
- }
793
-
794
- // shortcut: tip
795
- if (props.tooltip !== undefined) {
796
- this.setAttribute('tip', props.tooltip.replace(/\n/gi, '<br/>'));
797
- }
798
-
799
-
800
- // prepare iprops
801
- if (props.dom_events) {
802
- for (let ename in props.dom_events) {
803
- this._setDomEvent(ename, (props.dom_events as any)[ename]);
804
- }
805
- }
806
-
807
- this._genClassName();
808
- this.m_props.cls = undefined; // now classes are tranfered to m_iprops
809
-
810
- // create self
811
- let vdom = this.m_iprops;
812
-
813
- if (props.ns) {
814
- this.m_dom = <HTMLElement>x4document.createElementNS(props.ns, props.tag ?? 'div');
815
- }
816
- else {
817
- this.m_dom = x4document.createElement( (props.tag ?? 'div') as any );
818
- }
819
-
820
- (this.m_dom as any)[_x4_el_sym] = this;
821
-
822
- //let me = Object.getPrototypeOf(this);
823
- //console.log( 'create', this.m_iprops.uid, me.constructor.name );
824
-
825
- // classes
826
- this.m_dom.classList.add(...Object.keys(vdom.classes));
827
-
828
- // styles
829
- let sty = props.style;
830
- if (sty) {
831
- for (let s in sty) {
832
- this._setDomStyleValue(s, sty[s]);
833
- }
834
- }
835
-
836
- // attributes
837
- let att = props.attrs;
838
- if (att) {
839
- for (let a in att) {
840
- const attr = att[a];
841
- if( attr!==false && attr!==undefined ) {
842
- this._setDomAttribute(a, ''+att[a] );
843
- }
844
- }
845
- }
846
-
847
- // special properties
848
- if (this.m_props.id) {
849
- this._setDomAttribute('id', this.m_props.id);
850
- }
851
-
852
- // events
853
- let evt = this.m_iprops.dom_events;
854
- if (evt) {
855
- for (let e in evt) {
856
- let handlers = evt[e];
857
- for (let h of handlers) {
858
- this._createEvent(e, h.listener, h.passive );
859
- }
860
- }
861
- }
862
-
863
- // create children
864
- let content = props.content;
865
- if (content) {
866
-
867
- if (!isArray(content)) {
868
- content = [content];
869
- }
870
-
871
- content.forEach((el) => {
872
- if (!el) {
873
- return;
874
- }
875
-
876
- if (isString(el)) {
877
- this.m_dom.insertAdjacentText('beforeend', el);
878
- }
879
- else if (isHtmlString(el)) {
880
- this.m_dom.insertAdjacentHTML('beforeend', el as string);
881
- }
882
- else if (el instanceof Component) {
883
- this.m_dom.append(el._build());
884
- }
885
- else {
886
- console.log( 'unknown element type: ', el );
887
- }
888
- });
889
- }
890
-
891
- // wait for dom insertion inside document.body
892
- if (!Component.__createObserver) {
893
- Component.__createObserver = new MutationObserver(Component._observeCreation);
894
- Component.__createObserver.observe(x4document.body, { childList: true, subtree: true });
895
- }
896
-
897
- return this.m_dom;
898
- }
899
-
900
- protected _setTabIndex(tabIndex: number | boolean, defValue = 0) {
901
-
902
- if (tabIndex === true) {
903
- tabIndex = 0;
904
- }
905
- else if (tabIndex === undefined) {
906
- tabIndex = defValue;
907
- }
908
-
909
- if (tabIndex !== false && tabIndex !== undefined) {
910
- this.setAttribute('tabindex', tabIndex);
911
- }
912
-
913
- this.m_props.tabIndex = tabIndex;
914
- }
915
-
916
- private static _observeCreation(mutations: MutationRecord[]) {
917
-
918
- // notify descendants that we have been created (dom exists)
919
-
920
- const notify = ( c: Component ) => {
921
-
922
- if( !c.m_iprops.created ) {
923
- if (c.dom && c.m_iprops.dom_events && c.m_iprops.dom_events.create) {
924
- c.dom.dispatchEvent(new Event('create'));
925
- }
926
-
927
- c.componentCreated();
928
- c.m_iprops.created = true;
929
- }
930
- }
931
-
932
- for (let mutation of mutations) {
933
-
934
- if (mutation.type == 'childList') {
935
-
936
- for (let i = 0, n = mutation.addedNodes.length; i < n; i++) {
937
-
938
- let add = mutation.addedNodes[i] as HTMLElement;
939
- let el = (add as any)[_x4_el_sym] as Component;
940
-
941
- if (el) {
942
- el.enumChilds((c: Component) => {
943
- notify( c );
944
- }, true);
945
-
946
- notify( el );
947
- }
948
- }
949
- }
950
- }
951
-
952
-
953
- }
954
-
955
- public dispose() {
956
- if (this.m_dom) {
957
- this._dispose(true,true);
958
- }
959
- }
960
-
961
- protected _dispose(with_dom: boolean, timers: boolean ) {
962
-
963
- let _dom = this.m_dom;
964
-
965
- // free attached resources
966
- delete (_dom as any)[_x4_el_sym];
967
- delete (_dom as any)[_x4_el_store];
968
-
969
- //
970
- if (with_dom) {
971
- _dom.remove();
972
- }
973
-
974
- // notify every child that they will be removed
975
- this.enumChilds((c: Component) => {
976
- c._dispose(false,true);
977
- });
978
-
979
- this.m_dom = null;
980
-
981
- if( timers ) {
982
- this.disposeTimers();
983
- }
984
-
985
- this.componentDisposed();
986
- this.m_iprops.created = false;
987
- // todo: pb on update this.removeAllListeners( null );
988
- }
989
-
990
- componentDisposed() {
991
- }
992
-
993
- componentCreated() {
994
- }
995
-
996
-
997
-
998
-
999
-
1000
-
1001
-
1002
- /**
1003
- *
1004
- */
1005
-
1006
- public update(delay = 0) {
1007
-
1008
- if (this.m_dom) {
1009
-
1010
- const _update = () => {
1011
- let oldDOM = this.m_dom;
1012
- this._dispose(false,false);
1013
-
1014
- let newDOM = this._build();
1015
- console.assert( !!oldDOM.parentNode, 'update in componentCreated is not allowed, use updateContent' );
1016
-
1017
- oldDOM.parentNode.replaceChild(newDOM, oldDOM);
1018
- }
1019
-
1020
- if (delay) {
1021
- this.singleShot(_update, delay);
1022
- }
1023
- else {
1024
- _update();
1025
- }
1026
- }
1027
- }
1028
-
1029
- /**
1030
- * empty the node
1031
- */
1032
- public _empty( ) {
1033
- //this.m_dom.innerHTML = '';
1034
-
1035
- const el = this.m_dom;
1036
- if( !el ) {
1037
- return;
1038
- }
1039
-
1040
- while (el.firstChild) {
1041
- el.removeChild(el.firstChild);
1042
- }
1043
- }
1044
-
1045
- public _updateContent() {
1046
-
1047
- if (!this.m_dom) {
1048
- return;
1049
- }
1050
-
1051
- this._empty( );
1052
-
1053
- let content = this.m_props.content;
1054
-
1055
- // create children
1056
- if (content) {
1057
-
1058
- if (!isArray(content)) {
1059
- content = [content];
1060
- }
1061
-
1062
- content.forEach((el) => {
1063
- if (!el) {
1064
- return;
1065
- }
1066
-
1067
- if (isHtmlString(el)) {
1068
- this.m_dom.insertAdjacentHTML('beforeend', el as string);
1069
- }
1070
- else if (el instanceof Component) {
1071
- this.m_dom.append(el._build());
1072
- }
1073
- else {
1074
- this.m_dom.insertAdjacentText('beforeend', el + '');
1075
- }
1076
- });
1077
- }
1078
-
1079
- }
1080
-
1081
- /**
1082
- * @return the bounding rectangle
1083
- * @example ```typescript
1084
- * let rc = el.getBoundingRect( );
1085
- * console.log( rc.left, rc.top, rc.right, rc.bottom );
1086
- */
1087
-
1088
- public getBoundingRect(withMargins = false): Rect {
1089
- console.assert(this.dom != null, 'cannot get bounding rect of an non DOM element');
1090
- let r = this.dom.getBoundingClientRect();
1091
-
1092
- let rc = new Rect(r.left, r.top, r.width, r.height);
1093
-
1094
- if (withMargins) {
1095
-
1096
- let st = this.getComputedStyle();
1097
-
1098
- let tm = st.parse('marginTop'),
1099
- bm = st.parse('marginBottom'),
1100
- lm = st.parse('marginLeft'),
1101
- rm = st.parse('marginRight');
1102
-
1103
- rc.left -= lm;
1104
- rc.width += lm + rm;
1105
-
1106
- rc.top -= tm;
1107
- rc.height += tm + bm;
1108
- }
1109
-
1110
- return rc;
1111
- }
1112
-
1113
- /**
1114
- * append a new dom event handler
1115
- * @param name - you can specify multiple names separated by a space
1116
- * @param handler
1117
- * @example
1118
- *
1119
- * this.setDomEvent( 'drag drop', this._handleDrag, this );
1120
- * this.setDomEvent( 'dblclick', this._handleDblClick, this );
1121
- */
1122
-
1123
- public setDomEvent<K extends keyof X4ElementEventMap>(type: K, listener: (this: HTMLDivElement, ev: X4ElementEventMap[K]) => void, passive?: boolean ) {
1124
- let _listener = listener as EventListener;
1125
- this._setDomEvent(type as string, _listener, passive);
1126
- }
1127
-
1128
- private _setDomEvent(type: string, listener: EventListener, passive?: boolean) {
1129
-
1130
- // add event to the vdom
1131
- if (!this.m_iprops.dom_events) {
1132
- this.m_iprops.dom_events = {};
1133
- }
1134
-
1135
- let listeners = this.m_iprops.dom_events[type];
1136
- if (!listeners) {
1137
- listeners = this.m_iprops.dom_events[type] = [{listener,passive}];
1138
- }
1139
- else {
1140
- listeners.push({listener,passive});
1141
- }
1142
-
1143
- if (this.m_dom) {
1144
- //this.m_dom.addEventListener(type, listener);
1145
- this._createEvent(type, listener,passive);
1146
- }
1147
- }
1148
-
1149
- /**
1150
- *
1151
- */
1152
-
1153
- public clearDomEvent<K extends keyof X4ElementEventMap>(type: K) {
1154
- if (!this.m_iprops.dom_events) {
1155
- return;
1156
- }
1157
-
1158
- delete this.m_iprops.dom_events[type];
1159
-
1160
- let _dom = this.m_dom;
1161
- if (_dom) {
1162
- let store = (_dom as any)[_x4_el_store];
1163
- if (store) {
1164
- delete store[type];
1165
- }
1166
- }
1167
- }
1168
-
1169
- /**
1170
- *
1171
- * @param name
1172
- * @param handler
1173
- */
1174
-
1175
- private _createEvent(name: string, handler: Function, passive?: boolean) {
1176
-
1177
- let _dom = this.m_dom;
1178
- let store = (_dom as any)[_x4_el_store];
1179
-
1180
- if (!store) {
1181
- store = (_dom as any)[_x4_el_store] = {};
1182
- }
1183
-
1184
- if (!store[name]) {
1185
- // no handler for this event...
1186
- store[name] = [handler];
1187
- }
1188
- else {
1189
- // append the handler
1190
- store[name].push(handler);
1191
- }
1192
-
1193
- if ((unbubbleEvents as any)[name] === 1) {
1194
- (_dom as any)['on' + name] = Component._dispatchUnbubbleEvent;
1195
- }
1196
- else if (!Component.__privateEvents[name]) {
1197
- Component.__privateEvents[name] = true; // todo count it
1198
-
1199
- if( passive===undefined ) {
1200
- if ( (passiveEvents as any)[name] ) {
1201
- x4document.addEventListener(name as any, Component._dispatchEvent, { passive: false, capture: true });
1202
- }
1203
- else {
1204
- x4document.addEventListener(name as any, Component._dispatchEvent, true);
1205
- }
1206
- }
1207
- else {
1208
- x4document.addEventListener(name as any, Component._dispatchEvent, { passive, capture: passive ? true : false } );
1209
- }
1210
- }
1211
-
1212
- if (name === 'sizechange') {
1213
- if (!Component.__sizeObserver) {
1214
- Component.__sizeObserver = new ResizeObserver(Component._observeSize);
1215
- }
1216
-
1217
- Component.__sizeObserver.observe(this.m_dom);
1218
- }
1219
- }
1220
-
1221
- /**
1222
- * dispatch a dom event to the appropriated component
1223
- * called by the system
1224
- */
1225
-
1226
- private static _dispatchEvent(ev: any) {
1227
-
1228
- let target = ev.target,
1229
- noup = (unbubbleEvents as any)[ev.type] === 2;
1230
-
1231
- while (target) {
1232
- if (target[_x4_el_store]) {
1233
- let store = target[_x4_el_store][ev.type];
1234
- if (store) {
1235
- let el = target[_x4_el_sym];
1236
- let root = el?.root ?? null;
1237
-
1238
- if (store instanceof Array) {
1239
- store.some((fn) => {
1240
- fn(ev, root);
1241
- if (!el.dom) {
1242
- return true;
1243
- }
1244
- });
1245
- }
1246
- else {
1247
- store(ev, root);
1248
- }
1249
-
1250
- if (ev.cancelBubble || ev.defaultPrevented || noup) {
1251
- break;
1252
- }
1253
- }
1254
- }
1255
-
1256
- target = target.parentNode;
1257
-
1258
- // no need to go above
1259
- if (target == document) {
1260
- break;
1261
- }
1262
- }
1263
- }
1264
-
1265
- /**
1266
- * dispatch a dom event to the appropriated component
1267
- * called by the system
1268
- */
1269
-
1270
- private static _dispatchUnbubbleEvent(ev: any) {
1271
-
1272
- let target = ev.currentTarget || ev.target,
1273
- eventType = ev.type;
1274
-
1275
- let eventStore = target[_x4_el_store],
1276
- store = eventStore && eventStore[eventType];
1277
-
1278
- if (store) {
1279
-
1280
- let el = target[_x4_el_sym];
1281
- let root = el?.root ?? null;
1282
-
1283
- if (store instanceof Array) {
1284
- store.forEach((fn) => {
1285
- fn(ev, root);
1286
- });
1287
- }
1288
- else {
1289
- store(ev, root);
1290
- }
1291
- }
1292
- }
1293
-
1294
- /**
1295
- * called when a size change on an observed component
1296
- */
1297
-
1298
- private static _observeSize(entries: ResizeObserverEntry[]) {
1299
-
1300
- entries.forEach((entry) => {
1301
- let dom = entry.target as HTMLElement;
1302
- if (dom.offsetParent !== null) {
1303
- dom.dispatchEvent(new Event('sizechange'));
1304
- }
1305
- });
1306
- }
1307
-
1308
- /**
1309
- * enum all children recursively
1310
- * @param recursive - if true do a full sub-child search
1311
- * @param cb - callback
1312
- * return true to stop enumeration
1313
- */
1314
-
1315
- enumChilds(cb: (child: Component) => boolean | void, recursive = false): boolean {
1316
-
1317
- // use dom if available
1318
- if (this.m_dom) {
1319
-
1320
- let el = this.m_dom.firstChild;
1321
-
1322
- while (el) {
1323
- // get component (if any)
1324
- let cel = (el as any)[_x4_el_sym];
1325
- if (cel) {
1326
- cb(cel);
1327
-
1328
- if (recursive && cel.enumChilds(cb, true) === true) {
1329
- return true;
1330
- }
1331
- }
1332
-
1333
- el = el.nextSibling;
1334
- }
1335
- }
1336
- else {
1337
- let content = this.m_props.content;
1338
- if (!content) {
1339
- return;
1340
- }
1341
-
1342
- if (!isArray(content)) {
1343
- content = [content];
1344
- }
1345
-
1346
- content.some((el) => {
1347
- if (!el || isString(el) || isHtmlString(el)) {
1348
- return;
1349
- }
1350
-
1351
- if (cb(el)) {
1352
- return true;
1353
- }
1354
-
1355
- if (recursive && el.enumChilds(cb, true) === true) {
1356
- return true;
1357
- }
1358
- });
1359
- }
1360
-
1361
- return false;
1362
- }
1363
-
1364
- /**
1365
- * apprend a child to the DOM
1366
- * @param props child to append (or string)
1367
- */
1368
-
1369
- private _appendChild(el: ComponentOrString) {
1370
-
1371
- if (isString(el)) {
1372
- this.m_dom.insertAdjacentText('beforeend', el);
1373
- }
1374
- else if (isHtmlString(el)) {
1375
- this.m_dom.insertAdjacentHTML('beforeend', el as string);
1376
- }
1377
- else {
1378
- let component: Component = el;
1379
-
1380
- try {
1381
- component._build();
1382
- this.m_dom.appendChild(component.m_dom);
1383
- }
1384
- catch (e) {
1385
- console.error(e);
1386
- }
1387
- }
1388
- }
1389
-
1390
- /**
1391
- * generate classes from the component inheritance
1392
- * @example
1393
- * Button extends Component will give
1394
- * x-comp x-button
1395
- */
1396
-
1397
- private _genClassName() {
1398
-
1399
- this.addClass('@comp');
1400
-
1401
- let me = Object.getPrototypeOf(this);
1402
- while (me && me.constructor !== Component) {
1403
- let clsname = me.constructor.name;
1404
- this.addClass('@' + pascalCase(clsname));
1405
-
1406
- me = Object.getPrototypeOf(me);
1407
- }
1408
-
1409
- //done in ctor now
1410
- //this.addClass(this.m_props.cls);
1411
- }
1412
-
1413
- /**
1414
- * prepend the system class name prefix on a name if needed (if class starts with @)
1415
- */
1416
-
1417
- private _makeCls(cls: string): string {
1418
- if (cls[0] == '@') {
1419
- return cls = _x4_ns_prefix + cls.substring(1);
1420
- }
1421
- else {
1422
- return cls;
1423
- }
1424
- }
1425
-
1426
- /**
1427
- *
1428
- */
1429
-
1430
- private static dispatchCaptures(event: Event) {
1431
- Component.__capture.handler(event as UIEvent);
1432
- }
1433
-
1434
- /**
1435
- * capture mouse events
1436
- * @param capture name of the current capture
1437
- * @param callback funciton to call on captured mouse events
1438
- *
1439
- * @example
1440
- * Component.setCapture( this, ( ev: MouseEvent, initiator: Component ) => {
1441
- * if( ev.type=='mousemove' ) {
1442
- * this.setStyle( {
1443
- * left: ev.clientX,
1444
- * top: ev.clientY
1445
- * } );
1446
- * }
1447
- * else if( ev.type=='mouseup' ) {
1448
- * Component.releaseCapture( );
1449
- * }
1450
- * } );
1451
- */
1452
-
1453
- protected static setCapture(initiator: Component, listener: EventHandler<UIEvent>) {
1454
-
1455
- console.assert(!Component.__capture);
1456
- if (Component.__capture) {
1457
- debugger;
1458
- }
1459
-
1460
- // todo: review that
1461
-
1462
- let iframes = x4document.querySelectorAll<HTMLIFrameElement>("iframe");
1463
- iframes.forEach( f => {
1464
- flyWrap(f).setStyleValue( 'pointer-events', 'none' );
1465
- });
1466
-
1467
- let overs = x4document.querySelectorAll(":hover");
1468
-
1469
- let cursor = null;
1470
- if (overs.length) {
1471
- let elementOver = <HTMLElement>overs[overs.length - 1];
1472
- let style = window.getComputedStyle(elementOver);
1473
- cursor = style.cursor;
1474
- }
1475
-
1476
- Component.__capture_mask = x4document.createElement('div');
1477
- let mask = flyWrap(Component.__capture_mask);
1478
- mask.addClass('@capture-mask');
1479
-
1480
- if (cursor) {
1481
- mask.setStyleValue('cursor', cursor);
1482
- }
1483
-
1484
- x4document.body.appendChild(mask.dom);
1485
-
1486
- x4document.addEventListener('mousedown', Component.dispatchCaptures);
1487
- x4document.addEventListener('mousemove', Component.dispatchCaptures);
1488
- x4document.addEventListener('mouseup', Component.dispatchCaptures);
1489
-
1490
- x4document.addEventListener('touchstart', Component.dispatchCaptures);
1491
- x4document.addEventListener('touchmove', Component.dispatchCaptures);
1492
- x4document.addEventListener('touchend', Component.dispatchCaptures);
1493
-
1494
- Component.__capture = {
1495
- initiator,
1496
- handler: listener,
1497
- iframes
1498
- };
1499
- }
1500
-
1501
- protected static releaseCapture() {
1502
-
1503
- console.assert(!!Component.__capture);
1504
-
1505
- x4document.removeEventListener('touchstart', Component.dispatchCaptures);
1506
- x4document.removeEventListener('touchmove', Component.dispatchCaptures);
1507
- x4document.removeEventListener('touchend', Component.dispatchCaptures);
1508
-
1509
- x4document.removeEventListener('mousedown', Component.dispatchCaptures);
1510
- x4document.removeEventListener('mousemove', Component.dispatchCaptures);
1511
- x4document.removeEventListener('mouseup', Component.dispatchCaptures);
1512
-
1513
- Component.__capture.iframes.forEach( f => {
1514
- flyWrap(f).setStyleValue( 'pointer-events', null );
1515
- })
1516
-
1517
- Component.__capture = null;
1518
- if (Component.__capture_mask) {
1519
- x4document.body.removeChild(Component.__capture_mask);
1520
- Component.__capture_mask = null;
1521
- }
1522
- }
1523
-
1524
- /**
1525
- * ensure the component is visible
1526
- * @param: alignToTop
1527
- */
1528
-
1529
- public scrollIntoView(arg?: boolean | ScrollIntoViewOptions) {
1530
- if (this.m_dom) {
1531
-
1532
- const rel = new Rect( this.dom.getBoundingClientRect( ) );
1533
-
1534
- let top = undefined;
1535
- let bot = undefined;
1536
- let left = undefined;
1537
- let right = undefined;
1538
-
1539
- let pn = this.dom.parentElement;
1540
- const bdy = x4document.body;
1541
-
1542
- while( pn && pn!=bdy ) {
1543
-
1544
- const pr = pn.getBoundingClientRect( );
1545
-
1546
- if( top===undefined || top<pr.top ) {
1547
- top = pr.top;
1548
- }
1549
-
1550
- if( bot===undefined || bot>pr.bottom ) {
1551
- bot = pr.bottom;
1552
- }
1553
-
1554
- if( left===undefined || left<pr.left ) {
1555
- left = pr.left;
1556
- }
1557
-
1558
- if( right===undefined || right>pr.right ) {
1559
- right = pr.right;
1560
- }
1561
-
1562
- pn = pn.parentElement;
1563
- }
1564
-
1565
- if( top===undefined || rel.top<top || rel.bottom>bot || rel.left<left || rel.right>right ) {
1566
- //this.m_dom.scrollIntoView( true );
1567
- this.m_dom.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'start' });
1568
- }
1569
-
1570
- //this.m_dom.scrollIntoView(arg);
1571
- }
1572
- }
1573
-
1574
- /**
1575
- * search for a given css selector
1576
- * @param selector
1577
- * @returns child or null
1578
- */
1579
-
1580
- public queryItem<T extends Component>(selector: string): T {
1581
- let result = <HTMLElement>this.dom.querySelector(selector);
1582
- return result ? Component.getElement<T>(result) : null;
1583
- }
1584
-
1585
- public queryAll(selector: string, cb?: (el: Component) => void): HTMLElement[] {
1586
- let elements:HTMLElement[] = Array.from( this.m_dom.querySelectorAll<HTMLElement>(selector) );
1587
-
1588
- if( cb ) {
1589
- elements.forEach((el) => {
1590
- cb(flyWrap(el as HTMLElement));
1591
- });
1592
- }
1593
-
1594
- return elements;
1595
- }
1596
-
1597
- /**
1598
- * find a child with the given ID
1599
- * @param id id (without '#')
1600
- * @returns child or null
1601
- *
1602
- * @example
1603
- * let btn = this.childWithId<Button>( 'myButtonId' );
1604
- */
1605
- public itemWithId<T extends Component>(id: string): T {
1606
- let result = <HTMLElement>this.dom.querySelector('#' + id);
1607
- return result ? Component.getElement<T>(result) : null;
1608
- }
1609
-
1610
- /**
1611
- * find a child with given ref
1612
- * @param ref
1613
- * @return found child or null
1614
- */
1615
-
1616
- public itemWithRef<T = Component>(ref: string): T {
1617
-
1618
- let result = null;
1619
- this.enumChilds((c: Component) => {
1620
- if (c.m_props.ref === ref) {
1621
- result = c;
1622
- return true;
1623
- }
1624
- }, true);
1625
-
1626
- return result as T;
1627
- }
1628
-
1629
- /**
1630
- *
1631
- */
1632
-
1633
- get ref() {
1634
- return this.m_props.ref;
1635
- }
1636
-
1637
- /**
1638
- *
1639
- */
1640
-
1641
- static getCss(): Stylesheet {
1642
- if (!Component.__css) {
1643
- Component.__css = new Stylesheet();
1644
- }
1645
-
1646
- return Component.__css;
1647
- }
1648
-
1649
- /**
1650
- * return the parent element
1651
- * care, object must have been created (dom!=null)
1652
- */
1653
-
1654
- public getParent(): Component {
1655
- console.assert(!!this.m_dom);
1656
-
1657
- let elParent = this.dom.parentNode;
1658
- return Component.getElement(<HTMLElement>elParent);
1659
- }
1660
-
1661
- /**
1662
- * get a component from a DOM element
1663
- * move up to the hierarchy to find the request class type.
1664
- * @param dom
1665
- * @param classname
1666
- * @returns
1667
- *
1668
- * @example
1669
- *
1670
- * with a DOM like that:
1671
- * Button
1672
- * Label
1673
- * Icon <- the DOM you have (dom-icon)
1674
- *
1675
- * let btn = Component.getElement( dom-icon, Button );
1676
- */
1677
-
1678
- static getElement<T extends Component>(dom: HTMLElement | Element, classname?: Constructor<T> | string ): T {
1679
-
1680
- if (classname) {
1681
-
1682
- const srhCls = isString(classname);
1683
-
1684
- while (dom) {
1685
- let el: Component = (dom as any)[_x4_el_sym];
1686
-
1687
- if( srhCls ) {
1688
- if( el && el.hasClass(classname) ) {
1689
- return el as T;
1690
- }
1691
- }
1692
- else if (el instanceof classname) {
1693
- return el;
1694
- }
1695
-
1696
- dom = dom.parentElement;
1697
- }
1698
-
1699
- return null;
1700
- }
1701
- else {
1702
- return dom ? (dom as any)[_x4_el_sym] : null;
1703
- }
1704
- }
1705
-
1706
- /**
1707
- * compute the scrollbar size ( width = height)
1708
- */
1709
-
1710
- static getScrollbarSize() {
1711
-
1712
- if (Component.__sb_width === undefined) {
1713
- let outerDiv = x4document.createElement('div');
1714
- outerDiv.style.cssText = 'overflow:auto;position:absolute;top:0;width:100px;height:100px';
1715
-
1716
- let innerDiv = x4document.createElement('div');
1717
- innerDiv.style.width = '200px';
1718
- innerDiv.style.height = '200px';
1719
-
1720
- outerDiv.appendChild(innerDiv);
1721
- x4document.body.appendChild(outerDiv);
1722
-
1723
- Component.__sb_width = outerDiv.offsetWidth - outerDiv.clientWidth;
1724
- x4document.body.removeChild(outerDiv);
1725
- }
1726
-
1727
- return Component.__sb_width;
1728
- }
1729
-
1730
- /**
1731
- * check if the Component is visible to the user
1732
- */
1733
-
1734
- isUserVisible(): boolean {
1735
- if (!this.m_dom) {
1736
- return false;
1737
- }
1738
-
1739
- return (this.m_dom.offsetParent !== null);
1740
- }
1741
- }
1742
-
1743
- /** @ignore */
1744
- let fly_element: Component = null;
1745
-
1746
- /**
1747
- * warp <b>temporarily</b> a DOM element to be able to acces to exact API
1748
- * @param dom dom element to wrap
1749
- * @review qui libere le fly_element ? -> timeout
1750
- */
1751
-
1752
- export function flyWrap<T extends Component>(dom: HTMLElement | EventTarget): T {
1753
-
1754
- if ((dom as any)[_x4_el_sym]) {
1755
- return (dom as any)[_x4_el_sym];
1756
- }
1757
-
1758
- let f = fly_element;
1759
- if (!f) { f = fly_element = new Component({}); }
1760
- (<any>f).m_dom = dom;
1761
-
1762
- return f as T;
1763
- }
1764
-
1765
-
1766
-
1767
-
1768
-
1769
-
1770
-
1771
-
1772
- /**
1773
- * simple flex spacer
1774
- */
1775
-
1776
- export class Flex extends Component {
1777
-
1778
- constructor(props: CProps = {}) {
1779
- if (!props.flex) {
1780
- props.flex = 1;
1781
- }
1782
-
1783
- super(props);
1784
- }
1785
- }
1786
-
1787
- /**
1788
- * simple space between 2 elements
1789
- */
1790
-
1791
- export class Space extends Component {
1792
-
1793
- m_size: number | string;
1794
-
1795
- constructor(size: number | string) {
1796
- super({});
1797
-
1798
- this.m_size = size;
1799
- }
1800
-
1801
- componentCreated() {
1802
-
1803
- // try to find if we are in a hz / vt / abs container
1804
- let dom = this.dom;
1805
- let style = null;
1806
-
1807
- while (dom) {
1808
- let el: Component = (dom as any)[_x4_el_sym];
1809
- if (el.hasClass('@hlayout')) {
1810
- style = { width: this.m_size };
1811
- break;
1812
- }
1813
- else if (el.hasClass('@vlayout')) {
1814
- style = { height: this.m_size };
1815
- break;
1816
- }
1817
-
1818
- dom = dom.parentElement;
1819
- }
1820
-
1821
- if (!style) {
1822
- style = { width: this.m_size, height: this.m_size };
1823
- }
1824
-
1825
- this.setStyle(style);
1826
- }
1827
- }
1828
-
1829
- /**
1830
- * sizable separator
1831
- */
1832
-
1833
- type SizeMode = null | 'minimize' | 'maximize' | 'restore';
1834
-
1835
- export interface EvSize extends BasicEvent {
1836
- readonly size: Size;
1837
- readonly mode: SizeMode;
1838
- }
1839
-
1840
- export function EvSize(size: Size, mode: SizeMode = null, context: any = null ): EvSize {
1841
- return BasicEvent<EvSize>({ size, mode, context });
1842
- }
1843
-
1844
- interface SeparatorEventMap extends CEventMap {
1845
- resize?: EvSize;
1846
- }
1847
-
1848
- interface SeparatorProps extends CProps<SeparatorEventMap> {
1849
- readonly orientation: 'vertical' | 'horizontal'; // vertical means vertical sizer so it resize horizontally
1850
- readonly sizing: 'before' | 'after';
1851
- readonly collapsible?: boolean;
1852
- }
1853
-
1854
- export class Separator extends Component<SeparatorProps, SeparatorEventMap> {
1855
-
1856
- m_irect: Rect;
1857
- m_delta: number;
1858
- m_target: Component;
1859
-
1860
- constructor(props: SeparatorProps) {
1861
- super(props);
1862
-
1863
- this.setDomEvent('mousedown', (e) => this._mousedown(e));
1864
- this.setDomEvent('touchstart', (e) => this._mousedown(e));
1865
- this.setDomEvent('dblclick', (e) => this._collapse(e) );
1866
- }
1867
-
1868
- render() {
1869
- this.addClass(this.m_props.orientation);
1870
- }
1871
-
1872
- private _collapse( ev: UIEvent ) {
1873
- if( this.m_props.collapsible ) {
1874
- this._findTarget();
1875
- if( this.m_target ) {
1876
- this.m_target.toggleClass( '@collapsed' );
1877
- }
1878
- }
1879
- }
1880
-
1881
- private _mousedown(ev: UIEvent) {
1882
-
1883
- if (ev.type == 'touchstart') {
1884
- let te = ev as TouchEvent;
1885
- if (te.touches.length == 1) {
1886
- this._startMoving(te.touches[0].pageX, te.touches[0].pageY, ev);
1887
- }
1888
- }
1889
- else {
1890
- let me = ev as MouseEvent;
1891
- this._startMoving(me.pageX, me.pageY, ev);
1892
- }
1893
- }
1894
-
1895
- _startMoving(x: number, y: number, ev: UIEvent) {
1896
- //if( this.m_props.callback ) {
1897
- // this.m_props.callback( ev, this );
1898
- //}
1899
- //else
1900
- {
1901
- this._findTarget();
1902
-
1903
- if (this.m_target) {
1904
-
1905
- if (this.m_props.orientation == 'horizontal') {
1906
- if (this.m_props.sizing == 'before') {
1907
- this.m_delta = x - this.m_irect.right;
1908
- }
1909
- else {
1910
- this.m_delta = x - this.m_irect.left;
1911
- }
1912
- }
1913
- else {
1914
- if (this.m_props.sizing == 'before') {
1915
- this.m_delta = y - this.m_irect.bottom;
1916
- }
1917
- else {
1918
- this.m_delta = y - this.m_irect.top;
1919
- }
1920
- }
1921
-
1922
- ev.preventDefault();
1923
- ev.stopPropagation();
1924
-
1925
- this.m_target.addClass('sizing');
1926
-
1927
- Component.setCapture(this, (e) => this._pointerMoved(e));
1928
- }
1929
- }
1930
- }
1931
-
1932
- private _pointerMoved(ev: UIEvent) {
1933
-
1934
- let __move = (ex: number, ey: number) => {
1935
-
1936
- if (this.m_props.orientation == 'horizontal') {
1937
-
1938
- let width;
1939
- if (this.m_props.sizing == 'after') {
1940
- width = this.m_irect.right - (ex - this.m_delta);
1941
- }
1942
- else {
1943
- width = (ex - this.m_delta) - this.m_irect.left
1944
- }
1945
-
1946
- if (width > 0) {
1947
- let size = new Size(width, 0);
1948
- this.emit('resize', EvSize(size));
1949
-
1950
- this.m_target.setStyleValue('width', size.width);
1951
- this.m_target.setStyleValue('flex', null); // for flex>1
1952
- this.m_target.removeClass('@flex');
1953
- }
1954
- }
1955
- else {
1956
-
1957
- let height;
1958
- if (this.m_props.sizing == 'after') {
1959
- height = this.m_irect.bottom - (ey - this.m_delta);
1960
- }
1961
- else {
1962
- height = (ey - this.m_delta) - this.m_irect.top;
1963
- }
1964
-
1965
- if (height > 0) {
1966
- let size = new Size(0, height);
1967
- this.emit('resize', EvSize(size));
1968
-
1969
- this.m_target.setStyleValue('height', size.height);
1970
- this.m_target.setStyleValue('flex', null); // for flex>1
1971
- this.m_target.removeClass('@flex');
1972
- }
1973
- }
1974
- }
1975
-
1976
- if (ev.type == 'mousemove') {
1977
-
1978
- let mev = ev as MouseEvent;
1979
- __move(mev.pageX, mev.pageY);
1980
-
1981
- ev.preventDefault();
1982
- ev.stopPropagation();
1983
- }
1984
- else if (ev.type == 'touchmove') {
1985
-
1986
- let tev = ev as TouchEvent;
1987
- __move(tev.touches[0].pageX, tev.touches[0].pageY);
1988
-
1989
- ev.preventDefault();
1990
- ev.stopPropagation();
1991
- }
1992
- else if (ev.type == 'mouseup' || ev.type == 'touchend') {
1993
- this.m_target.removeClass('sizing');
1994
-
1995
- Component.releaseCapture();
1996
- ev.preventDefault();
1997
- ev.stopPropagation();
1998
- }
1999
- }
2000
-
2001
- private _findTarget() {
2002
-
2003
- if (!this.m_target) {
2004
-
2005
- if (this.m_props.sizing == 'before') {
2006
- let prevDom = this.dom.previousElementSibling;
2007
- let prevEl = prevDom ? Component.getElement(prevDom as HTMLElement) : null;
2008
- this.m_target = prevEl;
2009
- }
2010
- else {
2011
- let nextDom = this.dom.nextElementSibling;
2012
- let nextEl = nextDom ? Component.getElement(nextDom as HTMLElement) : null;
2013
- this.m_target = nextEl;
2014
- }
2015
- }
2016
-
2017
- if (this.m_target) {
2018
- this.m_irect = this.m_target.getBoundingRect();
2019
- }
2020
- else {
2021
- this.m_irect = null;
2022
- }
2023
- }
2024
- }
2025
-
2026
-
2027
-
2028
-
2029
- // :: SIZERBAR ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
2030
-
2031
- /**
2032
- * properties
2033
- */
2034
-
2035
- type SizerOverlaySens = 'left' | 'top' | 'right' | 'bottom' | 'topleft' | 'topright' | 'bottomleft' | 'bottomright';
2036
-
2037
- export interface EvOverlayResize extends BasicEvent {
2038
- ui_event: UIEvent;
2039
- sens: SizerOverlaySens;
2040
- }
2041
-
2042
- export function EvOverlayResize(ui_event: UIEvent, sens: SizerOverlaySens, context: any = null) {
2043
- return BasicEvent<EvOverlayResize>({ ui_event, sens, context });
2044
- }
2045
-
2046
- interface SizerOverlayEventMap extends CEventMap {
2047
- resize: EvSize;
2048
- rawresize: EvOverlayResize;
2049
- }
2050
-
2051
-
2052
- export interface SizerOverlayProps extends CProps<SizerOverlayEventMap> {
2053
- sens: SizerOverlaySens;
2054
- target: Component;
2055
-
2056
- resize?: EventCallback<EvSize>; // shortcut to events: { size: ... }
2057
- }
2058
-
2059
- export class SizerOverlay extends Component<SizerOverlayProps, SizerOverlayEventMap> {
2060
-
2061
- private m_delta: number;
2062
- private m_irect: Rect;
2063
-
2064
- constructor(props: SizerOverlayProps) {
2065
- super(props);
2066
-
2067
- this.addClass(props.sens);
2068
- this.setDomEvent('mousedown', (e) => this._mousedown(e));
2069
- this.setDomEvent('touchstart', (e) => this._mousedown(e));
2070
- this.setDomEvent('dblclick', (e) => this.resetflex(e)); // todo: add option for that
2071
-
2072
- props.target.appendChild(this);
2073
-
2074
- if( props.resize ) {
2075
- this.on( 'resize', this.m_props.resize );
2076
- }
2077
- }
2078
-
2079
- resetflex(event: UIEvent) {
2080
- this.m_props.target.addClass('@flex');
2081
- this.emit('resize', EvSize( { width: -1, height: 0})); // todo: see that
2082
- event.preventDefault();
2083
- event.stopPropagation();
2084
- }
2085
-
2086
- // @review move that in component
2087
-
2088
- _mousedown(ev: UIEvent) {
2089
-
2090
- ev.preventDefault();
2091
- ev.stopPropagation();
2092
-
2093
- let eev = EvOverlayResize(ev, this.m_props.sens);
2094
- this.emit('rawresize', eev);
2095
- if (eev.defaultPrevented) {
2096
- return;
2097
- }
2098
-
2099
- let pos = getMousePos(ev, true);
2100
- this.m_irect = this.m_props.target.getBoundingRect();
2101
-
2102
- if (this.m_props.sens == 'right') {
2103
- this.m_delta = pos.x - this.m_irect.right;
2104
- }
2105
- else if (this.m_props.sens == 'left') {
2106
- this.m_delta = pos.x - this.m_irect.left;
2107
- }
2108
- else if (this.m_props.sens == 'bottom') {
2109
- this.m_delta = pos.y - this.m_irect.bottom;
2110
- }
2111
- else if (this.m_props.sens == 'top') {
2112
- this.m_delta = pos.y - this.m_irect.top;
2113
- }
2114
-
2115
- this.m_props.target.addClass('sizing');
2116
- Component.setCapture(this, (e) => this._handle_mouse(e));
2117
- }
2118
-
2119
- private _is_horz() {
2120
- return this.m_props.sens == 'left' || this.m_props.sens == 'right';
2121
- }
2122
-
2123
- public get sens() {
2124
- return this.m_props.sens;
2125
- }
2126
-
2127
- private _handle_mouse(ev: UIEvent) {
2128
- let __move = (ex:number, ey:number) => {
2129
- if (this._is_horz()) {
2130
-
2131
- let width;
2132
- if (this.m_props.sens == 'left') {
2133
- width = this.m_irect.right - (ex - this.m_delta);
2134
- }
2135
- else {
2136
- width = (ex - this.m_delta) - this.m_irect.left
2137
- }
2138
-
2139
- if (width > 0) {
2140
- let size = {
2141
- width,
2142
- height: undefined
2143
- };
2144
-
2145
- this.emit('resize', EvSize(size));
2146
-
2147
- this.m_props.target.setStyleValue('width', size.width);
2148
- this.m_props.target.setStyleValue('flex', null); // for flex>1
2149
- this.m_props.target.removeClass('@flex');
2150
- }
2151
- }
2152
- else {
2153
-
2154
- let height;
2155
- if (this.m_props.sens == 'top') {
2156
- height = this.m_irect.bottom - (ey - this.m_delta);
2157
- }
2158
- else {
2159
- height = (ey - this.m_delta) - this.m_irect.top;
2160
- }
2161
-
2162
- if (height > 0) {
2163
- let size = new Size(0, height);
2164
- this.emit('resize', EvSize(size));
2165
-
2166
- this.m_props.target.setStyleValue('height', size.height);
2167
- this.m_props.target.setStyleValue('flex', null); // for flex>1
2168
- this.m_props.target.removeClass('@flex');
2169
- }
2170
- }
2171
- }
2172
-
2173
- if (ev.type == 'mousemove') {
2174
-
2175
- let mev = ev as MouseEvent;
2176
- __move(mev.pageX, mev.pageY);
2177
-
2178
- ev.preventDefault();
2179
- ev.stopPropagation();
2180
- }
2181
- else if (ev.type == 'touchmove') {
2182
-
2183
- let tev = ev as TouchEvent;
2184
- __move(tev.touches[0].pageX, tev.touches[0].pageY);
2185
-
2186
- ev.preventDefault();
2187
- ev.stopPropagation();
2188
- }
2189
- else if (ev.type == 'mouseup' || ev.type == 'touchend') {
2190
- this.m_props.target.removeClass('sizing');
2191
-
2192
- Component.releaseCapture();
2193
- ev.preventDefault();
2194
- ev.stopPropagation();
2195
- }
2196
- }
2197
- }
2198
-
2199
- /**
2200
- * sequence: Shift+Ctrl+Alt+A
2201
- */
2202
-
2203
- export interface Shortcut {
2204
- sequence: string;
2205
- name: string;
2206
- immediate: boolean;
2207
- callback?: EventHandler<KeyboardEvent>;
2208
- }
2209
-
2210
- interface EvShortcut extends BasicEvent {
2211
- name: string;
2212
- }
2213
-
2214
- function EvShortcut(name: string) {
2215
- return BasicEvent<EvShortcut>({ name });
2216
- }
2217
-
2218
- export interface ContainerEventMap extends CEventMap {
2219
- shortcut: EvShortcut;
2220
- }
2221
-
2222
- export interface ContainerProps<E extends ContainerEventMap = ContainerEventMap> extends CProps<E> {
2223
- }
2224
-
2225
- /**
2226
- * you can construct a Container as usual with it's properties but also directly with it's children array
2227
- *
2228
- * @example
2229
- * new Container( [
2230
- * child1,
2231
- * child2
2232
- * ])
2233
- */
2234
-
2235
- export class Container<P extends ContainerProps = ContainerProps, E extends ContainerEventMap = ContainerEventMap> extends Component<P, E> {
2236
-
2237
- private m_shortcuts: Shortcut[];
2238
-
2239
- constructor( props: P );
2240
- constructor( items: ComponentOrString[], cls?: string );
2241
- constructor( props: P | ComponentOrString[], cls?: string ) {
2242
- if( isArray(props) ) {
2243
- super( {content: props,cls} as P );
2244
- }
2245
- else {
2246
- super( props );
2247
- }
2248
- }
2249
-
2250
- /**
2251
- * add an application shortcut
2252
- * @param sequence key sequence Shift+Ctrl+Alt+K
2253
- * @param callback callback to call
2254
- */
2255
-
2256
- public addShortcut(sequence: string | string[], name: string, callback: EventHandler<KeyboardEvent> = null, immediate = false) {
2257
-
2258
- // first time
2259
- if (!this.m_shortcuts) {
2260
- this.m_shortcuts = [];
2261
- this.setDomEvent('keydown', (e) => this._handleKeydown(e));
2262
- }
2263
-
2264
- if (!isArray(sequence)) {
2265
- sequence = [sequence];
2266
- }
2267
-
2268
- sequence.forEach((seq: string) => {
2269
- let reseq = '';
2270
-
2271
- let shift = seq.match(/SHIFT/i);
2272
- if (shift) {
2273
- seq = seq.replace(/SHIFT/i, '');
2274
- reseq += 'shift+';
2275
- }
2276
-
2277
- let ctrl = seq.match(/CTRL/i);
2278
- if (ctrl) {
2279
- seq = seq.replace(/CTRL/i, '');
2280
- reseq += 'ctrl+';
2281
- }
2282
-
2283
- let cmd = seq.match(/CMD/i);
2284
- if (cmd) {
2285
- seq = seq.replace(/CMD/i, '');
2286
- reseq += 'cmd+';
2287
- }
2288
-
2289
- let alt = seq.match(/ALT/i);
2290
- if (alt) {
2291
- seq = seq.replace(/ALT/i, '');
2292
- reseq += 'alt+';
2293
- }
2294
-
2295
- reseq += seq.replace('+', '').toLowerCase();
2296
-
2297
- this.m_shortcuts.push({
2298
- sequence: reseq,
2299
- name,
2300
- immediate,
2301
- callback
2302
- });
2303
- });
2304
- }
2305
-
2306
- /**
2307
- * remove all shortcuts for a target
2308
- */
2309
-
2310
- removeShortcuts() {
2311
- if (this.m_shortcuts) {
2312
- this.m_shortcuts = [];
2313
- }
2314
- }
2315
-
2316
- /** @ignore this function is binded */
2317
- private _handleKeydown(e: KeyboardEvent) {
2318
-
2319
- if (!this.m_shortcuts) {
2320
- return;
2321
- }
2322
-
2323
- let seq = '';
2324
-
2325
- if (e.shiftKey) {
2326
- seq += 'shift+';
2327
- }
2328
-
2329
- if (e.ctrlKey) {
2330
- seq += 'ctrl+';
2331
- }
2332
-
2333
- if (e.metaKey) {
2334
- seq += 'cmd+';
2335
- }
2336
-
2337
- if (e.altKey) {
2338
- seq += 'alt+';
2339
- }
2340
-
2341
- seq += e.key.toLowerCase();
2342
- //console.log( seq );
2343
-
2344
- this.m_shortcuts.some((sk) => {
2345
- if (sk.sequence == seq) {
2346
-
2347
- if (sk.callback) {
2348
- if (sk.immediate) {
2349
- sk.callback(e);
2350
- }
2351
- else {
2352
- asap(() => { sk.callback(e); });
2353
- }
2354
- }
2355
- else {
2356
- this.emit('shortcut', EvShortcut(sk.name));
2357
- }
2358
-
2359
- e.preventDefault();
2360
- e.stopPropagation();
2361
- return true;
2362
- }
2363
- });
2364
- }
2365
- }
2366
-
2367
- export type ComponentConstructor<T> = new (props: CProps) => T;