-
-
Notifications
You must be signed in to change notification settings - Fork 815
NiceGUI 3.0 #5109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
NiceGUI 3.0 #5109
Conversation
I changed some instances where it had python 3.8 as Poetry 2.0.0 dropped support for 3.8. I noticed this when trying to run "docker.sh buildup app" --------- Co-authored-by: Jesse Harwin <[email protected]> Co-authored-by: Falko Schindler <[email protected]>
### Motivation In #4925 we noticed that the height of a `ui.log` element can be affected by its content, which is unexpected and different than, e.g., for `ui.scroll_area`: ```py with ui.column().classes('w-60 items-stretch h-72'): ui.label('h-24').classes('border h-24') with ui.column().classes('h-full'): log = ui.log().classes('h-full') with ui.column().classes('w-60 items-stretch h-72'): ui.label('h-24').classes('border h-24') with ui.column().classes('h-full'): scroll = ui.scroll_area().classes('h-full border') def add_content(): log.push(time.time()) with scroll: ui.label(time.time()) ui.button('Log', on_click=add_content) ``` ### Implementation The problem seems to be that `ui.log` doesn't wrap its content in multiple layers like `ui.scroll_area` does. To keep things simple, this PR implements `ui.log` based on `<q-scroll_area>` instead of `<div>`. This way we only need to adjust the CSS rules a little bit and now the layout behavior of `ui.log` is identical to `ui.scroll_area`. ### Caveat I don't think that the different underlying Vue component is a breaking change. **But** now `ui.log` collapses if placed inside a container with an unspecified width: ```py ui.log() # ok with ui.column(): ui.log() # collapses with ui.column().classes('w-60'): ui.log() # ok ``` Therefore I'm not sure if it is safe to release this PR before 3.0. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests are not necessary. - [x] Documentation is not necessary.
### Motivation In PR #4749 we couldn't get an ESM for ajv-formats from registry.npmjs.org. So we decided to switch to Jsdelivr. ### Implementation This PR re-implements parts of npm.py to work with Jsdelivr. The special case for TailwindCSS could be removed. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests are not necessary. - [x] Documentation is not necessary.
Inspired by discussion #4410, this PR introduces a `previous_value` field for `ValueChangeEventArguments`. This change is pretty straightforward and only requires adjustments for two mixins and two elements. Right now it is lacking backward-compatibility though. If user code derives custom `ValueChangeEventArguments`, they are now missing the `previous_value` field. Should we add a default value? But it should be clear that this default is _not_ the previous value, but "not set". We might need a sentinel... And in 3.0 I'd like to enforce the `previous_value` field. A deprecation warning for the default value would be great, but probably hard to achieve.
### Motivation There are several problems with the existing dependency management via npm.json and npm.py. **No control over importmap key** Example: Newer Three.js versions come with three.module.js which points to three.core.js. We can declare both files as a dependency, but they use the same key "three" in the importmap. A similar problem came up in #4163. **"Manual" code modifications needed** If a file like three.module.js references `./three.core.js`, we have to replace the relative link because the file three.core.js isn't found at this relative URL. Similarly there is an import of `../utils/BufferGeometryUtils.js` in Three.js' GLTFLoader which we need to replace with `BufferGeometryUtils`. These corrections happen in npm.py. **Complex dependencies hard or impossible to integrate** There is an extra script at scripts/codemirror/bundle.bash to integrate CodeMirror. And we wanted to integrate ajv-formats for `ui.json_editor` (https://cdn.jsdelivr.net/npm/[email protected]/+esm), but it includes references to e.g. "/npm/[email protected]/+esm" which don't work in a local NiceGUI app (see #4749). ### Implementation For this PR I decided to move modules with dependencies into separate directories (e.g. nicegui/elements/scene/) instead of holding all dependencies in one giant lib/ directory. This includes codemirror, scene, aggrid, joystick, json_editor, plotly, echarts, leaflet and mermaid. Each of these subdirectories contains an independent package.json, bundler configuration (rollup or vite), src/ directory with an index.js, a Git-ignored node-modules/ directory, and a generated dist/ directory with the resulting (sometimes chunked) bundle. This way we can configure the build individually for each UI element and don't have to find a one-fits-all solution. Furthermore, we can rely on standard NPM tools without the need for our own custom JSON format and Python scripts for downloading and extracting packages. Now the build process is 1. `npm install` and 2. `npm run build`. For registering the module in NiceGUI, I introduced a new `esm` parameter when subclassing an `Element`, e.g.: ```py class JsonEditor(Element, component='json_editor.js', esm={'nicegui-json-editor': 'dist'}): ... ``` This defines an entry to the importmap named "nicegui-json-editor" pointing to the relative directory dist/. This replaces the definition of individual `dependencies=[...]`, which we will probably deprecate. ### Progress The PR is still work in progress. The change has quite some implications that still need to be implemented. - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] remove deprecated parameters from `Element.__init_subclass__` - [x] create custom package.jsons per UI element - [x] create package.json for NiceGUI's core dependencies (Vue, Quasar, Tailwind) - [x] remove npm.json/npm.py - [x] update examples for custom components (number_checker, signature_pad) - [x] update gitattributes - [x] generate DEPENDENCIES.md? - [x] what about minification and maps? - [x] Pytests are not necessary. - [x] Documentation has been updated.
This PR removes code and APIs which have been marked as deprecated. Exceptions: - Since we probably won't update to Tailwind 4, the documentation links to version 3 remain unchanged. - Parameters for subclassing `ui.element` are already updated in PR #5021. - `previous_value` will only be enforced in NiceGUI 4
### Motivation As noticed by @M6stafa in #5045, Quasar elements and the connection popup haven't been rendered correctly for languages with right-to-left text direction. ### Implementation This PR loads the quasar.rtl*.css files and adjusts the CSS rules for the connection popup for `:dir(ltr)` and `:dir(rtl)` respectively. It can be tested with some input like ```py ui.input(label='متن ورودی', placeholder='متن خود را وارد کنیم') ``` and choosing a language like ```py ui.run(language='fa-IR') ``` ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests are not necessary. - [x] Documentation is not necessary.
### Motivation NiceGUI 3.0 should upgrade its Tailwind dependency to version 4 which has been released quite a while ago. ### Implementation This PR had to address several challenges: - update package.json and extract_core_libraries to use Tailwind from node_modules/ instead of http://cdn.tailwindcss.com/ - remove warning about not using this version in production - introduce CSS layers to properly integrate Tailwind with Quasar and NiceGUI's CSS rules - update our hack to preserve borders of card children - fix a conflict between Tailwind's "inline" class a Quasar components like QSelect, QCheckbox and QRadio - update documentation and tests to use new border classes - update links in documentation - ~~update~~ remove the `.tailwind` API An open question is what to do with the `.tailwind` API: It is getting more and more useless after Tailwind removed classes like "p-0" and mainly kept generic classes like "p-<number>", "p-(<custom-property>)", and "p-[<value>]". We could try to improve the API to support numbers beside literal strings, but it would require significant work. Apart from that the current PR causes several breaking changes because, e.g., [borders](https://tailwindcss.com/docs/border-style) now can start with "border-" _or_ "divide-". Therefore the literal string options change from "solid" etc. to "border-solid", "divide-solid" etc. It feels like Tailwind is getting to complex and flexible to keep trying to mirror its API. A potential way forward could be to release the new API as deprecated and discourage using it until we shut it down in NiceGUI 4.0. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests have been added (or are not necessary). - [x] Documentation has been added (or is not necessary). - [x] Remove the `.tailwind` API.
#4978) ### Motivation When reviewing PR #4969, we noticed that it would be handy if the props dictionary would automatically update the frontend when changed: ```py b = ui.button('test', on_click=lambda: b.props.update(icon='face')) ``` ### Implementation This PR replaces the `dict` base class with an `ObservableDict` and calls a new `_update` method. In order to avoid keeping strong reference to `element`, this method needs to use the weak reference `self._element`. I'm marking the PR as draft because we should carefully evaluate memory and performance, add pytests and documentation. Although the change shouldn't break anything, it might be a good idea to release this feature in 3.0. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Introduce `@helpers.prevent_recursion` - [x] Existing pytests are passing. - [x] Pytests have been added. - [x] Documentation has been added. - [x] This PR breaks https://nicegui.io/documentation/table#show_and_hide_columns [BREAKING CHANGE]. - [x] The condition `if self._send_update_on_value_change:` might be ineffective now. - [x] `test_apply_format_on_blur` is red
### Motivation When refactoring or renaming variables their string names in bindings are often missed since the rename IDE action doesn't pick it up. We discussed creating a method that gets the variable name as a string from a reference which proved possible but costly. As an alternative to raise awareness to missed variable names after renaming we propose to add an option that checks if the binding target exists and warns if it's not. ### Implementation The implementation is rather straight forward and involves a lot of trivial parameter adding. The check takes place in the functions `bind`, `bind_to` and `bind_from` in `bindings.py` which are used in all the other occurrences. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] update all docstrings - [x] `direction` could be a `Literal` - [x] check both, source and target - [x] change it to `check: bool | None = None` and check non-dicts by default - [x] Pytests have been added. - [x] Documentation has been added. --------- Co-authored-by: Falko Schindler <[email protected]>
…5104) ### Motivation I stumbled upon the built-in function `str.translate` which can be used to replace multiple characters more efficiently than calling `str.replace` repeatedly. This reminded me of our `client.build_response` method which calls `str.replace` five times to escape special characters for HTML. ### Implementation `client.build_response` is further optimized by globally defining a translation dictionary which is re-used by every client. ~~While being at it, two other occurrences of repeated `str.replace` calls have been replaced.~~ ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests are not necessary. - [x] Documentation is not necessary.
…he code (#5107) ### Motivation While working on PR #5005, we noticed that some projects like our ROS2 example might depend on the shared nature of the long-living auto-index client. If there is some kind of "event" in the data model (or hardware controller), they can simply update the UI if there is only one and exactly one client. When switching to page functions, they updating UI becomes more complicated in this scenario. Therefore we decided to move our event system from [RoSys](https://github.com/zauberzeug/rosys/) to NiceGUI. It provides a simple API to define events, subscribe arbitrary callbacks to them, and to emit/call them if the event happens. ### Implementation The core implementation is taken from RoSys. But on a second look some details have changed, partly by relaxing some assumptions or because we can make better use of NiceGUI's infrastructure. - I chose the method names "subscribe" and "subscribe_ui" instead of "register" and "register_ui". - Callbacks can still be sync or async. - Callbacks can - but don't have to - receive arguments. - Callbacks are automatically called within the same slot where they subscribed. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Merge `subscribe` and `subscribe_ui` into a single `subscribe(..., unsubscribe_on_disconnect: bool | None = None)`. - [x] Pytests have been added. - [x] Documentation has been added.
…tional root parameter in ui.run (#5005) ### Motivation As discussed in #4964, we could streamline the the setup of NiceGUI as an SPA by providing a positional `root` parameter in `ui.run`. The basic pattern becomes so simple that we do not need auto-index pages anymore 🎉 ### Implementation This PR implements an idea form @falkoschindler and me while discussing #4964: by evaluating the `root` builder function in the FastAPI default 404 handler we do not need to register catch-all FastAPI routes at all. Thereby any other defined FastAPI routes take precedence. Only if the FastAPI routing fails we route the path to the `root` builder function passed via `ui.run`. A basic (already working) example looks like this: ```py from nicegui import ui def root(): ui.link('Go to main page', '/') ui.link('Go to other page', '/other') ui.sub_pages({ '/': main, '/other': other, }) def main(): ui.label('Main page content') def other(): ui.label('Another page content') @ui.page('/test') def test(): ui.label('Test page content') ui.run(root) ``` Of course that only works when auto-index page does not register a `/` page on it's own. ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] Finish `root` implementation: - [x] use new parameter for `ui.sub_pages` demos - [x] use page-creation code - [x] provide support for async root pages - [x] inject required parameters into root page similar to what ui.sub_pages does - [x] either make fallback to `root` builder behave exactly like `ui.page` or ensure via tests that both are working as expected - [x] `root` parameter for `ui.run_with` - [x] remove auto-index client - [x] `client.request` is never `None` - [x] remove `client.shared` - [x] remove `individual_target` - [x] documentation not starting - [x] support NiceGUI scripts - [x] remove `# @ui.page('/')` hack in demos (not needed anymore?) - [x] remove "Needs page builder function" in storage documentation - [x] fix app.storage.user for scripts (and client, tab, browser, general, and update documentation) - [x] fix pytests still refering to `auto_index_client` - [x] fix ROS2 example (using a new event system?) - [x] raise when combining script mode with `ui.page` - [x] Pytests have been added. - [x] Documentation has been updated. --------- Co-authored-by: Falko Schindler <[email protected]>
This comment was marked as resolved.
This comment was marked as resolved.
Also can we potentially make the searching a bit easier by re-phrasing "upstream breaking change" to "upstream changes which broke our code" in old release notes, so that searching for "break" or "breaking" yields only the breaking changes? Or the other way round with "our breaking changes" so that we can search by "our break" / "our breaking" |
This comment was marked as resolved.
This comment was marked as resolved.
@evnchn We updated the release notes to include deprecations. Updating old release notes is an interesting idea. But it will probably break (pun not intended) in the future as soon as someone forgets that "break" is a forbidden word for non-breaking changes. |
I read through the release notes. Although may not affect everyone, aren't the JavaScript version bumps also potentially breaking? I think I'll do a search. |
THIS MAY NOT BE ACCURATE!!! JavaScript dependencies with major version bump:
JavaScript dependencies with minor version bump:
New JavaScript dependencies:
Unchanged JavaScript dependencies:
|
This PR collects all changes and release notes for NiceGUI 3.0.
It shouldn't get squashed.
New features and enhancements
root
page parameter forui.run
to simplify single-page applications (Streamline the setup of single page applications (SPAs) #4964, Drop auto-index pages in favour of streamlined usage of SPAs via positional root parameter in ui.run #5005 by @rodja, @falkoschindler)Event
system to communicate between UI and long-living objects (Introduce Event class for sending events between different parts of the code #5107 by @falkoschindler, @denniswittich)ValueChangeEventArguments.previous_value
(Previous value of the element #4410, IntroduceValueChangeEventArguments.previous_value
#4456 by @me21, @falkoschindler, @evnchn)strict
option for binding methods (add an option to bindings to check if target exists #5040 by @denniswittich, @codingpaula, @falkoschindler)q-scroll_area
forui.log
to preserve a fixed height (ui.log
shouldn't grow when pushing lines #4925, Use q-scroll_area forui.log
to preserve a fixed height #4935 by @e-nesse, @falkoschindler)ui.clipboard.read()
returnNone
if the clipboard API isn't available (How to determine whether the current connection is secure #4236,ui.clipboard
should returnNone
if the clipboard API isn't available #4237 by @weinibuliu, @falkoschindler, @evnchn)Shared auto-index pages (not using
@ui.page
)UI elements defined in global scope have been added to a static shared "auto-index client" served at "/". This caused a multitude of problems throughout the code base, so we decided to remove this shared client.
In version 3.0 you have the following options:
Keep putting (all) UI elements in global scope. We call such apps without page functions "NiceGUI scripts". They will automatically re-evaluated inside an implicit page function when visiting "/". This is almost a drop-in replacement for the auto-index client, but:
Wrap all UI in a singe function and pass it to the new positional
root
parameter inui.run
. This is especially handy in combination withui.sub_pages
. This way you can create rich single-page applications without worrying about defining the correct routes with@page
decorators.Use page functions for all your pages, including the index page at "/".
Note that we are introducing a new
Event
class (see below). Together with the binding module this helps to synchronize long-living objects with short-living UI without relying on a long-living shared client.Calling
.props()
,.classes()
or.style()
with subsequent.update()
A subsequent
.update()
is not necessary anymore because props, classes and style are now observable collections that trigger updates automatically.When overwriting the
update
method in custom elements, infinite recursions can occur. If, e.g., theupdate
methods uses.prop()
before callingsuper().update()
, the.prop()
call will cause an infinite cycle. Wrap it withwith self._props.suspend_updates():
(and similar for classes and style) to pause automatic updates in such cases.Upgrade to Tailwind 4; dropping the
ui.element.tailwind
APIAlthough very similar, Tailwind 4 comes with some breaking changes. Check your layout carefully after upgrading. We noticed differences especially with line spacing and borders.
For technical reasons updating and maintaining our
ui.element.tailwind
API became unfeasible. So we decided to remove it. For auto-completing Tailwind classes, we recommend the NiceGUI extension for Visual Studio Code by @DaelonSuzuka.Dropping support for Python 3.8
Almost one year after Python 3.8 reached its end-of-life, it was time to drop support. This allowed us to update the code to a newer standard and resolve some issues with Python dependencies that already dropped 3.8 a while ago.
ValueChangeEventArguments
got a newprevious_value
attributeIn some situations it might be helpful to have access to both, the current and the previous value. Therefore we added
previous_value
to theValueChangeEventArguments
.Custom elements that emit
ValueChangeEventArguments
need to provide the previous value.Binding from and to non-existing object properties
Binding used to fail silently if one of the attributes doesn't exist. In case of dictionaries this was intended, because they are often used to bind to persistent storage which is empty by default. But for object properties this can lead to very subtle bugs, e.g. after renaming properties and not updating the attribue names in binding functions.
In 3.0 object properties will be checked for existence by default. Missing dictionaries will continue to be ignored.
You can fine-tune this behavior using the
strict: bool | None = None
parameter (None
: check object properties and ignore dictionaries).ui.log
with unspecified width can collapse inside containers with unspecified widthWe noticed that the height of
ui.log
could be affected by its content, which is unexpected. Therefore we decided to use a scroll area for a more robust layout.Now the width of
ui.log
can collapse when placed inside a container with an unspecified width. Either give the container some width or specify the width ofui.log
.ui.clipboard.read()
returnsNone
if the clipboard API isn't availableThe
read()
function used to return an empty string if the clipboard API is not available. This was indistinguishable from an empty clipboard.Now the
read()
function returnsNone
if the clipboard API is not available.Remove deprecated code and APIs (Remove deprecated code and APIs #5037 by @falkoschindler)
app.add_static_file
andapp.add_media_file
raisesFileNotFoundError
instead ofValueError
in case of non-existing files. SinceFileNotFoundError
is NOT a subclass ofValueError
, existing code whichexcept ValueError
will fail to catch the newFileNotFoundError
.ui.aggrid
:run_column_method
is gone. Userun_grid_method
instead.ui.table
callingadd_rows()
/remove_rows()
with variable-length arguments no longer works. Pass a list instead or useadd_row()
/remove_row()
for a single row.ui.open
is gone. Useui.navigate.to
.nicegui.testing.conftest
is gone and you can no longer import it. Usepytest_plugins = ["nicegui.testing.plugin"]
instead.element.on
parameterjs_handler
now has typestr
instead ofstr | None
. You can pass aNone
and it still works, but your type checker won't be happy, and we can't promise it will work for much longer afterwards.JavaScript Dependencies
The infrastructure for managing node packages has been improved significantly (#4163, #5021 by @simontaurus, @evnchn, @falkoschindler).
The following JavaScript dependencies have been updated to the latest versions (#5034 by @falkoschindler):
'rowSelection': {'mode':"multiRow",'headerCheckbox':False}
won't work #3854 by @python-and-novella, @mockey, @falkoschindler)Open tasks:
root
parameterroot
andui.sub_pages
in examples