tosijs-ui 1.0.0 → 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 (94) 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.d.ts +1 -1
  5. package/dist/index.js +15 -37
  6. package/dist/index.js.map +39 -39
  7. package/dist/version.d.ts +1 -1
  8. package/package.json +2 -2
  9. package/dist/ab-test.js +0 -116
  10. package/dist/babylon-3d.js +0 -292
  11. package/dist/bodymovin-player.js +0 -172
  12. package/dist/bp-loader.js +0 -26
  13. package/dist/carousel.js +0 -308
  14. package/dist/code-editor.js +0 -102
  15. package/dist/color-input.js +0 -112
  16. package/dist/data-table.js +0 -774
  17. package/dist/drag-and-drop.js +0 -386
  18. package/dist/editable-rect.js +0 -450
  19. package/dist/filter-builder.js +0 -468
  20. package/dist/float.js +0 -170
  21. package/dist/form.js +0 -466
  22. package/dist/gamepad.js +0 -115
  23. package/dist/icon-data.js +0 -308
  24. package/dist/icon-types.js +0 -1
  25. package/dist/icons.js +0 -374
  26. package/dist/index-iife.js +0 -4
  27. package/dist/live-example.js +0 -611
  28. package/dist/localize.js +0 -381
  29. package/dist/make-sorter.js +0 -119
  30. package/dist/make-sorter.test.d.ts +0 -1
  31. package/dist/make-sorter.test.js +0 -48
  32. package/dist/mapbox.js +0 -161
  33. package/dist/markdown-viewer.js +0 -173
  34. package/dist/match-shortcut.js +0 -13
  35. package/dist/match-shortcut.test.d.ts +0 -1
  36. package/dist/match-shortcut.test.js +0 -194
  37. package/dist/menu.js +0 -614
  38. package/dist/notifications.js +0 -308
  39. package/dist/password-strength.js +0 -302
  40. package/dist/playwright.config.d.ts +0 -9
  41. package/dist/playwright.config.js +0 -73
  42. package/dist/pop-float.js +0 -231
  43. package/dist/rating.js +0 -192
  44. package/dist/rich-text.js +0 -296
  45. package/dist/segmented.js +0 -298
  46. package/dist/select.js +0 -427
  47. package/dist/side-nav.js +0 -106
  48. package/dist/size-break.js +0 -118
  49. package/dist/sizer.js +0 -92
  50. package/dist/src/ab-test.d.ts +0 -14
  51. package/dist/src/babylon-3d.d.ts +0 -53
  52. package/dist/src/bodymovin-player.d.ts +0 -32
  53. package/dist/src/bp-loader.d.ts +0 -0
  54. package/dist/src/carousel.d.ts +0 -113
  55. package/dist/src/code-editor.d.ts +0 -27
  56. package/dist/src/color-input.d.ts +0 -41
  57. package/dist/src/data-table.d.ts +0 -79
  58. package/dist/src/drag-and-drop.d.ts +0 -2
  59. package/dist/src/editable-rect.d.ts +0 -97
  60. package/dist/src/filter-builder.d.ts +0 -64
  61. package/dist/src/float.d.ts +0 -18
  62. package/dist/src/form.d.ts +0 -68
  63. package/dist/src/gamepad.d.ts +0 -34
  64. package/dist/src/icon-data.d.ts +0 -309
  65. package/dist/src/icon-types.d.ts +0 -7
  66. package/dist/src/icons.d.ts +0 -17
  67. package/dist/src/index.d.ts +0 -37
  68. package/dist/src/live-example.d.ts +0 -51
  69. package/dist/src/localize.d.ts +0 -30
  70. package/dist/src/make-sorter.d.ts +0 -3
  71. package/dist/src/mapbox.d.ts +0 -24
  72. package/dist/src/markdown-viewer.d.ts +0 -15
  73. package/dist/src/match-shortcut.d.ts +0 -9
  74. package/dist/src/menu.d.ts +0 -60
  75. package/dist/src/notifications.d.ts +0 -106
  76. package/dist/src/password-strength.d.ts +0 -35
  77. package/dist/src/pop-float.d.ts +0 -10
  78. package/dist/src/rating.d.ts +0 -62
  79. package/dist/src/rich-text.d.ts +0 -28
  80. package/dist/src/segmented.d.ts +0 -80
  81. package/dist/src/select.d.ts +0 -43
  82. package/dist/src/side-nav.d.ts +0 -36
  83. package/dist/src/size-break.d.ts +0 -18
  84. package/dist/src/sizer.d.ts +0 -34
  85. package/dist/src/tab-selector.d.ts +0 -91
  86. package/dist/src/tag-list.d.ts +0 -37
  87. package/dist/src/track-drag.d.ts +0 -5
  88. package/dist/src/version.d.ts +0 -1
  89. package/dist/src/via-tag.d.ts +0 -2
  90. package/dist/tab-selector.js +0 -326
  91. package/dist/tag-list.js +0 -375
  92. package/dist/track-drag.js +0 -143
  93. package/dist/version.js +0 -1
  94. 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
- });