@plohoj/html-editor 0.0.7 → 0.1.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 -21
- package/README.md +137 -123
- package/dist/index.js +256 -99
- package/dist/index.js.map +1 -1
- package/package.json +25 -25
- package/src/index.ts +25 -23
- package/src/observable/await-element.ts +47 -29
- package/src/observable/await-elements.ts +38 -20
- package/src/observable/await-random-element.ts +40 -22
- package/src/observable/observe-mutation.ts +10 -12
- package/src/observable/observe-pressed-keyboard-buttons.ts +29 -30
- package/src/observable/observe-query-selector-all.ts +154 -101
- package/src/observable/observe-query-selector.ts +176 -112
- package/src/observable/observe-url-changes.ts +23 -0
- package/src/observable/url-change.ts +37 -38
- package/src/operators/blur-element.ts +21 -0
- package/src/operators/click-element.ts +20 -21
- package/src/operators/focus-element.ts +21 -22
- package/src/operators/merge-map-added-elements.ts +120 -77
- package/src/operators/merge-map-by-condition.ts +124 -0
- package/src/operators/merge-map-by-string-condition.ts +57 -0
- package/src/operators/remove-element.ts +8 -9
- package/src/operators/restore-history.ts +53 -51
- package/src/operators/set-input-value.ts +20 -21
- package/src/utils/compose-restore-history.ts +61 -61
- package/src/utils/find-recursively.ts +203 -203
- package/src/utils/random-from-array.ts +6 -6
- package/src/utils/stubs.ts +7 -7
- package/src/operators/merge-map-string-toggle.ts +0 -36
|
@@ -1,112 +1,176 @@
|
|
|
1
|
-
|
|
2
|
-
import { concat, defer, EMPTY, Observable, of } from "rxjs";
|
|
3
|
-
import {
|
|
4
|
-
import { trueStub } from '../utils/stubs';
|
|
5
|
-
import { observeElementMutation } from "./observe-mutation";
|
|
6
|
-
|
|
7
|
-
export interface IObserveQuerySelectorBaseOptions<T extends Element = Element> {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
1
|
+
|
|
2
|
+
import { concat, defer, distinctUntilChanged, EMPTY, mergeMap, Observable, of, switchMap, throttleTime } from "rxjs";
|
|
3
|
+
import { IMergeMapElementChangeOptions, mergeMapAddedElements } from '../operators/merge-map-added-elements';
|
|
4
|
+
import { trueStub } from '../utils/stubs';
|
|
5
|
+
import { observeElementMutation } from "./observe-mutation";
|
|
6
|
+
|
|
7
|
+
export interface IObserveQuerySelectorBaseOptions<T extends Element = Element> {
|
|
8
|
+
query: string;
|
|
9
|
+
/**
|
|
10
|
+
* The parent element within which changes are tracked.
|
|
11
|
+
* @default document.documentElement
|
|
12
|
+
*/
|
|
13
|
+
parent?: Element;
|
|
14
|
+
/** Checks if the added element has any child elements that match the query selectors. */
|
|
15
|
+
has?: string;
|
|
16
|
+
/** Custom validation of each item */
|
|
17
|
+
filter?: (element: T) => boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface IObserveQuerySelectorOptions<E extends Element = Element, O = unknown>
|
|
21
|
+
extends IObserveQuerySelectorBaseOptions<E>, Omit<IMergeMapElementChangeOptions<E, O>, 'project'> {
|
|
22
|
+
/**
|
|
23
|
+
* * When the {@link asRemovedWhen} option emits a `true` value,
|
|
24
|
+
* all currently added items will be returned as removed.
|
|
25
|
+
* * When the {@link asRemovedWhen} option emits a `false` value,
|
|
26
|
+
* the search will resume and all items will again be returned as added.
|
|
27
|
+
*/
|
|
28
|
+
asRemovedWhen?: Observable<Boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* The function that will be called to generate a new stream
|
|
31
|
+
* for each element discovered the first time after it is added.
|
|
32
|
+
*/
|
|
33
|
+
project?: (element: E) => Observable<O>;
|
|
34
|
+
/**
|
|
35
|
+
* The {@link tap} function will be called once for each element discovered the first time after it is added.
|
|
36
|
+
* The {@link tap} function is called before the {@link project} function is called.
|
|
37
|
+
*/
|
|
38
|
+
tap?: (element: E) => void,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IObservedElementChange<T extends Element = Element> {
|
|
42
|
+
/** Element that satisfy the filtering condition. */
|
|
43
|
+
target?: T;
|
|
44
|
+
/** New element that have been added since the last emit. */
|
|
45
|
+
added?: T;
|
|
46
|
+
/** Element that have been removed since the last emit. */
|
|
47
|
+
removed?: T;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Observation changes (addition and deletion) of elements that match to query selectors as an Rx stream. */
|
|
51
|
+
export function observeQuerySelector<E extends Element = Element>(
|
|
52
|
+
options: IObserveQuerySelectorOptions<E> & { project?: undefined },
|
|
53
|
+
): Observable<IObservedElementChange<E>>;
|
|
54
|
+
export function observeQuerySelector<E extends Element = Element, O = unknown>(
|
|
55
|
+
options: IObserveQuerySelectorOptions<E, O>,
|
|
56
|
+
): Observable<O>;
|
|
57
|
+
export function observeQuerySelector<E extends Element = Element>(
|
|
58
|
+
query: string,
|
|
59
|
+
options?: Omit<IObserveQuerySelectorOptions<E>, 'query'> & { project?: undefined },
|
|
60
|
+
): Observable<IObservedElementChange<E>>;
|
|
61
|
+
export function observeQuerySelector<E extends Element = Element, O = unknown>(
|
|
62
|
+
query: string,
|
|
63
|
+
options: Omit<IObserveQuerySelectorOptions<E, O>, 'query'>,
|
|
64
|
+
): Observable<O>;
|
|
65
|
+
export function observeQuerySelector<E extends Element = Element, O = unknown>(
|
|
66
|
+
query: string,
|
|
67
|
+
project: ((element: E) => Observable<O>),
|
|
68
|
+
options: Omit<IObserveQuerySelectorOptions<E>, 'query' | 'project'>,
|
|
69
|
+
): Observable<O>;
|
|
70
|
+
export function observeQuerySelector<E extends Element = Element, O = unknown>(
|
|
71
|
+
queryOrOptions: string | IObserveQuerySelectorOptions<E, O>,
|
|
72
|
+
projectOrOptions?: ((element: E) => Observable<O>) | Omit<IObserveQuerySelectorOptions<E, O>, 'query'>,
|
|
73
|
+
options?: Omit<IObserveQuerySelectorOptions<E>, 'query' | 'project'>
|
|
74
|
+
): Observable<IObservedElementChange<E> | O> {
|
|
75
|
+
// #region Options parsing
|
|
76
|
+
let query: string;
|
|
77
|
+
let project: ((element: E) => Observable<O>) | undefined;
|
|
78
|
+
let stableOptions: Omit<IObserveQuerySelectorOptions<E>, 'query' | 'project'>;
|
|
79
|
+
if (typeof queryOrOptions === 'string') {
|
|
80
|
+
query = queryOrOptions;
|
|
81
|
+
if (typeof projectOrOptions === 'function') {
|
|
82
|
+
project = projectOrOptions;
|
|
83
|
+
stableOptions = options || {};
|
|
84
|
+
} else {
|
|
85
|
+
stableOptions = projectOrOptions || {};
|
|
86
|
+
project = projectOrOptions?.project;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
stableOptions = queryOrOptions;
|
|
90
|
+
query = queryOrOptions.query;
|
|
91
|
+
project = queryOrOptions?.project;
|
|
92
|
+
}
|
|
93
|
+
const {
|
|
94
|
+
parent = document.documentElement,
|
|
95
|
+
asRemovedWhen,
|
|
96
|
+
filter = trueStub,
|
|
97
|
+
has,
|
|
98
|
+
tap,
|
|
99
|
+
...restOfStableOptions
|
|
100
|
+
} = stableOptions;
|
|
101
|
+
// #endregion
|
|
102
|
+
|
|
103
|
+
let targetElement: E | undefined;
|
|
104
|
+
|
|
105
|
+
function checkChanges(): Observable<IObservedElementChange<E>> {
|
|
106
|
+
const querySelectedElements: NodeListOf<E> = parent.querySelectorAll<E>(query);
|
|
107
|
+
let filteredSelectedElement: E | undefined;
|
|
108
|
+
const changes: IObservedElementChange<E> = {};
|
|
109
|
+
|
|
110
|
+
for (const querySelectedElement of querySelectedElements) {
|
|
111
|
+
if (has && !querySelectedElement.querySelector(has)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!filter(querySelectedElement)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
filteredSelectedElement = querySelectedElement;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (filteredSelectedElement === targetElement) {
|
|
123
|
+
return EMPTY;
|
|
124
|
+
}
|
|
125
|
+
if (targetElement) {
|
|
126
|
+
changes.removed = targetElement;
|
|
127
|
+
}
|
|
128
|
+
if (filteredSelectedElement) {
|
|
129
|
+
changes.added = filteredSelectedElement;
|
|
130
|
+
tap?.(filteredSelectedElement);
|
|
131
|
+
}
|
|
132
|
+
changes.target = filteredSelectedElement;
|
|
133
|
+
targetElement = filteredSelectedElement;
|
|
134
|
+
|
|
135
|
+
return of(changes);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const observeQuerySelector$: Observable<IObservedElementChange<E>> = concat(
|
|
139
|
+
defer(() => checkChanges()),
|
|
140
|
+
observeElementMutation(parent, { subtree: true, childList: true }).pipe(
|
|
141
|
+
throttleTime(0, undefined, { leading: true, trailing: true }),
|
|
142
|
+
mergeMap(checkChanges),
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
let observeQuerySelectorWithRemovedWhen$ = observeQuerySelector$;
|
|
147
|
+
|
|
148
|
+
if (asRemovedWhen) {
|
|
149
|
+
const removedObserver$ = defer(() => {
|
|
150
|
+
if (!targetElement) {
|
|
151
|
+
return EMPTY;
|
|
152
|
+
}
|
|
153
|
+
const changes: IObservedElementChange<E> = {
|
|
154
|
+
removed: targetElement,
|
|
155
|
+
};
|
|
156
|
+
targetElement = undefined;
|
|
157
|
+
return of(changes);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
observeQuerySelectorWithRemovedWhen$ = asRemovedWhen.pipe(
|
|
161
|
+
distinctUntilChanged(),
|
|
162
|
+
switchMap(asRemoved => asRemoved ? removedObserver$ : observeQuerySelector$),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (project) {
|
|
167
|
+
return observeQuerySelectorWithRemovedWhen$.pipe(
|
|
168
|
+
mergeMapAddedElements({
|
|
169
|
+
project,
|
|
170
|
+
...restOfStableOptions,
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return observeQuerySelectorWithRemovedWhen$;
|
|
176
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { IMergeMapByStringConditionOptions, mergeMapStringCondition } from '../operators/merge-map-by-string-condition';
|
|
3
|
+
import { urlChange$ } from './url-change';
|
|
4
|
+
|
|
5
|
+
export interface IObserveUrlChangesOptions<T = unknown> extends Partial<IMergeMapByStringConditionOptions<T>> {}
|
|
6
|
+
|
|
7
|
+
/** Observation of `URL` changes that satisfy the conditions. */
|
|
8
|
+
export function observeUrlChanges(
|
|
9
|
+
options?: IObserveUrlChangesOptions<unknown> & { project?: undefined }
|
|
10
|
+
): Observable<string>;
|
|
11
|
+
export function observeUrlChanges<O>(
|
|
12
|
+
options: IObserveUrlChangesOptions<O>
|
|
13
|
+
): Observable<O>;
|
|
14
|
+
export function observeUrlChanges<O>(
|
|
15
|
+
options: IObserveUrlChangesOptions<O> = {}
|
|
16
|
+
): Observable<O | string> {
|
|
17
|
+
return urlChange$.pipe(
|
|
18
|
+
mergeMapStringCondition({
|
|
19
|
+
...options,
|
|
20
|
+
condition: options.condition || (() => true),
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -1,38 +1,37 @@
|
|
|
1
|
-
import { Observable, Subscriber } from "rxjs";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
let
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
window.addEventListener('
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
window.removeEventListener('
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
);
|
|
1
|
+
import { Observable, Subscriber, distinctUntilChanged, shareReplay } from "rxjs";
|
|
2
|
+
|
|
3
|
+
let pushStateSubscriber$: Subscriber<string> | undefined;
|
|
4
|
+
let isPushStateWasInjected = false;
|
|
5
|
+
|
|
6
|
+
function injectPushStateHandler(): void {
|
|
7
|
+
if (isPushStateWasInjected) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const pushState = history.pushState;
|
|
11
|
+
history.pushState = function (...args) {
|
|
12
|
+
pushState.apply(this, args);
|
|
13
|
+
pushStateSubscriber$?.next(location.href);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Emit new location url when the URL is changes
|
|
19
|
+
*/
|
|
20
|
+
export const urlChange$ = new Observable<string>(subscriber$ => {
|
|
21
|
+
function updateURL(): void {
|
|
22
|
+
subscriber$.next(location.href)
|
|
23
|
+
}
|
|
24
|
+
window.addEventListener('hashchange', updateURL);
|
|
25
|
+
window.addEventListener('popstate', updateURL);
|
|
26
|
+
pushStateSubscriber$ = subscriber$;
|
|
27
|
+
subscriber$.next(location.href);
|
|
28
|
+
injectPushStateHandler();
|
|
29
|
+
return () => {
|
|
30
|
+
pushStateSubscriber$ = undefined;
|
|
31
|
+
window.removeEventListener('hashchange', updateURL);
|
|
32
|
+
window.removeEventListener('popstate', updateURL);
|
|
33
|
+
}
|
|
34
|
+
}).pipe(
|
|
35
|
+
distinctUntilChanged(),
|
|
36
|
+
shareReplay(1),
|
|
37
|
+
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MonoTypeOperatorFunction, tap } from "rxjs";
|
|
2
|
+
|
|
3
|
+
function blurElementImmediately(element: Element): void {
|
|
4
|
+
if ('blur' in element) {
|
|
5
|
+
(element as HTMLElement).blur();
|
|
6
|
+
} else {
|
|
7
|
+
element.dispatchEvent(new FocusEvent('blur'));
|
|
8
|
+
element.dispatchEvent(new FocusEvent('focusout', {bubbles: true}));
|
|
9
|
+
}
|
|
10
|
+
console.log(`Blur: `, element);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function blurElement<T extends Element>(): MonoTypeOperatorFunction<T>;
|
|
14
|
+
export function blurElement<T extends Element>(element: T): void;
|
|
15
|
+
export function blurElement<T extends Element>(element?: T): MonoTypeOperatorFunction<T> | void {
|
|
16
|
+
if (element) {
|
|
17
|
+
blurElementImmediately(element);
|
|
18
|
+
} else {
|
|
19
|
+
return tap((element: T) => blurElementImmediately(element));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { MonoTypeOperatorFunction } from "rxjs";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export function clickElement<T extends Element>():
|
|
14
|
-
export function clickElement<T extends Element>(element
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
1
|
+
import { MonoTypeOperatorFunction, tap } from "rxjs";
|
|
2
|
+
|
|
3
|
+
function clickElementImmediately(element: Element): void {
|
|
4
|
+
if ('click' in element) {
|
|
5
|
+
(element as HTMLElement).click();
|
|
6
|
+
} else {
|
|
7
|
+
element.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
8
|
+
}
|
|
9
|
+
console.log(`Click: `, element);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function clickElement<T extends Element>(): MonoTypeOperatorFunction<T>;
|
|
13
|
+
export function clickElement<T extends Element>(element: T): void;
|
|
14
|
+
export function clickElement<T extends Element>(element?: T): MonoTypeOperatorFunction<T> | void {
|
|
15
|
+
if (element) {
|
|
16
|
+
clickElementImmediately(element);
|
|
17
|
+
} else {
|
|
18
|
+
return tap((element: T) => clickElementImmediately(element));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { MonoTypeOperatorFunction } from "rxjs";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
element.dispatchEvent(new FocusEvent('
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export function focusElement<T extends Element>():
|
|
15
|
-
export function focusElement<T extends Element>(element
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
1
|
+
import { MonoTypeOperatorFunction, tap } from "rxjs";
|
|
2
|
+
|
|
3
|
+
function focusElementImmediately(element: Element): void {
|
|
4
|
+
if ('focus' in element) {
|
|
5
|
+
(element as HTMLElement).focus();
|
|
6
|
+
} else {
|
|
7
|
+
element.dispatchEvent(new FocusEvent('focus'));
|
|
8
|
+
element.dispatchEvent(new FocusEvent('focusin', {bubbles: true}));
|
|
9
|
+
}
|
|
10
|
+
console.log(`Focus: `, element);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function focusElement<T extends Element>(): MonoTypeOperatorFunction<T>;
|
|
14
|
+
export function focusElement<T extends Element>(element: T): void;
|
|
15
|
+
export function focusElement<T extends Element>(element?: T): MonoTypeOperatorFunction<T> | void {
|
|
16
|
+
if (element) {
|
|
17
|
+
focusElementImmediately(element);
|
|
18
|
+
} else {
|
|
19
|
+
return tap((element: T) => focusElementImmediately(element));
|
|
20
|
+
}
|
|
21
|
+
}
|