Skip to content

Commit

Permalink
Merge remote-tracking branch 'nicegui/main' into feature/client_data
Browse files Browse the repository at this point in the history
  • Loading branch information
Alyxion committed May 10, 2024
2 parents b4dd186 + 2825394 commit cb28497
Show file tree
Hide file tree
Showing 154 changed files with 1,569 additions and 321 deletions.
4 changes: 2 additions & 2 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ ENV CHROME_BINARY_LOCATION=/usr/bin/chromium

# Install nicegui
RUN pip install -U pip && pip install poetry==$POETRY_VERSION
COPY nicegui pyproject.toml poetry.lock README.md ./
RUN poetry install --all-extras
COPY pyproject.toml poetry.lock README.md ./
RUN poetry install --all-extras --no-root

USER $USERNAME

Expand Down
1 change: 0 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
}
}
}
}
}
},
// More info: https://aka.ms/dev-containers-non-root.
Expand Down
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
nicegui/elements/tailwind.py linguist-generated
nicegui/elements/tailwind_types/** linguist-generated
nicegui/elements/lib/** linguist-vendored
nicegui/scripts/codemirror/*.json linguist-vendored
nicegui/scripts/codemirror/node_modules linguist-vendored
nicegui/static/es-module-shims.js linguist-vendored
nicegui/static/fonts/** linguist-vendored
nicegui/static/fonts.css linguist-vendored
Expand Down
1 change: 1 addition & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- socket.io: 4.7.2 ([MIT](https://opensource.org/licenses/MIT))
- es-module-shims: 1.8.0 ([MIT](https://opensource.org/licenses/MIT))
- aggrid: 30.2.0 ([MIT](https://opensource.org/licenses/MIT))
- codemirror: 6.0.1 ([MIT](https://opensource.org/licenses/MIT))
- echarts: 5.4.3 ([Apache-2.0](https://opensource.org/licenses/Apache-2.0))
- leaflet: 1.9.4 ([BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause))
- leaflet-draw: 1.0.4 ([MIT](https://opensource.org/licenses/MIT))
Expand Down
1 change: 1 addition & 0 deletions nicegui/.syncignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.mypy_cache
__pycache__/
.DS_Store
*.tmp
178 changes: 178 additions & 0 deletions nicegui/elements/codemirror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
export default {
template: `
<div></div>
`,
props: {
value: String,
language: String,
theme: String,
resource_path: String,
lineWrapping: Boolean,
disable: Boolean,
indent: String,
highlightWhitespace: Boolean,
},
watch: {
value(newValue) {
this.setEditorValue(newValue);
},
language(newLanguage) {
this.setLanguage(newLanguage);
},
theme(newTheme) {
this.setTheme(newTheme);
},
disable(newDisable) {
this.setDisabled(newDisable);
},
},
data() {
return {
// To let other methods wait for the editor to be created because
// they might be called by the server before the editor is created.
editorPromise: new Promise((resolve) => {
this.resolveEditor = resolve;
}),
};
},
methods: {
// Find the language's extension by its name. Case insensitive.
findLanguage(name) {
for (const language of this.languages)
for (const alias of [language.name, ...language.alias])
if (name.toLowerCase() === alias.toLowerCase()) return language;

console.error(`Language not found: ${this.language}`);
console.info("Supported language names:", languages.map((lang) => lang.name).join(", "));
return null;
},
// Get the names of all supported languages
async getLanguages() {
if (!this.editor) await this.editorPromise;
// Over 100 supported languages: https://github.com/codemirror/language-data/blob/main/src/language-data.ts
return this.languages.map((lang) => lang.name).sort(Intl.Collator("en").compare);
},
setLanguage(language) {
if (!language) {
this.editor.dispatch({
effects: this.languageConfig.reconfigure([]),
});
return;
}

const lang_description = this.findLanguage(language, this.languages);
if (!lang_description) {
console.error("Language not found:", language);
return;
}

lang_description.load().then((extension) => {
this.editor.dispatch({
effects: this.languageConfig.reconfigure([extension]),
});
});
},
async getThemes() {
if (!this.editor) await this.editorPromise;
// `this.themes` also contains some non-theme objects
// The real themes are Arrays
return Object.keys(this.themes)
.filter((key) => Array.isArray(this.themes[key]))
.sort(Intl.Collator("en").compare);
},
setTheme(theme) {
const new_theme = this.themes[theme];
if (new_theme === undefined) {
console.error("Theme not found:", theme);
return;
}
this.editor.dispatch({
effects: this.themeConfig.reconfigure([new_theme]),
});
},
setEditorValue(value) {
if (!this.editor) return;
if (this.editor.state.doc.toString() === value) return;

this.emitting = false;
this.editor.dispatch({ changes: { from: 0, to: this.editor.state.doc.length, insert: value } });
this.emitting = true;
},
setDisabled(disabled) {
this.editor.dispatch({
effects: this.editableConfig.reconfigure(this.editableStates[!disabled]),
});
},
setupExtensions() {
const CM = this.CM;

const self = this;

// Sends a ChangeSet https://codemirror.net/docs/ref/#state.ChangeSet
// containing only the changes made to the document.
// This could potentially be optimized further by sending updates
// periodically instead of on every change and accumulating changesets
// with ChangeSet.compose.
const changeSender = this.CM.ViewPlugin.fromClass(
class {
update(update) {
if (!update.docChanged) return;
if (!self.emitting) return;
self.$emit("update:value", update.changes);
}
}
);

const extensions = [
CM.basicSetup,
changeSender,
// Enables the Tab key to indent the current lines https://codemirror.net/examples/tab/
CM.keymap.of([CM.indentWithTab]),
// Sets indentation https://codemirror.net/docs/ref/#language.indentUnit
CM.indentUnit.of(this.indent),
// We will set these Compartments later and dynamically through props
this.themeConfig.of([]),
this.languageConfig.of([]),
this.editableConfig.of([]),
CM.EditorView.theme({
"&": { height: "100%" },
".cm-scroller": { overflow: "auto" },
}),
];

if (this.lineWrapping) extensions.push(CM.EditorView.lineWrapping);
if (this.highlightWhitespace) extensions.push([CM.highlightWhitespace()]);

return extensions;
},
},
async mounted() {
this.CM = await import(`${this.resource_path}/editor.js`);
const CM = this.CM;

// This is used to prevent emitting the value we just received from the server.
this.emitting = true;

// The Compartments are used to change the properties of the editor ("extensions") dynamically
this.themes = { ...CM.themes, oneDark: CM.oneDark };
this.themeConfig = new CM.Compartment();
this.languages = CM.languages;
this.languageConfig = new CM.Compartment();
this.editableConfig = new CM.Compartment();
this.editableStates = { true: CM.EditorView.editable.of(true), false: CM.EditorView.editable.of(false) };

const extensions = this.setupExtensions();

this.editor = new CM.EditorView({
doc: this.value,
extensions: extensions,
parent: this.$el,
});

this.resolveEditor(this.editor);

this.setLanguage(this.language);
this.setTheme(this.theme);
this.setDisabled(this.disable);
},
};
Loading

0 comments on commit cb28497

Please sign in to comment.