tosijs-ui 1.0.0

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 (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/ab-test.d.ts +14 -0
  4. package/dist/ab-test.js +116 -0
  5. package/dist/babylon-3d.d.ts +53 -0
  6. package/dist/babylon-3d.js +292 -0
  7. package/dist/bodymovin-player.d.ts +32 -0
  8. package/dist/bodymovin-player.js +172 -0
  9. package/dist/bp-loader.d.ts +1 -0
  10. package/dist/bp-loader.js +26 -0
  11. package/dist/carousel.d.ts +113 -0
  12. package/dist/carousel.js +308 -0
  13. package/dist/code-editor.d.ts +27 -0
  14. package/dist/code-editor.js +102 -0
  15. package/dist/color-input.d.ts +41 -0
  16. package/dist/color-input.js +112 -0
  17. package/dist/data-table.d.ts +79 -0
  18. package/dist/data-table.js +774 -0
  19. package/dist/drag-and-drop.d.ts +2 -0
  20. package/dist/drag-and-drop.js +386 -0
  21. package/dist/editable-rect.d.ts +97 -0
  22. package/dist/editable-rect.js +450 -0
  23. package/dist/filter-builder.d.ts +64 -0
  24. package/dist/filter-builder.js +468 -0
  25. package/dist/float.d.ts +18 -0
  26. package/dist/float.js +170 -0
  27. package/dist/form.d.ts +68 -0
  28. package/dist/form.js +466 -0
  29. package/dist/gamepad.d.ts +34 -0
  30. package/dist/gamepad.js +115 -0
  31. package/dist/icon-data.d.ts +312 -0
  32. package/dist/icon-data.js +308 -0
  33. package/dist/icon-types.d.ts +7 -0
  34. package/dist/icon-types.js +1 -0
  35. package/dist/icons.d.ts +17 -0
  36. package/dist/icons.js +374 -0
  37. package/dist/iife.js +69 -0
  38. package/dist/iife.js.map +49 -0
  39. package/dist/index-iife.d.ts +1 -0
  40. package/dist/index-iife.js +4 -0
  41. package/dist/index.d.ts +37 -0
  42. package/dist/index.js +37 -0
  43. package/dist/index.js.map +47 -0
  44. package/dist/live-example.d.ts +63 -0
  45. package/dist/live-example.js +611 -0
  46. package/dist/localize.d.ts +46 -0
  47. package/dist/localize.js +381 -0
  48. package/dist/make-sorter.d.ts +3 -0
  49. package/dist/make-sorter.js +119 -0
  50. package/dist/make-sorter.test.d.ts +1 -0
  51. package/dist/make-sorter.test.js +48 -0
  52. package/dist/mapbox.d.ts +24 -0
  53. package/dist/mapbox.js +161 -0
  54. package/dist/markdown-viewer.d.ts +17 -0
  55. package/dist/markdown-viewer.js +173 -0
  56. package/dist/match-shortcut.d.ts +9 -0
  57. package/dist/match-shortcut.js +13 -0
  58. package/dist/match-shortcut.test.d.ts +1 -0
  59. package/dist/match-shortcut.test.js +194 -0
  60. package/dist/menu.d.ts +60 -0
  61. package/dist/menu.js +614 -0
  62. package/dist/notifications.d.ts +106 -0
  63. package/dist/notifications.js +308 -0
  64. package/dist/password-strength.d.ts +35 -0
  65. package/dist/password-strength.js +302 -0
  66. package/dist/playwright.config.d.ts +9 -0
  67. package/dist/playwright.config.js +73 -0
  68. package/dist/pop-float.d.ts +10 -0
  69. package/dist/pop-float.js +231 -0
  70. package/dist/rating.d.ts +62 -0
  71. package/dist/rating.js +192 -0
  72. package/dist/rich-text.d.ts +35 -0
  73. package/dist/rich-text.js +296 -0
  74. package/dist/segmented.d.ts +80 -0
  75. package/dist/segmented.js +298 -0
  76. package/dist/select.d.ts +43 -0
  77. package/dist/select.js +427 -0
  78. package/dist/side-nav.d.ts +36 -0
  79. package/dist/side-nav.js +106 -0
  80. package/dist/size-break.d.ts +18 -0
  81. package/dist/size-break.js +118 -0
  82. package/dist/sizer.d.ts +34 -0
  83. package/dist/sizer.js +92 -0
  84. package/dist/src/ab-test.d.ts +14 -0
  85. package/dist/src/babylon-3d.d.ts +53 -0
  86. package/dist/src/bodymovin-player.d.ts +32 -0
  87. package/dist/src/bp-loader.d.ts +0 -0
  88. package/dist/src/carousel.d.ts +113 -0
  89. package/dist/src/code-editor.d.ts +27 -0
  90. package/dist/src/color-input.d.ts +41 -0
  91. package/dist/src/data-table.d.ts +79 -0
  92. package/dist/src/drag-and-drop.d.ts +2 -0
  93. package/dist/src/editable-rect.d.ts +97 -0
  94. package/dist/src/filter-builder.d.ts +64 -0
  95. package/dist/src/float.d.ts +18 -0
  96. package/dist/src/form.d.ts +68 -0
  97. package/dist/src/gamepad.d.ts +34 -0
  98. package/dist/src/icon-data.d.ts +309 -0
  99. package/dist/src/icon-types.d.ts +7 -0
  100. package/dist/src/icons.d.ts +17 -0
  101. package/dist/src/index.d.ts +37 -0
  102. package/dist/src/live-example.d.ts +51 -0
  103. package/dist/src/localize.d.ts +30 -0
  104. package/dist/src/make-sorter.d.ts +3 -0
  105. package/dist/src/mapbox.d.ts +24 -0
  106. package/dist/src/markdown-viewer.d.ts +15 -0
  107. package/dist/src/match-shortcut.d.ts +9 -0
  108. package/dist/src/menu.d.ts +60 -0
  109. package/dist/src/notifications.d.ts +106 -0
  110. package/dist/src/password-strength.d.ts +35 -0
  111. package/dist/src/pop-float.d.ts +10 -0
  112. package/dist/src/rating.d.ts +62 -0
  113. package/dist/src/rich-text.d.ts +28 -0
  114. package/dist/src/segmented.d.ts +80 -0
  115. package/dist/src/select.d.ts +43 -0
  116. package/dist/src/side-nav.d.ts +36 -0
  117. package/dist/src/size-break.d.ts +18 -0
  118. package/dist/src/sizer.d.ts +34 -0
  119. package/dist/src/tab-selector.d.ts +91 -0
  120. package/dist/src/tag-list.d.ts +37 -0
  121. package/dist/src/track-drag.d.ts +5 -0
  122. package/dist/src/version.d.ts +1 -0
  123. package/dist/src/via-tag.d.ts +2 -0
  124. package/dist/tab-selector.d.ts +91 -0
  125. package/dist/tab-selector.js +326 -0
  126. package/dist/tag-list.d.ts +37 -0
  127. package/dist/tag-list.js +375 -0
  128. package/dist/track-drag.d.ts +5 -0
  129. package/dist/track-drag.js +143 -0
  130. package/dist/version.d.ts +1 -0
  131. package/dist/version.js +1 -0
  132. package/dist/via-tag.d.ts +2 -0
  133. package/dist/via-tag.js +102 -0
  134. package/package.json +58 -0
@@ -0,0 +1,375 @@
1
+ /*#
2
+ # tag-list
3
+
4
+ Building a tag-list from standard HTML elements is a bit of a nightmare.
5
+
6
+ `<xin-tag-list>` allows you to display an editable or read-only tag list (represented either
7
+ as a comma-delimited string or an array of strings).
8
+
9
+ ```html
10
+ <label style="position: absolute; right: 10px; top: 10px; display: block">
11
+ <input type="checkbox" class="disable-toggle">
12
+ <b>Disable All</b>
13
+ </label>
14
+ <label>
15
+ <b>Display Only</b>
16
+ <xin-tag-list
17
+ value="this,that,,the-other"
18
+ ></xin-tag-list>
19
+ </label>
20
+ <xin-tag-list
21
+ class="compact"
22
+ value="this,that,,the-other"
23
+ ></xin-tag-list>
24
+ <br>
25
+ <label>
26
+ <b>Editable</b>
27
+ <xin-tag-list
28
+ class="editable-tag-list"
29
+ value="belongs,also belongs,custom"
30
+ editable
31
+ available-tags="belongs,also belongs,not initially chosen"
32
+ ></xin-tag-list>
33
+ </label>
34
+ <br>
35
+ <b>Text-Entry</b>
36
+ <xin-tag-list
37
+ value="this,that,the-other,not,enough,space"
38
+ editable
39
+ text-entry
40
+ available-tags="tomasina,dick,,harriet"
41
+ ></xin-tag-list>
42
+ ```
43
+ ```css
44
+ .preview .compact {
45
+ --spacing: 8px;
46
+ --font-size: 12px;
47
+ --line-height: 18px;
48
+ }
49
+ .preview label {
50
+ display: flex;
51
+ flex-direction: column;
52
+ align-items: flex-start;
53
+ }
54
+ ```
55
+ ```js
56
+ preview.addEventListener('change', (event) => {
57
+ if (event.target.matches('xin-tag-list')) {
58
+ console.log(event.target, event.target.value)
59
+ }
60
+ }, true)
61
+ preview.querySelector('.disable-toggle').addEventListener('change', (event) => {
62
+ const tagLists = Array.from(preview.querySelectorAll('xin-tag-list'))
63
+ for(const tagList of tagLists) {
64
+ tagList.disabled = event.target.checked
65
+ }
66
+ })
67
+ ```
68
+
69
+ ## Properties
70
+
71
+ ### `value`: string | string[]
72
+
73
+ A list of tags
74
+
75
+ ### `tags`: string[]
76
+
77
+ ## `popSelectMenu`: () => void
78
+
79
+ This is the method called when the user clicks the menu button. By default is displays a
80
+ pick list of tags, but if you wish to customize the behavior, just replace this method.
81
+
82
+ A read-only property giving the value as an array.
83
+
84
+ ### `available-tags`: string | string[]
85
+
86
+ A list of tags that will be displayed in the popup menu by default. The popup menu
87
+ will always display custom tags (allowing their removal).
88
+
89
+ ### `editable`: boolean
90
+
91
+ Allows the tag list to be modified via menu and removing tags.
92
+
93
+ ### `text-entry`: boolean
94
+
95
+ If `editable`, an input field is provided for entering tags directly.
96
+
97
+ ### `placeholder`: string = 'enter tags'
98
+
99
+ Placeholder shown on input field.
100
+ */
101
+ import { Component as WebComponent, elements, vars, varDefault, } from 'xinjs';
102
+ import { popMenu } from './menu';
103
+ import { icons } from './icons';
104
+ const { div, input, span, button } = elements;
105
+ export class XinTag extends WebComponent {
106
+ caption = '';
107
+ removeable = false;
108
+ removeCallback = () => {
109
+ this.remove();
110
+ };
111
+ content = () => [
112
+ span({ part: 'caption' }, this.caption),
113
+ button(icons.x(), {
114
+ part: 'remove',
115
+ hidden: !this.removeable,
116
+ onClick: this.removeCallback,
117
+ }),
118
+ ];
119
+ constructor() {
120
+ super();
121
+ this.initAttributes('caption', 'removeable');
122
+ }
123
+ }
124
+ export const xinTag = XinTag.elementCreator({
125
+ tag: 'xin-tag',
126
+ styleSpec: {
127
+ ':host': {
128
+ '--tag-close-button-color': '#000c',
129
+ '--tag-close-button-bg': '#fffc',
130
+ '--tag-button-opacity': '0.5',
131
+ '--tag-button-hover-opacity': '0.75',
132
+ '--tag-bg': varDefault.brandColor('blue'),
133
+ '--tag-text-color': varDefault.brandTextColor('white'),
134
+ display: 'inline-flex',
135
+ borderRadius: varDefault.tagRoundedRadius(vars.spacing50),
136
+ color: vars.tagTextColor,
137
+ background: vars.tagBg,
138
+ padding: `0 ${vars.spacing75} 0 ${vars.spacing75}`,
139
+ height: `calc(${vars.lineHeight} + ${vars.spacing50})`,
140
+ lineHeight: `calc(${vars.lineHeight} + ${vars.spacing50})`,
141
+ },
142
+ ':host > [part="caption"]': {
143
+ position: 'relative',
144
+ whiteSpace: 'nowrap',
145
+ overflow: 'hidden',
146
+ flex: '1 1 auto',
147
+ fontSize: varDefault.fontSize('16px'),
148
+ color: vars.tagTextColor,
149
+ textOverflow: 'ellipsis',
150
+ },
151
+ ':host [part="remove"]': {
152
+ boxShadow: 'none',
153
+ margin: `0 ${vars.spacing_50} 0 ${vars.spacing25}`,
154
+ padding: 0,
155
+ display: 'inline-flex',
156
+ alignItems: 'center',
157
+ alignSelf: 'center',
158
+ justifyContent: 'center',
159
+ height: vars.spacing150,
160
+ width: vars.spacing150,
161
+ '--text-color': vars.tagCloseButtonColor,
162
+ background: vars.tagCloseButtonBg,
163
+ borderRadius: varDefault.tagCloseButtonRadius('99px'),
164
+ opacity: vars.tagButtonOpacity,
165
+ },
166
+ ':host [part="remove"]:hover': {
167
+ background: vars.tagCloseButtonBg,
168
+ opacity: vars.tagButtonHoverOpacity,
169
+ },
170
+ },
171
+ });
172
+ export class XinTagList extends WebComponent {
173
+ disabled = false;
174
+ name = '';
175
+ availableTags = [];
176
+ value = [];
177
+ textEntry = false;
178
+ editable = false;
179
+ placeholder = 'enter tags';
180
+ get tags() {
181
+ return typeof this.value === 'string'
182
+ ? this.value
183
+ .split(',')
184
+ .map((tag) => tag.trim())
185
+ .filter((tag) => tag !== '')
186
+ : this.value;
187
+ }
188
+ constructor() {
189
+ super();
190
+ this.initAttributes('name', 'value', 'textEntry', 'availableTags', 'editable', 'placeholder', 'disabled');
191
+ }
192
+ addTag = (tag) => {
193
+ if (tag.trim() === '') {
194
+ return;
195
+ }
196
+ const { tags } = this;
197
+ if (!tags.includes(tag)) {
198
+ tags.push(tag);
199
+ }
200
+ this.value = tags;
201
+ this.queueRender(true);
202
+ };
203
+ toggleTag = (toggled) => {
204
+ if (this.tags.includes(toggled)) {
205
+ this.value = this.tags.filter((tag) => tag !== toggled);
206
+ }
207
+ else {
208
+ this.addTag(toggled);
209
+ }
210
+ this.queueRender(true);
211
+ };
212
+ enterTag = (event) => {
213
+ const { tagInput } = this.parts;
214
+ switch (event.key) {
215
+ case ',':
216
+ {
217
+ const tag = tagInput.value.split(',')[0];
218
+ this.addTag(tag);
219
+ }
220
+ break;
221
+ case 'Enter':
222
+ {
223
+ const tag = tagInput.value.split(',')[0];
224
+ this.addTag(tag);
225
+ }
226
+ event.stopPropagation();
227
+ event.preventDefault();
228
+ break;
229
+ default:
230
+ // do nothing
231
+ }
232
+ };
233
+ popSelectMenu = () => {
234
+ const { toggleTag } = this;
235
+ const { tagMenu } = this.parts;
236
+ const tags = typeof this.availableTags === 'string'
237
+ ? this.availableTags.split(',')
238
+ : this.availableTags;
239
+ const extraTags = this.tags.filter((tag) => !tags.includes(tag));
240
+ if (extraTags.length) {
241
+ tags.push(null, ...extraTags);
242
+ }
243
+ const menuItems = tags.map((tag) => {
244
+ if (tag === '' || tag === null) {
245
+ return null;
246
+ }
247
+ else if (typeof tag === 'object') {
248
+ return {
249
+ checked: () => this.tags.includes(tag.value),
250
+ caption: tag.caption,
251
+ action() {
252
+ toggleTag(tag.value);
253
+ },
254
+ };
255
+ }
256
+ else {
257
+ return {
258
+ checked: () => this.tags.includes(tag),
259
+ caption: tag,
260
+ action() {
261
+ toggleTag(tag);
262
+ },
263
+ };
264
+ }
265
+ });
266
+ popMenu({
267
+ target: tagMenu,
268
+ width: 'auto',
269
+ menuItems,
270
+ });
271
+ };
272
+ content = () => [
273
+ // this button is simply here to eat click events sent via a label
274
+ button({ style: { visibility: 'hidden' }, tabindex: -1 }),
275
+ div({
276
+ part: 'tagContainer',
277
+ class: 'row',
278
+ }),
279
+ input({
280
+ part: 'tagInput',
281
+ class: 'elastic',
282
+ onKeydown: this.enterTag,
283
+ }),
284
+ button({
285
+ title: 'add tag',
286
+ part: 'tagMenu',
287
+ onClick: this.popSelectMenu,
288
+ }, icons.chevronDown()),
289
+ ];
290
+ removeTag = (event) => {
291
+ if (this.editable && !this.disabled) {
292
+ const tag = event.target.closest(XinTag.tagName);
293
+ this.value = this.tags.filter((value) => value !== tag.caption);
294
+ tag.remove();
295
+ this.queueRender(true);
296
+ }
297
+ event.stopPropagation();
298
+ event.preventDefault();
299
+ };
300
+ render() {
301
+ super.render();
302
+ const { tagContainer, tagMenu, tagInput } = this.parts;
303
+ tagMenu.disabled = this.disabled;
304
+ tagInput.value = '';
305
+ tagInput.setAttribute('placeholder', this.placeholder);
306
+ if (this.editable && !this.disabled) {
307
+ tagMenu.toggleAttribute('hidden', false);
308
+ tagInput.toggleAttribute('hidden', !this.textEntry);
309
+ }
310
+ else {
311
+ tagMenu.toggleAttribute('hidden', true);
312
+ tagInput.toggleAttribute('hidden', true);
313
+ }
314
+ tagContainer.textContent = '';
315
+ const { tags } = this;
316
+ for (const tag of tags) {
317
+ tagContainer.append(xinTag({
318
+ caption: tag,
319
+ removeable: this.editable && !this.disabled,
320
+ removeCallback: this.removeTag,
321
+ }));
322
+ }
323
+ }
324
+ }
325
+ export const xinTagList = XinTagList.elementCreator({
326
+ tag: 'xin-tag-list',
327
+ styleSpec: {
328
+ ':host': {
329
+ '--tag-list-bg': '#f8f8f8',
330
+ '--touch-size': '44px',
331
+ '--spacing': '16px',
332
+ display: 'grid',
333
+ gridTemplateColumns: 'auto',
334
+ alignItems: 'center',
335
+ background: vars.tagListBg,
336
+ gap: vars.spacing25,
337
+ borderRadius: varDefault.taglistRoundedRadius(vars.spacing50),
338
+ overflow: 'hidden',
339
+ },
340
+ ':host[editable]': {
341
+ gridTemplateColumns: `0px auto ${vars.touchSize}`,
342
+ },
343
+ ':host[editable][text-entry]': {
344
+ gridTemplateColumns: `0px 2fr 1fr ${vars.touchSize}`,
345
+ },
346
+ ':host [part="tagContainer"]': {
347
+ display: 'flex',
348
+ content: '" "',
349
+ alignItems: 'center',
350
+ background: vars.inputBg,
351
+ borderRadius: varDefault.tagContainerRadius(vars.spacing50),
352
+ boxShadow: vars.borderShadow,
353
+ flexWrap: 'nowrap',
354
+ overflow: 'auto hidden',
355
+ gap: vars.spacing25,
356
+ minHeight: `calc(${vars.lineHeight} + ${vars.spacing})`,
357
+ padding: vars.spacing25,
358
+ },
359
+ ':host [part="tagMenu"]': {
360
+ width: vars.touchSize,
361
+ height: vars.touchSize,
362
+ lineHeight: vars.touchSize,
363
+ textAlign: 'center',
364
+ padding: 0,
365
+ margin: 0,
366
+ },
367
+ ':host [hidden]': {
368
+ display: 'none !important',
369
+ },
370
+ ':host button[part="tagMenu"]': {
371
+ background: vars.brandColor,
372
+ color: vars.brandTextColor,
373
+ },
374
+ },
375
+ });
@@ -0,0 +1,5 @@
1
+ type TrackerCallback = (dx: number, dy: number, event: PointerEvent) => boolean | undefined;
2
+ export declare const trackDrag: (event: PointerEvent, callback: TrackerCallback, cursor?: string) => void;
3
+ export declare const findHighestZ: (selector?: string) => number;
4
+ export declare const bringToFront: (element: HTMLElement, selector?: string) => void;
5
+ export {};
@@ -0,0 +1,143 @@
1
+ import { elements } from 'xinjs';
2
+ /*#
3
+ # trackDrag
4
+
5
+ Sometimes you want to track a mouse-drag or touch-drag operation without messing around.
6
+ This is how the resizeable columns in `<xin-table>` work.
7
+
8
+ Just call `trackDrag(event, (dx, dy, event) => { ... })` and you'll get updates on corresponding events until
9
+ you return `true` from the event-handler (or, in the case of `touch` events, the last `touch` ends).
10
+ For mouse events, a "tracker" element is thrown up in front of everything for the event.
11
+
12
+ ```html
13
+ <p>
14
+ Try dragging the squares…<br>
15
+ (You can drag them separately with multi-touch!)
16
+ </p>
17
+ <div class="draggable" style="top: 20px; left: 40px; background: #f008"></div>
18
+ <div class="draggable" style="left: 40%; bottom: 30%; background: #0f08"></div>
19
+ <div class="draggable" style="bottom: 30px; right: 10px; background: #00f8"></div>
20
+ ```
21
+ ```css
22
+ .preview {
23
+ touch-action: none;
24
+ }
25
+
26
+ .draggable {
27
+ content: ' ';
28
+ position: absolute;
29
+ width: 50px;
30
+ height: 50px;
31
+ cursor: move;
32
+ }
33
+
34
+ .preview p {
35
+ position: absolute;
36
+ left: 50%;
37
+ top: 50%;
38
+ transform: translateX(-50%) translateY(-50%);
39
+ }
40
+ ```
41
+ ```js
42
+ const { trackDrag } = xinjsui
43
+
44
+ function dragItem(event) {
45
+ const draggable = event.target
46
+ if (draggable.classList.contains('draggable')) {
47
+ const x = draggable.offsetLeft
48
+ const y = draggable.offsetTop
49
+ trackDrag(event, (dx, dy, event) => {
50
+ draggable.style.left = (x + dx) + 'px'
51
+ draggable.style.top = (y + dy) + 'px'
52
+ draggable.style.bottom = 'auto'
53
+ draggable.style.right = 'auto'
54
+ return event.type === 'mouseup'
55
+ })
56
+ }
57
+ }
58
+
59
+ preview.addEventListener('mousedown', dragItem )
60
+ preview.addEventListener('touchstart', dragItem, { passive: true } )
61
+ ```
62
+
63
+ For `touch` events, `dx` and `dy` are based on tracking `event.changedTouches[0]` which
64
+ is almost certainly what you want.
65
+
66
+ To handle multi-touch gestures you will need to track the touches yourself.
67
+
68
+ ## bringToFront
69
+
70
+ `bringToFront(element: HTMLElement, selector = 'body *')` gives the element the highest
71
+ `z-index` of any element matching the selector (which is passed to findHighestZ).
72
+
73
+ ## findHighestZ
74
+
75
+ `findHighestZ(selector = 'body *'): number` returns the the highest `z-index` of any element
76
+ matching `selector`.
77
+ */
78
+ const TRACKER = elements.div({
79
+ style: {
80
+ content: ' ',
81
+ position: 'fixed',
82
+ top: 0,
83
+ left: 0,
84
+ right: 0,
85
+ bottom: 0,
86
+ },
87
+ });
88
+ const PASSIVE = { passive: true };
89
+ export const trackDrag = (event, callback, cursor = 'move') => {
90
+ const isTouchEvent = event.type.startsWith('touch');
91
+ if (!isTouchEvent) {
92
+ const origX = event.clientX;
93
+ const origY = event.clientY;
94
+ TRACKER.style.cursor = cursor;
95
+ bringToFront(TRACKER);
96
+ document.body.append(TRACKER);
97
+ const wrappedCallback = (event) => {
98
+ const dx = event.clientX - origX;
99
+ const dy = event.clientY - origY;
100
+ if (callback(dx, dy, event) === true) {
101
+ TRACKER.removeEventListener('mousemove', wrappedCallback);
102
+ TRACKER.removeEventListener('mouseup', wrappedCallback);
103
+ TRACKER.remove();
104
+ }
105
+ };
106
+ TRACKER.addEventListener('mousemove', wrappedCallback, PASSIVE);
107
+ TRACKER.addEventListener('mouseup', wrappedCallback, PASSIVE);
108
+ }
109
+ else if (event instanceof TouchEvent) {
110
+ const touch = event.changedTouches[0];
111
+ const touchId = touch.identifier;
112
+ const origX = touch.clientX;
113
+ const origY = touch.clientY;
114
+ const target = event.target;
115
+ let dx = 0;
116
+ let dy = 0;
117
+ const wrappedCallback = (event) => {
118
+ const touch = [...event.touches].find((touch) => touch.identifier === touchId);
119
+ if (touch !== undefined) {
120
+ dx = touch.clientX - origX;
121
+ dy = touch.clientY - origY;
122
+ }
123
+ if (event.type === 'touchmove') {
124
+ event.stopPropagation();
125
+ event.preventDefault();
126
+ }
127
+ if (callback(dx, dy, event) === true || touch === undefined) {
128
+ target.removeEventListener('touchmove', wrappedCallback);
129
+ target.removeEventListener('touchend', wrappedCallback);
130
+ target.removeEventListener('touchcancel', wrappedCallback);
131
+ }
132
+ };
133
+ target.addEventListener('touchmove', wrappedCallback);
134
+ target.addEventListener('touchend', wrappedCallback, PASSIVE);
135
+ target.addEventListener('touchcancel', wrappedCallback, PASSIVE);
136
+ }
137
+ };
138
+ export const findHighestZ = (selector = 'body *') => [...document.querySelectorAll(selector)]
139
+ .map((elt) => parseFloat(getComputedStyle(elt).zIndex))
140
+ .reduce((z, highest) => (isNaN(z) || Number(z) < highest ? highest : Number(z)), 0);
141
+ export const bringToFront = (element, selector = 'body *') => {
142
+ element.style.zIndex = String(findHighestZ(selector) + 1);
143
+ };
@@ -0,0 +1 @@
1
+ export declare const version = "1.0.0";
@@ -0,0 +1 @@
1
+ export const version = '0.9.15';
@@ -0,0 +1,2 @@
1
+ export declare function scriptTag(src: string, existingSymbolName?: string): Promise<any>;
2
+ export declare function styleSheet(href: string): Promise<void>;
@@ -0,0 +1,102 @@
1
+ /*#
2
+ # scriptTag & styleSheet
3
+
4
+ ## scriptTag
5
+
6
+ If you need to load an old school (cjs) javascript or css library via cdn then use these two functions.
7
+
8
+ `xinjs-ui` uses this library to implement the `<xin-code>`, `<xin-lottie>`, and `<xin-map>`
9
+ elements.
10
+
11
+ `scriptTag()` and `styleSheet()` return promises that resolve `globalThis` when the module in question
12
+ has loaded and otherwise behave as much like `import()` as possible.
13
+
14
+ This example uses `scriptTag` and `styleSheet` to load [quilljs](https://quilljs.com) on-the-fly.
15
+
16
+ ```js
17
+ const { elements } = xinjs
18
+ const { scriptTag, styleSheet } = xinjsui
19
+
20
+ const toolbarOptions = [
21
+ [{ header: [1, 2, 3, 4, false] }],
22
+ ['blockquote', 'code-block'],
23
+ [{ 'align': [] }],
24
+ ['bold', 'italic', 'strike'],
25
+ ['link', 'image', 'video'],
26
+ [{ 'list': 'ordered'}, { 'list': 'bullet' }, { 'list': 'check' }],
27
+ [{ 'indent': '-1'}, { 'indent': '+1' }],
28
+ ['clean']
29
+ ]
30
+
31
+ ;(async () => {
32
+ await Promise.all([
33
+ styleSheet('https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.core.css'),
34
+ styleSheet('https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css'),
35
+ ])
36
+
37
+ const container = elements.div()
38
+ const { Quill } = await scriptTag('https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js')
39
+ preview.append(container)
40
+
41
+ const quill = new Quill(container, {
42
+ debug: 'info',
43
+ modules: {
44
+ toolbar: toolbarOptions,
45
+ },
46
+ theme: 'snow',
47
+ })
48
+ })()
49
+ ```
50
+
51
+ Note that `scriptTag` will resolve `globalThis` so it behaves as much like async `import()`
52
+ as possible.
53
+
54
+ As an aside:
55
+
56
+ `<xin-lottie>` is implemented in such a way that if you've preloaded the module
57
+ (e.g. via a script tag or packaging) it won't load it again, which affords offline
58
+ use.
59
+
60
+ There's no point for `<xin-map>` since it won't work without connectivity anyway.
61
+
62
+ ## styleSheet
63
+
64
+ styleSheet creates a `<link>` tag for a specified css file.
65
+
66
+ Using `styleSheet`:
67
+
68
+ styleSheet('../path/to/style.css')
69
+
70
+ This is awaitable, if you care. The stylesheet `<link>` will only be inserted _once_.
71
+ */
72
+ import { elements } from 'xinjs';
73
+ const loadedScripts = {};
74
+ export function scriptTag(src, existingSymbolName) {
75
+ if (loadedScripts[src] === undefined) {
76
+ if (existingSymbolName !== undefined) {
77
+ const existing = globalThis[existingSymbolName];
78
+ loadedScripts[src] = Promise.resolve({ [existingSymbolName]: existing });
79
+ }
80
+ const scriptElt = elements.script({ src });
81
+ document.head.append(scriptElt);
82
+ loadedScripts[src] = new Promise((resolve) => {
83
+ scriptElt.onload = () => resolve(globalThis);
84
+ });
85
+ }
86
+ return loadedScripts[src];
87
+ }
88
+ const loadedStyleSheets = {};
89
+ export function styleSheet(href) {
90
+ if (loadedStyleSheets[href] === undefined) {
91
+ const linkElement = elements.link({
92
+ rel: 'stylesheet',
93
+ type: 'text/css',
94
+ href,
95
+ });
96
+ document.head.append(linkElement);
97
+ loadedStyleSheets[href] = new Promise((resolve) => {
98
+ linkElement.onload = resolve;
99
+ });
100
+ }
101
+ return loadedStyleSheets[href];
102
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "tosijs-ui",
3
+ "version": "1.0.0",
4
+ "description": "simple robust web-components for use with xinjs or anything else",
5
+ "scripts": {
6
+ "start": "bun --watch bin/dev.ts",
7
+ "format": "bun eslint src demo --fix && bun prettier --write .",
8
+ "tests": "bun test && bun playwright test",
9
+ "latest": "rm -rf node_modules && bun update"
10
+ },
11
+ "keywords": [
12
+ "tosijs",
13
+ "ui",
14
+ "ux",
15
+ "user interface",
16
+ "gui",
17
+ "widgets",
18
+ "web-components",
19
+ "component library"
20
+ ],
21
+ "author": "Tonio Loewald",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/tonioloewald/xinjs-ui.git"
26
+ },
27
+ "devDependencies": {
28
+ "@playwright/test": "^1.54.1",
29
+ "@types/jsdom": "^21.1.7",
30
+ "@types/node": "^22.16.3",
31
+ "@types/react": "^19.1.8",
32
+ "@typescript-eslint/eslint-plugin": "^5.62.0",
33
+ "@typescript-eslint/parser": "^5.62.0",
34
+ "bun-types": "latest",
35
+ "caniuse-lite": "^1.0.30001727",
36
+ "chokidar": "^4.0.3",
37
+ "eslint": "^8.57.1",
38
+ "prettier": "^2.8.8",
39
+ "typescript": "^5.8.3"
40
+ },
41
+ "peerDependencies": {
42
+ "marked": "^16.0.0",
43
+ "tosijs": "^1.0.3"
44
+ },
45
+ "files": [
46
+ "/dist",
47
+ "/LICENSE",
48
+ "/README.md"
49
+ ],
50
+ "source": "src/index.ts",
51
+ "types": "dist/index.d.ts",
52
+ "exports": {
53
+ "import": "./dist/index.js",
54
+ "browser": "./dist/iife.js",
55
+ "default": "./dist/index.js",
56
+ "types": "./dist/index.d.ts"
57
+ }
58
+ }