Skip to content

Commit 58b4ba2

Browse files
committed
Finish find-and-replace; Add tests for it and for events
1 parent 0e48db4 commit 58b4ba2

File tree

10 files changed

+1120
-140
lines changed

10 files changed

+1120
-140
lines changed

code-input.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,19 @@ code-input:not(.code-input_registered)::before {
122122
content: "Use codeInput.registerTemplate to set up.";
123123
display: block;
124124
color: grey;
125+
}
126+
127+
/* Contains dialog boxes that might appear as the result of a plugin.
128+
Sticks to the top of the code-input element */
129+
code-input .code-input_dialog-container {
130+
z-index: 2;
131+
132+
position: sticky;
133+
grid-row: 1;
134+
grid-column: 1;
135+
136+
top: 0px;
137+
left: 0;
138+
width: 100%;
139+
height: 0;
125140
}

code-input.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,12 @@ var codeInput = {
434434
*/
435435
codeElement = null;
436436

437+
/**
438+
* Exposed non-scrolling element designed to contain dialog boxes etc. that shouldn't scroll
439+
* with the code-input element.
440+
*/
441+
dialogContainerElement = null;
442+
437443
/**
438444
* Form-Associated Custom Element Callbacks
439445
* https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example
@@ -504,6 +510,7 @@ var codeInput = {
504510
this.update();
505511
this.needsHighlight = false;
506512
}
513+
507514
window.requestAnimationFrame(this.animateFrame.bind(this));
508515
}
509516

@@ -634,6 +641,12 @@ var codeInput = {
634641
}
635642
}
636643

644+
// dialogContainerElement used to store non-scrolling dialog boxes, etc.
645+
let dialogContainerElement = document.createElement("div");
646+
dialogContainerElement.classList.add("code-input_dialog-container");
647+
this.append(dialogContainerElement);
648+
this.dialogContainerElement = dialogContainerElement;
649+
637650
this.pluginEvt("afterElementsAdded");
638651

639652
this.dispatchEvent(new CustomEvent("code-input_load"));
@@ -793,10 +806,12 @@ var codeInput = {
793806
* @override
794807
*/
795808
addEventListener(type, listener, options = undefined) {
809+
// Save a copy of the callback where `this` refers to the code-input element
796810
let boundCallback = listener.bind(this);
797811
this.boundEventCallbacks[listener] = boundCallback;
798812

799813
if (codeInput.textareaSyncEvents.includes(type)) {
814+
// Synchronise with textarea
800815
if (options === undefined) {
801816
if(this.textareaElement == null) {
802817
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback); });
@@ -811,6 +826,7 @@ var codeInput = {
811826
}
812827
}
813828
} else {
829+
// Synchronise with code-input element
814830
if (options === undefined) {
815831
super.addEventListener(type, boundCallback);
816832
} else {
@@ -822,22 +838,32 @@ var codeInput = {
822838
/**
823839
* @override
824840
*/
825-
removeEventListener(type, listener, options = null) {
841+
removeEventListener(type, listener, options = undefined) {
842+
// Save a copy of the callback where `this` refers to the code-input element
826843
let boundCallback = this.boundEventCallbacks[listener];
827-
if (type == "change") {
828-
if (options === null) {
829-
this.textareaElement.removeEventListener("change", boundCallback);
844+
845+
if (codeInput.textareaSyncEvents.includes(type)) {
846+
// Synchronise with textarea
847+
if (options === undefined) {
848+
if(this.textareaElement == null) {
849+
this.addEventListener("code-input_load", () => { this.textareaElement.removeEventListener(type, boundCallback); });
850+
} else {
851+
this.textareaElement.removeEventListener(type, boundCallback);
852+
}
830853
} else {
831-
this.textareaElement.removeEventListener("change", boundCallback, options);
854+
if(this.textareaElement == null) {
855+
this.addEventListener("code-input_load", () => { this.textareaElement.removeEventListener(type, boundCallback, options); });
856+
} else {
857+
this.textareaElement.removeEventListener(type, boundCallback, options);
858+
}
832859
}
833-
} else if (type == "selectionchange") {
834-
if (options === null) {
835-
this.textareaElement.removeEventListener("selectionchange", boundCallback);
860+
} else {
861+
// Synchronise with code-input element
862+
if (options === undefined) {
863+
super.removeEventListener(type, boundCallback);
836864
} else {
837-
this.textareaElement.removeEventListener("selectionchange", boundCallback, options);
865+
super.removeEventListener(type, boundCallback, options);
838866
}
839-
} else {
840-
super.removeEventListener(type, listener, options);
841867
}
842868
}
843869

plugins/find-and-replace.css

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* Find functionality matches */
2+
.code-input_find-and-replace_find-match {
3+
color: inherit;
4+
text-shadow: none!important;
5+
background-color: #ffff00!important;
6+
}
7+
.code-input_find-and-replace_find-match-focused, .code-input_find-and-replace_find-match-focused * {
8+
background-color: #ff8800!important;
9+
color: black!important;
10+
}
11+
.code-input_find-and-replace_start-newline::before {
12+
content: "⤶";
13+
}
14+
15+
/* Find-and-replace dialog */
16+
17+
@keyframes code-input_find-and-replace_roll-in {
18+
0% {opacity: 0; transform: translateY(-34px);}
19+
100% {opacity: 1; transform: translateY(0px);}
20+
}
21+
22+
@keyframes code-input_find-and-replace_roll-out {
23+
0% {opacity: 1;top: 0;}
24+
100% {opacity: 0;top: -34px;}
25+
}
26+
27+
.code-input_find-and-replace_dialog {
28+
position: absolute;
29+
top: 0;
30+
right: 14px;
31+
padding: 6px;
32+
padding-top: 8px;
33+
border: solid 1px #00000044;
34+
background-color: white;
35+
border-radius: 6px;
36+
box-shadow: 0 .2em 1em .2em rgba(0, 0, 0, 0.16);
37+
}
38+
39+
.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog) {
40+
animation: code-input_find-and-replace_roll-in .2s;
41+
opacity: 1;
42+
pointer-events: all;
43+
}
44+
45+
.code-input_find-and-replace_dialog.code-input_find-and-replace_hidden-dialog {
46+
animation: code-input_find-and-replace_roll-out .2s;
47+
opacity: 0;
48+
pointer-events: none;
49+
}
50+
51+
.code-input_find-and-replace_dialog input::placeholder {
52+
font-size: 80%;
53+
}
54+
55+
.code-input_find-and-replace_dialog input {
56+
position: relative;
57+
width: 240px; height: 32px; top: -3px;
58+
font-size: large;
59+
color: #000000aa;
60+
border: 0;
61+
}
62+
63+
.code-input_find-and-replace_dialog input:hover {
64+
outline: none;
65+
}
66+
67+
.code-input_find-and-replace_dialog input.code-input_find-and-replace_error {
68+
color: #ff0000aa;
69+
}
70+
71+
.code-input_find-and-replace_dialog button, .code-input_find-and-replace_dialog input[type="checkbox"] {
72+
display: inline-block;
73+
line-height: 24px;
74+
font-size: 22px;
75+
cursor: pointer;
76+
appearance: none;
77+
width: min-content;
78+
79+
margin: 5px;
80+
padding: 5px;
81+
border: 0;
82+
background-color: #dddddd;
83+
84+
text-align: center;
85+
color: black;
86+
vertical-align: top;
87+
}
88+
89+
.code-input_find-and-replace_dialog input[type="checkbox"].code-input_find-and-replace_case-sensitive-checkbox::before {
90+
content: "Aa";
91+
}
92+
.code-input_find-and-replace_dialog input[type="checkbox"].code-input_find-and-replace_reg-exp-checkbox::before {
93+
content: ".*";
94+
}
95+
96+
.code-input_find-and-replace_dialog button:hover, .code-input_find-and-replace_dialog input[type="checkbox"]:hover {
97+
background-color: #bbbbbb;
98+
}
99+
100+
.code-input_find-and-replace_dialog input[type="checkbox"]:checked {
101+
background-color: #222222;
102+
color: white;
103+
}
104+
105+
.code-input_find-and-replace_match-description {
106+
display: block; /* So not on same line as other */
107+
color: #444444;
108+
}
109+
110+
.code-input_find-and-replace_dialog details summary, .code-input_find-and-replace_dialog button {
111+
cursor: pointer;
112+
}
113+
114+
115+
.code-input_find-and-replace_dialog button.code-input_find-and-replace_button-hidden {
116+
opacity: 0;
117+
pointer-events: none;
118+
}
119+
120+
/* Cancel icon */
121+
.code-input_find-and-replace_dialog span {
122+
display: block;
123+
float: right;
124+
margin: 5px;
125+
padding: 5px;
126+
127+
width: 24px;
128+
line-height: 24px;
129+
font-family: system-ui;
130+
font-size: 22px;
131+
font-weight: 500;
132+
text-align: center;
133+
border-radius: 50%;
134+
color: black;
135+
opacity: 0.6;
136+
}
137+
138+
.code-input_find-and-replace_dialog span:before {
139+
content: "\00d7";
140+
}
141+
142+
.code-input_find-and-replace_dialog span:hover {
143+
opacity: .8;
144+
background-color: #00000018;
145+
}

0 commit comments

Comments
 (0)