Skip to content

Commit 7820240

Browse files
authored
Merge pull request #166 from WebCoder49/main
Add latest fixes to `esm-support`
2 parents 84e4ac1 + 6a34502 commit 7820240

36 files changed

+328
-151
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ The next step is to set up a `template` to link `code-input` to your syntax-high
6464

6565
- *Highlight.js:*
6666
```js
67-
codeInput.registerTemplate("syntax-highlighted", codeInput.templates.hljs(hljs, [] /* Array of plugins (see below) */));
67+
codeInput.registerTemplate("syntax-highlighted", new codeInput.templates.Hljs(hljs, [] /* Array of plugins (see below) */));
6868
```
6969

7070
- *Prism.js:*
7171
```js
72-
codeInput.registerTemplate("syntax-highlighted", codeInput.templates.prism(Prism, [] /* Array of plugins (see below) */));
72+
codeInput.registerTemplate("syntax-highlighted", new codeInput.templates.Prism(Prism, [] /* Array of plugins (see below) */));
7373
```
7474

7575
- *Custom:*
@@ -106,7 +106,7 @@ The next step is to set up a `template` to link `code-input` to your syntax-high
106106
<!--...-->
107107
<script>
108108
codeInput.registerTemplate("syntax-highlighted",
109-
codeInput.templates.hljs(
109+
new codeInput.templates.Hljs(
110110
hljs,
111111
[
112112
new codeInput.plugins.Autodetect(),
@@ -122,13 +122,13 @@ The next step is to set up a `template` to link `code-input` to your syntax-high
122122
To see a full list of plugins and their functions, please see [plugins/README.md](./plugins/README.md).
123123

124124
### 4. Using the component
125-
Now that you have registered a template, you can use the custom `<code-input>` element in HTML. If you have more than one template registered, you need to add the template name as the `template` attribute. With the element, using the `language` attribute will add a `language-{value}` class to the `pre code` block. You can now use HTML attributes and events, as well as CSS styles, to make your element as simple or interactive as you like, as if it were a `textarea` element!
125+
Now that you have registered a template, you can use the custom `<code-input>` element in HTML. I recommend it surrounds a fallback `<textarea code-input-fallback>` element which will be used instead when JavaScript support is absent, and will pass its attributes to the `<code-input>` element otherwise, as shown below. If you have more than one template registered, you need to add the template name as the `template` attribute. With the element, using the `language` attribute will add a `language-{value}` class to the `pre code` block. You can now use HTML attributes and events, as well as CSS styles, to make your element as simple or interactive as you like, as if it were a `textarea` element!
126126
```HTML
127-
<code-input language="HTML"></code-input>
127+
<code-input language="HTML"><textarea code-input-fallback></textarea></code-input>
128128
```
129129
*or*
130130
```HTML
131-
<code-input language="HTML" placeholder="Type code here" template="syntax-highlighted" onchange="console.log('Your code is', this.value)">&lt; href='https://github.com/WebCoder49/code-input'>code-input&lt;/a></code-input>
131+
<code-input language="HTML" template="syntax-highlighted" onchange="console.log('Your code is', this.value)"><textarea code-input-fallback placeholder="Type code here">&lt; href='https://github.com/WebCoder49/code-input'>code-input&lt;/a></textarea></code-input>
132132
```
133133

134134
> ⚠️ At the moment, you need to set the `--padding` property rather than `padding` for a `code-input` element's CSS. All other properties should work as normal.

code-input.css

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ code-input {
1212
top: 0;
1313
left: 0;
1414

15+
color: black;
16+
background-color: white;
17+
1518
/* Normal inline styles */
1619
margin: 8px;
1720
--padding: 16px;
@@ -38,8 +41,9 @@ code-input textarea, code-input:not(.code-input_pre-element-styled) pre code, co
3841
margin: 0px!important;
3942
padding: var(--padding, 16px)!important;
4043
border: 0;
41-
min-width: calc(100% - var(--padding) * 2);
42-
min-height: calc(100% - var(--padding) * 2);
44+
min-width: calc(100% - var(--padding, 16px) * 2);
45+
min-height: calc(100% - var(--padding, 16px) * 2);
46+
box-sizing: content-box; /* Make height, width work consistently no matter the box-sizing of ancestors; dialogs can be styled as wanted so are excluded. */
4347
overflow: hidden;
4448
resize: none;
4549
grid-row: 1;
@@ -56,8 +60,6 @@ code-input:not(.code-input_pre-element-styled) pre, code-input.code-input_pre-el
5660
/* Remove all margin and padding from others */
5761
margin: 0px!important;
5862
padding: 0px!important;
59-
width: 100%;
60-
height: 100%;
6163
}
6264

6365
code-input textarea, code-input pre, code-input pre * {
@@ -91,7 +93,7 @@ code-input textarea, code-input pre {
9193

9294
/* Move the textarea in front of the result */
9395

94-
code-input textarea {
96+
code-input textarea:not([code-input-fallback]) {
9597
z-index: 1;
9698
}
9799
code-input pre {
@@ -100,7 +102,7 @@ code-input pre {
100102

101103
/* Make textarea almost completely transparent, except for caret and placeholder */
102104

103-
code-input textarea {
105+
code-input textarea:not([code-input-fallback]) {
104106
color: transparent;
105107
background: transparent;
106108
caret-color: inherit!important; /* Or choose your favourite color */
@@ -123,7 +125,7 @@ code-input textarea {
123125
outline: none!important;
124126
}
125127
code-input:has(textarea:focus):not(.code-input_mouse-focused) {
126-
outline: 2px solid black;
128+
outline: 2px solid currentColor;
127129
}
128130

129131
/* Before registering give a hint about how to register. */
@@ -135,19 +137,30 @@ code-input:not(.code-input_registered) {
135137

136138
code-input:not(.code-input_registered)::after {
137139
/* Display message to register */
138-
content: "Use codeInput.registerTemplate to set up.";
140+
content: "No-JavaScript fallback. For highlighting and plugins: as a user use a newer browser/enable JavaScript support; as a developer use codeInput.registerTemplate.";
139141
display: block;
140142
position: absolute;
141-
bottom: var(--padding);
142-
left: var(--padding);
143-
width: calc(100% - 2 * var(--padding));
143+
bottom: 0;
144+
left: var(--padding, 16px);
145+
width: calc(100% - 2 * var(--padding, 16px));
146+
overflow-x: auto;
144147

145-
border-top: 1px solid grey;
146-
outline: var(--padding) solid white;
147-
background-color: white;
148+
border-top: 1px solid currentColor;
149+
outline-top: 0;
150+
background-color: inherit;
151+
color: inherit;
152+
153+
margin: 0;
154+
padding: 0;
155+
height: 2em;
148156
}
149157

150-
code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) textarea {
158+
code-input:not(.code-input_registered) textarea {
159+
/* Don't overlap with message */
160+
min-height: calc(100% - var(--padding, 16px) * 2 - 2em);
161+
}
162+
163+
code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) textarea:not([code-input-fallback]) {
151164
opacity: 0;
152165
}
153166

@@ -166,12 +179,16 @@ code-input .code-input_dialog-container {
166179

167180
margin: 0;
168181
padding: 0;
169-
width: 100%;
170182
height: 0;
183+
width: 100%;
171184

172185
/* Dialog boxes' text is based on text-direction */
173186
text-align: inherit;
174187
}
188+
code-input.code-input_pre-element-styled .code-input_dialog-container {
189+
width: calc(100% + 2 * var(--padding, 16px) - 2px);
190+
}
191+
175192
[dir=rtl] code-input .code-input_dialog-container, code-input[dir=rtl] .code-input_dialog-container {
176193
left: unset;
177194
right: 0;
@@ -200,16 +217,28 @@ code-input:has(pre[dir=rtl]) .code-input_dialog-container .code-input_keyboard-n
200217
right: 0;
201218
}
202219

203-
code-input:not(:has(textarea:focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions,
220+
code-input:not(:has(textarea:not([code-input-fallback]):focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions,
204221
code-input.code-input_mouse-focused .code-input_dialog-container .code-input_keyboard-navigation-instructions,
205222
code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions:empty {
206223
/* When not keyboard-focused / no instructions don't show instructions */
207224
display: none;
208225
}
209226

210227
/* Things with padding when instructions are present */
211-
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused) textarea,
212-
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code,
213-
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre {
214-
padding-top: calc(var(--padding) + 3em)!important;
228+
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused) textarea,
229+
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code,
230+
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre {
231+
padding-top: calc(var(--padding, 16px) + 3em)!important;
232+
}
233+
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused) textarea, code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code, code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([code-input-fallback]):focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre {
234+
min-height: calc(100% - var(--padding, 16px) * 2 - 3em);
235+
}
236+
237+
/* No JavaScript fallback - styles to override all previous */
238+
239+
code-input textarea[code-input-fallback] {
240+
overflow: auto;
241+
background-color: inherit;
242+
color: inherit;
243+
height: max-content;
215244
}

code-input.d.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export namespace plugins {
9191
* Create an auto-close brackets plugin to pass into a template
9292
* @param {Object} bracketPairs Opening brackets mapped to closing brackets, default and example {"(": ")", "[": "]", "{": "}", '"': '"'}. All brackets must only be one character.
9393
*/
94-
constructor(bracketPairs: Object);
94+
constructor(bracketPairs?: Object);
9595
}
9696
// ESM-SUPPORT-END-PLUGIN-auto-close-brackets Do not (re)move this - it's needed for ESM generation
9797

@@ -103,9 +103,9 @@ export namespace plugins {
103103
class Autocomplete extends Plugin {
104104
/**
105105
* Pass in a function to create a plugin that displays the popup that takes in (popup element, textarea, textarea.selectionEnd).
106-
* @param {(popupElement: HTMLElement, textarea: HTMLTextAreaElement, selectionEnd: number) => void} updatePopupCallback a function to display the popup that takes in (popup element, textarea, textarea.selectionEnd).
106+
* @param {(popupElement: HTMLElement, textarea: HTMLTextAreaElement, selectionEnd: number, selectionStart?: number) => void} updatePopupCallback a function to display the popup that takes in (popup element, textarea, textarea.selectionEnd).
107107
*/
108-
constructor(updatePopupCallback: (popupElem: HTMLElement, textarea: HTMLTextAreaElement, selectionEnd: number) => void);
108+
constructor(updatePopupCallback: (popupElem: HTMLElement, textarea: HTMLTextAreaElement, selectionEnd: number, selectionStart?: number) => void);
109109
}
110110
// ESM-SUPPORT-END-PLUGIN-autocomplete Do not (re)move this - it's needed for ESM generation
111111

@@ -120,21 +120,6 @@ export namespace plugins {
120120
}
121121
// ESM-SUPPORT-END-PLUGIN-autodetect Do not (re)move this - it's needed for ESM generation
122122

123-
// E doesn't exist? SM-SUPPORT-START-PLUGIN-debounce-update Do not (re)move this - it's needed for ESM generation
124-
/**
125-
* Debounce the update and highlighting function
126-
* https://medium.com/@jamischarles/what-is-debouncing-2505c0648ff1
127-
* Files: debounce-update.js
128-
*/
129-
class DebounceUpdate extends Plugin {
130-
/**
131-
* Create a debounced update plugin to pass into a template.
132-
* @param {Number} delayMs Delay, in ms, to wait until updating the syntax highlighting
133-
*/
134-
constructor(delayMs: number);
135-
}
136-
// E doesn't exist? SM-SUPPORT-END-PLUGIN-debounce-update Do not (re)move this - it's needed for ESM generation
137-
138123
// ESM-SUPPORT-START-PLUGIN-find-and-replace Do not (re)move this - it's needed for ESM generation
139124
/**
140125
* Add Find-and-Replace (Ctrl+F for find, Ctrl+H for replace by default) functionality to the code editor.
@@ -189,10 +174,15 @@ export namespace plugins {
189174
* @param {boolean} useCtrlG Should Ctrl+G be overriden for go-to-line functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`.
190175
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text.
191176
*/
192-
constructor(useCtrlG: boolean,
177+
constructor(useCtrlG?: boolean,
193178
instructionTranslations?: {
194179
closeDialog?: string;
195180
input?: string;
181+
guidanceFormat?: string;
182+
guidanceLineRange?: (current:Number, max: Number) => string;
183+
guidanceColumnRange?: (line: Number, current: Number, max: Number) => string;
184+
guidanceValidLine?: (line: Number) => string;
185+
guidanceValidColumn?: (line: Number, column: Number) => string;
196186
});
197187
/**
198188
* Show a search-like dialog prompting line number.
@@ -217,7 +207,7 @@ export namespace plugins {
217207
* @param {boolean} escTabToChangeFocus Whether pressing the Escape key before (Shift+)Tab should make this keypress focus on a different element (Tab's default behaviour). You should always either enable this or use this plugin's disableTabIndentation and enableTabIndentation methods linked to other keyboard shortcuts, for accessibility.
218208
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text.
219209
*/
220-
constructor(defaultSpaces?: boolean, numSpaces?: Number, bracketPairs?: Object, escTabToChangeFocus?: boolean, instructionTranslations?: {
210+
constructor(defaultSpaces?: boolean, numSpaces?: Number, bracketPairs?: Object, escTabToChangeFocus?: boolean, instructionTwranslations?: {
221211
tabForIndentation?: string;
222212
tabForNavigation?: string;
223213
});
@@ -270,7 +260,7 @@ export namespace plugins {
270260
* @param {string} selectedClass The CSS class that will be present on tokens only when they are part of the selected text in the `<code-input>` element. Defaults to "code-input_select-token-callbacks_selected".
271261
* @returns {TokenSelectorCallbacks} A new TokenSelectorCallbacks instance that encodes this behaviour.
272262
*/
273-
static createClassSynchronisation(selectedClass: string): codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks;
263+
static createClassSynchronisation(selectedClass?: string): codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks;
274264
}
275265
}
276266
// ESM-SUPPORT-END-PLUGIN-select-token-callbacks Do not (re)move this - it's needed for ESM generation

code-input.js

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,6 @@ var codeInput = {
478478
* to syntax-highlight it. */
479479

480480
needsHighlight = false; // Just inputted
481-
handleEventsFromTextarea = true; // Turn to false when unusual internal events are called on the textarea
482481
originalAriaDescription;
483482

484483
/**
@@ -519,14 +518,6 @@ var codeInput = {
519518

520519
this.syncSize();
521520

522-
// If editing here, scroll to the caret by focusing, though this shouldn't count as a focus event
523-
if(this.textareaElement === document.activeElement) {
524-
this.handleEventsFromTextarea = false;
525-
this.textareaElement.blur();
526-
this.textareaElement.focus();
527-
this.handleEventsFromTextarea = true;
528-
}
529-
530521
this.pluginEvt("afterHighlight");
531522
}
532523

@@ -614,16 +605,34 @@ var codeInput = {
614605

615606
this.pluginEvt("beforeElementsAdded");
616607

608+
const fallbackTextarea = this.querySelector("textarea[code-input-fallback]");
609+
let value;
610+
if(fallbackTextarea) {
611+
// Fallback textarea exists
612+
// Sync attributes; existing code-input attributes take priority
613+
let textareaAttributeNames = fallbackTextarea.getAttributeNames();
614+
for(let i = 0; i < textareaAttributeNames.length; i++) {
615+
const attr = textareaAttributeNames[i];
616+
if(!this.hasAttribute(attr)) {
617+
this.setAttribute(attr, fallbackTextarea.getAttribute(attr));
618+
}
619+
}
620+
// Sync value
621+
value = fallbackTextarea.value;
622+
} else {
623+
value = this.unescapeHtml(this.innerHTML);
624+
}
625+
value = value || this.getAttribute("value") || "";
626+
617627
// First-time attribute sync
618-
let lang = this.getAttribute("language") || this.getAttribute("lang");
619-
let placeholder = this.getAttribute("placeholder") || this.getAttribute("language") || this.getAttribute("lang") || "";
620-
let value = this.unescapeHtml(this.innerHTML) || this.getAttribute("value") || "";
621-
// Value attribute deprecated, but included for compatibility
628+
const lang = this.getAttribute("language") || this.getAttribute("lang");
629+
const placeholder = this.getAttribute("placeholder") || lang || "";
630+
622631

623632
this.initialValue = value; // For form reset
624633

625634
// Create textarea
626-
let textarea = document.createElement("textarea");
635+
const textarea = document.createElement("textarea");
627636
textarea.placeholder = placeholder;
628637
if(value != "") {
629638
textarea.value = value;
@@ -642,9 +651,7 @@ var codeInput = {
642651
this.classList.add("code-input_mouse-focused");
643652
});
644653
textarea.addEventListener("blur", () => {
645-
if(this.handleEventsFromTextarea) {
646-
this.classList.remove("code-input_mouse-focused");
647-
}
654+
this.classList.remove("code-input_mouse-focused");
648655
});
649656

650657
this.innerHTML = ""; // Clear Content
@@ -868,22 +875,20 @@ var codeInput = {
868875
this.boundEventCallbacks[listener] = boundCallback;
869876

870877
if (codeInput.textareaSyncEvents.includes(type)) {
871-
// Synchronise with textarea, only when handleEventsFromTextarea is true
872-
// This callback is modified to only run when the handleEventsFromTextarea is set.
873-
let conditionalBoundCallback = function(evt) { if(this.handleEventsFromTextarea) boundCallback(evt); }.bind(this);
874-
this.boundEventCallbacks[listener] = conditionalBoundCallback;
878+
// Synchronise with textarea
879+
this.boundEventCallbacks[listener] = boundCallback;
875880

876881
if (options === undefined) {
877882
if(this.textareaElement == null) {
878883
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback); });
879884
} else {
880-
this.textareaElement.addEventListener(type, conditionalBoundCallback);
885+
this.textareaElement.addEventListener(type, boundCallback);
881886
}
882887
} else {
883888
if(this.textareaElement == null) {
884889
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback, options); });
885890
} else {
886-
this.textareaElement.addEventListener(type, conditionalBoundCallback, options);
891+
this.textareaElement.addEventListener(type, boundCallback, options);
887892
}
888893
}
889894
} else {
@@ -943,10 +948,12 @@ var codeInput = {
943948
if (val === null || val === undefined) {
944949
val = "";
945950
}
951+
946952
// Save in editable textarea element
947953
this.textareaElement.value = val;
948954
// Trigger highlight
949955
this.scheduleHighlight();
956+
950957
return val;
951958
}
952959

0 commit comments

Comments
 (0)