@douyinfe/semi-foundation 2.70.1 → 2.71.0-alpha.3
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/cascader/constants.ts +2 -0
- package/cascader/foundation.ts +49 -2
- package/dragMove/foundation.ts +194 -0
- package/highlight/foundation.ts +211 -0
- package/jsonViewer/constants.ts +7 -0
- package/jsonViewer/foundation.ts +72 -0
- package/jsonViewer/jsonViewer.scss +200 -0
- package/jsonViewer/script/build.js +51 -0
- package/jsonViewer/variables.scss +15 -0
- package/lib/cjs/cascader/constants.d.ts +2 -0
- package/lib/cjs/cascader/constants.js +3 -1
- package/lib/cjs/cascader/foundation.d.ts +3 -0
- package/lib/cjs/cascader/foundation.js +55 -3
- package/lib/cjs/dragMove/foundation.d.ts +45 -0
- package/lib/cjs/dragMove/foundation.js +162 -0
- package/lib/cjs/highlight/foundation.d.ts +84 -0
- package/lib/cjs/highlight/foundation.js +184 -0
- package/lib/cjs/jsonViewer/constants.d.ts +4 -0
- package/lib/cjs/jsonViewer/constants.js +10 -0
- package/lib/cjs/jsonViewer/foundation.d.ts +24 -0
- package/lib/cjs/jsonViewer/foundation.js +69 -0
- package/lib/cjs/jsonViewer/jsonViewer.css +168 -0
- package/lib/cjs/jsonViewer/jsonViewer.scss +200 -0
- package/lib/cjs/jsonViewer/variables.scss +15 -0
- package/lib/cjs/lottie/foundation.d.ts +1 -1
- package/lib/cjs/resizable/foundation.d.ts +6 -2
- package/lib/cjs/tree/tree.css +1 -0
- package/lib/cjs/tree/tree.scss +2 -0
- package/lib/es/cascader/constants.d.ts +2 -0
- package/lib/es/cascader/constants.js +3 -1
- package/lib/es/cascader/foundation.d.ts +3 -0
- package/lib/es/cascader/foundation.js +55 -3
- package/lib/es/dragMove/foundation.d.ts +45 -0
- package/lib/es/dragMove/foundation.js +153 -0
- package/lib/es/highlight/foundation.d.ts +84 -0
- package/lib/es/highlight/foundation.js +174 -0
- package/lib/es/jsonViewer/constants.d.ts +4 -0
- package/lib/es/jsonViewer/constants.js +5 -0
- package/lib/es/jsonViewer/foundation.d.ts +24 -0
- package/lib/es/jsonViewer/foundation.js +62 -0
- package/lib/es/jsonViewer/jsonViewer.css +168 -0
- package/lib/es/jsonViewer/jsonViewer.scss +200 -0
- package/lib/es/jsonViewer/variables.scss +15 -0
- package/lib/es/lottie/foundation.d.ts +1 -1
- package/lib/es/resizable/foundation.d.ts +6 -2
- package/lib/es/resizable/foundation.js +3 -2
- package/lib/es/tree/tree.css +1 -0
- package/lib/es/tree/tree.scss +2 -0
- package/lottie/foundation.ts +2 -2
- package/package.json +5 -3
- package/resizable/foundation.ts +19 -5
- package/tree/tree.scss +2 -0
- package/tsconfig.json +1 -1
- package/lib/cjs/utils/getHighlight.d.ts +0 -45
- package/lib/cjs/utils/getHighlight.js +0 -175
- package/lib/es/utils/getHighlight.d.ts +0 -45
- package/lib/es/utils/getHighlight.js +0 -166
- package/utils/getHighlight.ts +0 -178
package/lib/cjs/tree/tree.css
CHANGED
package/lib/cjs/tree/tree.scss
CHANGED
|
@@ -137,6 +137,8 @@ $module: #{$prefix}-tree;
|
|
|
137
137
|
&-highlight {
|
|
138
138
|
font-weight: $font-tree_option_hightlight-fontWeight;
|
|
139
139
|
color: $color-tree_option_hightlight-text;
|
|
140
|
+
// set inherit to override highlight component default bgc
|
|
141
|
+
background-color: inherit;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
&-hidden {
|
|
@@ -14,6 +14,8 @@ declare const strings: {
|
|
|
14
14
|
readonly NONE_MERGE_TYPE: "none";
|
|
15
15
|
readonly SEARCH_POSITION_TRIGGER: "trigger";
|
|
16
16
|
readonly SEARCH_POSITION_CUSTOM: "custom";
|
|
17
|
+
readonly RELATED: "related";
|
|
18
|
+
readonly UN_RELATED: "unRelated";
|
|
17
19
|
};
|
|
18
20
|
declare const numbers: {};
|
|
19
21
|
export { cssClasses, strings, numbers };
|
|
@@ -15,7 +15,9 @@ const strings = {
|
|
|
15
15
|
AUTO_MERGE_VALUE_MERGE_TYPE: 'autoMergeValue',
|
|
16
16
|
NONE_MERGE_TYPE: 'none',
|
|
17
17
|
SEARCH_POSITION_TRIGGER: 'trigger',
|
|
18
|
-
SEARCH_POSITION_CUSTOM: 'custom'
|
|
18
|
+
SEARCH_POSITION_CUSTOM: 'custom',
|
|
19
|
+
RELATED: 'related',
|
|
20
|
+
UN_RELATED: 'unRelated'
|
|
19
21
|
};
|
|
20
22
|
const numbers = {};
|
|
21
23
|
export { cssClasses, strings, numbers };
|
|
@@ -112,6 +112,7 @@ export interface BasicCascaderProps {
|
|
|
112
112
|
enableLeafClick?: boolean;
|
|
113
113
|
preventScroll?: boolean;
|
|
114
114
|
virtualizeInSearch?: Virtualize;
|
|
115
|
+
checkRelation?: string;
|
|
115
116
|
onClear?: () => void;
|
|
116
117
|
triggerRender?: (props: BasicTriggerRenderProps) => any;
|
|
117
118
|
onListScroll?: (e: any, panel: BasicScrollPanelProps) => void;
|
|
@@ -228,6 +229,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
|
|
|
228
229
|
notifyIfLoadData(item: BasicEntity | BasicData): void;
|
|
229
230
|
handleSingleSelect(e: any, item: BasicEntity | BasicData): void;
|
|
230
231
|
_handleMultipleSelect(item: BasicEntity | BasicData): void;
|
|
232
|
+
_handleRelatedMultipleSelect(item: BasicEntity | BasicData): void;
|
|
233
|
+
_handleUnRelatedMultipleSelect(item: BasicEntity | BasicData): void;
|
|
231
234
|
calcNonDisabledCheckedKeys(eventKey: string, targetStatus: boolean): {
|
|
232
235
|
checkedKeys: Set<string>;
|
|
233
236
|
halfCheckedKeys: Set<any>;
|
|
@@ -595,6 +595,19 @@ export default class CascaderFoundation extends BaseFoundation {
|
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
597
|
_handleMultipleSelect(item) {
|
|
598
|
+
const {
|
|
599
|
+
checkRelation
|
|
600
|
+
} = this.getProps();
|
|
601
|
+
if (checkRelation === strings.RELATED) {
|
|
602
|
+
this._handleRelatedMultipleSelect(item);
|
|
603
|
+
} else if (checkRelation === 'unRelated') {
|
|
604
|
+
this._handleUnRelatedMultipleSelect(item);
|
|
605
|
+
}
|
|
606
|
+
this._adapter.updateStates({
|
|
607
|
+
inputValue: ''
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
_handleRelatedMultipleSelect(item) {
|
|
598
611
|
const {
|
|
599
612
|
key
|
|
600
613
|
} = item;
|
|
@@ -659,9 +672,48 @@ export default class CascaderFoundation extends BaseFoundation {
|
|
|
659
672
|
if (curCheckedStatus) {
|
|
660
673
|
this._notifySelect(curRealCheckedKeys);
|
|
661
674
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
675
|
+
}
|
|
676
|
+
_handleUnRelatedMultipleSelect(item) {
|
|
677
|
+
const {
|
|
678
|
+
key
|
|
679
|
+
} = item;
|
|
680
|
+
const {
|
|
681
|
+
checkedKeys,
|
|
682
|
+
keyEntities
|
|
683
|
+
} = this.getStates();
|
|
684
|
+
const {
|
|
685
|
+
max
|
|
686
|
+
} = this.getProps();
|
|
687
|
+
const newCheckedKeys = new Set(checkedKeys);
|
|
688
|
+
let targetStatus;
|
|
689
|
+
const prevCheckedStatus = checkedKeys.has(key);
|
|
690
|
+
if (prevCheckedStatus) {
|
|
691
|
+
newCheckedKeys.delete(key);
|
|
692
|
+
targetStatus = false;
|
|
693
|
+
} else {
|
|
694
|
+
// 查看是否超出 max
|
|
695
|
+
if (_isNumber(max)) {
|
|
696
|
+
if (checkedKeys.size >= max) {
|
|
697
|
+
const checkedEntities = [];
|
|
698
|
+
checkedKeys.forEach(itemKey => {
|
|
699
|
+
checkedEntities.push(keyEntities[itemKey]);
|
|
700
|
+
});
|
|
701
|
+
this._adapter.notifyOnExceed(checkedEntities);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
newCheckedKeys.add(key);
|
|
706
|
+
targetStatus = true;
|
|
707
|
+
}
|
|
708
|
+
if (!this._isControlledComponent()) {
|
|
709
|
+
this._adapter.updateStates({
|
|
710
|
+
checkedKeys: newCheckedKeys
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
this._notifyChange(newCheckedKeys);
|
|
714
|
+
if (targetStatus) {
|
|
715
|
+
this._notifySelect(newCheckedKeys);
|
|
716
|
+
}
|
|
665
717
|
}
|
|
666
718
|
calcNonDisabledCheckedKeys(eventKey, targetStatus) {
|
|
667
719
|
const {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
2
|
+
export declare function clampValueInRange(value: number, min: number, max: number): number;
|
|
3
|
+
export interface DragMoveAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
4
|
+
getDragElement: () => HTMLElement;
|
|
5
|
+
getConstrainer: () => HTMLElement | null;
|
|
6
|
+
getHandler: () => HTMLElement;
|
|
7
|
+
notifyMouseDown?: (e: MouseEvent) => void;
|
|
8
|
+
notifyMouseMove?: (e: MouseEvent) => void;
|
|
9
|
+
notifyMouseUp?: (e: MouseEvent) => void;
|
|
10
|
+
notifyTouchStart?: (e: TouchEvent) => void;
|
|
11
|
+
notifyTouchMove?: (e: TouchEvent) => void;
|
|
12
|
+
notifyTouchEnd?: (e: TouchEvent) => void;
|
|
13
|
+
notifyTouchCancel?: (e: TouchEvent) => void;
|
|
14
|
+
}
|
|
15
|
+
export default class DragMoveFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<DragMoveAdapter<P, S>, P, S> {
|
|
16
|
+
element: HTMLElement;
|
|
17
|
+
xMax: number;
|
|
18
|
+
xMin: number;
|
|
19
|
+
yMax: number;
|
|
20
|
+
yMin: number;
|
|
21
|
+
startOffsetX: number;
|
|
22
|
+
startOffsetY: number;
|
|
23
|
+
get constrainer(): HTMLElement;
|
|
24
|
+
get handler(): HTMLElement;
|
|
25
|
+
constructor(adapter: DragMoveAdapter<P, S>);
|
|
26
|
+
init(): void;
|
|
27
|
+
destroy(): void;
|
|
28
|
+
_registerDocMouseEvent: () => void;
|
|
29
|
+
_unRegisterDocMouseEvent: () => void;
|
|
30
|
+
_registerDocTouchEvent: () => void;
|
|
31
|
+
_unRegisterDocTouchEvent: () => void;
|
|
32
|
+
_unRegisterEvent(): void;
|
|
33
|
+
_calcMoveRange(): void;
|
|
34
|
+
_allowMove(e: MouseEvent | TouchEvent): any;
|
|
35
|
+
_calcOffset: (e: Touch | MouseEvent) => void;
|
|
36
|
+
_preventDefault: (e: MouseEvent | TouchEvent) => void;
|
|
37
|
+
onMouseDown: (e: MouseEvent) => void;
|
|
38
|
+
onTouchStart: (e: TouchEvent) => void;
|
|
39
|
+
_changePos: (e: Touch | MouseEvent) => void;
|
|
40
|
+
_onMouseMove: (e: MouseEvent) => void;
|
|
41
|
+
_onTouchMove: (e: TouchEvent) => void;
|
|
42
|
+
_onMouseUp: (e: MouseEvent) => void;
|
|
43
|
+
_onTouchEnd: (e: TouchEvent) => void;
|
|
44
|
+
_onTouchCancel: (e: TouchEvent) => void;
|
|
45
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import BaseFoundation from '../base/foundation';
|
|
2
|
+
export function clampValueInRange(value, min, max) {
|
|
3
|
+
return Math.min(Math.max(value, min), max);
|
|
4
|
+
}
|
|
5
|
+
export default class DragMoveFoundation extends BaseFoundation {
|
|
6
|
+
get constrainer() {
|
|
7
|
+
return this._adapter.getConstrainer();
|
|
8
|
+
}
|
|
9
|
+
get handler() {
|
|
10
|
+
return this._adapter.getHandler();
|
|
11
|
+
}
|
|
12
|
+
constructor(adapter) {
|
|
13
|
+
super(Object.assign({}, adapter));
|
|
14
|
+
this._registerDocMouseEvent = () => {
|
|
15
|
+
document.addEventListener('mousemove', this._onMouseMove);
|
|
16
|
+
document.addEventListener('mouseup', this._onMouseUp);
|
|
17
|
+
};
|
|
18
|
+
this._unRegisterDocMouseEvent = () => {
|
|
19
|
+
document.removeEventListener('mousemove', this._onMouseMove);
|
|
20
|
+
document.removeEventListener('mouseup', this._onMouseUp);
|
|
21
|
+
};
|
|
22
|
+
this._registerDocTouchEvent = () => {
|
|
23
|
+
document.addEventListener('touchend', this._onTouchEnd);
|
|
24
|
+
document.addEventListener('touchmove', this._onTouchMove);
|
|
25
|
+
document.addEventListener('touchcancel', this._onTouchCancel);
|
|
26
|
+
};
|
|
27
|
+
this._unRegisterDocTouchEvent = () => {
|
|
28
|
+
document.removeEventListener('touchend', this._onTouchEnd);
|
|
29
|
+
document.removeEventListener('touchmove', this._onTouchMove);
|
|
30
|
+
document.removeEventListener('touchcancel', this._onTouchCancel);
|
|
31
|
+
};
|
|
32
|
+
this._calcOffset = e => {
|
|
33
|
+
this.startOffsetX = e.clientX - this.element.offsetLeft;
|
|
34
|
+
this.startOffsetY = e.clientY - this.element.offsetTop;
|
|
35
|
+
};
|
|
36
|
+
this._preventDefault = e => {
|
|
37
|
+
// prevent default behavior, avoid other element(like img, text) be selected
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
};
|
|
40
|
+
this.onMouseDown = e => {
|
|
41
|
+
this._calcMoveRange();
|
|
42
|
+
this._adapter.notifyMouseDown(e);
|
|
43
|
+
if (!this._allowMove(e)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this._registerDocMouseEvent();
|
|
47
|
+
// store origin offset
|
|
48
|
+
this._calcOffset(e);
|
|
49
|
+
this._preventDefault(e);
|
|
50
|
+
};
|
|
51
|
+
this.onTouchStart = e => {
|
|
52
|
+
this._calcMoveRange();
|
|
53
|
+
this._adapter.notifyTouchStart(e);
|
|
54
|
+
if (!this._allowMove(e)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this._registerDocTouchEvent();
|
|
58
|
+
const touch = e.targetTouches[0];
|
|
59
|
+
this._calcOffset(touch);
|
|
60
|
+
this._preventDefault(e);
|
|
61
|
+
};
|
|
62
|
+
this._changePos = e => {
|
|
63
|
+
const {
|
|
64
|
+
customMove
|
|
65
|
+
} = this.getProps();
|
|
66
|
+
let newLeft = e.clientX - this.startOffsetX;
|
|
67
|
+
let newTop = e.clientY - this.startOffsetY;
|
|
68
|
+
if (this.constrainer) {
|
|
69
|
+
newLeft = clampValueInRange(newLeft, this.xMin, this.xMax);
|
|
70
|
+
newTop = clampValueInRange(newTop, this.yMin, this.yMax);
|
|
71
|
+
}
|
|
72
|
+
requestAnimationFrame(() => {
|
|
73
|
+
if (customMove) {
|
|
74
|
+
customMove(this.element, newTop, newLeft);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.element.style.top = newTop + 'px';
|
|
78
|
+
this.element.style.left = newLeft + 'px';
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
this._onMouseMove = e => {
|
|
82
|
+
this._adapter.notifyMouseMove(e);
|
|
83
|
+
this._changePos(e);
|
|
84
|
+
};
|
|
85
|
+
this._onTouchMove = e => {
|
|
86
|
+
this._adapter.notifyTouchMove(e);
|
|
87
|
+
const touch = e.targetTouches[0];
|
|
88
|
+
this._changePos(touch);
|
|
89
|
+
};
|
|
90
|
+
this._onMouseUp = e => {
|
|
91
|
+
this._adapter.notifyMouseUp(e);
|
|
92
|
+
this._unRegisterDocMouseEvent();
|
|
93
|
+
};
|
|
94
|
+
this._onTouchEnd = e => {
|
|
95
|
+
this._adapter.notifyTouchEnd(e);
|
|
96
|
+
this._unRegisterDocTouchEvent();
|
|
97
|
+
};
|
|
98
|
+
this._onTouchCancel = e => {
|
|
99
|
+
this._adapter.notifyTouchCancel(e);
|
|
100
|
+
this._unRegisterDocTouchEvent();
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
init() {
|
|
104
|
+
const element = this._adapter.getDragElement();
|
|
105
|
+
if (!element) {
|
|
106
|
+
throw new Error('drag element must be a valid element');
|
|
107
|
+
}
|
|
108
|
+
this.element = element;
|
|
109
|
+
this.element.style.position = 'absolute';
|
|
110
|
+
this.handler.style.cursor = 'move';
|
|
111
|
+
}
|
|
112
|
+
destroy() {
|
|
113
|
+
this._unRegisterEvent();
|
|
114
|
+
}
|
|
115
|
+
_unRegisterEvent() {
|
|
116
|
+
this._unRegisterDocMouseEvent();
|
|
117
|
+
this._unRegisterDocTouchEvent();
|
|
118
|
+
}
|
|
119
|
+
_calcMoveRange() {
|
|
120
|
+
// Calculate the range within which an element can move
|
|
121
|
+
if (this.constrainer) {
|
|
122
|
+
let node = this.element.offsetParent;
|
|
123
|
+
let startX = 0;
|
|
124
|
+
let startY = 0;
|
|
125
|
+
while (node !== this.constrainer && node !== null) {
|
|
126
|
+
startX -= node.offsetLeft;
|
|
127
|
+
startY -= node.offsetTop;
|
|
128
|
+
node = node.offsetParent;
|
|
129
|
+
}
|
|
130
|
+
this.xMin = startX;
|
|
131
|
+
this.xMax = startX + this.constrainer.offsetWidth - this.element.offsetWidth;
|
|
132
|
+
this.yMin = startY;
|
|
133
|
+
this.yMax = startY + this.constrainer.offsetHeight - this.element.offsetHeight;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
_allowMove(e) {
|
|
137
|
+
const {
|
|
138
|
+
allowMove,
|
|
139
|
+
allowInputDrag
|
|
140
|
+
} = this.getProps();
|
|
141
|
+
// When the clicked object is an input or textarea, clicking should be allowed but dragging should not be allowed.
|
|
142
|
+
if (!allowInputDrag) {
|
|
143
|
+
let target = e.target.tagName.toLowerCase();
|
|
144
|
+
if (target === 'input' || target === 'textarea') {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (allowMove) {
|
|
149
|
+
return allowMove(e, this.element);
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
2
|
+
interface HighlightAdapter extends Partial<DefaultAdapter> {
|
|
3
|
+
}
|
|
4
|
+
interface ChunkQuery {
|
|
5
|
+
autoEscape?: boolean;
|
|
6
|
+
caseSensitive?: boolean;
|
|
7
|
+
searchWords: SearchWords;
|
|
8
|
+
sourceString: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Chunk {
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
highlight: boolean;
|
|
14
|
+
className: string;
|
|
15
|
+
style: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface ComplexSearchWord {
|
|
18
|
+
text: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
style?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
export type SearchWord = string | ComplexSearchWord | undefined;
|
|
23
|
+
export type SearchWords = SearchWord[];
|
|
24
|
+
export default class HighlightFoundation extends BaseFoundation<HighlightAdapter> {
|
|
25
|
+
constructor(adapter?: HighlightAdapter);
|
|
26
|
+
/**
|
|
27
|
+
* Creates an array of chunk objects representing both higlightable and non highlightable pieces of text that match each search word.
|
|
28
|
+
*
|
|
29
|
+
findAll ['z'], 'aaazaaazaaa'
|
|
30
|
+
result #=> [
|
|
31
|
+
{ start: 0, end: 3, highlight: false }
|
|
32
|
+
{ start: 3, end: 4, highlight: true }
|
|
33
|
+
{ start: 4, end: 7, highlight: false }
|
|
34
|
+
{ start: 7, end: 8, highlight: true }
|
|
35
|
+
{ start: 8, end: 11, highlight: false }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
findAll ['do', 'dollar'], 'aaa do dollar aaa'
|
|
39
|
+
#=> chunks: [
|
|
40
|
+
{ start: 4, end: 6 },
|
|
41
|
+
{ start: 7, end: 9 },
|
|
42
|
+
{ start: 7, end: 13 },
|
|
43
|
+
]
|
|
44
|
+
#=> chunksToHight: [
|
|
45
|
+
{ start: 4, end: 6 },
|
|
46
|
+
{ start: 7, end: 13 },
|
|
47
|
+
]
|
|
48
|
+
#=> result: [
|
|
49
|
+
{ start: 0, end: 4, highlight: false },
|
|
50
|
+
{ start: 4, end: 6, highlight: true },
|
|
51
|
+
{ start: 6, end: 7, highlight: false },
|
|
52
|
+
{ start: 7, end: 13, highlight: true },
|
|
53
|
+
{ start: 13, end: 17, highlight: false },
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
* @return Array of "chunks" (where a Chunk is { start:number, end:number, highlight:boolean })
|
|
57
|
+
*/
|
|
58
|
+
findAll: ({ autoEscape, caseSensitive, searchWords, sourceString }: ChunkQuery) => Chunk[];
|
|
59
|
+
/**
|
|
60
|
+
* Examine text for any matches.
|
|
61
|
+
* If we find matches, add them to the returned array as a "chunk" object ({start:number, end:number}).
|
|
62
|
+
* @return { start:number, end:number }[]
|
|
63
|
+
*/
|
|
64
|
+
findChunks: ({ autoEscape, caseSensitive, searchWords, sourceString }: ChunkQuery) => Chunk[];
|
|
65
|
+
/**
|
|
66
|
+
* Takes an array of {start:number, end:number} objects and combines chunks that overlap into single chunks.
|
|
67
|
+
* @return {start:number, end:number}[]
|
|
68
|
+
*/
|
|
69
|
+
combineChunks: ({ chunks }: {
|
|
70
|
+
chunks: Chunk[];
|
|
71
|
+
}) => Chunk[];
|
|
72
|
+
/**
|
|
73
|
+
* Given a set of chunks to highlight, create an additional set of chunks
|
|
74
|
+
* to represent the bits of text between the highlighted text.
|
|
75
|
+
* @param chunksToHighlight {start:number, end:number}[]
|
|
76
|
+
* @param totalLength number
|
|
77
|
+
* @return {start:number, end:number, highlight:boolean}[]
|
|
78
|
+
*/
|
|
79
|
+
fillInChunks: ({ chunksToHighlight, totalLength }: {
|
|
80
|
+
chunksToHighlight: Chunk[];
|
|
81
|
+
totalLength: number;
|
|
82
|
+
}) => Chunk[];
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import _isString from "lodash/isString"; // Modified version based on 'highlight-words-core'
|
|
2
|
+
import BaseFoundation from '../base/foundation';
|
|
3
|
+
const escapeRegExpFn = string => string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
|
4
|
+
export default class HighlightFoundation extends BaseFoundation {
|
|
5
|
+
constructor(adapter) {
|
|
6
|
+
super(Object.assign({}, adapter));
|
|
7
|
+
/**
|
|
8
|
+
* Creates an array of chunk objects representing both higlightable and non highlightable pieces of text that match each search word.
|
|
9
|
+
*
|
|
10
|
+
findAll ['z'], 'aaazaaazaaa'
|
|
11
|
+
result #=> [
|
|
12
|
+
{ start: 0, end: 3, highlight: false }
|
|
13
|
+
{ start: 3, end: 4, highlight: true }
|
|
14
|
+
{ start: 4, end: 7, highlight: false }
|
|
15
|
+
{ start: 7, end: 8, highlight: true }
|
|
16
|
+
{ start: 8, end: 11, highlight: false }
|
|
17
|
+
]
|
|
18
|
+
findAll ['do', 'dollar'], 'aaa do dollar aaa'
|
|
19
|
+
#=> chunks: [
|
|
20
|
+
{ start: 4, end: 6 },
|
|
21
|
+
{ start: 7, end: 9 },
|
|
22
|
+
{ start: 7, end: 13 },
|
|
23
|
+
]
|
|
24
|
+
#=> chunksToHight: [
|
|
25
|
+
{ start: 4, end: 6 },
|
|
26
|
+
{ start: 7, end: 13 },
|
|
27
|
+
]
|
|
28
|
+
#=> result: [
|
|
29
|
+
{ start: 0, end: 4, highlight: false },
|
|
30
|
+
{ start: 4, end: 6, highlight: true },
|
|
31
|
+
{ start: 6, end: 7, highlight: false },
|
|
32
|
+
{ start: 7, end: 13, highlight: true },
|
|
33
|
+
{ start: 13, end: 17, highlight: false },
|
|
34
|
+
]
|
|
35
|
+
* @return Array of "chunks" (where a Chunk is { start:number, end:number, highlight:boolean })
|
|
36
|
+
*/
|
|
37
|
+
this.findAll = _ref => {
|
|
38
|
+
let {
|
|
39
|
+
autoEscape = true,
|
|
40
|
+
caseSensitive = false,
|
|
41
|
+
searchWords,
|
|
42
|
+
sourceString
|
|
43
|
+
} = _ref;
|
|
44
|
+
if (_isString(searchWords)) {
|
|
45
|
+
searchWords = [searchWords];
|
|
46
|
+
}
|
|
47
|
+
const chunks = this.findChunks({
|
|
48
|
+
autoEscape,
|
|
49
|
+
caseSensitive,
|
|
50
|
+
searchWords,
|
|
51
|
+
sourceString
|
|
52
|
+
});
|
|
53
|
+
const chunksToHighlight = this.combineChunks({
|
|
54
|
+
chunks
|
|
55
|
+
});
|
|
56
|
+
const result = this.fillInChunks({
|
|
57
|
+
chunksToHighlight,
|
|
58
|
+
totalLength: sourceString ? sourceString.length : 0
|
|
59
|
+
});
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Examine text for any matches.
|
|
64
|
+
* If we find matches, add them to the returned array as a "chunk" object ({start:number, end:number}).
|
|
65
|
+
* @return { start:number, end:number }[]
|
|
66
|
+
*/
|
|
67
|
+
this.findChunks = _ref2 => {
|
|
68
|
+
let {
|
|
69
|
+
autoEscape,
|
|
70
|
+
caseSensitive,
|
|
71
|
+
searchWords,
|
|
72
|
+
sourceString
|
|
73
|
+
} = _ref2;
|
|
74
|
+
return searchWords.map(searchWord => typeof searchWord === 'string' ? {
|
|
75
|
+
text: searchWord
|
|
76
|
+
} : searchWord).filter(searchWord => searchWord.text) // Remove empty words
|
|
77
|
+
.reduce((chunks, searchWord) => {
|
|
78
|
+
let searchText = searchWord.text;
|
|
79
|
+
if (autoEscape) {
|
|
80
|
+
searchText = escapeRegExpFn(searchText);
|
|
81
|
+
}
|
|
82
|
+
const regex = new RegExp(searchText, caseSensitive ? 'g' : 'gi');
|
|
83
|
+
let match;
|
|
84
|
+
while (match = regex.exec(sourceString)) {
|
|
85
|
+
const start = match.index;
|
|
86
|
+
const end = regex.lastIndex;
|
|
87
|
+
if (end > start) {
|
|
88
|
+
chunks.push({
|
|
89
|
+
highlight: true,
|
|
90
|
+
start,
|
|
91
|
+
end,
|
|
92
|
+
className: searchWord.className,
|
|
93
|
+
style: searchWord.style
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (match.index === regex.lastIndex) {
|
|
97
|
+
regex.lastIndex++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return chunks;
|
|
101
|
+
}, []);
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Takes an array of {start:number, end:number} objects and combines chunks that overlap into single chunks.
|
|
105
|
+
* @return {start:number, end:number}[]
|
|
106
|
+
*/
|
|
107
|
+
this.combineChunks = _ref3 => {
|
|
108
|
+
let {
|
|
109
|
+
chunks
|
|
110
|
+
} = _ref3;
|
|
111
|
+
return chunks.sort((first, second) => first.start - second.start).reduce((processedChunks, nextChunk) => {
|
|
112
|
+
// First chunk just goes straight in the array...
|
|
113
|
+
if (processedChunks.length === 0) {
|
|
114
|
+
return [nextChunk];
|
|
115
|
+
} else {
|
|
116
|
+
// ... subsequent chunks get checked to see if they overlap...
|
|
117
|
+
const prevChunk = processedChunks.pop();
|
|
118
|
+
if (nextChunk.start <= prevChunk.end) {
|
|
119
|
+
// It may be the case that prevChunk completely surrounds nextChunk, so take the
|
|
120
|
+
// largest of the end indeces.
|
|
121
|
+
const endIndex = Math.max(prevChunk.end, nextChunk.end);
|
|
122
|
+
processedChunks.push({
|
|
123
|
+
highlight: true,
|
|
124
|
+
start: prevChunk.start,
|
|
125
|
+
end: endIndex,
|
|
126
|
+
className: prevChunk.className || nextChunk.className,
|
|
127
|
+
style: Object.assign(Object.assign({}, prevChunk.style), nextChunk.style)
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
processedChunks.push(prevChunk, nextChunk);
|
|
131
|
+
}
|
|
132
|
+
return processedChunks;
|
|
133
|
+
}
|
|
134
|
+
}, []);
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Given a set of chunks to highlight, create an additional set of chunks
|
|
138
|
+
* to represent the bits of text between the highlighted text.
|
|
139
|
+
* @param chunksToHighlight {start:number, end:number}[]
|
|
140
|
+
* @param totalLength number
|
|
141
|
+
* @return {start:number, end:number, highlight:boolean}[]
|
|
142
|
+
*/
|
|
143
|
+
this.fillInChunks = _ref4 => {
|
|
144
|
+
let {
|
|
145
|
+
chunksToHighlight,
|
|
146
|
+
totalLength
|
|
147
|
+
} = _ref4;
|
|
148
|
+
const allChunks = [];
|
|
149
|
+
const append = (start, end, highlight, className, style) => {
|
|
150
|
+
if (end - start > 0) {
|
|
151
|
+
allChunks.push({
|
|
152
|
+
start,
|
|
153
|
+
end,
|
|
154
|
+
highlight,
|
|
155
|
+
className,
|
|
156
|
+
style
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
if (chunksToHighlight.length === 0) {
|
|
161
|
+
append(0, totalLength, false);
|
|
162
|
+
} else {
|
|
163
|
+
let lastIndex = 0;
|
|
164
|
+
chunksToHighlight.forEach(chunk => {
|
|
165
|
+
append(lastIndex, chunk.start, false);
|
|
166
|
+
append(chunk.start, chunk.end, true, chunk.className, chunk.style);
|
|
167
|
+
lastIndex = chunk.end;
|
|
168
|
+
});
|
|
169
|
+
append(lastIndex, totalLength, false);
|
|
170
|
+
}
|
|
171
|
+
return allChunks;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JsonViewer, JsonViewerOptions } from '@douyinfe/semi-json-viewer-core';
|
|
2
|
+
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
3
|
+
export type { JsonViewerOptions };
|
|
4
|
+
export interface JsonViewerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
5
|
+
getEditorRef: () => HTMLElement;
|
|
6
|
+
getSearchRef: () => HTMLInputElement;
|
|
7
|
+
notifyChange: (value: string) => void;
|
|
8
|
+
notifyHover: (value: string, el: HTMLElement) => HTMLElement | undefined;
|
|
9
|
+
setSearchOptions: (key: string) => void;
|
|
10
|
+
showSearchBar: () => void;
|
|
11
|
+
}
|
|
12
|
+
declare class JsonViewerFoundation extends BaseFoundation<JsonViewerAdapter> {
|
|
13
|
+
constructor(adapter: JsonViewerAdapter);
|
|
14
|
+
jsonViewer: JsonViewer | null;
|
|
15
|
+
init(): void;
|
|
16
|
+
search(searchText: string): void;
|
|
17
|
+
prevSearch(): void;
|
|
18
|
+
nextSearch(): void;
|
|
19
|
+
replace(replaceText: string): void;
|
|
20
|
+
replaceAll(replaceText: string): void;
|
|
21
|
+
setSearchOptions(key: string): void;
|
|
22
|
+
showSearchBar(): void;
|
|
23
|
+
}
|
|
24
|
+
export default JsonViewerFoundation;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { JsonViewer } from '@douyinfe/semi-json-viewer-core';
|
|
2
|
+
import BaseFoundation from '../base/foundation';
|
|
3
|
+
class JsonViewerFoundation extends BaseFoundation {
|
|
4
|
+
constructor(adapter) {
|
|
5
|
+
super(Object.assign(Object.assign({}, JsonViewerFoundation), adapter));
|
|
6
|
+
this.jsonViewer = null;
|
|
7
|
+
}
|
|
8
|
+
init() {
|
|
9
|
+
const props = this.getProps();
|
|
10
|
+
const editorRef = this._adapter.getEditorRef();
|
|
11
|
+
this.jsonViewer = new JsonViewer(editorRef, props.value, props.options);
|
|
12
|
+
this.jsonViewer.layout();
|
|
13
|
+
this.jsonViewer.emitter.on('contentChanged', e => {
|
|
14
|
+
var _a;
|
|
15
|
+
this._adapter.notifyChange((_a = this.jsonViewer) === null || _a === void 0 ? void 0 : _a.getModel().getValue());
|
|
16
|
+
if (this.getState('showSearchBar')) {
|
|
17
|
+
this.search(this._adapter.getSearchRef().value);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
this.jsonViewer.emitter.on('hoverNode', e => {
|
|
21
|
+
const el = this._adapter.notifyHover(e.value, e.target);
|
|
22
|
+
if (el) {
|
|
23
|
+
this.jsonViewer.emitter.emit('renderHoverNode', {
|
|
24
|
+
el
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
search(searchText) {
|
|
30
|
+
var _a;
|
|
31
|
+
const state = this.getState('searchOptions');
|
|
32
|
+
const {
|
|
33
|
+
caseSensitive,
|
|
34
|
+
wholeWord,
|
|
35
|
+
regex
|
|
36
|
+
} = state;
|
|
37
|
+
(_a = this.jsonViewer) === null || _a === void 0 ? void 0 : _a.getSearchWidget().search(searchText, caseSensitive, wholeWord, regex);
|
|
38
|
+
}
|
|
39
|
+
prevSearch() {
|
|
40
|
+
var _a;
|
|
41
|
+
(_a = this.jsonViewer) === null || _a === void 0 ? void 0 : _a.getSearchWidget().navigateResults(-1);
|
|
42
|
+
}
|
|
43
|
+
nextSearch() {
|
|
44
|
+
var _a;
|
|
45
|
+
(_a = this.jsonViewer) === null || _a === void 0 ? void 0 : _a.getSearchWidget().navigateResults(1);
|
|
46
|
+
}
|
|
47
|
+
replace(replaceText) {
|
|
48
|
+
var _a;
|
|
49
|
+
(_a = this.jsonViewer) === null || _a === void 0 ? void 0 : _a.getSearchWidget().replace(replaceText);
|
|
50
|
+
}
|
|
51
|
+
replaceAll(replaceText) {
|
|
52
|
+
var _a;
|
|
53
|
+
(_a = this.jsonViewer) === null || _a === void 0 ? void 0 : _a.getSearchWidget().replaceAll(replaceText);
|
|
54
|
+
}
|
|
55
|
+
setSearchOptions(key) {
|
|
56
|
+
this._adapter.setSearchOptions(key);
|
|
57
|
+
}
|
|
58
|
+
showSearchBar() {
|
|
59
|
+
this._adapter.showSearchBar();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export default JsonViewerFoundation;
|