be-intl 0.0.48 → 0.0.49

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 CHANGED
@@ -45,30 +45,23 @@ The semantic example above involves a lot of keyboard tapping of the letters "be
45
45
  🌐-weekday=long 🌐-year=numeric 🌐-month=long 🌐-day=numeric></time>
46
46
  ```
47
47
 
48
- ## Viewing Locally
48
+ ## Viewing Demos Locally
49
49
 
50
- Any web server than can serve static files will do, but...
50
+ 1. Install git
51
+ 2. Fork/clone this repo
52
+ 3. Install node.js
53
+ 4. Open command window to folder where you cloned this repo
54
+ 5. > git submodule add https://github.com/bahrus/types.git types
55
+ 6. > git submodule update --init --recursive
56
+ 7. > npm install
57
+ 8. > npm run build
58
+ 9. > npm run serve
59
+ 10. Open http://localhost:8000/demo/ in a modern browser
51
60
 
52
- 1. Install git.
53
- 2. Fork/clone this repo.
54
- 3. Install node.
55
- 4. Open command window to folder where you cloned this repo.
56
- 5. > npm install
57
- 6. > npm run serve
58
- 7. Open http://localhost:8000/demo in a modern browser.
59
-
60
- ## Importing in ES Modules:
61
-
62
- ```JavaScript
63
- import 'be-intl/be-intl.js';
61
+ ## Running Tests
64
62
 
65
63
  ```
66
-
67
- ## Using from CDN:
68
-
69
- ```html
70
- <script type=module crossorigin=anonymous>
71
- import 'https://esm.run/be-intl';
72
- </script>
64
+ > npm run test
73
65
  ```
74
66
 
67
+
package/be-intl.js CHANGED
@@ -1,87 +1,148 @@
1
1
  // @ts-check
2
- import { propInfo, rejected, resolved } from 'be-enhanced/cc.js';
3
- import { BeValueAdded } from 'be-value-added/be-value-added.js';
4
- /** @import {Actions, AP} from './ts-refs/be-intl/types' */
5
- /** @import {BEConfig, IEnhancement, BEAllProps} from './ts-refs/be-enhanced/types.d.ts' */
6
- /** @import {Positraction, PropLookup, PropInfo} from './ts-refs/trans-render/froop/types.d.ts' */
7
- /** @import {BVAActions, BVAAllProps, BVAP} from './ts-refs/be-value-added/types' */;
2
+ /** @import {Actions, PAP, AllProps, AP} from './types/be-intl/types' */;
3
+ /** @import {RoundaboutOptions} from './types/roundabout/types' */;
4
+ /** @import {ElementEnhancementGateway, SpawnContext} from './types/assign-gingerly/types' */;
5
+ /** @import {EMC} from './types/mount-observer/types' */;
6
+ /** @import {RAConfig} from './types/roundabout/types' */;
7
+
8
+ const defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
9
+
10
+ /** Semantic sugar attributes folded into `format`. */
11
+ const SEMANTIC_KEYS = ['style', 'currency', 'weekday', 'year', 'month', 'day'];
8
12
 
9
13
  /**
10
14
  * @implements {Actions}
11
15
  */
12
- class BeIntl extends BeValueAdded {
16
+ class BeIntl {
17
+
13
18
  /**
14
- * @type {BEConfig<AP & BEAllProps, Actions & IEnhancement>}
19
+ * @this {AllProps & Actions}
20
+ * @param {Element & ElementEnhancementGateway} enhancedElement
21
+ * @param {SpawnContext} ctx
22
+ * @param {PAP} initVals
15
23
  */
16
- static config = {
17
- propInfo: {
18
- ...propInfo,
19
- locale: {},
20
- format: {},
21
- intlDateFormat: {},
22
- intlNumberFormat: {},
23
- currency: {},
24
- },
25
- actions: {
26
-
27
- hydrate: {
28
- ifAllOf: ['attached'],
29
- },
30
- formatNumber: {
31
- ifKeyIn: ['value'],
32
- ifAllOf: ['intlNumberFormat']
33
- },
34
- formatDate: {
35
- ifKeyIn: ['value'],
36
- ifAllOf: ['intlDateFormat']
37
- },
38
- onFormattingChange: {
39
- ifAllOf: ['locale'],
40
- ifKeyIn: ['format', 'currency']
24
+ constructor(enhancedElement, ctx, initVals){
25
+ this.init(this, enhancedElement, ctx, initVals);
26
+ }
27
+
28
+ /**
29
+ * @param {AllProps} self
30
+ * @param {Element & ElementEnhancementGateway} enhancedElement
31
+ * @param {SpawnContext} ctx
32
+ * @param {PAP} initVals
33
+ */
34
+ async init(self, enhancedElement, ctx, initVals){
35
+ const {customData} = /** @type {EMC<any, AllProps, Element, RAConfig<AllProps, Actions>>} */ (ctx.emc);
36
+ /**
37
+ * @type {RoundaboutOptions}
38
+ */
39
+ const raOptions = {
40
+ ...customData,
41
+ vm: self,
42
+ initialPropVals: {
43
+ enhancedElement,
44
+ ...customData?.defaultPropVals,
45
+ ...initVals
41
46
  }
42
- },
43
- positractions: [
44
- /** @type {Positraction<BVAP & BEAllProps>} */
45
- (resolved),
46
- /** @type {Positraction<BVAP & BEAllProps>} */
47
- (rejected),
48
- ]
49
- };
47
+ };
48
+ await (await import('roundabout-lib/roundabout.js')).roundabout(raOptions);
49
+ self.initialized = true;
50
+ }
51
+
50
52
  /**
51
- *
52
- * @param {AP & BEAllProps} self
53
- * @returns {void}
53
+ * Wire up live-update observation, resolve the locale, and seed the value.
54
+ * @param {AP} self
55
+ * @returns {Promise<PAP>}
54
56
  */
55
- formatNumber(self) {
56
- const { enhancedElement, value } = self;
57
- if (value === undefined) {
58
- enhancedElement.textContent = '';
59
- return;
57
+ async hydrate(self){
58
+ const {enhancedElement} = self;
59
+ if(enhancedElement instanceof HTMLElement){
60
+ enhancedElement.ariaLive = 'polite';
61
+ }
62
+
63
+ // Re-format whenever the element's underlying value property changes.
64
+ // <data>/<output> reflect through `value`, <time> through `dateTime`.
65
+ const valueProp = enhancedElement.localName === 'time' ? 'dateTime' : 'value';
66
+ const inference = await infer(enhancedElement);
67
+ const propagator = await inference.getPropagator();
68
+ propagator.addEventListener(valueProp, () => {
69
+ self.value = inference.value;
70
+ });
71
+
72
+ // Opt-in: track lang changes.
73
+ if(self.observeLang && enhancedElement instanceof HTMLElement){
74
+ const langObserver = new MutationObserver(() => {
75
+ self.locale = enhancedElement.lang || defaultLocale;
76
+ });
77
+ langObserver.observe(enhancedElement, {attributes: true, attributeFilter: ['lang']});
60
78
  }
61
- const { intlNumberFormat } = self;
62
- enhancedElement.textContent = intlNumberFormat.format(value);
79
+
80
+ const locale = self.locale
81
+ || (enhancedElement instanceof HTMLElement ? enhancedElement.lang : '')
82
+ || defaultLocale;
83
+ return {locale, value: inference.value};
63
84
  }
64
- formatDate(self) {
65
- const { enhancedElement, value, intlNumberFormat } = self;
66
- enhancedElement.textContent = self.intlDateFormat.format(value);
85
+
86
+ /**
87
+ * (Re)build the Intl formatter. `value` is seeded by `hydrate` and kept
88
+ * fresh by the propagator, so it isn't read here.
89
+ * @param {AP} self
90
+ * @returns {PAP}
91
+ */
92
+ onFormattingChange(self){
93
+ const {enhancedElement, locale} = self;
94
+ // Explicit JSON (`self.format`) wins; semantic sub-attributes fill the gaps.
95
+ const format = {.../** @type {Record<string, any>} */ (self.format || {})};
96
+ for(const key of SEMANTIC_KEYS){
97
+ const val = /** @type {Record<string, any>} */ (self)[key];
98
+ if(val !== undefined && format[key] === undefined) format[key] = val;
99
+ }
100
+ if(enhancedElement.localName === 'time'){
101
+ return {
102
+ intlDateFormat: new Intl.DateTimeFormat(locale, /** @type {Intl.DateTimeFormatOptions} */ (format)),
103
+ resolved: true,
104
+ };
105
+ }
106
+ return {
107
+ intlNumberFormat: new Intl.NumberFormat(locale, /** @type {Intl.NumberFormatOptions} */ (format)),
108
+ resolved: true,
109
+ };
67
110
  }
68
- onFormattingChange(self) {
69
- const { enhancedElement, locale, format, currency } = self;
70
- switch (enhancedElement.localName) {
71
- case 'time':
72
- return {
73
- intlDateFormat: new Intl.DateTimeFormat(locale, format),
74
- resolved: true
75
- };
76
- default:
77
- if (currency !== undefined)
78
- format.currency = currency;
79
- return {
80
- intlNumberFormat: new Intl.NumberFormat(locale, format),
81
- resolved: true
82
- };
111
+
112
+ /**
113
+ * @param {AP} self
114
+ */
115
+ formatNumber(self){
116
+ const {enhancedElement, value, intlNumberFormat} = self;
117
+ if(intlNumberFormat === undefined || value === undefined || value === null){
118
+ enhancedElement.textContent = '';
119
+ return;
83
120
  }
121
+ enhancedElement.textContent = intlNumberFormat.format(/** @type {number} */ (value));
84
122
  }
123
+
124
+ /**
125
+ * @param {AP} self
126
+ */
127
+ formatDate(self){
128
+ const {enhancedElement, value, intlDateFormat} = self;
129
+ if(intlDateFormat === undefined || value === undefined || value === null) return;
130
+ enhancedElement.textContent = intlDateFormat.format(/** @type {Date} */ (value));
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Resolve the `inferencer` enhancement instance for an element. Typed loosely
136
+ * (`Promise<any>`) so the class doesn't couple to the `Infer` shape; `.value`
137
+ * is a live, type-coerced read (`Date` for `<time>`, `number` for `<data>`),
138
+ * `getPropagator()` emits on value-property changes.
139
+ * @param {Element & ElementEnhancementGateway} from
140
+ * @returns {Promise<any>}
141
+ */
142
+ async function infer(from){
143
+ return /** @type {any} */ (
144
+ from.enh.get((await import('inferencer/inferencer.js')).registryItem)
145
+ );
85
146
  }
86
- await BeIntl.bootUp();
147
+
87
148
  export { BeIntl };
package/emc.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "enhConfig": {
3
+ "enhKey": "BeIntl",
4
+ "spawn": "be-intl/be-intl.js",
5
+ "withAttrs": {
6
+ "base": "be-intl",
7
+ "_base": {
8
+ "mapsTo": "format",
9
+ "instanceOf": "Object",
10
+ "valIfNull": {}
11
+ },
12
+ "style": "${base}-style",
13
+ "currency": "${base}-currency",
14
+ "weekday": "${base}-weekday",
15
+ "year": "${base}-year",
16
+ "month": "${base}-month",
17
+ "day": "${base}-day",
18
+ "observeLang": "${base}-observe-lang",
19
+ "_observeLang": {
20
+ "instanceOf": "Boolean"
21
+ }
22
+ }
23
+ },
24
+ "customData": {
25
+ "weakRef": {
26
+ "properties": [
27
+ "enhancedElement"
28
+ ]
29
+ },
30
+ "actions": {
31
+ "hydrate": {
32
+ "ifAllOf": [
33
+ "enhancedElement",
34
+ "initialized"
35
+ ],
36
+ "ifKeyIn": [
37
+ "initialized"
38
+ ]
39
+ },
40
+ "onFormattingChange": {
41
+ "ifAllOf": [
42
+ "locale",
43
+ "initialized"
44
+ ],
45
+ "ifKeyIn": [
46
+ "locale",
47
+ "format",
48
+ "initialized"
49
+ ]
50
+ },
51
+ "formatNumber": {
52
+ "ifAllOf": [
53
+ "intlNumberFormat"
54
+ ],
55
+ "ifKeyIn": [
56
+ "value",
57
+ "intlNumberFormat"
58
+ ]
59
+ },
60
+ "formatDate": {
61
+ "ifAllOf": [
62
+ "intlDateFormat"
63
+ ],
64
+ "ifKeyIn": [
65
+ "value",
66
+ "intlDateFormat"
67
+ ]
68
+ }
69
+ }
70
+ }
71
+ }
package/package.json CHANGED
@@ -1,43 +1,46 @@
1
- {
2
- "name": "be-intl",
3
- "version": "0.0.48",
4
- "keywords": [
5
- "web-components",
6
- "web-component",
7
- "custom-element",
8
- "custom-elements"
9
- ],
10
- "description": "Format numbers, dates automatically and semantically.",
11
- "main": "be-intl.js",
12
- "module": "be-intl.js",
13
- "exports": {
14
- ".": "./be-intl.js",
15
- "./be-intl.js": "./be-intl.js",
16
- "./emc.js": "./emc.js",
17
- "./🌐.js": "./🌐.js"
18
- },
19
- "files": [
20
- "*.js",
21
- "types.d.ts"
22
- ],
23
- "types": "./ts-refs/types.d.ts",
24
- "scripts": {
25
- "test": "playwright test",
26
- "serve": "python3 ./node_modules/ssi-server/ssi_server.py",
27
- "safari": "npx playwright wk http://localhost:8000",
28
- "update": "ncu -u && npm install"
29
- },
30
- "dependencies": {
31
- "be-enhanced": "0.0.163",
32
- "be-hive": "0.0.243",
33
- "be-propagating": "0.0.58",
34
- "be-value-added": "0.0.51",
35
- "trans-render": "0.0.877"
36
- },
37
- "devDependencies": {
38
- "ssi-server": "0.0.1",
39
- "@playwright/test": "1.49.1"
40
- },
41
- "author": "anderson.bruce.b@gmail.com",
42
- "license": "MIT"
43
- }
1
+ {
2
+ "name": "be-intl",
3
+ "version": "0.0.49",
4
+ "keywords": [
5
+ "web-components",
6
+ "web-component",
7
+ "custom-element",
8
+ "custom-elements"
9
+ ],
10
+ "description": "Format numbers, dates automatically and semantically.",
11
+ "main": "be-intl.js",
12
+ "module": "be-intl.js",
13
+ "exports": {
14
+ ".": "./be-intl.js",
15
+ "./be-intl.js": "./be-intl.js",
16
+ "./🌐.js": "./🌐.js",
17
+ "./emc.json": "./emc.json",
18
+ "./🌐.json": "./🌐.json"
19
+ },
20
+ "files": [
21
+ "*.js",
22
+ "*.json",
23
+ "types.d.ts"
24
+ ],
25
+ "types": "./types/be-intl/types.d.ts",
26
+ "scripts": {
27
+ "build": "node emc.mjs > emc.json && node 🌐.mjs > 🌐.json",
28
+ "serve": "node ./node_modules/spa-ssi/serve.js",
29
+ "test": "playwright test",
30
+ "safari": "npx playwright wk http://localhost:8000",
31
+ "update": "ncu -u && npm install"
32
+ },
33
+ "dependencies": {
34
+ "assign-gingerly": "0.0.92",
35
+ "be-hive": "0.1.18",
36
+ "inferencer": "0.0.13",
37
+ "mount-observer": "0.1.53",
38
+ "roundabout-lib": "0.0.36"
39
+ },
40
+ "devDependencies": {
41
+ "spa-ssi": "0.0.27",
42
+ "@playwright/test": "1.62.1"
43
+ },
44
+ "author": "anderson.bruce.b@gmail.com",
45
+ "license": "MIT"
46
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "enhConfig": {
3
+ "enhKey": "🌐",
4
+ "spawn": "be-intl/be-intl.js",
5
+ "withAttrs": {
6
+ "base": "🌐",
7
+ "_base": {
8
+ "mapsTo": "format",
9
+ "instanceOf": "Object",
10
+ "valIfNull": {}
11
+ },
12
+ "style": "${base}-style",
13
+ "currency": "${base}-currency",
14
+ "weekday": "${base}-weekday",
15
+ "year": "${base}-year",
16
+ "month": "${base}-month",
17
+ "day": "${base}-day",
18
+ "observeLang": "${base}-observe-lang",
19
+ "_observeLang": {
20
+ "instanceOf": "Boolean"
21
+ }
22
+ }
23
+ },
24
+ "customData": {
25
+ "weakRef": {
26
+ "properties": [
27
+ "enhancedElement"
28
+ ]
29
+ },
30
+ "actions": {
31
+ "hydrate": {
32
+ "ifAllOf": [
33
+ "enhancedElement",
34
+ "initialized"
35
+ ],
36
+ "ifKeyIn": [
37
+ "initialized"
38
+ ]
39
+ },
40
+ "onFormattingChange": {
41
+ "ifAllOf": [
42
+ "locale",
43
+ "initialized"
44
+ ],
45
+ "ifKeyIn": [
46
+ "locale",
47
+ "format",
48
+ "initialized"
49
+ ]
50
+ },
51
+ "formatNumber": {
52
+ "ifAllOf": [
53
+ "intlNumberFormat"
54
+ ],
55
+ "ifKeyIn": [
56
+ "value",
57
+ "intlNumberFormat"
58
+ ]
59
+ },
60
+ "formatDate": {
61
+ "ifAllOf": [
62
+ "intlDateFormat"
63
+ ],
64
+ "ifKeyIn": [
65
+ "value",
66
+ "intlDateFormat"
67
+ ]
68
+ }
69
+ }
70
+ }
71
+ }
package/emc.js DELETED
@@ -1,60 +0,0 @@
1
- // @ts-check
2
- import { BeHive, MountObserver, seed } from 'be-hive/be-hive.js';
3
- const defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
4
- /** @import {EMC} from './ts-refs/trans-render/be/types' */
5
-
6
- /**
7
- * @type {EMC}
8
- */
9
- export const emc = {
10
- base: 'be-intl',
11
- branches: ['', 'style', 'currency', 'weekday', 'year', 'month', 'day'],
12
- map: {
13
- '0.0': {
14
- instanceOf: 'Object',
15
- mapsTo: 'format',
16
- valIfFalsy: {},
17
- },
18
- '1.0': {
19
- instanceOf: 'String',
20
- mapsTo: '?.format?.style'
21
- },
22
- '2.0': {
23
- instanceOf: 'String',
24
- mapsTo: '?.format?.currency'
25
- },
26
- '3.0': {
27
- instanceOf: 'String',
28
- mapsTo: '?.format?.weekday'
29
- },
30
- '4.0': {
31
- instanceOf: 'String',
32
- mapsTo: '?.format?.year'
33
- },
34
- '5.0': {
35
- instanceOf: 'String',
36
- mapsTo: '?.format?.month'
37
- },
38
- '6.0': {
39
- instanceOf: 'String',
40
- mapsTo: '?.format?.day'
41
- }
42
- },
43
- osotas: [
44
- {
45
- name: 'lang',
46
- valIfNull: defaultLocale,
47
- mapsTo: 'locale',
48
- }
49
- ],
50
- enhPropKey: 'beIntl',
51
- importEnh: async () => {
52
- const { BeIntl } =
53
- /** @type {{new(): IEnhancement<Element>}} */
54
- /** @type {any} */
55
- (await import('./be-intl.js'));
56
- return BeIntl;
57
- }
58
- };
59
- const mose = seed(emc);
60
- MountObserver.synthesize(document, BeHive, mose);
@@ -1,15 +0,0 @@
1
- // @ts-check
2
- import { MountObserver, seed, BeHive } from 'be-hive/be-hive.js';
3
- import { emc as baseEMC } from './emc.js';
4
- /** @import {EMC} from './ts-refs/trans-render/be/types.d.ts' */
5
-
6
- /**
7
- * @type {EMC}
8
- */
9
- export const emc = {
10
- ...baseEMC,
11
- base: '🌐',
12
- enhPropKey: '🌐',
13
- };
14
- const mose = seed(emc);
15
- MountObserver.synthesize(document, BeHive, mose);