tosijs-ui 1.0.1 → 1.0.2

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 (93) hide show
  1. package/README.md +4 -2
  2. package/dist/iife.js +70 -60
  3. package/dist/iife.js.map +42 -42
  4. package/dist/index.js +15 -37
  5. package/dist/index.js.map +39 -39
  6. package/dist/version.d.ts +1 -1
  7. package/package.json +2 -2
  8. package/dist/ab-test.js +0 -116
  9. package/dist/babylon-3d.js +0 -292
  10. package/dist/bodymovin-player.js +0 -172
  11. package/dist/bp-loader.js +0 -26
  12. package/dist/carousel.js +0 -308
  13. package/dist/code-editor.js +0 -102
  14. package/dist/color-input.js +0 -112
  15. package/dist/data-table.js +0 -774
  16. package/dist/drag-and-drop.js +0 -386
  17. package/dist/editable-rect.js +0 -450
  18. package/dist/filter-builder.js +0 -468
  19. package/dist/float.js +0 -170
  20. package/dist/form.js +0 -466
  21. package/dist/gamepad.js +0 -115
  22. package/dist/icon-data.js +0 -308
  23. package/dist/icon-types.js +0 -1
  24. package/dist/icons.js +0 -374
  25. package/dist/index-iife.js +0 -4
  26. package/dist/live-example.js +0 -611
  27. package/dist/localize.js +0 -381
  28. package/dist/make-sorter.js +0 -119
  29. package/dist/make-sorter.test.d.ts +0 -1
  30. package/dist/make-sorter.test.js +0 -48
  31. package/dist/mapbox.js +0 -161
  32. package/dist/markdown-viewer.js +0 -173
  33. package/dist/match-shortcut.js +0 -13
  34. package/dist/match-shortcut.test.d.ts +0 -1
  35. package/dist/match-shortcut.test.js +0 -194
  36. package/dist/menu.js +0 -614
  37. package/dist/notifications.js +0 -308
  38. package/dist/password-strength.js +0 -302
  39. package/dist/playwright.config.d.ts +0 -9
  40. package/dist/playwright.config.js +0 -73
  41. package/dist/pop-float.js +0 -231
  42. package/dist/rating.js +0 -192
  43. package/dist/rich-text.js +0 -296
  44. package/dist/segmented.js +0 -298
  45. package/dist/select.js +0 -427
  46. package/dist/side-nav.js +0 -106
  47. package/dist/size-break.js +0 -118
  48. package/dist/sizer.js +0 -92
  49. package/dist/src/ab-test.d.ts +0 -14
  50. package/dist/src/babylon-3d.d.ts +0 -53
  51. package/dist/src/bodymovin-player.d.ts +0 -32
  52. package/dist/src/bp-loader.d.ts +0 -0
  53. package/dist/src/carousel.d.ts +0 -113
  54. package/dist/src/code-editor.d.ts +0 -27
  55. package/dist/src/color-input.d.ts +0 -41
  56. package/dist/src/data-table.d.ts +0 -79
  57. package/dist/src/drag-and-drop.d.ts +0 -2
  58. package/dist/src/editable-rect.d.ts +0 -97
  59. package/dist/src/filter-builder.d.ts +0 -64
  60. package/dist/src/float.d.ts +0 -18
  61. package/dist/src/form.d.ts +0 -68
  62. package/dist/src/gamepad.d.ts +0 -34
  63. package/dist/src/icon-data.d.ts +0 -309
  64. package/dist/src/icon-types.d.ts +0 -7
  65. package/dist/src/icons.d.ts +0 -17
  66. package/dist/src/index.d.ts +0 -37
  67. package/dist/src/live-example.d.ts +0 -51
  68. package/dist/src/localize.d.ts +0 -30
  69. package/dist/src/make-sorter.d.ts +0 -3
  70. package/dist/src/mapbox.d.ts +0 -24
  71. package/dist/src/markdown-viewer.d.ts +0 -15
  72. package/dist/src/match-shortcut.d.ts +0 -9
  73. package/dist/src/menu.d.ts +0 -60
  74. package/dist/src/notifications.d.ts +0 -106
  75. package/dist/src/password-strength.d.ts +0 -35
  76. package/dist/src/pop-float.d.ts +0 -10
  77. package/dist/src/rating.d.ts +0 -62
  78. package/dist/src/rich-text.d.ts +0 -28
  79. package/dist/src/segmented.d.ts +0 -80
  80. package/dist/src/select.d.ts +0 -43
  81. package/dist/src/side-nav.d.ts +0 -36
  82. package/dist/src/size-break.d.ts +0 -18
  83. package/dist/src/sizer.d.ts +0 -34
  84. package/dist/src/tab-selector.d.ts +0 -91
  85. package/dist/src/tag-list.d.ts +0 -37
  86. package/dist/src/track-drag.d.ts +0 -5
  87. package/dist/src/version.d.ts +0 -1
  88. package/dist/src/via-tag.d.ts +0 -2
  89. package/dist/tab-selector.js +0 -326
  90. package/dist/tag-list.js +0 -375
  91. package/dist/track-drag.js +0 -143
  92. package/dist/version.js +0 -1
  93. package/dist/via-tag.js +0 -102
package/dist/menu.js DELETED
@@ -1,614 +0,0 @@
1
- /*#
2
- # menu
3
-
4
- Being able to pop a menu up anywhere is just so nice, and `xinjs-ui` allows menus
5
- to be generated on-the-fly, and even supports hierarchical menus.
6
-
7
- ## popMenu and `<xin-menu>`
8
-
9
- `popMenu({target, menuItems, …})` will spawn a menu from a target.
10
-
11
- The `<xin-menu>` component places creates a trigger button, hosts
12
- menuItems, and (because it persists in the DOM) supports keyboard
13
- shortcuts.
14
-
15
- ```js
16
- const { popMenu, localize, xinMenu, postNotification, xinLocalized, icons } = xinjsui
17
- const { elements } = xinjs
18
-
19
- let picked = ''
20
- let testingEnabled = false
21
-
22
- const menuItems = [
23
- {
24
- icon: 'thumbsUp',
25
- caption: 'Like',
26
- shortcut: '^L',
27
- action() {
28
- postNotification({
29
- message: 'I like it!',
30
- icon: 'thumbsUp',
31
- duration: 1
32
- })
33
- }
34
- },
35
- {
36
- icon: 'heart',
37
- caption: 'Love',
38
- shortcut: '⌘⇧L',
39
- action() {
40
- postNotification({
41
- type: 'success',
42
- message: 'I LOVE it!',
43
- icon: 'heart',
44
- duration: 1
45
- })
46
- }
47
- },
48
- {
49
- icon: 'thumbsDown',
50
- caption: 'dislike',
51
- shortcut: '⌘D',
52
- action() {
53
- postNotification({
54
- type: 'error',
55
- message: 'Awwwwwww…',
56
- icon: 'thumbsDown',
57
- duration: 1
58
- })
59
- }
60
- },
61
- null, // separator
62
- {
63
- caption: localize('Localized placeholder'),
64
- action() {
65
- alert(localize('Localized placeholder'))
66
- }
67
- },
68
- {
69
- icon: elements.span('🥹'),
70
- caption: 'Also see…',
71
- menuItems: [
72
- {
73
- icon: elements.span('😳'),
74
- caption: 'And that’s not all…',
75
- menuItems: [
76
- {
77
- icon: 'externalLink',
78
- caption: 'timezones',
79
- action: 'https://timezones.xinjs.net/'
80
- },
81
- {
82
- icon: 'externalLink',
83
- caption: 'b8rjs',
84
- action: 'https://b8rjs.com'
85
- },
86
- ]
87
- },
88
- {
89
- icon: 'xinjs',
90
- caption: 'xinjs',
91
- action: 'https://xinjs.net'
92
- },
93
- {
94
- icon: 'xinie',
95
- caption: 'xinie',
96
- action: 'https://xinie.net'
97
- },
98
- ]
99
- },
100
- {
101
- icon: testingEnabled ? 'check' : '',
102
- caption: 'Testing Enabled',
103
- action() {
104
- testingEnabled = !testingEnabled
105
- }
106
- },
107
- {
108
- caption: 'Testing…',
109
- enabled() {
110
- return testingEnabled
111
- },
112
- menuItems: [
113
- {
114
- caption: 'one',
115
- checked: () => picked === 'one',
116
- action () {
117
- picked = 'one'
118
- }
119
- },
120
- {
121
- caption: 'two',
122
- checked: () => picked === 'two',
123
- action () {
124
- picked = 'two'
125
- }
126
- },
127
- {
128
- caption: 'three',
129
- checked: () => picked === 'three',
130
- action () {
131
- picked = 'three'
132
- }
133
- }
134
- ]
135
- }
136
- ]
137
-
138
- preview.addEventListener('click', (event) => {
139
- if (!event.target.closest('button')) {
140
- return
141
- }
142
- popMenu({
143
- target: event.target,
144
- menuItems
145
- })
146
- })
147
-
148
- preview.append(
149
- xinMenu(
150
- {
151
- menuItems,
152
- localized: true,
153
- },
154
- xinLocalized('Menu'),
155
- icons.chevronDown()
156
- )
157
- )
158
- ```
159
- ```html
160
- <button title="menu test">
161
- <xin-icon icon="moreVertical"></xin-icon>
162
- </button>
163
- <button title="menu test from bottom-right" style="position: absolute; bottom: 0; right: 0">
164
- <xin-icon icon="moreVertical"></xin-icon>
165
- </button>
166
- ```
167
- ```css
168
- .preview button {
169
- min-width: 44px;
170
- text-align: center;
171
- height: 44px;
172
- margin: 5px;
173
- }
174
- ```
175
-
176
- ## Overflow test
177
-
178
- ```js
179
- const { popMenu, icons, postNotification } = xinjsui
180
- const { elements } = xinjs
181
-
182
- preview.querySelector('button').addEventListener('click', (event) => {
183
- popMenu({
184
- target: event.target,
185
- menuItems: Object.keys(icons).map(icon => ({
186
- icon,
187
- caption: icon,
188
- action() {
189
- postNotification({
190
- icon: icon,
191
- message: icon,
192
- duration: 1
193
- })
194
- }
195
- }))
196
- })
197
- })
198
- ```
199
- ```html
200
- <button title="big menu test" style="position: absolute; top: 0; left: 0">
201
- Big Menu Test
202
- </button>
203
- ```
204
-
205
- ## popMenu({target, width, menuItems…})
206
-
207
- ```
208
- export interface PopMenuOptions {
209
- target: HTMLElement
210
- menuItems: MenuItem[]
211
- width?: string | number
212
- position?: FloatPosition
213
- submenuDepth?: number // don't set this, it's set internally by popMenu
214
- submenuOffset?: { x: number; y: number }
215
- localized?: boolean
216
- }
217
- ```
218
-
219
- `popMenu` will spawn a menu on a target element. A menu is just a `MenuItem[]`.
220
-
221
- ## MenuItem
222
-
223
- A `MenuItem` can be one of three things:
224
-
225
- - `null` denotes a separator
226
- - `MenuAction` denotes a labeled button or `<a>` tag based on whether the `action` provided
227
- is a url (string) or an event handler (function).
228
- - `SubMenu` is a submenu.
229
-
230
- ### MenuAction
231
-
232
- Note that popMenu does not implement shortcuts for you (yet!).
233
-
234
- ```
235
- interface MenuAction {
236
- caption: string
237
- shortcut?: string
238
- checked?: () => boolean
239
- enabled?: () => boolean
240
- action: ActionCallback | string
241
- icon?: string | Element
242
- }
243
- ```
244
-
245
- ### SubMenu
246
-
247
- ```
248
- interface SubMenu {
249
- caption: string
250
- enabled?: () => boolean
251
- menuItems: MenuItem[]
252
- icon?: string | Element
253
- }
254
- ```
255
-
256
- ### Keyboard Shortcuts
257
-
258
- If a menu is embodied in a `<xin-menu>` it is supported by keyboard
259
- shortcuts. Both text and symbolic shortcut descriptions are supported,
260
- e.g.
261
-
262
- - `⌘C` or `meta-C`
263
- - `⇧P` for `shift-P`
264
- - `^F` or `ctrl-f`
265
- - `⌥x`, `⎇x`, `alt-x` or `option-x`
266
-
267
- ## Localization
268
-
269
- If you set `localized: true` in `PopMenuOptions` then menu captions will be be
270
- passed through `localize`. You'll need to provide the appropriate localized strings,
271
- of course.
272
-
273
- > `<xin-menu>` supports the `localized` attribute but it doesn't localize
274
- > its trigger button.
275
-
276
- To see this in action, see the example below, or look at the
277
- [table example](?data-table.ts). It uses a `localized` menu
278
- to render column names when you show hidden columns.
279
-
280
- ```js
281
- const { elements } = xinjs
282
- const { xinLocalized, localize, icons, popMenu, postNotification } = xinjsui
283
- const { button } = elements
284
- const makeItem = s => ({
285
- caption: s,
286
- action() {
287
- postNotification({
288
- message: localize(s),
289
- duration: 1
290
- })
291
- }
292
- })
293
- const target = button(
294
- {
295
- onClick(event) {
296
- popMenu({
297
- target: event.target.closest('button'),
298
- localized: true,
299
- menuItems: [
300
- makeItem('New'),
301
- makeItem('Open...'),
302
- makeItem('Save'),
303
- makeItem('Close'),
304
- ]
305
- })
306
- }
307
- },
308
- xinLocalized(
309
- { style: { marginRight: '5px' }},
310
- 'menu'
311
- ),
312
- icons.chevronDown()
313
- )
314
- preview.append(target)
315
- ```
316
-
317
- ## Why another menu library?!
318
-
319
- Support for menus is sadly lacking in HTML, and unfortunately there's a huge conceptual problem
320
- with menus implemented the way React and React-influenced libraries work, i.e. you need
321
- to have an instance of a menu "wrapped around" the DOM element that triggers it, whereas
322
- a better approach (and one dating back at least as far as the original Mac UI) is to treat
323
- a menu as a separate resource that can be instantiated on demand.
324
-
325
- A simple example where this becomes really obvious is if you want to associate a "more options"
326
- menu with every row of a large table. Either you end up having an enormous DOM (virtual or otherwise)
327
- or you have to painfully swap out components on-the-fly.
328
-
329
- And, finally, submenus are darn useful for any serious app.
330
-
331
- For this reason, `xinjs-ui` has its own menu implementation.
332
- */
333
- import { elements, varDefault, vars, StyleSheet, Component, } from 'xinjs';
334
- import { popFloat } from './pop-float';
335
- import { icons } from './icons';
336
- import { localize } from './localize';
337
- import { matchShortcut } from './match-shortcut';
338
- const { div, button, span, a, xinSlot } = elements;
339
- StyleSheet('xin-menu-helper', {
340
- '.xin-menu': {
341
- overflow: 'hidden auto',
342
- maxHeight: `calc(${vars.maxHeight} - ${varDefault.menuInset('8px')})`,
343
- borderRadius: vars.spacing50,
344
- background: varDefault.menuBg('#fafafa'),
345
- boxShadow: varDefault.menuShadow(`${vars.spacing13} ${vars.spacing50} ${vars.spacing} #0004`),
346
- },
347
- '.xin-menu > div': {
348
- width: varDefault.menuWidth('auto'),
349
- },
350
- '.xin-menu-trigger': {
351
- paddingLeft: 0,
352
- paddingRight: 0,
353
- minWidth: varDefault.touchSize('48px'),
354
- },
355
- '.xin-menu-separator': {
356
- display: 'inline-block',
357
- content: ' ',
358
- height: '1px',
359
- width: '100%',
360
- background: varDefault.menuSeparatorColor('#2224'),
361
- margin: varDefault.menuSeparatorMargin('8px 0'),
362
- },
363
- '.xin-menu-item': {
364
- boxShadow: 'none',
365
- border: 'none !important',
366
- display: 'grid',
367
- alignItems: 'center',
368
- justifyContent: 'flex-start',
369
- textDecoration: 'none',
370
- gridTemplateColumns: '0px 1fr 30px',
371
- width: '100%',
372
- gap: 0,
373
- background: 'transparent',
374
- padding: varDefault.menuItemPadding('0 16px'),
375
- height: varDefault.menuItemHeight('48px'),
376
- lineHeight: varDefault.menuItemHeight('48px'),
377
- textAlign: 'left',
378
- },
379
- '.xin-menu-item, .xin-menu-item > span': {
380
- color: varDefault.menuItemColor('#222'),
381
- },
382
- '.xin-menu-with-icons .xin-menu-item': {
383
- gridTemplateColumns: '30px 1fr 30px',
384
- },
385
- '.xin-menu-item svg': {
386
- stroke: varDefault.menuItemIconColor('#222'),
387
- },
388
- '.xin-menu-item.xin-menu-item-checked': {
389
- background: varDefault.menuItemHoverBg('#eee'),
390
- },
391
- '.xin-menu-item > span:nth-child(2)': {
392
- whiteSpace: 'nowrap',
393
- overflow: 'hidden',
394
- textOverflow: 'ellipsis',
395
- textAlign: 'left',
396
- },
397
- '.xin-menu-item:hover': {
398
- // chrome rendering bug
399
- boxShadow: 'none !important',
400
- background: varDefault.menuItemHoverBg('#eee'),
401
- },
402
- '.xin-menu-item:active': {
403
- // chrome rendering bug
404
- boxShadow: 'none !important',
405
- background: varDefault.menuItemActiveBg('#aaa'),
406
- color: varDefault.menuItemActiveColor('#000'),
407
- },
408
- '.xin-menu-item:active svg': {
409
- stroke: varDefault.menuItemIconActiveColor('#000'),
410
- },
411
- });
412
- export const createMenuAction = (item, options) => {
413
- const checked = (item.checked && item.checked() && 'check') || false;
414
- let icon = item?.icon || checked || span(' ');
415
- if (typeof icon === 'string') {
416
- icon = icons[icon]();
417
- }
418
- let menuItem;
419
- if (typeof item?.action === 'string') {
420
- menuItem = a({
421
- class: 'xin-menu-item',
422
- href: item.action,
423
- }, icon, options.localized ? span(localize(item.caption)) : span(item.caption), span(item.shortcut || ' '));
424
- }
425
- else {
426
- menuItem = button({
427
- class: 'xin-menu-item',
428
- onClick: item.action,
429
- }, icon, options.localized ? span(localize(item.caption)) : span(item.caption), span(item.shortcut || ' '));
430
- }
431
- menuItem.classList.toggle('xin-menu-item-checked', checked !== false);
432
- if (item?.enabled && !item.enabled()) {
433
- menuItem.setAttribute('disabled', '');
434
- }
435
- return menuItem;
436
- };
437
- export const createSubMenu = (item, options) => {
438
- const checked = (item.checked && item.checked() && 'check') || false;
439
- let icon = item?.icon || checked || span(' ');
440
- if (typeof icon === 'string') {
441
- icon = icons[icon]();
442
- }
443
- const submenuItem = button({
444
- class: 'xin-menu-item',
445
- disabled: !(!item.enabled || item.enabled()),
446
- onClick(event) {
447
- popMenu(Object.assign({}, options, {
448
- menuItems: item.menuItems,
449
- target: submenuItem,
450
- submenuDepth: (options.submenuDepth || 0) + 1,
451
- position: 'side',
452
- }));
453
- event.stopPropagation();
454
- event.preventDefault();
455
- },
456
- }, icon, options.localized ? span(localize(item.caption)) : span(item.caption), icons.chevronRight({ style: { justifySelf: 'flex-end' } }));
457
- return submenuItem;
458
- };
459
- export const createMenuItem = (item, options) => {
460
- if (item === null) {
461
- return span({ class: 'xin-menu-separator' });
462
- }
463
- else if (item?.action) {
464
- return createMenuAction(item, options);
465
- }
466
- else {
467
- return createSubMenu(item, options);
468
- }
469
- };
470
- export const menu = (options) => {
471
- const { target, width, menuItems } = options;
472
- const hasIcons = menuItems.find((item) => item?.icon || item?.checked);
473
- return div({
474
- class: hasIcons ? 'xin-menu xin-menu-with-icons' : 'xin-menu',
475
- onClick() {
476
- removeLastMenu(0);
477
- },
478
- }, div({
479
- style: {
480
- minWidth: target.offsetWidth + 'px',
481
- width: typeof width === 'number' ? `${width}px` : width,
482
- },
483
- onMousedown(event) {
484
- event.preventDefault();
485
- event.stopPropagation();
486
- },
487
- }, ...menuItems.map((item) => createMenuItem(item, options))));
488
- };
489
- let lastPopped;
490
- const poppedMenus = [];
491
- export const removeLastMenu = (depth = 0) => {
492
- const toBeRemoved = poppedMenus.splice(depth);
493
- for (const popped of toBeRemoved) {
494
- popped.menu.remove();
495
- }
496
- lastPopped = toBeRemoved[0];
497
- return depth > 0 ? poppedMenus[depth - 1] : undefined;
498
- };
499
- document.body.addEventListener('mousedown', (event) => {
500
- if (event.target &&
501
- !poppedMenus.find((popped) => popped.target.contains(event.target))) {
502
- removeLastMenu(0);
503
- }
504
- });
505
- document.body.addEventListener('keydown', (event) => {
506
- if (event.key === 'Escape') {
507
- removeLastMenu(0);
508
- }
509
- });
510
- export const popMenu = (options) => {
511
- options = Object.assign({ submenuDepth: 0 }, options);
512
- const { target, position, submenuDepth } = options;
513
- if (lastPopped && !document.body.contains(lastPopped?.menu)) {
514
- lastPopped = undefined;
515
- }
516
- if (poppedMenus.length && !document.body.contains(poppedMenus[0].menu)) {
517
- poppedMenus.splice(0);
518
- }
519
- if (submenuDepth === 0 && lastPopped?.target === target)
520
- return;
521
- const popped = removeLastMenu(submenuDepth);
522
- if (lastPopped?.target === target)
523
- return;
524
- if (popped && popped.target === target) {
525
- removeLastMenu();
526
- return;
527
- }
528
- if (!options.menuItems?.length) {
529
- return;
530
- }
531
- const content = menu(options);
532
- const float = popFloat({
533
- content,
534
- target,
535
- position,
536
- });
537
- float.remainOnScroll = 'remove';
538
- poppedMenus.push({
539
- target,
540
- menu: float,
541
- });
542
- };
543
- function findShortcutAction(items, event) {
544
- for (const item of items) {
545
- if (!item)
546
- continue;
547
- const { shortcut } = item;
548
- const { menuItems } = item;
549
- if (shortcut) {
550
- if (matchShortcut(event, shortcut)) {
551
- return item;
552
- }
553
- }
554
- else if (menuItems) {
555
- const foundAction = findShortcutAction(menuItems, event);
556
- if (foundAction) {
557
- return foundAction;
558
- }
559
- }
560
- }
561
- return undefined;
562
- }
563
- export class XinMenu extends Component {
564
- menuItems = [];
565
- menuWidth = 'auto';
566
- localized = false;
567
- showMenu = (event) => {
568
- if (event.type === 'click' || event.code === 'Space') {
569
- popMenu({
570
- target: this.parts.trigger,
571
- width: this.menuWidth,
572
- localized: this.localized,
573
- menuItems: this.menuItems,
574
- });
575
- event.stopPropagation();
576
- event.preventDefault();
577
- }
578
- };
579
- content = () => button({ tabindex: 0, part: 'trigger', onClick: this.showMenu }, xinSlot());
580
- handleShortcut = async (event) => {
581
- const menuAction = findShortcutAction(this.menuItems, event);
582
- if (menuAction) {
583
- if (menuAction.action instanceof Function) {
584
- menuAction.action();
585
- }
586
- }
587
- };
588
- constructor() {
589
- super();
590
- this.initAttributes('menuWidth', 'localized', 'icon');
591
- this.addEventListener('keydown', this.showMenu);
592
- }
593
- connectedCallback() {
594
- super.connectedCallback();
595
- document.addEventListener('keydown', this.handleShortcut, true);
596
- }
597
- disconnectedCallback() {
598
- super.disconnectedCallback();
599
- document.removeEventListener('keydown', this.handleShortcut);
600
- }
601
- }
602
- export const xinMenu = XinMenu.elementCreator({
603
- tag: 'xin-menu',
604
- styleSpec: {
605
- ':host': {
606
- display: 'inline-block',
607
- },
608
- ':host button > xin-slot': {
609
- display: 'flex',
610
- alignItems: 'center',
611
- gap: varDefault.xinMenuTriggerGap('10px'),
612
- },
613
- },
614
- });