lib0 1.0.0-rc.2 → 1.0.0-rc.21

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 (141) hide show
  1. package/README.md +0 -1315
  2. package/{types → dist}/buffer.d.ts +2 -10
  3. package/{types → dist}/delta/delta.d.ts +582 -144
  4. package/dist/delta/position.d.ts +65 -0
  5. package/dist/delta/rdt/delta.d.ts +57 -0
  6. package/dist/delta/rdt/dom.d.ts +102 -0
  7. package/dist/delta/rdt.d.ts +180 -0
  8. package/dist/delta/transformer/attr.d.ts +64 -0
  9. package/dist/delta/transformer/attribution-to-format.d.ts +52 -0
  10. package/dist/delta/transformer/children.d.ts +69 -0
  11. package/dist/delta/transformer/conform.d.ts +83 -0
  12. package/dist/delta/transformer/core.d.ts +189 -0
  13. package/dist/delta/transformer/full-attributions.d.ts +37 -0
  14. package/dist/delta/transformer/id.d.ts +2 -0
  15. package/dist/delta/transformer/inline.d.ts +89 -0
  16. package/dist/delta/transformer/pipe.d.ts +182 -0
  17. package/dist/delta/transformer/project.d.ts +223 -0
  18. package/dist/delta/transformer/rename-attrs.d.ts +75 -0
  19. package/dist/delta/transformer/rename.d.ts +41 -0
  20. package/dist/delta/transformer/value.d.ts +92 -0
  21. package/dist/delta/transformer.d.ts +14 -0
  22. package/dist/diff/patience.d.ts +25 -0
  23. package/{types → dist}/environment.d.ts +10 -0
  24. package/dist/hash/fnv1a.d.ts +11 -0
  25. package/{types → dist}/schema.d.ts +49 -38
  26. package/{types → dist}/ts.d.ts +4 -0
  27. package/dist/webcrypto.d.ts +3 -0
  28. package/package.json +148 -82
  29. package/src/bin/0serve.js +11 -3
  30. package/src/broadcastchannel.js +6 -1
  31. package/src/buffer.js +35 -25
  32. package/src/decoding.js +1 -1
  33. package/src/delta/delta.js +2531 -564
  34. package/src/delta/position.js +165 -0
  35. package/src/delta/rdt/delta.js +105 -0
  36. package/src/delta/rdt/dom.js +307 -0
  37. package/src/delta/rdt.js +255 -0
  38. package/src/delta/transformer/attr.js +126 -0
  39. package/src/delta/transformer/attribution-to-format.js +335 -0
  40. package/src/delta/transformer/children.js +219 -0
  41. package/src/delta/transformer/conform.js +401 -0
  42. package/src/delta/transformer/core.js +323 -0
  43. package/src/delta/transformer/full-attributions.js +282 -0
  44. package/src/delta/transformer/id.js +13 -0
  45. package/src/delta/transformer/inline.js +818 -0
  46. package/src/delta/transformer/pipe.js +272 -0
  47. package/src/delta/transformer/project.js +526 -0
  48. package/src/delta/transformer/rename-attrs.js +152 -0
  49. package/src/delta/transformer/rename.js +84 -0
  50. package/src/delta/transformer/value.js +214 -0
  51. package/src/delta/transformer.js +38 -0
  52. package/src/diff/patience.js +22 -11
  53. package/src/dom.js +2 -2
  54. package/src/encoding.js +1 -1
  55. package/src/environment.js +15 -0
  56. package/src/hash/fnv1a.js +82 -0
  57. package/src/number.js +1 -3
  58. package/src/performance.node.js +3 -3
  59. package/src/schema.js +231 -177
  60. package/src/string.js +3 -5
  61. package/src/testing.js +1 -1
  62. package/src/trait/equality.js +1 -1
  63. package/src/trait/fingerprint.js +1 -1
  64. package/src/ts.js +11 -0
  65. package/src/bin/gendocs.js +0 -117
  66. package/src/component.js +0 -414
  67. package/src/delta/binding.js +0 -372
  68. package/src/tree.js +0 -546
  69. package/types/bin/gendocs.d.ts +0 -3
  70. package/types/component.d.ts +0 -86
  71. package/types/delta/binding.d.ts +0 -106
  72. package/types/diff/patience.d.ts +0 -16
  73. package/types/tree.d.ts +0 -96
  74. package/types/webcrypto.d.ts +0 -3
  75. /package/{types → dist}/array.d.ts +0 -0
  76. /package/{types → dist}/bin/0ecdsa-generate-keypair.d.ts +0 -0
  77. /package/{types → dist}/bin/0serve.d.ts +0 -0
  78. /package/{types → dist}/bin/gentesthtml.d.ts +0 -0
  79. /package/{types → dist}/binary.d.ts +0 -0
  80. /package/{types → dist}/broadcastchannel.d.ts +0 -0
  81. /package/{types → dist}/cache.d.ts +0 -0
  82. /package/{types → dist}/conditions.d.ts +0 -0
  83. /package/{types → dist}/crypto/aes-gcm.d.ts +0 -0
  84. /package/{types → dist}/crypto/common.d.ts +0 -0
  85. /package/{types → dist}/crypto/ecdsa.d.ts +0 -0
  86. /package/{types → dist}/crypto/jwt.d.ts +0 -0
  87. /package/{types → dist}/crypto/rsa-oaep.d.ts +0 -0
  88. /package/{types → dist}/decoding.d.ts +0 -0
  89. /package/{types → dist}/diff.d.ts +0 -0
  90. /package/{types → dist}/dom.d.ts +0 -0
  91. /package/{types → dist}/encoding.d.ts +0 -0
  92. /package/{types → dist}/error.d.ts +0 -0
  93. /package/{types → dist}/eventloop.d.ts +0 -0
  94. /package/{types → dist}/function.d.ts +0 -0
  95. /package/{types → dist}/hash/rabin-gf2-polynomial.d.ts +0 -0
  96. /package/{types → dist}/hash/rabin-uncached.d.ts +0 -0
  97. /package/{types → dist}/hash/rabin.d.ts +0 -0
  98. /package/{types → dist}/hash/sha256.d.ts +0 -0
  99. /package/{types → dist}/hash/sha256.node.d.ts +0 -0
  100. /package/{types → dist}/indexeddb.d.ts +0 -0
  101. /package/{types → dist}/indexeddbV2.d.ts +0 -0
  102. /package/{types → dist}/iterator.d.ts +0 -0
  103. /package/{types → dist}/json.d.ts +0 -0
  104. /package/{types → dist}/list.d.ts +0 -0
  105. /package/{types → dist}/logging.common.d.ts +0 -0
  106. /package/{types → dist}/logging.d.ts +0 -0
  107. /package/{types → dist}/logging.node.d.ts +0 -0
  108. /package/{types → dist}/map.d.ts +0 -0
  109. /package/{types → dist}/math.d.ts +0 -0
  110. /package/{types → dist}/metric.d.ts +0 -0
  111. /package/{types → dist}/mutex.d.ts +0 -0
  112. /package/{types → dist}/number.d.ts +0 -0
  113. /package/{types → dist}/object.d.ts +0 -0
  114. /package/{types → dist}/observable.d.ts +0 -0
  115. /package/{types → dist}/pair.d.ts +0 -0
  116. /package/{types → dist}/performance.d.ts +0 -0
  117. /package/{types → dist}/performance.node.d.ts +0 -0
  118. /package/{types → dist}/pledge.d.ts +0 -0
  119. /package/{types → dist}/prng/Mt19937.d.ts +0 -0
  120. /package/{types → dist}/prng/Xoroshiro128plus.d.ts +0 -0
  121. /package/{types → dist}/prng/Xorshift32.d.ts +0 -0
  122. /package/{types → dist}/prng.d.ts +0 -0
  123. /package/{types → dist}/promise.d.ts +0 -0
  124. /package/{types → dist}/queue.d.ts +0 -0
  125. /package/{types → dist}/random.d.ts +0 -0
  126. /package/{types → dist}/set.d.ts +0 -0
  127. /package/{types → dist}/sort.d.ts +0 -0
  128. /package/{types → dist}/statistics.d.ts +0 -0
  129. /package/{types → dist}/storage.d.ts +0 -0
  130. /package/{types → dist}/string.d.ts +0 -0
  131. /package/{types → dist}/symbol.d.ts +0 -0
  132. /package/{types → dist}/testing.d.ts +0 -0
  133. /package/{types → dist}/time.d.ts +0 -0
  134. /package/{types → dist}/trait/equality.d.ts +0 -0
  135. /package/{types → dist}/trait/fingerprint.d.ts +0 -0
  136. /package/{types → dist}/trait/traits.d.ts +0 -0
  137. /package/{types → dist}/url.d.ts +0 -0
  138. /package/{types → dist}/webcrypto.deno.d.ts +0 -0
  139. /package/{types → dist}/webcrypto.node.d.ts +0 -0
  140. /package/{types → dist}/webcrypto.react-native.d.ts +0 -0
  141. /package/{types → dist}/websocket.d.ts +0 -0
package/src/string.js CHANGED
@@ -84,8 +84,7 @@ export const _encodeUtf8Native = str => utf8TextEncoder.encode(str)
84
84
  * @param {string} str
85
85
  * @return {Uint8Array}
86
86
  */
87
- /* c8 ignore next 2 */
88
- /* @__NO_SIDE_EFFECTS__ */
87
+ /* c8 ignore next 1 */
89
88
  export const encodeUtf8 = /* @__PURE__ */(() => utf8TextEncoder ? _encodeUtf8Native : _encodeUtf8Polyfill)()
90
89
 
91
90
  /**
@@ -135,9 +134,8 @@ export const _decodeUtf8Native = buf => /** @type {TextDecoder} */ (utf8TextDeco
135
134
  * @param {Uint8Array} buf
136
135
  * @return {string}
137
136
  */
138
- /* c8 ignore next 2 */
139
- /* @__NO_SIDE_EFFECTS__ */
140
- export const decodeUtf8 = utf8TextDecoder ? _decodeUtf8Native : _decodeUtf8Polyfill
137
+ /* c8 ignore next 1 */
138
+ export const decodeUtf8 = /* @__PURE__ */ (() => utf8TextDecoder ? _decodeUtf8Native : _decodeUtf8Polyfill)()
141
139
 
142
140
  /**
143
141
  * @param {string} str The initial string
package/src/testing.js CHANGED
@@ -63,7 +63,7 @@ export { production } from './environment.js'
63
63
  export const extensive = env.hasConf('extensive')
64
64
 
65
65
  /* c8 ignore next */
66
- export const envSeed = env.hasParam('--seed') ? Number.parseInt(env.getParam('--seed', '0')) : null
66
+ export const envSeed = /* @__PURE__ */(() => env.hasParam('--seed') ? Number.parseInt(env.getParam('--seed', '0')) : null)()
67
67
 
68
68
  export class TestCase {
69
69
  /**
@@ -1,4 +1,4 @@
1
- export const EqualityTraitSymbol = Symbol('Equality')
1
+ export const EqualityTraitSymbol = Symbol.for('0:equality')
2
2
 
3
3
  /**
4
4
  * @typedef {{ [EqualityTraitSymbol]:(other:EqualityTrait)=>boolean }} EqualityTrait
@@ -2,7 +2,7 @@ import * as encoding from '../encoding.js'
2
2
  import * as rabin from '../hash/rabin.js'
3
3
  import * as buffer from '../buffer.js'
4
4
 
5
- export const FingerprintTraitSymbol = Symbol('Fingerprint')
5
+ export const FingerprintTraitSymbol = Symbol.for('0:fingerprint')
6
6
 
7
7
  /**
8
8
  * When implementing this trait, it is recommended to write some sort of "magic number" first to
package/src/ts.js CHANGED
@@ -45,6 +45,17 @@
45
45
  * @typedef {{[K in keyof OBJ as K extends keyof Renames ? Renames[K] : K extends Renames[keyof Renames] ? never : K]: OBJ[K]}} PropsRename
46
46
  */
47
47
 
48
+ /**
49
+ * Convert a union type to an intersection type.
50
+ *
51
+ * @example
52
+ * // { a: string } & { b: number }
53
+ * type R = UnionToIntersection<{ a: string } | { b: number }>
54
+ *
55
+ * @template U
56
+ * @typedef {(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never} UnionToIntersection
57
+ */
58
+
48
59
  /**
49
60
  * Merge two objects, keeping only keys present in both with intersected value types.
50
61
  * e.g. IntersectObjects<{a:string,b:string|number},{b:string,c:number}> = {b:string}
@@ -1,117 +0,0 @@
1
- #!/usr/bin/env node
2
- // @ts-ignore
3
- import jsdoc from 'jsdoc-api'
4
- import * as fs from 'fs'
5
-
6
- const firstTagContentRegex = /<\w>([^<]+)<\/\w>([^]*)/
7
- const jsdocReturnRegex = /\* @return {(.*)}/
8
- const jsdocTypeRegex = /\* @type {(.*)}/
9
-
10
- const files = fs.readdirSync('./').filter(file => /(?<!(test|config))\.js$/.test(file))
11
-
12
- const _ltregex = /</g
13
- const _rtregex = />/g
14
- /**
15
- * @param {string} s
16
- */
17
- const toSafeHtml = s => s.replace(_ltregex, '&lt;').replace(_rtregex, '&gt;')
18
-
19
- const READMEcontent = fs.readFileSync('./README.md', 'utf8')
20
-
21
- jsdoc.explain({
22
- files,
23
- configure: '.jsdoc.json'
24
- }).then(/** @param {Array<any>} json */ json => {
25
- const strBuilder = []
26
- /**
27
- * @type {Object<string, { items: Array<any>, name: string, description: string }>}
28
- */
29
- const modules = {}
30
- json.forEach(item => {
31
- if (item.meta && item.meta.filename) {
32
- const mod = modules[item.meta.filename] || (modules[item.meta.filename] = { items: [], name: item.meta.filename.slice(0, -3), description: '' })
33
- if (item.kind === 'module') {
34
- mod.name = item.name
35
- mod.description = item.description || ''
36
- } else {
37
- mod.items.push(item)
38
- }
39
- }
40
- })
41
- /**
42
- * @type {Object<string,string>}
43
- */
44
- const classDescriptions = {}
45
- for (const fileName in modules) {
46
- const mod = modules[fileName]
47
- const items = mod.items
48
- const desc = firstTagContentRegex.exec(mod.description)
49
- const descHead = desc ? desc[1] : ''
50
- const descRest = desc ? desc[2] : ''
51
- strBuilder.push(`<details><summary><b>[lib0/${mod.name}]</b> ${descHead}</summary>`)
52
- strBuilder.push(`<pre>import * as ${mod.name} from 'lib0/${fileName.slice(0, -3)}'</pre>`)
53
- if (descRest.length > 0) {
54
- strBuilder.push(descRest)
55
- }
56
- strBuilder.push('<dl>')
57
- for (let i = 0; i < items.length; i++) {
58
- const item = items[i]
59
- if (!item.ignore && item.scope !== 'inner' && item.name[0] !== '_' && item.longname.indexOf('~') < 0) {
60
- // strBuilder.push(JSON.stringify(item)) // output json for debugging
61
- switch (item.kind) {
62
- case 'class': {
63
- if (item.params == null) {
64
- classDescriptions[item.longname] = item.classdesc
65
- break
66
- }
67
- }
68
- // eslint-disable-next-line
69
- case 'constant': {
70
- if (item.params == null && item.returns == null) {
71
- const typeEval = jsdocTypeRegex.exec(item.comment)
72
- strBuilder.push(`<b><code>${item.longname.slice(7)}${typeEval ? (': ' + toSafeHtml(typeEval[1])) : ''}</code></b><br>`)
73
- if (item.description) {
74
- strBuilder.push(`<dd>${item.description}</dd>`)
75
- }
76
- break
77
- }
78
- }
79
- // eslint-disable-next-line
80
- case 'function': {
81
- /**
82
- * @param {string} name
83
- */
84
- const getOriginalParamTypeDecl = name => {
85
- const regval = new RegExp('@param {(.*)} \\[?' + name + '\\]?[^\\w]*').exec(item.comment)
86
- return regval ? regval[1] : null
87
- }
88
- if (item.params == null && item.returns == null) {
89
- break
90
- }
91
- const paramVal = (item.params || []).map(/** @param {any} ret */ ret => `${ret.name}: ${getOriginalParamTypeDecl(ret.name) || ret.type.names.join('|')}`).join(', ')
92
- const evalReturnRegex = jsdocReturnRegex.exec(item.comment)
93
- const returnVal = evalReturnRegex ? `: ${evalReturnRegex[1]}` : (item.returns ? item.returns.map(/** @param {any} r */ r => r.type.names.join('|')).join('|') : '')
94
- strBuilder.push(`<b><code>${item.kind === 'class' ? 'new ' : ''}${item.longname.slice(7)}(${toSafeHtml(paramVal)})${toSafeHtml(returnVal)}</code></b><br>`)
95
- const desc = item.description || item.classdesc || classDescriptions[item.longname] || null
96
- if (desc) {
97
- strBuilder.push(`<dd>${desc}</dd>`)
98
- }
99
- break
100
- }
101
- case 'member': {
102
- if (item.type) {
103
- strBuilder.push(`<b><code>${item.longname.slice(7)}: ${toSafeHtml(/** @type {RegExpExecArray} */ (jsdocTypeRegex.exec(item.comment))[1])}</code></b><br>`)
104
- if (item.description) {
105
- strBuilder.push(`<dd>${item.description}</dd>`)
106
- }
107
- }
108
- }
109
- }
110
- }
111
- }
112
- strBuilder.push('</dl>')
113
- strBuilder.push('</details>')
114
- }
115
- const replaceReadme = READMEcontent.replace(/<details>[^]*<\/details>/, strBuilder.join('\n'))
116
- fs.writeFileSync('./README.md', replaceReadme)
117
- })
package/src/component.js DELETED
@@ -1,414 +0,0 @@
1
- /* eslint-env browser */
2
-
3
- /**
4
- * Web components.
5
- *
6
- * @module component
7
- */
8
-
9
- import * as dom from './dom.js'
10
- import * as diff from './diff.js'
11
- import * as object from './object.js'
12
- import * as json from './json.js'
13
- import * as string from './string.js'
14
- import * as array from './array.js'
15
- import * as number from './number.js'
16
- import * as func from './function.js'
17
-
18
- /**
19
- * @type {CustomElementRegistry}
20
- */
21
- export const registry = customElements
22
-
23
- /**
24
- * @param {string} name
25
- * @param {any} constr
26
- * @param {ElementDefinitionOptions} [opts]
27
- */
28
- export const define = (name, constr, opts) => registry.define(name, constr, opts)
29
-
30
- /**
31
- * @param {string} name
32
- * @return {Promise<CustomElementConstructor>}
33
- */
34
- export const whenDefined = name => registry.whenDefined(name)
35
-
36
- const upgradedEventName = 'upgraded'
37
- const connectedEventName = 'connected'
38
- const disconnectedEventName = 'disconnected'
39
-
40
- /**
41
- * @template S
42
- */
43
- export class Lib0Component extends HTMLElement {
44
- /**
45
- * @param {S} [state]
46
- */
47
- constructor (state) {
48
- super()
49
- /**
50
- * @type {S|null}
51
- */
52
- this.state = /** @type {any} */ (state)
53
- /**
54
- * @type {any}
55
- */
56
- this._internal = {}
57
- }
58
-
59
- /**
60
- * @param {S} _state
61
- * @param {boolean} [_forceStateUpdate] Force that the state is rerendered even if state didn't change
62
- */
63
- setState (_state, _forceStateUpdate = true) {}
64
-
65
- /**
66
- * @param {any} _stateUpdate
67
- */
68
- updateState (_stateUpdate) { }
69
- }
70
-
71
- /**
72
- * @param {any} val
73
- * @param {"json"|"string"|"number"} type
74
- * @return {string}
75
- */
76
- const encodeAttrVal = (val, type) => {
77
- if (type === 'json') {
78
- val = json.stringify(val)
79
- }
80
- return val + ''
81
- }
82
-
83
- /**
84
- * @param {any} val
85
- * @param {"json"|"string"|"number"|"bool"} type
86
- * @return {any}
87
- */
88
- const parseAttrVal = (val, type) => {
89
- switch (type) {
90
- case 'json':
91
- return json.parse(val)
92
- case 'number':
93
- return Number.parseFloat(val)
94
- case 'string':
95
- return val
96
- case 'bool':
97
- return val != null
98
- default:
99
- return null
100
- }
101
- }
102
-
103
- /**
104
- * @template S
105
- * @typedef {Object} CONF
106
- * @property {string?} [CONF.template] Template for the shadow dom.
107
- * @property {string} [CONF.style] shadow dom style. Is only used when
108
- * `CONF.template` is defined
109
- * @property {S} [CONF.state] Initial component state.
110
- * @property {function(S,S|null,Lib0Component<S>):void} [CONF.onStateChange] Called when
111
- * the state changes.
112
- * @property {Object<string,function(any, any):Object>} [CONF.childStates] maps from
113
- * CSS-selector to transformer function. The first element that matches the
114
- * CSS-selector receives state updates via the transformer function.
115
- * @property {Object<string,"json"|"number"|"string"|"bool">} [CONF.attrs]
116
- * attrs-keys and state-keys should be camelCase, but the DOM uses kebap-case. I.e.
117
- * `attrs = { myAttr: 4 }` is represeted as `<my-elem my-attr="4" />` in the DOM
118
- * @property {Object<string, function(CustomEvent, Lib0Component<any>):boolean|void>} [CONF.listeners] Maps from dom-event-name
119
- * to event listener.
120
- * @property {function(S, S, Lib0Component<S>):Object<string,string>} [CONF.slots] Fill slots
121
- * automatically when state changes. Maps from slot-name to slot-html.
122
- */
123
-
124
- /**
125
- * @template T
126
- * @param {string} name
127
- * @param {CONF<T>} cnf
128
- * @return {typeof Lib0Component}
129
- */
130
- export const createComponent = (name, { template, style = '', state: defaultState, onStateChange = () => {}, childStates = { }, attrs = {}, listeners = {}, slots = () => ({}) }) => {
131
- /**
132
- * Maps from camelCase attribute name to kebap-case attribute name.
133
- * @type {Object<string,string>}
134
- */
135
- const normalizedAttrs = {}
136
- for (const key in attrs) {
137
- normalizedAttrs[string.fromCamelCase(key, '-')] = key
138
- }
139
- const templateElement = template
140
- ? /** @type {HTMLTemplateElement} */ (dom.parseElement(`
141
- <template>
142
- <style>${style}</style>
143
- ${template}
144
- </template>
145
- `))
146
- : null
147
-
148
- class _Lib0Component extends HTMLElement {
149
- /**
150
- * @param {T} [state]
151
- */
152
- constructor (state) {
153
- super()
154
- /**
155
- * @type {Array<{d:Lib0Component<T>, s:function(any, any):Object}>}
156
- */
157
- this._childStates = []
158
- /**
159
- * @type {Object<string,string>}
160
- */
161
- this._slots = {}
162
- this._init = false
163
- /**
164
- * @type {any}
165
- */
166
- this._internal = {}
167
- /**
168
- * @type {any}
169
- */
170
- this.state = state || null
171
- this.connected = false
172
- // init shadow dom
173
- if (templateElement) {
174
- const shadow = /** @type {ShadowRoot} */ (this.attachShadow({ mode: 'open' }))
175
- shadow.appendChild(templateElement.content.cloneNode(true))
176
- // fill child states
177
- for (const key in childStates) {
178
- this._childStates.push({
179
- d: /** @type {Lib0Component<T>} */ (dom.querySelector(/** @type {any} */ (shadow), key)),
180
- s: childStates[key]
181
- })
182
- }
183
- }
184
- dom.emitCustomEvent(this, upgradedEventName, { bubbles: true })
185
- }
186
-
187
- connectedCallback () {
188
- this.connected = true
189
- if (!this._init) {
190
- this._init = true
191
- const shadow = this.shadowRoot
192
- if (shadow) {
193
- dom.addEventListener(shadow, upgradedEventName, event => {
194
- this.setState(this.state, true)
195
- event.stopPropagation()
196
- })
197
- }
198
- /**
199
- * @type {Object<string, any>}
200
- */
201
- const startState = this.state || object.assign({}, defaultState)
202
- if (attrs) {
203
- for (const key in attrs) {
204
- const normalizedKey = string.fromCamelCase(key, '-')
205
- const val = parseAttrVal(this.getAttribute(normalizedKey), attrs[key])
206
- if (val) {
207
- startState[key] = val
208
- }
209
- }
210
- }
211
- // add event listeners
212
- for (const key in listeners) {
213
- dom.addEventListener(shadow || this, key, event => {
214
- if (listeners[key](/** @type {CustomEvent} */ (event), this) !== false) {
215
- event.stopPropagation()
216
- event.preventDefault()
217
- return false
218
- }
219
- })
220
- }
221
- // first setState call
222
- this.state = null
223
- this.setState(startState)
224
- }
225
- dom.emitCustomEvent(/** @type {any} */ (this.shadowRoot || this), connectedEventName, { bubbles: true })
226
- }
227
-
228
- disconnectedCallback () {
229
- this.connected = false
230
- dom.emitCustomEvent(/** @type {any} */ (this.shadowRoot || this), disconnectedEventName, { bubbles: true })
231
- this.setState(null)
232
- }
233
-
234
- static get observedAttributes () {
235
- return object.keys(normalizedAttrs)
236
- }
237
-
238
- /**
239
- * @param {string} name
240
- * @param {string} oldVal
241
- * @param {string} newVal
242
- *
243
- * @private
244
- */
245
- attributeChangedCallback (name, oldVal, newVal) {
246
- const curState = /** @type {any} */ (this.state)
247
- const camelAttrName = normalizedAttrs[name]
248
- const type = attrs[camelAttrName]
249
- const parsedVal = parseAttrVal(newVal, type)
250
- if (curState && (type !== 'json' || json.stringify(curState[camelAttrName]) !== newVal) && curState[camelAttrName] !== parsedVal && !number.isNaN(parsedVal)) {
251
- this.updateState({ [camelAttrName]: parsedVal })
252
- }
253
- }
254
-
255
- /**
256
- * @param {any} stateUpdate
257
- */
258
- updateState (stateUpdate) {
259
- this.setState(object.assign({}, this.state, stateUpdate))
260
- }
261
-
262
- /**
263
- * @param {any} state
264
- */
265
- setState (state, forceStateUpdates = false) {
266
- const prevState = this.state
267
- this.state = state
268
- if (this._init && (!func.equalityFlat(state, prevState) || forceStateUpdates)) {
269
- // fill slots
270
- if (state) {
271
- const slotElems = slots(state, prevState, this)
272
- for (const key in slotElems) {
273
- const slotContent = slotElems[key]
274
- if (this._slots[key] !== slotContent) {
275
- this._slots[key] = slotContent
276
- const currentSlots = /** @type {Array<any>} */ (key !== 'default' ? array.from(dom.querySelectorAll(this, `[slot="${key}"]`)) : array.from(this.childNodes).filter(/** @param {any} child */ child => !dom.checkNodeType(child, dom.ELEMENT_NODE) || !child.hasAttribute('slot')))
277
- currentSlots.slice(1).map(dom.remove)
278
- const nextSlot = dom.parseFragment(slotContent)
279
- if (key !== 'default') {
280
- array.from(nextSlot.children).forEach(c => c.setAttribute('slot', key))
281
- }
282
- if (currentSlots.length > 0) {
283
- dom.replaceWith(currentSlots[0], nextSlot)
284
- } else {
285
- dom.appendChild(this, nextSlot)
286
- }
287
- }
288
- }
289
- }
290
- onStateChange(state, prevState, this)
291
- if (state != null) {
292
- this._childStates.forEach(cnf => {
293
- const d = cnf.d
294
- if (d.updateState) {
295
- d.updateState(cnf.s(state, this))
296
- }
297
- })
298
- }
299
- for (const key in attrs) {
300
- const normalizedKey = string.fromCamelCase(key, '-')
301
- if (state == null) {
302
- this.removeAttribute(normalizedKey)
303
- } else {
304
- const stateVal = state[key]
305
- const attrsType = attrs[key]
306
- if (!prevState || prevState[key] !== stateVal) {
307
- if (attrsType === 'bool') {
308
- if (stateVal) {
309
- this.setAttribute(normalizedKey, '')
310
- } else {
311
- this.removeAttribute(normalizedKey)
312
- }
313
- } else if (stateVal == null && (attrsType === 'string' || attrsType === 'number')) {
314
- this.removeAttribute(normalizedKey)
315
- } else {
316
- this.setAttribute(normalizedKey, encodeAttrVal(stateVal, attrsType))
317
- }
318
- }
319
- }
320
- }
321
- }
322
- }
323
- }
324
- define(name, _Lib0Component)
325
- // @ts-ignore
326
- return _Lib0Component
327
- }
328
-
329
- /**
330
- * @param {function} definer function that defines a component when executed
331
- */
332
- export const createComponentDefiner = definer => {
333
- /**
334
- * @type {any}
335
- */
336
- let defined = null
337
- return () => {
338
- if (!defined) {
339
- defined = definer()
340
- }
341
- return defined
342
- }
343
- }
344
-
345
- export const defineListComponent = createComponentDefiner(() => {
346
- const ListItem = createComponent('lib0-list-item', {
347
- template: '<slot name="content"></slot>',
348
- slots: state => ({
349
- content: `<div>${state}</div>`
350
- })
351
- })
352
- return createComponent('lib0-list', {
353
- state: { list: /** @type {Array<string>} */ ([]), Item: ListItem },
354
- onStateChange: (state, prevState, component) => {
355
- if (state == null) {
356
- return
357
- }
358
- const { list = /** @type {Array<any>} */ ([]), Item = ListItem } = state
359
- // @todo deep compare here by providing another parameter to simpleDiffArray
360
- let { index, remove, insert } = diff.simpleDiffArray(prevState ? prevState.list : [], list, func.equalityFlat)
361
- if (remove === 0 && insert.length === 0) {
362
- return
363
- }
364
- let child = /** @type {Lib0Component<any>} */ (component.firstChild)
365
- while (index-- > 0) {
366
- child = /** @type {Lib0Component<any>} */ (child.nextElementSibling)
367
- }
368
- let insertStart = 0
369
- while (insertStart < insert.length && remove-- > 0) {
370
- // update existing state
371
- child.setState(insert[insertStart++])
372
- child = /** @type {Lib0Component<any>} */ (child.nextElementSibling)
373
- }
374
- while (remove-- > 0) {
375
- // remove remaining
376
- const prevChild = child
377
- child = /** @type {Lib0Component<any>} */ (child.nextElementSibling)
378
- component.removeChild(prevChild)
379
- }
380
- // insert remaining
381
- component.insertBefore(dom.fragment(insert.slice(insertStart).map(/** @param {any} insState */ insState => {
382
- const el = new Item()
383
- el.setState(insState)
384
- return el
385
- })), child)
386
- }
387
- })
388
- })
389
-
390
- export const defineLazyLoadingComponent = createComponentDefiner(() => createComponent('lib0-lazy', {
391
- state: /** @type {{component:null|String,import:null|function():Promise<any>,state:null|object}} */ ({
392
- component: null, import: null, state: null
393
- }),
394
- attrs: {
395
- component: 'string'
396
- },
397
- onStateChange: ({ component, state, import: getImport }, prevState, componentEl) => {
398
- if (component !== null) {
399
- if (getImport) {
400
- getImport()
401
- }
402
- if (!prevState || component !== prevState.component) {
403
- const el = /** @type {any} */ (dom.createElement(component))
404
- componentEl.innerHTML = ''
405
- componentEl.insertBefore(el, null)
406
- }
407
- const el = /** @type {any} */ (componentEl.firstElementChild)
408
- // @todo generalize setting state and check if setState is defined
409
- if (el.setState) {
410
- el.setState(state)
411
- }
412
- }
413
- }
414
- }))