Skip to content

Commit 3773c0f

Browse files
authored
feat: Add “align” parameter for ui.inline #1535 (#1737)
1 parent be32c42 commit 3773c0f

File tree

13 files changed

+115
-12
lines changed

13 files changed

+115
-12
lines changed

py/examples/inline.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Inline
2+
# Create an inline (horizontal) list of components.
3+
# ---
4+
5+
from h2o_wave import main, app, Q, ui
6+
7+
all_justify = ['start', 'end', 'center', 'between', 'around']
8+
all_align = ['start', 'end', 'center']
9+
10+
justify_choices = [ui.choice(opt, opt) for opt in all_justify]
11+
align_choices = [ui.choice(opt, opt) for opt in all_align]
12+
13+
@app('/demo')
14+
async def serve(q: Q):
15+
justify_current = q.args.justify if q.args.justify else 'start'
16+
align_current = q.args.align if q.args.align else 'center'
17+
18+
q.page['example'] = ui.form_card(box='1 1 -1 3', items=[
19+
ui.inline([
20+
ui.choice_group(name='justify', label='justify', value=justify_current, choices=justify_choices, trigger=True),
21+
ui.choice_group(name='align', label='align', value=align_current, choices=align_choices, trigger=True),
22+
], justify=justify_current, align=align_current)
23+
])
24+
await q.page.save()

py/examples/tour.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ tags.py
103103
image.py
104104
image_popup.py
105105
image_annotator.py
106+
inline.py
106107
file_stream.py
107108
frame.py
108109
frame_path.py

py/h2o_wave/types.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5925,33 +5925,49 @@ class InlineJustify:
59255925
AROUND = 'around'
59265926

59275927

5928+
_InlineAlign = ['start', 'end', 'center', 'baseline']
5929+
5930+
5931+
class InlineAlign:
5932+
START = 'start'
5933+
END = 'end'
5934+
CENTER = 'center'
5935+
BASELINE = 'baseline'
5936+
5937+
59285938
class Inline:
59295939
"""Create an inline (horizontal) list of components.
59305940
"""
59315941
def __init__(
59325942
self,
59335943
items: List['Component'],
59345944
justify: Optional[str] = None,
5945+
align: Optional[str] = None,
59355946
inset: Optional[bool] = None,
59365947
):
59375948
_guard_vector('Inline.items', items, (Component,), False, False, False)
59385949
_guard_enum('Inline.justify', justify, _InlineJustify, True)
5950+
_guard_enum('Inline.align', align, _InlineAlign, True)
59395951
_guard_scalar('Inline.inset', inset, (bool,), False, True, False)
59405952
self.items = items
59415953
"""The components laid out inline."""
59425954
self.justify = justify
59435955
"""Specifies how to lay out the individual components. Defaults to 'start'. One of 'start', 'end', 'center', 'between', 'around'. See enum h2o_wave.ui.InlineJustify."""
5956+
self.align = align
5957+
"""Specifies how the individual components are aligned on the vertical axis. Defaults to 'center'. One of 'start', 'end', 'center', 'baseline'. See enum h2o_wave.ui.InlineAlign."""
59445958
self.inset = inset
59455959
"""Whether to display the components inset from the parent form, with a contrasting background."""
59465960

59475961
def dump(self) -> Dict:
59485962
"""Returns the contents of this object as a dict."""
59495963
_guard_vector('Inline.items', self.items, (Component,), False, False, False)
59505964
_guard_enum('Inline.justify', self.justify, _InlineJustify, True)
5965+
_guard_enum('Inline.align', self.align, _InlineAlign, True)
59515966
_guard_scalar('Inline.inset', self.inset, (bool,), False, True, False)
59525967
return _dump(
59535968
items=[__e.dump() for __e in self.items],
59545969
justify=self.justify,
5970+
align=self.align,
59555971
inset=self.inset,
59565972
)
59575973

@@ -5962,14 +5978,18 @@ def load(__d: Dict) -> 'Inline':
59625978
_guard_vector('Inline.items', __d_items, (dict,), False, False, False)
59635979
__d_justify: Any = __d.get('justify')
59645980
_guard_enum('Inline.justify', __d_justify, _InlineJustify, True)
5981+
__d_align: Any = __d.get('align')
5982+
_guard_enum('Inline.align', __d_align, _InlineAlign, True)
59655983
__d_inset: Any = __d.get('inset')
59665984
_guard_scalar('Inline.inset', __d_inset, (bool,), False, True, False)
59675985
items: List['Component'] = [Component.load(__e) for __e in __d_items]
59685986
justify: Optional[str] = __d_justify
5987+
align: Optional[str] = __d_align
59695988
inset: Optional[bool] = __d_inset
59705989
return Inline(
59715990
items,
59725991
justify,
5992+
align,
59735993
inset,
59745994
)
59755995

py/h2o_wave/ui.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,20 +2197,23 @@ def stats(
21972197
def inline(
21982198
items: List[Component],
21992199
justify: Optional[str] = None,
2200+
align: Optional[str] = None,
22002201
inset: Optional[bool] = None,
22012202
) -> Component:
22022203
"""Create an inline (horizontal) list of components.
22032204
22042205
Args:
22052206
items: The components laid out inline.
22062207
justify: Specifies how to lay out the individual components. Defaults to 'start'. One of 'start', 'end', 'center', 'between', 'around'. See enum h2o_wave.ui.InlineJustify.
2208+
align: Specifies how the individual components are aligned on the vertical axis. Defaults to 'center'. One of 'start', 'end', 'center', 'baseline'. See enum h2o_wave.ui.InlineAlign.
22072209
inset: Whether to display the components inset from the parent form, with a contrasting background.
22082210
Returns:
22092211
A `h2o_wave.types.Inline` instance.
22102212
"""
22112213
return Component(inline=Inline(
22122214
items,
22132215
justify,
2216+
align,
22142217
inset,
22152218
))
22162219

r/R/ui.R

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2577,19 +2577,24 @@ ui_stats <- function(
25772577
#' @param items The components laid out inline.
25782578
#' @param justify Specifies how to lay out the individual components. Defaults to 'start'.
25792579
#' One of 'start', 'end', 'center', 'between', 'around'. See enum h2o_wave.ui.InlineJustify.
2580+
#' @param align Specifies how the individual components are aligned on the vertical axis. Defaults to 'center'.
2581+
#' One of 'start', 'end', 'center', 'baseline'. See enum h2o_wave.ui.InlineAlign.
25802582
#' @param inset Whether to display the components inset from the parent form, with a contrasting background.
25812583
#' @return A Inline instance.
25822584
#' @export
25832585
ui_inline <- function(
25842586
items,
25852587
justify = NULL,
2588+
align = NULL,
25862589
inset = NULL) {
25872590
.guard_vector("items", "WaveComponent", items)
25882591
# TODO Validate justify
2592+
# TODO Validate align
25892593
.guard_scalar("inset", "logical", inset)
25902594
.o <- list(inline=list(
25912595
items=items,
25922596
justify=justify,
2597+
align=align,
25932598
inset=inset))
25942599
class(.o) <- append(class(.o), c(.wave_obj, "WaveComponent"))
25952600
return(.o)

tools/intellij-plugin/src/main/resources/templates/wave-components.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1304,8 +1304,9 @@
13041304
<option name="Python" value="true"/>
13051305
</context>
13061306
</template>
1307-
<template name="w_full_inline" value="ui.inline(justify='$justify$',inset=$inset$,items=[&#10; $items$ &#10;]),$END$" description="Create Wave Inline with full attributes." toReformat="true" toShortenFQNames="true">
1307+
<template name="w_full_inline" value="ui.inline(justify='$justify$',align='$align$',inset=$inset$,items=[&#10; $items$ &#10;]),$END$" description="Create Wave Inline with full attributes." toReformat="true" toShortenFQNames="true">
13081308
<variable name="justify" expression="" defaultValue="&quot;start&quot;" alwaysStopAt="true"/>
1309+
<variable name="align" expression="" defaultValue="&quot;center&quot;" alwaysStopAt="true"/>
13091310
<variable name="inset" expression="" defaultValue="&quot;False&quot;" alwaysStopAt="true"/>
13101311
<variable name="items" expression="" defaultValue="" alwaysStopAt="true"/>
13111312
<context>

tools/vscode-extension/component-snippets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@
11081108
"Wave Full Inline": {
11091109
"prefix": "w_full_inline",
11101110
"body": [
1111-
"ui.inline(justify='${1:start}', inset=${2:False}, items=[\n\t\t$3\t\t\n]),$0"
1111+
"ui.inline(justify='${1:start}', align='${2:center}', inset=${3:False}, items=[\n\t\t$4\t\t\n]),$0"
11121112
],
11131113
"description": "Create a full Wave Inline."
11141114
},

ui/src/form.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import { Text, TextL, TextM, TextS, TextXl, TextXs, XText } from './text'
5252
import { Textbox, XTextbox } from './textbox'
5353
import { TextAnnotator, XTextAnnotator } from './text_annotator'
5454
import { ImageAnnotator, XImageAnnotator } from './image_annotator'
55-
import { clas, cssVar, justifications, padding } from './theme'
55+
import { clas, cssVar, justifications, alignments, padding } from './theme'
5656
import { Toggle, XToggle } from './toggle'
5757
import { XToolTip } from './tooltip'
5858
import { bond } from './ui'
@@ -171,6 +171,8 @@ interface Inline {
171171
items: Component[]
172172
/** Specifies how to lay out the individual components. Defaults to 'start'. */
173173
justify?: 'start' | 'end' | 'center' | 'between' | 'around'
174+
/** Specifies how the individual components are aligned on the vertical axis. Defaults to 'center'. */
175+
align?: 'start' | 'end' | 'center' | 'baseline'
174176
/** Whether to display the components inset from the parent form, with a contrasting background. */
175177
inset?: B
176178
}
@@ -214,18 +216,19 @@ const
214216
},
215217
})
216218

217-
type XComponentAlignment = 'start' | 'end' | 'center' | 'between' | 'around'
219+
type Justification = 'start' | 'end' | 'center' | 'between' | 'around'
220+
type Alignment = 'start' | 'end' | 'center' | 'baseline'
218221

219222
export const
220-
XComponents = ({ items, alignment, inset }: { items: Component[], alignment?: XComponentAlignment, inset?: B }) => {
223+
XComponents = ({ items, justify, align, inset }: { items: Component[], justify?: Justification, align?: Alignment, inset?: B }) => {
221224
const
222225
components = items.map((m: any, i) => {
223226
const
224227
// All form items are wrapped by their component name (first and only prop of "m").
225228
[componentKey] = Object.keys(m),
226229
{ name, visible = true, width = 'auto' } = m[componentKey],
227230
visibleStyles: React.CSSProperties = visible ? {} : { display: 'none' },
228-
// TODO: Ugly, maybe introduce 'align' prop to ui.inline?
231+
// TODO: Ugly, maybe use ui.inline's 'align' prop instead?
229232
alignSelf = componentKey === 'links' ? 'flex-start' : undefined
230233

231234
return (
@@ -235,12 +238,19 @@ export const
235238
</div>
236239
)
237240
})
238-
return <div className={clas(alignment ? css.horizontal : css.vertical, inset ? css.inset : '')} style={{ justifyContent: justifications[alignment || ''] }}>{components}</div>
241+
return <div
242+
className={clas(justify ? css.horizontal : css.vertical, inset ? css.inset : '')}
243+
style={{
244+
justifyContent: justifications[justify || ''],
245+
alignItems: alignments[align || ''],
246+
}}
247+
>{components}</div>
239248
},
240249
XInline = ({ model: m }: { model: Inline }) => (
241250
<XComponents
242251
items={m.items}
243-
alignment={m.justify || 'start'}
252+
justify={m.justify || 'start'}
253+
align={m.align || 'center'}
244254
inset={m.inset}
245255
/>
246256
)

ui/src/message_bar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export const
138138
messageBarType={toMessageBarType(type)}
139139
className={css.messageBar}
140140
isMultiline={false}
141-
actions={btns?.length ? <XComponents items={btns || []} alignment='end' /> : undefined}
141+
actions={btns?.length ? <XComponents items={btns || []} justify='end' /> : undefined}
142142
messageBarIconProps={{ iconName }}
143143
>
144144
<Markdown source={text} />

ui/src/notification_bar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export const
102102
<Fluent.MessageBar
103103
messageBarType={toMessageBarType(currentModel?.type)}
104104
messageBarIconProps={{ iconName }}
105-
actions={buttons?.length ? <XComponents items={buttons || []} alignment='end' /> : undefined}
105+
actions={buttons?.length ? <XComponents items={buttons || []} justify='end' /> : undefined}
106106
isMultiline={isMultiline}
107107
onDismiss={onDismiss}
108108
className={css.messageBar}

ui/src/section.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const
6161
components = unpack<Component[]>(items), // XXX ugly
6262
form = items && (
6363
<div className={css.rhs}>
64-
<XComponents items={components} alignment='end' />
64+
<XComponents items={components} justify='end' />
6565
</div>
6666
)
6767

4.3 KB
Loading

website/widgets/form/inline.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ custom_edit_url: null
88

99
By default, all form items are laid out vertically (top to bottom). For more complex forms though, this might not be enough and
1010
that's where [ui.inline](/docs/api/ui#inline) comes into play. All you need to do is specify the `items` attribute, which accepts any form
11-
component. Optionally, you can also specify `justify` if you want to control the alignment too.
11+
component.
1212

1313
```py
1414
q.page['form'] = ui.form_card(
@@ -22,3 +22,42 @@ q.page['form'] = ui.form_card(
2222
```
2323

2424
Check the full API at [ui.inline](/docs/api/ui#inline).
25+
26+
## Horizontal alignment (`justify`)
27+
28+
You can specify how the elements should be horizontally aligned using the `justify` parameter. The default value is `start`.
29+
30+
```py
31+
items = [
32+
ui.button(name='primary_button', label='Primary', primary=True),
33+
ui.button(name='basic_caption_button', label='Secondary', caption='Caption'),
34+
ui.button(name='icon_button', icon='Heart', caption='Like'),
35+
]
36+
37+
q.page['justify'] = ui.form_card(box='1 1 3 5', items=[
38+
ui.inline(items, justify='start'),
39+
ui.inline(items, justify='center'),
40+
ui.inline(items, justify='end'),
41+
ui.inline(items, justify='between'),
42+
ui.inline(items, justify='around'),
43+
])
44+
```
45+
46+
## Vertical alignment (`align`)
47+
48+
You can specify how the elements should be horizontally aligned using the `align` parameter. The default value is `center`.
49+
50+
```py
51+
items = [
52+
ui.button(name='primary_button', label='Primary', primary=True),
53+
ui.button(name='basic_caption_button', label='Secondary', caption='Caption'),
54+
ui.button(name='icon_button', icon='Heart', caption='Like'),
55+
]
56+
57+
q.page['align'] = ui.form_card(box='1 1 3 5', items=[
58+
ui.inline(items, align='start'),
59+
ui.inline(items, align='baseline'),
60+
ui.inline(items, align='center'),
61+
ui.inline(items, align='end'),
62+
])
63+
```

0 commit comments

Comments
 (0)