zauberzeug / nicegui

Create web-based user interfaces with Python. The nice way.
https://nicegui.io
MIT License
10.02k stars 594 forks source link

ui.scroll_area's thumb-style property doesn't work (bar-style does work though) #3732

Closed pier124 closed 1 month ago

pier124 commented 1 month ago

Description

this one works

.props('bar-style="backgroundColor:#027be3; right:2px; borderRadius:9px; width:8px; opacity:0.2"')

this one doesn't (ui.scroll_area won't load at all):

.props('thumb-style="backgroundColor:#027be3; right:4px; borderRadius:7px; width:4px; opacity:0.75"')
rodja commented 1 month ago

There is something really strange. I tested with plain Quasar and got similar issues when using css as value of thumb-style. If I set the prop as an js object it seems to work: https://codepen.io/rodja/pen/qBzGRjd?editors=101

But I if I try the same in NiceGUI, the whole page breaks:

with ui.scroll_area().props(":thumb-style=\"{background: 'red'}\""):
    for i in range(100):
        ui.label(f'item {i}')

Connection is dropped with this JS error in the console:

vue.global.prod.js:5 Uncaught TypeError: Failed to set an indexed property [0] on 'CSSStyleDeclaration': Indexed property setter is not supported.
    at lr (vue.global.prod.js:5:64103)
    at vue.global.prod.js:5:72459
    at patchProp (vue.global.prod.js:5:72638)
    at $ (vue.global.prod.js:5:34579)
    at M (vue.global.prod.js:5:34286)
    at T (vue.global.prod.js:5:33656)
    at D (vue.global.prod.js:5:35048)
    at $ (vue.global.prod.js:5:34458)
    at M (vue.global.prod.js:5:34286)
    at T (vue.global.prod.js:5:33656)
falkoschindler commented 1 month ago

A difference between "bar-style" and "thumb-style" is that the latter doesn't accept strings:

Screenshot 2024-09-24 at 23 53 28

But I don't understand why .props(":thumb-style=\"{background: 'red'}\"") doesn't work.

falkoschindler commented 1 month ago

Oh I found a solution:

.props(''' :thumb-style="({background: 'red'})" ''')

The brackets () prevent the curly braces being interpreted as a block statement rather than an object literal when passed to JavaScript's eval().

This can be solved by replacing eval() in https://github.com/zauberzeug/nicegui/blob/2c676841090cf9d9f671a7cacf48c896eee95c51/nicegui/static/nicegui.js#L161 with new Function(`return (${value})`)(); like in https://github.com/zauberzeug/nicegui/blob/main/nicegui/static/utils/dynamic_properties.js. Maybe we can even use convertDynamicProperties() directly and don't need to duplicate code.

falkoschindler commented 1 month ago

I noticed one caveat:

Using new Function(`return ($(value)`)() instead of eval(value) breaks values with multiple statements:

ui.button('Loading').props('loading :percentage="{const p = 80; p}"')

This might be a very very rare case, but technically it would be a breaking change.