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.
- package/README.md +4 -2
- package/dist/iife.js +70 -60
- package/dist/iife.js.map +42 -42
- package/dist/index.js +15 -37
- package/dist/index.js.map +39 -39
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/dist/ab-test.js +0 -116
- package/dist/babylon-3d.js +0 -292
- package/dist/bodymovin-player.js +0 -172
- package/dist/bp-loader.js +0 -26
- package/dist/carousel.js +0 -308
- package/dist/code-editor.js +0 -102
- package/dist/color-input.js +0 -112
- package/dist/data-table.js +0 -774
- package/dist/drag-and-drop.js +0 -386
- package/dist/editable-rect.js +0 -450
- package/dist/filter-builder.js +0 -468
- package/dist/float.js +0 -170
- package/dist/form.js +0 -466
- package/dist/gamepad.js +0 -115
- package/dist/icon-data.js +0 -308
- package/dist/icon-types.js +0 -1
- package/dist/icons.js +0 -374
- package/dist/index-iife.js +0 -4
- package/dist/live-example.js +0 -611
- package/dist/localize.js +0 -381
- package/dist/make-sorter.js +0 -119
- package/dist/make-sorter.test.d.ts +0 -1
- package/dist/make-sorter.test.js +0 -48
- package/dist/mapbox.js +0 -161
- package/dist/markdown-viewer.js +0 -173
- package/dist/match-shortcut.js +0 -13
- package/dist/match-shortcut.test.d.ts +0 -1
- package/dist/match-shortcut.test.js +0 -194
- package/dist/menu.js +0 -614
- package/dist/notifications.js +0 -308
- package/dist/password-strength.js +0 -302
- package/dist/playwright.config.d.ts +0 -9
- package/dist/playwright.config.js +0 -73
- package/dist/pop-float.js +0 -231
- package/dist/rating.js +0 -192
- package/dist/rich-text.js +0 -296
- package/dist/segmented.js +0 -298
- package/dist/select.js +0 -427
- package/dist/side-nav.js +0 -106
- package/dist/size-break.js +0 -118
- package/dist/sizer.js +0 -92
- package/dist/src/ab-test.d.ts +0 -14
- package/dist/src/babylon-3d.d.ts +0 -53
- package/dist/src/bodymovin-player.d.ts +0 -32
- package/dist/src/bp-loader.d.ts +0 -0
- package/dist/src/carousel.d.ts +0 -113
- package/dist/src/code-editor.d.ts +0 -27
- package/dist/src/color-input.d.ts +0 -41
- package/dist/src/data-table.d.ts +0 -79
- package/dist/src/drag-and-drop.d.ts +0 -2
- package/dist/src/editable-rect.d.ts +0 -97
- package/dist/src/filter-builder.d.ts +0 -64
- package/dist/src/float.d.ts +0 -18
- package/dist/src/form.d.ts +0 -68
- package/dist/src/gamepad.d.ts +0 -34
- package/dist/src/icon-data.d.ts +0 -309
- package/dist/src/icon-types.d.ts +0 -7
- package/dist/src/icons.d.ts +0 -17
- package/dist/src/index.d.ts +0 -37
- package/dist/src/live-example.d.ts +0 -51
- package/dist/src/localize.d.ts +0 -30
- package/dist/src/make-sorter.d.ts +0 -3
- package/dist/src/mapbox.d.ts +0 -24
- package/dist/src/markdown-viewer.d.ts +0 -15
- package/dist/src/match-shortcut.d.ts +0 -9
- package/dist/src/menu.d.ts +0 -60
- package/dist/src/notifications.d.ts +0 -106
- package/dist/src/password-strength.d.ts +0 -35
- package/dist/src/pop-float.d.ts +0 -10
- package/dist/src/rating.d.ts +0 -62
- package/dist/src/rich-text.d.ts +0 -28
- package/dist/src/segmented.d.ts +0 -80
- package/dist/src/select.d.ts +0 -43
- package/dist/src/side-nav.d.ts +0 -36
- package/dist/src/size-break.d.ts +0 -18
- package/dist/src/sizer.d.ts +0 -34
- package/dist/src/tab-selector.d.ts +0 -91
- package/dist/src/tag-list.d.ts +0 -37
- package/dist/src/track-drag.d.ts +0 -5
- package/dist/src/version.d.ts +0 -1
- package/dist/src/via-tag.d.ts +0 -2
- package/dist/tab-selector.js +0 -326
- package/dist/tag-list.js +0 -375
- package/dist/track-drag.js +0 -143
- package/dist/version.js +0 -1
- package/dist/via-tag.js +0 -102
package/dist/localize.js
DELETED
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
/*#
|
|
2
|
-
# localize
|
|
3
|
-
|
|
4
|
-
`xinjs-ui` provides support for localization via the `localize` method and the `<xin-locale-picker>`
|
|
5
|
-
and `<xin-localized>` custom-elements.
|
|
6
|
-
|
|
7
|
-
> ### Important Note
|
|
8
|
-
> This module deals with the **language** used in the user interface. "locale" is
|
|
9
|
-
> *not the same thing*. The (usually) two-letter codes used designate **language**
|
|
10
|
-
> and **not locale**.
|
|
11
|
-
>
|
|
12
|
-
> E.g. the US *locale* includes things like measurement systems
|
|
13
|
-
> and date format. Most European locales use commas where we use decimal points in the US.
|
|
14
|
-
>
|
|
15
|
-
> Similarly, `ja` is the code for the Japanese **language** while `jp` is the **locale**.
|
|
16
|
-
|
|
17
|
-
## `initLocalization(localizationData: string)`
|
|
18
|
-
|
|
19
|
-
Enables localization from TSV string data.
|
|
20
|
-
|
|
21
|
-
## XinLocalePicker
|
|
22
|
-
|
|
23
|
-
A selector that lets the user pick from among supported languages.
|
|
24
|
-
|
|
25
|
-
```html
|
|
26
|
-
<h3>Locale Picker</h3>
|
|
27
|
-
<xin-locale-picker></xin-locale-picker>
|
|
28
|
-
|
|
29
|
-
<h3>Locale Picker with <code>hide-captions</code></h3>
|
|
30
|
-
<xin-locale-picker hide-caption></xin-locale-picker>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## `localize()`
|
|
34
|
-
|
|
35
|
-
If you just want to localize a string with code, use `localize(s: string): string`.
|
|
36
|
-
|
|
37
|
-
If the reference string only matches when both are converted to
|
|
38
|
-
lowercase, the output string will also be lowercase.
|
|
39
|
-
|
|
40
|
-
E.g. if you have localized `Cancel` as `Annuler`, then `localize("cancel")
|
|
41
|
-
will output `annuler`.
|
|
42
|
-
|
|
43
|
-
### ellipses
|
|
44
|
-
|
|
45
|
-
If you end a string with an ellipsis, `localize` will ignore the ellipsis,
|
|
46
|
-
localize the string, and then append the ellipsis.
|
|
47
|
-
|
|
48
|
-
## `setLocale(language: string)`
|
|
49
|
-
|
|
50
|
-
```js
|
|
51
|
-
const { button, p } = xinjs.elements
|
|
52
|
-
const { setLocale } = xinjsui
|
|
53
|
-
|
|
54
|
-
preview.append(
|
|
55
|
-
p(
|
|
56
|
-
button(
|
|
57
|
-
{
|
|
58
|
-
onClick() {
|
|
59
|
-
setLocale('en-US')
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
'setLocale("en-US")'
|
|
63
|
-
)
|
|
64
|
-
),
|
|
65
|
-
p(
|
|
66
|
-
button(
|
|
67
|
-
{
|
|
68
|
-
onClick() {
|
|
69
|
-
setLocale('fr')
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
'setLocale("fr")'
|
|
73
|
-
)
|
|
74
|
-
),
|
|
75
|
-
p(
|
|
76
|
-
button(
|
|
77
|
-
{
|
|
78
|
-
onClick() {
|
|
79
|
-
setLocale('qq')
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
'setLocale("qq") (see console for error message)'
|
|
83
|
-
)
|
|
84
|
-
),
|
|
85
|
-
)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
If you want to directly set locale, just use `setLocale()`.
|
|
89
|
-
|
|
90
|
-
## XinLocalized
|
|
91
|
-
|
|
92
|
-
A span-replacement that automatically localizes its text content.
|
|
93
|
-
By default the case in the localized data is preserved unless the
|
|
94
|
-
reference text is all lowercase, in which case the localized text
|
|
95
|
-
is also lowercased.
|
|
96
|
-
|
|
97
|
-
While viewing this documentation, all `<xin-localized>` elements should display a **red
|
|
98
|
-
underline**.
|
|
99
|
-
|
|
100
|
-
```html
|
|
101
|
-
<h3>Localized Widgets</h3>
|
|
102
|
-
<button><xin-localized>Yes</xin-localized></button>
|
|
103
|
-
<button><xin-localized>No</xin-localized></button>
|
|
104
|
-
<button><xin-localized>Open…</xin-localized></button> <i>note the ellipsis</i>
|
|
105
|
-
|
|
106
|
-
<h3>Lowercase is preserved</h3>
|
|
107
|
-
<button><xin-localized>yes</xin-localized></button>
|
|
108
|
-
<button><xin-localized>no</xin-localized></button>
|
|
109
|
-
<button><xin-localized>open…</xin-localized></button>
|
|
110
|
-
|
|
111
|
-
<h3>Localized Attribute</h3>
|
|
112
|
-
<input>
|
|
113
|
-
```
|
|
114
|
-
```css
|
|
115
|
-
xin-localized {
|
|
116
|
-
border-bottom: 2px solid red;
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
```js
|
|
120
|
-
const { xinLocalized, localize } = xinjsui
|
|
121
|
-
|
|
122
|
-
preview.append(xinLocalized({
|
|
123
|
-
refString: 'localized placeholder',
|
|
124
|
-
localeChanged() {
|
|
125
|
-
this.previousElementSibling.setAttribute('placeholder', localize(this.refString))
|
|
126
|
-
}
|
|
127
|
-
}))
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
`<xin-localized>` has a `refString` attribute (which defaults to its initial `textContent`)
|
|
131
|
-
which is the text that it localizes. You can set it directly.
|
|
132
|
-
|
|
133
|
-
It also has an `localeChanged` method which defaults to setting the content of the element
|
|
134
|
-
to the localized reference string, but which you can override, to (for example) set a property
|
|
135
|
-
or attribute of the parent element.
|
|
136
|
-
|
|
137
|
-
> `<xin-localized>` *can* be used inside the shadowDOM of other custom-elements.
|
|
138
|
-
|
|
139
|
-
## `i18n`
|
|
140
|
-
|
|
141
|
-
All of the data can be bound in the `i18n` proxy (`xin.i18n`), including the currently selected
|
|
142
|
-
locale (which will default to `navigator.language`).
|
|
143
|
-
|
|
144
|
-
You can take a look at `xin.i18n` in the console. `i18n` can be used to access localization
|
|
145
|
-
data directly, and also to determine which locales are available `i18n.locales` and set the
|
|
146
|
-
locale programmatically (e.g. `i18n.locale = 'en'`).
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
if (i18n.locales.includes('fr')) {
|
|
150
|
-
i18n.locale = 'fr'
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Creating Localized String Data
|
|
155
|
-
|
|
156
|
-
You can create your own localization data using any spreadsheet and exporting TSV.
|
|
157
|
-
|
|
158
|
-
E.g. you can automatically create localization data
|
|
159
|
-
using something like my [localized](https://docs.google.com/spreadsheets/d/1L0_4g_dDhVCwVVxLzYbMj_H86xSp9lsRCKj7IS9psso/edit?usp=sharing)
|
|
160
|
-
Google Sheet which leverages `googletranslate` to automatically translate reference strings
|
|
161
|
-
(and which you can manually override as you like).
|
|
162
|
-
|
|
163
|
-
E.g. in this demo I've replaced the incorrect translation of "Finnish"
|
|
164
|
-
(`googletranslate` used the word for Finnish nationality rather than the language).
|
|
165
|
-
|
|
166
|
-
The format of the input data is a table in TSV format, that looks like this:
|
|
167
|
-
|
|
168
|
-
en-US | fr | fi | sv | zh
|
|
169
|
-
------|----|----|----|----
|
|
170
|
-
English (US) | French | Finnish | Swedish | Chinese (Mandarin)
|
|
171
|
-
English (US) | Français | suomi | svenska | 中文(普通话)
|
|
172
|
-
🇺🇸 | 🇫🇷 | 🇫🇮 | 🇸🇪 | 🇨🇳
|
|
173
|
-
Icon | Icône | Kuvake | Ikon | 图标
|
|
174
|
-
Ok | D'accord | Ok | Ok | 好的
|
|
175
|
-
Cancel | Annuler | Peruuttaa | Avboka | 取消
|
|
176
|
-
|
|
177
|
-
- Column 1 is your reference language.
|
|
178
|
-
- Row 1 is [language code](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes).
|
|
179
|
-
- Row 2 is the name of the language in your reference language.
|
|
180
|
-
- Row 3 is the name of the language in itself (because it's silly to expect people
|
|
181
|
-
to know the name of their language in a language they don't know)
|
|
182
|
-
- Row 4 is the flag emoji for that language (yes, that's problematic, but languages
|
|
183
|
-
do not have flags, per se)
|
|
184
|
-
- Rows 5 and on are user interface strings you want to localize
|
|
185
|
-
|
|
186
|
-
In the spreadsheet provided, each cell contains a formula that translates the term
|
|
187
|
-
in the left-most column from the language in that column to the language in the
|
|
188
|
-
destination column. Once you have an automatic translation, you can hand off the
|
|
189
|
-
sheet to language experts to vet the translations.
|
|
190
|
-
|
|
191
|
-
Finally, create a `tsv` file and then turn that into a Typescript file by wrapping
|
|
192
|
-
the content thus:
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
export default `( content of tsv file )`
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
You use this data using `initLocalization()`.
|
|
199
|
-
|
|
200
|
-
## Leveraging XinLocalized Automatic Updates
|
|
201
|
-
|
|
202
|
-
If you want to leverage XinLocalized's automatic updates you simply need to
|
|
203
|
-
implement `updateLocale` and register yourself with `XinLocalized.allInstances`
|
|
204
|
-
(which is a `Set<AbstractLocalized>).
|
|
205
|
-
|
|
206
|
-
Typically, this would look like something like:
|
|
207
|
-
|
|
208
|
-
```
|
|
209
|
-
class MyLocalizedComponent extends Component {
|
|
210
|
-
...
|
|
211
|
-
|
|
212
|
-
// register yourself as a localized component
|
|
213
|
-
connectecCallback() {
|
|
214
|
-
super.connectedCallback()
|
|
215
|
-
|
|
216
|
-
XinLocalized.allInstances.add(this)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// avoid leaking!
|
|
220
|
-
disconnectecCallback() {
|
|
221
|
-
super.connectedCallback()
|
|
222
|
-
|
|
223
|
-
XinLocalized.allInstances.delete(this)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// presumably your render method does the right things
|
|
227
|
-
updateLocale = () => {
|
|
228
|
-
this.queueRender()
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
*/
|
|
233
|
-
import { Component, boxedProxy, elements, bindings, observe } from 'xinjs';
|
|
234
|
-
import { makeSorter } from './make-sorter';
|
|
235
|
-
import { xinSelect, XinSelect } from './select';
|
|
236
|
-
const { span } = elements;
|
|
237
|
-
export const { i18n } = boxedProxy({
|
|
238
|
-
i18n: {
|
|
239
|
-
locale: window.navigator.language,
|
|
240
|
-
locales: [window.navigator.language],
|
|
241
|
-
languages: [window.navigator.language],
|
|
242
|
-
emoji: [''],
|
|
243
|
-
stringMap: {},
|
|
244
|
-
localeOptions: [
|
|
245
|
-
{
|
|
246
|
-
icon: span(),
|
|
247
|
-
caption: window.navigator.language,
|
|
248
|
-
value: window.navigator.language,
|
|
249
|
-
},
|
|
250
|
-
],
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
bindings.localeOptions = {
|
|
254
|
-
toDOM(select, options) {
|
|
255
|
-
if (select instanceof XinSelect) {
|
|
256
|
-
select.options = options;
|
|
257
|
-
}
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
-
export const setLocale = (language) => {
|
|
261
|
-
if (i18n.locales.includes(language)) {
|
|
262
|
-
i18n.locale = language;
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
console.error(`language ${language} is not available`);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
export const updateLocalized = () => {
|
|
269
|
-
const localizeds = Array.from(XinLocalized.allInstances);
|
|
270
|
-
for (const localized of localizeds) {
|
|
271
|
-
localized.localeChanged();
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
observe(i18n.locale.xinPath, updateLocalized);
|
|
275
|
-
const captionSort = makeSorter((locale) => [
|
|
276
|
-
locale.caption.toLocaleLowerCase(),
|
|
277
|
-
]);
|
|
278
|
-
export function initLocalization(localizedStrings) {
|
|
279
|
-
const [locales, , languages, emoji, ...strings] = localizedStrings
|
|
280
|
-
.split('\n')
|
|
281
|
-
.map((line) => line.split('\t'));
|
|
282
|
-
if (locales && languages && emoji && strings) {
|
|
283
|
-
i18n.locales = locales;
|
|
284
|
-
i18n.languages = languages;
|
|
285
|
-
i18n.emoji = emoji;
|
|
286
|
-
i18n.stringMap = strings.reduce((map, strings) => {
|
|
287
|
-
map[strings[0].toLocaleLowerCase()] = strings;
|
|
288
|
-
return map;
|
|
289
|
-
}, {});
|
|
290
|
-
i18n.localeOptions = locales
|
|
291
|
-
.map((locale, index) => ({
|
|
292
|
-
icon: span({ title: locales[index] }, emoji[index]),
|
|
293
|
-
caption: languages[index],
|
|
294
|
-
value: locale,
|
|
295
|
-
}))
|
|
296
|
-
.sort(captionSort);
|
|
297
|
-
// if user locale isn't available, find the best match
|
|
298
|
-
if (!i18n.locales.includes(i18n.locale.valueOf())) {
|
|
299
|
-
const language = i18n.locale.substring(0, 2);
|
|
300
|
-
i18n.locale =
|
|
301
|
-
i18n.locales.find((locale) => locale.substring(0, 2) === language) ||
|
|
302
|
-
i18n.locales[0];
|
|
303
|
-
}
|
|
304
|
-
updateLocalized();
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
export function localize(ref) {
|
|
308
|
-
if (ref.endsWith('…')) {
|
|
309
|
-
return localize(ref.substring(0, ref.length - 1)) + '…';
|
|
310
|
-
}
|
|
311
|
-
const index = i18n.locales.indexOf(i18n.locale.valueOf());
|
|
312
|
-
if (index > -1) {
|
|
313
|
-
const map = i18n.stringMap[ref.toLocaleLowerCase()];
|
|
314
|
-
const localized = map && map[index];
|
|
315
|
-
if (localized) {
|
|
316
|
-
ref =
|
|
317
|
-
ref.toLocaleLowerCase() === ref
|
|
318
|
-
? localized.toLocaleLowerCase()
|
|
319
|
-
: localized.valueOf();
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return ref;
|
|
323
|
-
}
|
|
324
|
-
export class LocalePicker extends Component {
|
|
325
|
-
hideCaption = false;
|
|
326
|
-
content = () => {
|
|
327
|
-
return xinSelect({
|
|
328
|
-
part: 'select',
|
|
329
|
-
showIcon: true,
|
|
330
|
-
title: localize('Language'),
|
|
331
|
-
bindValue: i18n.locale,
|
|
332
|
-
bindLocaleOptions: i18n.localeOptions,
|
|
333
|
-
});
|
|
334
|
-
};
|
|
335
|
-
constructor() {
|
|
336
|
-
super();
|
|
337
|
-
this.initAttributes('hideCaption');
|
|
338
|
-
}
|
|
339
|
-
render() {
|
|
340
|
-
super.render();
|
|
341
|
-
this.parts.select.toggleAttribute('hide-caption', this.hideCaption);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
export const localePicker = LocalePicker.elementCreator({
|
|
345
|
-
tag: 'xin-locale-picker',
|
|
346
|
-
});
|
|
347
|
-
export class XinLocalized extends Component {
|
|
348
|
-
static allInstances = new Set();
|
|
349
|
-
contents = () => elements.xinSlot();
|
|
350
|
-
refString = '';
|
|
351
|
-
constructor() {
|
|
352
|
-
super();
|
|
353
|
-
this.initAttributes('refString');
|
|
354
|
-
}
|
|
355
|
-
connectedCallback() {
|
|
356
|
-
super.connectedCallback();
|
|
357
|
-
XinLocalized.allInstances.add(this);
|
|
358
|
-
}
|
|
359
|
-
disconnectedCallback() {
|
|
360
|
-
super.disconnectedCallback();
|
|
361
|
-
XinLocalized.allInstances.delete(this);
|
|
362
|
-
}
|
|
363
|
-
localeChanged() {
|
|
364
|
-
if (!this.refString) {
|
|
365
|
-
this.refString = this.textContent || '';
|
|
366
|
-
}
|
|
367
|
-
this.textContent = this.refString ? localize(this.refString) : '';
|
|
368
|
-
}
|
|
369
|
-
render() {
|
|
370
|
-
super.render();
|
|
371
|
-
this.localeChanged();
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
export const xinLocalized = XinLocalized.elementCreator({
|
|
375
|
-
tag: 'xin-localized',
|
|
376
|
-
styleSpec: {
|
|
377
|
-
':host': {
|
|
378
|
-
pointerEvents: 'none',
|
|
379
|
-
},
|
|
380
|
-
},
|
|
381
|
-
});
|
package/dist/make-sorter.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/*#
|
|
2
|
-
# makeSorter
|
|
3
|
-
|
|
4
|
-
I'm always confusing myself when writing sort functions, so I wrote `makeSorter()`. It's
|
|
5
|
-
insanely simple and just works™. It makes writing an array sort callback for anything
|
|
6
|
-
other than an array of numbers or strings easier.
|
|
7
|
-
|
|
8
|
-
```js
|
|
9
|
-
const { select, option, div, span, ul, li } = xinjs.elements
|
|
10
|
-
const { icons, makeSorter } = xinjsui
|
|
11
|
-
|
|
12
|
-
const people = [
|
|
13
|
-
{ first: 'Frasier', last: 'Crane', age: 38 },
|
|
14
|
-
{ first: 'Lilith', last: 'Crane', age: 37 },
|
|
15
|
-
{ first: 'Rebecca', last: 'Howe', age: 35 },
|
|
16
|
-
{ first: 'Woody', last: 'Boyd', age: 25 },
|
|
17
|
-
{ first: 'Sam', last: 'Malone', age: 40 },
|
|
18
|
-
{ first: 'Norm', last: 'Peterson', age: 38 },
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
const sorters = {
|
|
22
|
-
firstSort: makeSorter(person => [person.first]),
|
|
23
|
-
firstDescSort: makeSorter(person => [person.first], false),
|
|
24
|
-
nameSort: makeSorter(person => [person.last, person.first]),
|
|
25
|
-
ageFirst: makeSorter(person => [-person.age, person.last]),
|
|
26
|
-
ageLast: makeSorter(person => [person.age, person.first], [true, false]),
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function person({first, last, age}) {
|
|
30
|
-
return li(`${first} ${last}, ${age}`)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const list = ul()
|
|
34
|
-
sortPicker = select(
|
|
35
|
-
option('Sort by first', {value: 'firstSort'}),
|
|
36
|
-
option('Sort by first (desc)', {value: 'firstDescSort'}),
|
|
37
|
-
option('Sort by last, first', {value: 'nameSort'}),
|
|
38
|
-
option('Sort by age (desc), first', {value: 'ageFirst'}),
|
|
39
|
-
option('Sort by age, last (desc)', {value: 'ageLast'}),
|
|
40
|
-
{
|
|
41
|
-
onChange: render,
|
|
42
|
-
value: 'nameSort'
|
|
43
|
-
},
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
function render () {
|
|
47
|
-
list.textContent = ''
|
|
48
|
-
list.append(...people.sort(sorters[sortPicker.value]).map(person))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
preview.append(
|
|
52
|
-
div(
|
|
53
|
-
sortPicker,
|
|
54
|
-
icons.chevronDown()
|
|
55
|
-
),
|
|
56
|
-
list
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
render()
|
|
60
|
-
```
|
|
61
|
-
```css
|
|
62
|
-
.preview {
|
|
63
|
-
padding: var(--spacing);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.preview div {
|
|
67
|
-
position: absolute;
|
|
68
|
-
top: var(--spacing);
|
|
69
|
-
right: var(--spacing);
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Details
|
|
74
|
-
|
|
75
|
-
To create a sort callback that sorts by propA then propB (if propA is tied):
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
const sorter = makeSorter(
|
|
79
|
-
obj => [obj.propA, obj.propB]
|
|
80
|
-
)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
As above, but sort descending:
|
|
84
|
-
```
|
|
85
|
-
const sorter = makeSorter(
|
|
86
|
-
obj => [obj.propA, obj.propB],
|
|
87
|
-
false
|
|
88
|
-
)
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
As above but propA is sorted ascending, propB descending
|
|
92
|
-
```
|
|
93
|
-
const sorter = makeSorter(
|
|
94
|
-
obj => [obj.propA, obj.propB],
|
|
95
|
-
[true, false]
|
|
96
|
-
)
|
|
97
|
-
```
|
|
98
|
-
*/
|
|
99
|
-
export function makeSorter(sortValuator, ascending = true) {
|
|
100
|
-
return (p, q) => {
|
|
101
|
-
const pSort = sortValuator(p);
|
|
102
|
-
const qSort = sortValuator(q);
|
|
103
|
-
for (const i in pSort) {
|
|
104
|
-
if (pSort[i] !== qSort[i]) {
|
|
105
|
-
const isAscending = Array.isArray(ascending)
|
|
106
|
-
? ascending[i] !== false
|
|
107
|
-
: ascending;
|
|
108
|
-
return isAscending
|
|
109
|
-
? pSort[i] > qSort[i]
|
|
110
|
-
? 1
|
|
111
|
-
: -1
|
|
112
|
-
: pSort[i] > qSort[i]
|
|
113
|
-
? -1
|
|
114
|
-
: 1;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return 0;
|
|
118
|
-
};
|
|
119
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/make-sorter.test.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
import { test, expect } from 'bun:test';
|
|
3
|
-
import { makeSorter } from './make-sorter';
|
|
4
|
-
test('sorts simple values', () => {
|
|
5
|
-
const simpleSort = makeSorter((a) => [a]);
|
|
6
|
-
const array = [5, -1, 3];
|
|
7
|
-
expect([...array].sort(simpleSort)[0]).toBe(-1);
|
|
8
|
-
expect([...array].sort(simpleSort)[1]).toBe(3);
|
|
9
|
-
const strings = ['oh', 'be', 'a', 'fine'];
|
|
10
|
-
expect([...strings].sort(simpleSort)[0]).toBe('a');
|
|
11
|
-
expect([...strings].sort(simpleSort)[1]).toBe('be');
|
|
12
|
-
});
|
|
13
|
-
test('sorts simple values descending', () => {
|
|
14
|
-
const simpleSort = makeSorter((a) => [a], false);
|
|
15
|
-
const array = [5, -1, 3];
|
|
16
|
-
expect([...array].sort(simpleSort)[0]).toBe(5);
|
|
17
|
-
expect([...array].sort(simpleSort)[1]).toBe(3);
|
|
18
|
-
const strings = ['oh', 'be', 'a', 'fine'];
|
|
19
|
-
expect([...strings].sort(simpleSort)[0]).toBe('oh');
|
|
20
|
-
expect([...strings].sort(simpleSort)[1]).toBe('fine');
|
|
21
|
-
});
|
|
22
|
-
test('sorts compound objects', () => {
|
|
23
|
-
const sortNameThenId = makeSorter((a) => [
|
|
24
|
-
a.name,
|
|
25
|
-
a.id,
|
|
26
|
-
]);
|
|
27
|
-
const sortLowercaseNameThenId = makeSorter((a) => [a.name.toLocaleLowerCase(), a.id]);
|
|
28
|
-
const idThenName = makeSorter((a) => [
|
|
29
|
-
a.id,
|
|
30
|
-
a.name,
|
|
31
|
-
]);
|
|
32
|
-
const array = [
|
|
33
|
-
{ name: 'alice', id: 100 },
|
|
34
|
-
{ name: 'bob', id: 1 },
|
|
35
|
-
{ name: 'alice', id: 10 },
|
|
36
|
-
{ name: 'Zed', id: 5 },
|
|
37
|
-
{ name: 'smaug', id: -7 },
|
|
38
|
-
];
|
|
39
|
-
expect([...array].sort(sortNameThenId)[0].id).toBe(5);
|
|
40
|
-
expect([...array].sort(sortNameThenId)[2].id).toBe(100);
|
|
41
|
-
expect([...array].sort(sortNameThenId)[4].id).toBe(-7);
|
|
42
|
-
expect([...array].sort(sortLowercaseNameThenId)[0].id).toBe(10);
|
|
43
|
-
expect([...array].sort(sortLowercaseNameThenId)[2].id).toBe(1);
|
|
44
|
-
expect([...array].sort(sortLowercaseNameThenId)[4].id).toBe(5);
|
|
45
|
-
expect([...array].sort(idThenName)[0].id).toBe(-7);
|
|
46
|
-
expect([...array].sort(idThenName)[2].id).toBe(5);
|
|
47
|
-
expect([...array].sort(idThenName)[4].id).toBe(100);
|
|
48
|
-
});
|
package/dist/mapbox.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
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
|
-
});
|