Fix datalist and menu and choice widgets.

This commit is contained in:
David Bau
2022-05-07 01:01:22 -04:00
parent b04db4454d
commit eb6297ecba
3 changed files with 101 additions and 22 deletions
+22 -18
View File
@@ -747,13 +747,13 @@ class Choice(Widget):
A set of radio button choices.
"""
def __init__(self, choices=None, selection=None,
def __init__(self, choices=None, value=None,
**kwargs):
super().__init__(**kwargs)
if choices is None:
choices = []
self.choices = Property(choices)
self.selection = Property(selection)
self.value = Property(value)
def widget_js(self):
# Note that the 'input' event would enable during-drag feedback,
@@ -771,13 +771,13 @@ class Choice(Widget):
element.innerHTML = lines.join();
}
model.on('choices horizontal', render);
model.on('selection', (ev) => {
model.on('value', (ev) => {
[...element.querySelectorAll('input')].forEach((e) => {
e.checked = (e.value == ev.value);
})
});
element.addEventListener('change', (e) => {
model.set('selection', element.choice.value);
model.set('value', element.choice.value);
});
''')
@@ -786,9 +786,11 @@ class Choice(Widget):
with show.enter('form', self.std_attrs(), out=out):
for value in self.choices:
with show.enter('label', out=out):
show.emit('input',
(show.attrs(checked=None) if value == self.selection else None),
name='choice', type='radio', value=value, out=out)
show.emit(show.PLAIN('input',
(show.attr(checked=None) if value == self.value else None),
name='choice', type='radio', value=value), out=out)
with show.enter('div', out=out):
out.append(html.escape(str(value)))
return ''.join(out)
class Menu(Widget):
@@ -796,12 +798,12 @@ class Menu(Widget):
A dropdown choice.
"""
def __init__(self, choices=None, selection=None, **kwargs):
def __init__(self, choices=None, value=None, **kwargs):
super().__init__(**kwargs)
if choices is None:
choices = []
self.choices = Property(choices)
self.selection = Property(selection)
self.value = Property(value)
def widget_js(self):
return minify('''
@@ -810,21 +812,21 @@ class Menu(Widget):
.replace(/>/g, ">").replace(/"/g, """);
}
function render() {
var selection = model.get('selection');
var value = model.get('value');
var lines = model.get('choices').map((c) => {
return '<option value="' + esc(''+c) + '"' +
(c == selection ? ' selected' : '') +
(c == value ? ' selected' : '') +
'>' + esc(''+c) + '</option>';
});
element.menu.innerHTML = lines.join('\\n');
}
model.on('selection', (ev) => {
model.on('value', (ev) => {
[...element.querySelectorAll('option')].forEach((e) => {
e.selected = (e.value == ev.value);
})
});
element.addEventListener('change', (e) => {
model.set('selection', element.menu.value);
model.set('value', element.menu.value);
});
''')
@@ -834,7 +836,7 @@ class Menu(Widget):
with show.enter(show.Tag('select', name='menu'), out=out):
for value in self.choices:
with show.enter(show.Tag('option',
(show.attr(selected=None) if value == self.selection else None),
(show.attr(selected=None) if value == self.value else None),
value=value), out=out):
out.append(html.escape(str(value)))
return ''.join(out)
@@ -900,11 +902,13 @@ class Datalist(Widget):
out = []
with show.enter('form', self.std_attrs(),
onsubmit='return false;', out=out):
show.emit('input', name='inp', list=self.datalist_id(),
autocomplete='off', out=out)
with show.enter(show.Tag('datalist'), id=self.datalist_id()):
show.emit(show.PLAIN('input', name='inp', list=self.datalist_id(),
value=self.value, autocomplete='off'), out=out)
with show.enter(show.PLAIN('datalist', show.style(display='none')),
id=self.datalist_id(), out=out):
for value in self.choices:
show.emit('option', value=str(value))
show.emit('option', value=str(value), out=out)
return ''.join(out)
class Div(Widget):
+2 -1
View File
@@ -237,7 +237,8 @@ TR = Tag('tr', ChildTag(TD))
TABLE = Tag('table', ChildTag(TR))
# Remove defaults
PLAIN = Tag()
PLAIN = Tag(style(display=None, flex=None, flexFlow=None, gap=None),
ChildTag(None))
# The TIGHT style allows the content to provide the size, instead of
# expanding to fill the available space.
+77 -3
View File
@@ -191,9 +191,9 @@
"id": "b78f64fa",
"metadata": {},
"source": [
"## Tight columns and Wrapped rows\n",
"## Tight columns and wrapped rows\n",
"\n",
"A few useful `show.style` instances are predefined as constants. For example, `show.style(display='inline-flex')` is also called `show.TIGHT`, because it provides a tight layout of rows that are sized to fit the content instead of making the flexbox expand to fill its container. Similaly `show.WRAP` makes a row that packs the data to the left and wraps it (like the way text flows), instad of as spaced-out columns."
"A few useful `show.style` instances are predefined as constants. For example, `show.style(display='inline-flex')` is also called `show.TIGHT`, because it provides a tight layout of rows that are sized to fit the content instead of making the flexbox expand to fill its container. Similaly `show.WRAP` makes a row that packs the data to the left and wraps it (like the way text flows), instead of as spaced-out columns."
]
},
{
@@ -213,7 +213,7 @@
"metadata": {},
"outputs": [],
"source": [
"show(show.TIGHT,\n",
"show(show.TIGHT, show.style(gap=0),\n",
" [[[show.style(font='italic 24px serif'), 'lots of data',\n",
" show.style(background='gainsboro'),\n",
" show.WRAP, [f'({a},{b})' for a in range(i) for b in range(j)]]\n",
@@ -361,6 +361,80 @@
"show([[div, show.style(flex=7), ra3]])"
]
},
{
"cell_type": "markdown",
"id": "21ecb0c0",
"metadata": {},
"source": [
"## Menus, Choices, and Datalists\n",
"\n",
"You can make dropdown menus using `Menu`; radio button choices using `Choice`; and editable dropdowns (combobox) using `Datalist`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f3032e1",
"metadata": {},
"outputs": [],
"source": [
"from baukit import Menu\n",
"\n",
"menu = Menu(choices=list(range(0,101,10)))\n",
"ra4 = Range(step=10)\n",
"menu.value = ra4.prop('value')\n",
"\n",
"show([[menu, show.style(flex=7), ra4]])"
]
},
{
"cell_type": "markdown",
"id": "20d1070c",
"metadata": {},
"source": [
"Radio button choice arrays can be arranged horizontally or vertically based on the level of nesting."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8df5514a",
"metadata": {},
"outputs": [],
"source": [
"from baukit import Choice\n",
"\n",
"choice = Choice(choices=list(range(0,101,50)))\n",
"ra6 = Range(step=50)\n",
"choice.value = ra6.prop('value')\n",
"\n",
"show([[choice, show.style(flex=6), ra6]])\n",
"show([[[choice], show.style(flex=6), ra6]])"
]
},
{
"cell_type": "markdown",
"id": "8d85174a",
"metadata": {},
"source": [
"A Datalist allows free-form input while also providing a dropdown menu for choices."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "07a2ed9e",
"metadata": {},
"outputs": [],
"source": [
"from baukit import Datalist\n",
"dl = Datalist(choices=list(range(0,101,20)))\n",
"ra5 = Range(step=1)\n",
"dl.value = ra5.prop('value')\n",
"\n",
"show([[dl, show.style(flex=7), ra5]])"
]
},
{
"cell_type": "markdown",
"id": "8b8a23a9",