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.
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/ab-test.d.ts +14 -0
- package/dist/ab-test.js +116 -0
- package/dist/babylon-3d.d.ts +53 -0
- package/dist/babylon-3d.js +292 -0
- package/dist/bodymovin-player.d.ts +32 -0
- package/dist/bodymovin-player.js +172 -0
- package/dist/bp-loader.d.ts +1 -0
- package/dist/bp-loader.js +26 -0
- package/dist/carousel.d.ts +113 -0
- package/dist/carousel.js +308 -0
- package/dist/code-editor.d.ts +27 -0
- package/dist/code-editor.js +102 -0
- package/dist/color-input.d.ts +41 -0
- package/dist/color-input.js +112 -0
- package/dist/data-table.d.ts +79 -0
- package/dist/data-table.js +774 -0
- package/dist/drag-and-drop.d.ts +2 -0
- package/dist/drag-and-drop.js +386 -0
- package/dist/editable-rect.d.ts +97 -0
- package/dist/editable-rect.js +450 -0
- package/dist/filter-builder.d.ts +64 -0
- package/dist/filter-builder.js +468 -0
- package/dist/float.d.ts +18 -0
- package/dist/float.js +170 -0
- package/dist/form.d.ts +68 -0
- package/dist/form.js +466 -0
- package/dist/gamepad.d.ts +34 -0
- package/dist/gamepad.js +115 -0
- package/dist/icon-data.d.ts +312 -0
- package/dist/icon-data.js +308 -0
- package/dist/icon-types.d.ts +7 -0
- package/dist/icon-types.js +1 -0
- package/dist/icons.d.ts +17 -0
- package/dist/icons.js +374 -0
- package/dist/iife.js +69 -0
- package/dist/iife.js.map +49 -0
- package/dist/index-iife.d.ts +1 -0
- package/dist/index-iife.js +4 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +47 -0
- package/dist/live-example.d.ts +63 -0
- package/dist/live-example.js +611 -0
- package/dist/localize.d.ts +46 -0
- package/dist/localize.js +381 -0
- package/dist/make-sorter.d.ts +3 -0
- package/dist/make-sorter.js +119 -0
- package/dist/make-sorter.test.d.ts +1 -0
- package/dist/make-sorter.test.js +48 -0
- package/dist/mapbox.d.ts +24 -0
- package/dist/mapbox.js +161 -0
- package/dist/markdown-viewer.d.ts +17 -0
- package/dist/markdown-viewer.js +173 -0
- package/dist/match-shortcut.d.ts +9 -0
- package/dist/match-shortcut.js +13 -0
- package/dist/match-shortcut.test.d.ts +1 -0
- package/dist/match-shortcut.test.js +194 -0
- package/dist/menu.d.ts +60 -0
- package/dist/menu.js +614 -0
- package/dist/notifications.d.ts +106 -0
- package/dist/notifications.js +308 -0
- package/dist/password-strength.d.ts +35 -0
- package/dist/password-strength.js +302 -0
- package/dist/playwright.config.d.ts +9 -0
- package/dist/playwright.config.js +73 -0
- package/dist/pop-float.d.ts +10 -0
- package/dist/pop-float.js +231 -0
- package/dist/rating.d.ts +62 -0
- package/dist/rating.js +192 -0
- package/dist/rich-text.d.ts +35 -0
- package/dist/rich-text.js +296 -0
- package/dist/segmented.d.ts +80 -0
- package/dist/segmented.js +298 -0
- package/dist/select.d.ts +43 -0
- package/dist/select.js +427 -0
- package/dist/side-nav.d.ts +36 -0
- package/dist/side-nav.js +106 -0
- package/dist/size-break.d.ts +18 -0
- package/dist/size-break.js +118 -0
- package/dist/sizer.d.ts +34 -0
- package/dist/sizer.js +92 -0
- package/dist/src/ab-test.d.ts +14 -0
- package/dist/src/babylon-3d.d.ts +53 -0
- package/dist/src/bodymovin-player.d.ts +32 -0
- package/dist/src/bp-loader.d.ts +0 -0
- package/dist/src/carousel.d.ts +113 -0
- package/dist/src/code-editor.d.ts +27 -0
- package/dist/src/color-input.d.ts +41 -0
- package/dist/src/data-table.d.ts +79 -0
- package/dist/src/drag-and-drop.d.ts +2 -0
- package/dist/src/editable-rect.d.ts +97 -0
- package/dist/src/filter-builder.d.ts +64 -0
- package/dist/src/float.d.ts +18 -0
- package/dist/src/form.d.ts +68 -0
- package/dist/src/gamepad.d.ts +34 -0
- package/dist/src/icon-data.d.ts +309 -0
- package/dist/src/icon-types.d.ts +7 -0
- package/dist/src/icons.d.ts +17 -0
- package/dist/src/index.d.ts +37 -0
- package/dist/src/live-example.d.ts +51 -0
- package/dist/src/localize.d.ts +30 -0
- package/dist/src/make-sorter.d.ts +3 -0
- package/dist/src/mapbox.d.ts +24 -0
- package/dist/src/markdown-viewer.d.ts +15 -0
- package/dist/src/match-shortcut.d.ts +9 -0
- package/dist/src/menu.d.ts +60 -0
- package/dist/src/notifications.d.ts +106 -0
- package/dist/src/password-strength.d.ts +35 -0
- package/dist/src/pop-float.d.ts +10 -0
- package/dist/src/rating.d.ts +62 -0
- package/dist/src/rich-text.d.ts +28 -0
- package/dist/src/segmented.d.ts +80 -0
- package/dist/src/select.d.ts +43 -0
- package/dist/src/side-nav.d.ts +36 -0
- package/dist/src/size-break.d.ts +18 -0
- package/dist/src/sizer.d.ts +34 -0
- package/dist/src/tab-selector.d.ts +91 -0
- package/dist/src/tag-list.d.ts +37 -0
- package/dist/src/track-drag.d.ts +5 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/via-tag.d.ts +2 -0
- package/dist/tab-selector.d.ts +91 -0
- package/dist/tab-selector.js +326 -0
- package/dist/tag-list.d.ts +37 -0
- package/dist/tag-list.js +375 -0
- package/dist/track-drag.d.ts +5 -0
- package/dist/track-drag.js +143 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/dist/via-tag.d.ts +2 -0
- package/dist/via-tag.js +102 -0
- package/package.json +58 -0
package/dist/mapbox.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/*#
|
|
2
|
+
# map
|
|
3
|
+
|
|
4
|
+
A [mapboxgl](https://docs.mapbox.com/mapbox-gl-js/api/) wrapper.
|
|
5
|
+
|
|
6
|
+
```js
|
|
7
|
+
const pickStyle = preview.querySelector('select')
|
|
8
|
+
const mapbox = preview.querySelector('xin-map')
|
|
9
|
+
const here = preview.querySelector('button')
|
|
10
|
+
|
|
11
|
+
pickStyle.addEventListener('change', () => {
|
|
12
|
+
mapbox.mapStyle = pickStyle.value
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
function getUserGPSCoordinates() {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
// Check if geolocation is supported
|
|
18
|
+
if (!navigator.geolocation) {
|
|
19
|
+
console.log("Geolocation is not supported by this browser.");
|
|
20
|
+
resolve(null);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Request position with options
|
|
25
|
+
navigator.geolocation.getCurrentPosition(
|
|
26
|
+
// Success callback
|
|
27
|
+
(position) => {
|
|
28
|
+
resolve({
|
|
29
|
+
latitude: position.coords.latitude,
|
|
30
|
+
longitude: position.coords.longitude
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
// Error callback
|
|
34
|
+
(error) => {
|
|
35
|
+
console.log(`Error getting location: ${error.message}`);
|
|
36
|
+
resolve(null);
|
|
37
|
+
},
|
|
38
|
+
// Options
|
|
39
|
+
{
|
|
40
|
+
enableHighAccuracy: true, // Request high accuracy if available
|
|
41
|
+
timeout: 10000, // Time to wait for position (10 seconds)
|
|
42
|
+
maximumAge: 0 // Don't use cached position
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
here.addEventListener('click', async () => {
|
|
49
|
+
const location = await getUserGPSCoordinates()
|
|
50
|
+
if (location) {
|
|
51
|
+
mapbox.coords = `${location.latitude},${location.longitude},12`
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
```html
|
|
56
|
+
<!-- please don't abuse my mapbox token -->
|
|
57
|
+
<xin-map
|
|
58
|
+
style="width: 100%; height: 100%"
|
|
59
|
+
coords="14.0093606,120.995083,17"
|
|
60
|
+
token="pk.eyJ1IjoicG9kcGVyc29uIiwiYSI6ImNqc2JlbWU0bjA1ZmY0YW5ycHZod3VhbWcifQ.arvqfpOqMgFYkKgQ35UScA"
|
|
61
|
+
map-style="mapbox://styles/mapbox/streets-v12"
|
|
62
|
+
></xin-map>
|
|
63
|
+
<select>
|
|
64
|
+
<option selected value="mapbox://styles/mapbox/streets-v12">Streets</option>
|
|
65
|
+
<option value="mapbox://styles/mapbox/satellite-v9">Satellite</option>
|
|
66
|
+
<option value="mapbox://styles/mapbox/light-v11">Light</option>
|
|
67
|
+
<option value="mapbox://styles/mapbox/dark-v11">Dark</option>
|
|
68
|
+
<option value="mapbox://styles/mapbox/outdoors-v12">Outdoors</option>
|
|
69
|
+
</select>
|
|
70
|
+
<button>
|
|
71
|
+
<xin-icon icon="mapPin"></xin-icon>
|
|
72
|
+
<span>Your Location</span>
|
|
73
|
+
</button>
|
|
74
|
+
```
|
|
75
|
+
```css
|
|
76
|
+
.preview button {
|
|
77
|
+
position: absolute;
|
|
78
|
+
right: 10px;
|
|
79
|
+
top: 10px;
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 5px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.preview select {
|
|
86
|
+
position: absolute;
|
|
87
|
+
bottom: 10px;
|
|
88
|
+
right: 10px;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
There's no need to learn new APIs or write wrappers, just access the element's `map` property
|
|
93
|
+
and [use the standard mapbox APIs directly](https://docs.mapbox.com/api/maps/styles/).
|
|
94
|
+
*/
|
|
95
|
+
import { Component as WebComponent, elements } from 'xinjs';
|
|
96
|
+
import { styleSheet, scriptTag } from './via-tag';
|
|
97
|
+
const { div } = elements;
|
|
98
|
+
export class MapBox extends WebComponent {
|
|
99
|
+
coords = '65.01715565258993,25.48081004203459,12';
|
|
100
|
+
content = div({ style: { width: '100%', height: '100%' } });
|
|
101
|
+
get map() {
|
|
102
|
+
return this._map;
|
|
103
|
+
}
|
|
104
|
+
mapStyle = 'mapbox://styles/mapbox/streets-v12';
|
|
105
|
+
token = '';
|
|
106
|
+
static mapboxCSSAvailable;
|
|
107
|
+
static mapboxAvailable;
|
|
108
|
+
_map;
|
|
109
|
+
static styleSpec = {
|
|
110
|
+
':host': {
|
|
111
|
+
display: 'inline-block',
|
|
112
|
+
position: 'relative',
|
|
113
|
+
width: '400px',
|
|
114
|
+
height: '400px',
|
|
115
|
+
textAlign: 'left',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
constructor() {
|
|
119
|
+
super();
|
|
120
|
+
this.initAttributes('coords', 'token', 'mapStyle');
|
|
121
|
+
if (MapBox.mapboxCSSAvailable === undefined) {
|
|
122
|
+
MapBox.mapboxCSSAvailable = styleSheet('https://api.mapbox.com/mapbox-gl-js/v1.4.1/mapbox-gl.css').catch((e) => {
|
|
123
|
+
console.error('failed to load mapbox-gl.css', e);
|
|
124
|
+
});
|
|
125
|
+
MapBox.mapboxAvailable = scriptTag('https://api.mapbox.com/mapbox-gl-js/v1.4.1/mapbox-gl.js').catch((e) => {
|
|
126
|
+
console.error('failed to load mapbox-gl.js', e);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
connectedCallback() {
|
|
131
|
+
super.connectedCallback();
|
|
132
|
+
if (!this.token) {
|
|
133
|
+
console.error('mapbox requires an access token which you can provide via the token attribute');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
render() {
|
|
137
|
+
super.render();
|
|
138
|
+
if (!this.token) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const { div } = this.parts;
|
|
142
|
+
const [long, lat, zoom] = this.coords.split(',').map((x) => Number(x));
|
|
143
|
+
if (this.map) {
|
|
144
|
+
this.map.remove();
|
|
145
|
+
}
|
|
146
|
+
MapBox.mapboxAvailable.then(({ mapboxgl }) => {
|
|
147
|
+
console.log("%cmapbox may complain about missing css -- don't panic!", 'background: orange; color: black; padding: 0 5px;');
|
|
148
|
+
mapboxgl.accessToken = this.token;
|
|
149
|
+
this._map = new mapboxgl.Map({
|
|
150
|
+
container: div,
|
|
151
|
+
style: this.mapStyle,
|
|
152
|
+
zoom,
|
|
153
|
+
center: [lat, long],
|
|
154
|
+
});
|
|
155
|
+
this._map.on('render', () => this._map.resize());
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export const mapBox = MapBox.elementCreator({
|
|
160
|
+
tag: 'xin-map',
|
|
161
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Component, ElementCreator } from 'tosijs';
|
|
2
|
+
import { MarkedOptions } from 'marked';
|
|
3
|
+
export declare class MarkdownViewer extends Component {
|
|
4
|
+
src: string;
|
|
5
|
+
value: string;
|
|
6
|
+
content: null;
|
|
7
|
+
elements: boolean;
|
|
8
|
+
context: {
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
options: MarkedOptions;
|
|
12
|
+
constructor();
|
|
13
|
+
connectedCallback(): void;
|
|
14
|
+
didRender: (() => void) | (() => Promise<void>);
|
|
15
|
+
render(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const markdownViewer: ElementCreator<MarkdownViewer>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Component, xin } from 'xinjs';
|
|
2
|
+
import { marked } from 'marked';
|
|
3
|
+
/*#
|
|
4
|
+
# markdown
|
|
5
|
+
|
|
6
|
+
`<xin-md>` renders markdown using [marked](https://www.npmjs.com/package/marked).
|
|
7
|
+
|
|
8
|
+
`<xin-md>` renders [markdown](https://www.markdownguide.org/) anywhere, either using the
|
|
9
|
+
`src` attribute to load the file asynchronously, or rendering the text inside it.
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<xin-md>
|
|
13
|
+
## hello
|
|
14
|
+
world
|
|
15
|
+
</xin-md>
|
|
16
|
+
```
|
|
17
|
+
```css
|
|
18
|
+
xin-md {
|
|
19
|
+
display: block;
|
|
20
|
+
padding: var(--spacing);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Note that, by default, `<xin-md>` will use its `textContent` (not its `innerHTML`) as its source.
|
|
25
|
+
|
|
26
|
+
## rendering markdown from a url
|
|
27
|
+
|
|
28
|
+
Again, like an `<img>` tag, you can simply set a `<xin-md>`'s `src` attribute to a URL pointing
|
|
29
|
+
to markdown source and it will load it asynchronously and render it.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
<xin-md src="/path/to/file.md">
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## setting its `value`
|
|
36
|
+
|
|
37
|
+
Or, just set the element's `value` and it will render it for you. You can try
|
|
38
|
+
this in the console, e.g.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
$('.preview xin-md').value = 'testing\n\n## this is a test'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## elements
|
|
45
|
+
|
|
46
|
+
`<xin-md>` also (optionally) allows the embedding of inline HTML elements without blocking markdown
|
|
47
|
+
rendering, so that you can embed specific elements while retaining markdown. You need to explicitly set
|
|
48
|
+
the `elements` property, and for markdown rendering not to be blocked, the html elements need to
|
|
49
|
+
start on a new line and not be indented. E.g.
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<xin-md elements>
|
|
53
|
+
<form>
|
|
54
|
+
### this is a form
|
|
55
|
+
<label>
|
|
56
|
+
fill in this field.
|
|
57
|
+
**It's important!**
|
|
58
|
+
<input>
|
|
59
|
+
</label>
|
|
60
|
+
</form>
|
|
61
|
+
</xin-md>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
In this case `<xin-md>` uses its `innerHTML` and not its `textContent`.
|
|
65
|
+
|
|
66
|
+
## context and template variables
|
|
67
|
+
|
|
68
|
+
`<xin-md>` also supports **template** values. You need to provide data to the element in the form
|
|
69
|
+
of `context` (an arbitrary object, or a JSON string), and then embed the template text using
|
|
70
|
+
handlebars-style doubled curly braces, e.g. `{{path.to.value}}`.
|
|
71
|
+
|
|
72
|
+
If no value is found, the original text is passed through.
|
|
73
|
+
|
|
74
|
+
Finally, note that template substitution occurs *before* markdown transformation, which means you can
|
|
75
|
+
pass context data through to HTML elements.
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<xin-md
|
|
79
|
+
elements
|
|
80
|
+
context='{"title": "template example", "foo": {"bar": 17}, "nested": "*work*: {{foo.bar}}"}'
|
|
81
|
+
>
|
|
82
|
+
## {{title}}
|
|
83
|
+
|
|
84
|
+
The magic number is <input type="number" value={{foo.bar}}>
|
|
85
|
+
|
|
86
|
+
Oh, and nested templates {{nested}}.
|
|
87
|
+
</xin-md>
|
|
88
|
+
```
|
|
89
|
+
*/
|
|
90
|
+
function populate(basePath, source) {
|
|
91
|
+
if (source == null) {
|
|
92
|
+
source = '';
|
|
93
|
+
}
|
|
94
|
+
else if (typeof source !== 'string') {
|
|
95
|
+
source = String(source);
|
|
96
|
+
}
|
|
97
|
+
return source.replace(/\{\{([^}]+)\}\}/g, (original, prop) => {
|
|
98
|
+
const value = xin[`${basePath}${prop.startsWith('[') ? prop : '.' + prop}`];
|
|
99
|
+
return value === undefined ? original : populate(basePath, String(value));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export class MarkdownViewer extends Component {
|
|
103
|
+
src = '';
|
|
104
|
+
value = '';
|
|
105
|
+
content = null;
|
|
106
|
+
elements = false;
|
|
107
|
+
context = {};
|
|
108
|
+
constructor() {
|
|
109
|
+
super();
|
|
110
|
+
this.initAttributes('src', 'elements', 'context');
|
|
111
|
+
}
|
|
112
|
+
connectedCallback() {
|
|
113
|
+
super.connectedCallback();
|
|
114
|
+
if (this.src !== '') {
|
|
115
|
+
;
|
|
116
|
+
(async () => {
|
|
117
|
+
const request = await fetch(this.src);
|
|
118
|
+
this.value = await request.text();
|
|
119
|
+
})();
|
|
120
|
+
}
|
|
121
|
+
else if (this.value === '') {
|
|
122
|
+
if (this.elements) {
|
|
123
|
+
this.value = this.innerHTML;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.value = this.textContent != null ? this.textContent : '';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
didRender = () => {
|
|
131
|
+
/* do not care */
|
|
132
|
+
};
|
|
133
|
+
render() {
|
|
134
|
+
super.render();
|
|
135
|
+
xin[this.instanceId] =
|
|
136
|
+
typeof this.context === 'string' ? JSON.parse(this.context) : this.context;
|
|
137
|
+
const source = populate(this.instanceId, this.value);
|
|
138
|
+
if (this.elements) {
|
|
139
|
+
const chunks = source
|
|
140
|
+
.split('\n')
|
|
141
|
+
.reduce((chunks, line) => {
|
|
142
|
+
if (line.startsWith('<') || chunks.length === 0) {
|
|
143
|
+
chunks.push(line);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
const lastChunk = chunks[chunks.length - 1];
|
|
147
|
+
if (!lastChunk.startsWith('<') || !lastChunk.endsWith('>')) {
|
|
148
|
+
chunks[chunks.length - 1] += '\n' + line;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
chunks.push(line);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return chunks;
|
|
155
|
+
}, []);
|
|
156
|
+
this.innerHTML = chunks
|
|
157
|
+
.map((chunk) => chunk.startsWith('<') && chunk.endsWith('>')
|
|
158
|
+
? chunk
|
|
159
|
+
: marked(chunk, { mangle: false, headerIds: false }))
|
|
160
|
+
.join('');
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.innerHTML = marked(source, {
|
|
164
|
+
mangle: false,
|
|
165
|
+
headerIds: false,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
this.didRender();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export const markdownViewer = MarkdownViewer.elementCreator({
|
|
172
|
+
tag: 'xin-md',
|
|
173
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const matchShortcut = (keystroke, shortcut) => {
|
|
2
|
+
shortcut = shortcut.toLocaleLowerCase();
|
|
3
|
+
const ctrlKey = !!shortcut.match(/\^|ctrl/);
|
|
4
|
+
const metaKey = !!shortcut.match(/⌘|meta/);
|
|
5
|
+
const altKey = !!shortcut.match(/⌥|⎇|alt|option/);
|
|
6
|
+
const shiftKey = !!shortcut.match(/⇧|shift/);
|
|
7
|
+
const baseKey = shortcut.slice(-1);
|
|
8
|
+
return (keystroke.key === baseKey &&
|
|
9
|
+
keystroke.metaKey === metaKey &&
|
|
10
|
+
keystroke.ctrlKey === ctrlKey &&
|
|
11
|
+
keystroke.altKey === altKey &&
|
|
12
|
+
keystroke.shiftKey === shiftKey);
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { test, expect } from 'bun:test';
|
|
2
|
+
import { matchShortcut } from './match-shortcut';
|
|
3
|
+
test('simple shortcuts', () => {
|
|
4
|
+
expect(matchShortcut({
|
|
5
|
+
shiftKey: false,
|
|
6
|
+
ctrlKey: false,
|
|
7
|
+
altKey: false,
|
|
8
|
+
metaKey: false,
|
|
9
|
+
key: 'x',
|
|
10
|
+
}, 'X')).toBe(true);
|
|
11
|
+
expect(matchShortcut({
|
|
12
|
+
shiftKey: false,
|
|
13
|
+
ctrlKey: false,
|
|
14
|
+
altKey: false,
|
|
15
|
+
metaKey: false,
|
|
16
|
+
key: 'x',
|
|
17
|
+
}, 'x')).toBe(true);
|
|
18
|
+
expect(matchShortcut({
|
|
19
|
+
shiftKey: false,
|
|
20
|
+
ctrlKey: false,
|
|
21
|
+
altKey: false,
|
|
22
|
+
metaKey: false,
|
|
23
|
+
key: 'x',
|
|
24
|
+
}, 'y')).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
test('ctrl keys', () => {
|
|
27
|
+
expect(matchShortcut({
|
|
28
|
+
shiftKey: false,
|
|
29
|
+
ctrlKey: true,
|
|
30
|
+
altKey: false,
|
|
31
|
+
metaKey: false,
|
|
32
|
+
key: 'x',
|
|
33
|
+
}, 'ctrl-x')).toBe(true);
|
|
34
|
+
expect(matchShortcut({
|
|
35
|
+
shiftKey: false,
|
|
36
|
+
ctrlKey: true,
|
|
37
|
+
altKey: false,
|
|
38
|
+
metaKey: false,
|
|
39
|
+
key: 'x',
|
|
40
|
+
}, '^X')).toBe(true);
|
|
41
|
+
expect(matchShortcut({
|
|
42
|
+
shiftKey: false,
|
|
43
|
+
ctrlKey: true,
|
|
44
|
+
altKey: false,
|
|
45
|
+
metaKey: false,
|
|
46
|
+
key: 'x',
|
|
47
|
+
}, 'x')).toBe(false);
|
|
48
|
+
expect(matchShortcut({
|
|
49
|
+
shiftKey: false,
|
|
50
|
+
ctrlKey: false,
|
|
51
|
+
altKey: false,
|
|
52
|
+
metaKey: false,
|
|
53
|
+
key: 'x',
|
|
54
|
+
}, '^X')).toBe(false);
|
|
55
|
+
expect(matchShortcut({
|
|
56
|
+
shiftKey: false,
|
|
57
|
+
ctrlKey: false,
|
|
58
|
+
altKey: false,
|
|
59
|
+
metaKey: true,
|
|
60
|
+
key: 'x',
|
|
61
|
+
}, 'ctrl-x')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
test('meta keys', () => {
|
|
64
|
+
expect(matchShortcut({
|
|
65
|
+
shiftKey: false,
|
|
66
|
+
ctrlKey: false,
|
|
67
|
+
altKey: false,
|
|
68
|
+
metaKey: true,
|
|
69
|
+
key: 'x',
|
|
70
|
+
}, 'meta-x')).toBe(true);
|
|
71
|
+
expect(matchShortcut({
|
|
72
|
+
shiftKey: false,
|
|
73
|
+
ctrlKey: false,
|
|
74
|
+
altKey: false,
|
|
75
|
+
metaKey: true,
|
|
76
|
+
key: 'x',
|
|
77
|
+
}, '⌘-X')).toBe(true);
|
|
78
|
+
expect(matchShortcut({
|
|
79
|
+
shiftKey: false,
|
|
80
|
+
ctrlKey: false,
|
|
81
|
+
altKey: false,
|
|
82
|
+
metaKey: true,
|
|
83
|
+
key: 'x',
|
|
84
|
+
}, 'x')).toBe(false);
|
|
85
|
+
expect(matchShortcut({
|
|
86
|
+
shiftKey: false,
|
|
87
|
+
ctrlKey: false,
|
|
88
|
+
altKey: false,
|
|
89
|
+
metaKey: false,
|
|
90
|
+
key: 'x',
|
|
91
|
+
}, '⌘-X')).toBe(false);
|
|
92
|
+
expect(matchShortcut({
|
|
93
|
+
shiftKey: false,
|
|
94
|
+
ctrlKey: true,
|
|
95
|
+
altKey: false,
|
|
96
|
+
metaKey: false,
|
|
97
|
+
key: 'x',
|
|
98
|
+
}, 'meta-x')).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
test('alt keys', () => {
|
|
101
|
+
expect(matchShortcut({
|
|
102
|
+
shiftKey: false,
|
|
103
|
+
ctrlKey: false,
|
|
104
|
+
altKey: true,
|
|
105
|
+
metaKey: false,
|
|
106
|
+
key: 'x',
|
|
107
|
+
}, '⌥x')).toBe(true);
|
|
108
|
+
expect(matchShortcut({
|
|
109
|
+
shiftKey: false,
|
|
110
|
+
ctrlKey: false,
|
|
111
|
+
altKey: true,
|
|
112
|
+
metaKey: false,
|
|
113
|
+
key: 'x',
|
|
114
|
+
}, 'alt-X')).toBe(true);
|
|
115
|
+
expect(matchShortcut({
|
|
116
|
+
shiftKey: false,
|
|
117
|
+
ctrlKey: false,
|
|
118
|
+
altKey: true,
|
|
119
|
+
metaKey: false,
|
|
120
|
+
key: 'x',
|
|
121
|
+
}, '⎇-x')).toBe(true);
|
|
122
|
+
expect(matchShortcut({
|
|
123
|
+
shiftKey: false,
|
|
124
|
+
ctrlKey: false,
|
|
125
|
+
altKey: true,
|
|
126
|
+
metaKey: false,
|
|
127
|
+
key: 'x',
|
|
128
|
+
}, 'option-X')).toBe(true);
|
|
129
|
+
expect(matchShortcut({
|
|
130
|
+
shiftKey: false,
|
|
131
|
+
ctrlKey: false,
|
|
132
|
+
altKey: true,
|
|
133
|
+
metaKey: false,
|
|
134
|
+
key: 'x',
|
|
135
|
+
}, 'x')).toBe(false);
|
|
136
|
+
expect(matchShortcut({
|
|
137
|
+
shiftKey: false,
|
|
138
|
+
ctrlKey: false,
|
|
139
|
+
altKey: false,
|
|
140
|
+
metaKey: false,
|
|
141
|
+
key: 'x',
|
|
142
|
+
}, 'option-X')).toBe(false);
|
|
143
|
+
expect(matchShortcut({
|
|
144
|
+
shiftKey: false,
|
|
145
|
+
ctrlKey: true,
|
|
146
|
+
altKey: false,
|
|
147
|
+
metaKey: false,
|
|
148
|
+
key: 'x',
|
|
149
|
+
}, '⎇x')).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
test('chorded modifiers', () => {
|
|
152
|
+
expect(matchShortcut({
|
|
153
|
+
shiftKey: false,
|
|
154
|
+
ctrlKey: false,
|
|
155
|
+
altKey: true,
|
|
156
|
+
metaKey: true,
|
|
157
|
+
key: 'x',
|
|
158
|
+
}, '⌘⌥x')).toBe(true);
|
|
159
|
+
expect(matchShortcut({
|
|
160
|
+
shiftKey: false,
|
|
161
|
+
ctrlKey: false,
|
|
162
|
+
altKey: true,
|
|
163
|
+
metaKey: true,
|
|
164
|
+
key: 'x',
|
|
165
|
+
}, '⌘⌥x')).toBe(true);
|
|
166
|
+
expect(matchShortcut({
|
|
167
|
+
shiftKey: false,
|
|
168
|
+
ctrlKey: false,
|
|
169
|
+
altKey: false,
|
|
170
|
+
metaKey: true,
|
|
171
|
+
key: 'x',
|
|
172
|
+
}, '⌘⌥x')).toBe(false);
|
|
173
|
+
expect(matchShortcut({
|
|
174
|
+
shiftKey: false,
|
|
175
|
+
ctrlKey: false,
|
|
176
|
+
altKey: false,
|
|
177
|
+
metaKey: true,
|
|
178
|
+
key: 'x',
|
|
179
|
+
}, 'alt-meta-x')).toBe(false);
|
|
180
|
+
expect(matchShortcut({
|
|
181
|
+
shiftKey: false,
|
|
182
|
+
ctrlKey: false,
|
|
183
|
+
altKey: true,
|
|
184
|
+
metaKey: false,
|
|
185
|
+
key: 'x',
|
|
186
|
+
}, '⌘⌥x')).toBe(false);
|
|
187
|
+
expect(matchShortcut({
|
|
188
|
+
shiftKey: true,
|
|
189
|
+
ctrlKey: false,
|
|
190
|
+
altKey: true,
|
|
191
|
+
metaKey: false,
|
|
192
|
+
key: 'x',
|
|
193
|
+
}, '⌘⌥x')).toBe(false);
|
|
194
|
+
});
|
package/dist/menu.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Component, PartsMap } from 'tosijs';
|
|
2
|
+
import { FloatPosition } from './pop-float';
|
|
3
|
+
import { SvgIcon } from './icons';
|
|
4
|
+
export type ActionCallback = () => void | Promise<void>;
|
|
5
|
+
export interface MenuAction {
|
|
6
|
+
caption: string;
|
|
7
|
+
shortcut?: string;
|
|
8
|
+
checked?: () => boolean;
|
|
9
|
+
enabled?: () => boolean;
|
|
10
|
+
action: ActionCallback | string;
|
|
11
|
+
icon?: string | Element;
|
|
12
|
+
}
|
|
13
|
+
export interface SubMenu {
|
|
14
|
+
caption: string;
|
|
15
|
+
checked?: () => boolean;
|
|
16
|
+
enabled?: () => boolean;
|
|
17
|
+
menuItems: MenuItem[];
|
|
18
|
+
icon?: string | Element;
|
|
19
|
+
}
|
|
20
|
+
export type MenuSeparator = null;
|
|
21
|
+
export type MenuItem = MenuAction | SubMenu | MenuSeparator;
|
|
22
|
+
export declare const createMenuAction: (item: MenuAction, options: PopMenuOptions) => HTMLElement;
|
|
23
|
+
export declare const createSubMenu: (item: SubMenu, options: PopMenuOptions) => HTMLElement;
|
|
24
|
+
export declare const createMenuItem: (item: MenuItem, options: PopMenuOptions) => HTMLElement;
|
|
25
|
+
export declare const menu: (options: PopMenuOptions) => HTMLDivElement;
|
|
26
|
+
interface PoppedMenu {
|
|
27
|
+
target: HTMLElement;
|
|
28
|
+
menu: HTMLElement;
|
|
29
|
+
}
|
|
30
|
+
export declare const removeLastMenu: (depth?: number) => PoppedMenu | undefined;
|
|
31
|
+
export interface PopMenuOptions {
|
|
32
|
+
target: HTMLElement;
|
|
33
|
+
menuItems: MenuItem[];
|
|
34
|
+
width?: string | number;
|
|
35
|
+
position?: FloatPosition;
|
|
36
|
+
submenuDepth?: number;
|
|
37
|
+
submenuOffset?: {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
};
|
|
41
|
+
localized?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare const popMenu: (options: PopMenuOptions) => void;
|
|
44
|
+
interface XinMenuParts extends PartsMap {
|
|
45
|
+
trigger: HTMLButtonElement;
|
|
46
|
+
icon: SvgIcon;
|
|
47
|
+
}
|
|
48
|
+
export declare class XinMenu extends Component<XinMenuParts> {
|
|
49
|
+
menuItems: MenuItem[];
|
|
50
|
+
menuWidth: string;
|
|
51
|
+
localized: boolean;
|
|
52
|
+
showMenu: (event: Event) => void;
|
|
53
|
+
content: () => HTMLButtonElement;
|
|
54
|
+
handleShortcut: (event: KeyboardEvent) => Promise<void>;
|
|
55
|
+
constructor();
|
|
56
|
+
connectedCallback(): void;
|
|
57
|
+
disconnectedCallback(): void;
|
|
58
|
+
}
|
|
59
|
+
export declare const xinMenu: import("tosijs").ElementCreator<Component<PartsMap>>;
|
|
60
|
+
export {};
|