Skip to content

Commit 4fbe473

Browse files
committed
Add GridBox widget
1 parent 8b76edf commit 4fbe473

File tree

8 files changed

+175
-10
lines changed

8 files changed

+175
-10
lines changed

ipywidgets/widgets/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .widget_core import CoreWidget
1111
from .widget_bool import Checkbox, ToggleButton, Valid
1212
from .widget_button import Button, ButtonStyle
13-
from .widget_box import Box, HBox, VBox
13+
from .widget_box import Box, HBox, VBox, GridBox
1414
from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider, FloatLogSlider
1515
from .widget_image import Image
1616
from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider, Play, SliderStyle
@@ -23,5 +23,5 @@
2323
from .widget_controller import Controller
2424
from .interaction import interact, interactive, fixed, interact_manual, interactive_output
2525
from .widget_link import jslink, jsdlink
26-
from .widget_layout import Layout
26+
from .widget_layout import Layout, GridLayout, GridItemLayout
2727
from .widget_style import Style

ipywidgets/widgets/domwidget.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from traitlets import Unicode
77
from .widget import Widget, widget_serialization
88
from .trait_types import InstanceDict, TypedTuple
9-
from .widget_layout import Layout
9+
from .widget_layout import Layout, GridLayout, GridItemLayout
1010
from .widget_style import Style
1111

1212

@@ -16,6 +16,8 @@ class DOMWidget(Widget):
1616
_model_name = Unicode('DOMWidgetModel').tag(sync=True)
1717
_dom_classes = TypedTuple(trait=Unicode(), help="CSS classes applied to widget DOM element").tag(sync=True)
1818
layout = InstanceDict(Layout).tag(sync=True, **widget_serialization)
19+
grid_layout = InstanceDict(GridLayout).tag(sync=True, **widget_serialization)
20+
grid_item_layout = InstanceDict(GridItemLayout).tag(sync=True, **widget_serialization)
1921

2022
def add_class(self, className):
2123
"""

ipywidgets/widgets/widget_box.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,9 @@ class HBox(Box):
107107
"""
108108
_model_name = Unicode('HBoxModel').tag(sync=True)
109109
_view_name = Unicode('HBoxView').tag(sync=True)
110+
111+
@register
112+
class GridBox(Box):
113+
_model_name = Unicode('GridBoxModel').tag(sync=True)
114+
_view_name = Unicode('GridBoxView').tag(sync=True)
115+

ipywidgets/widgets/widget_layout.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,34 @@ def validate(self, obj, value):
7171
return super(LayoutTraitType, self).validate(obj, self.klass(**value))
7272
else:
7373
return super(LayoutTraitType, self).validate(obj, value)
74+
75+
@register
76+
class GridLayout(Widget):
77+
78+
_view_name = Unicode('GridLayoutView').tag(sync=True)
79+
_view_module = Unicode('@jupyter-widgets/base').tag(sync=True)
80+
_view_module_version = Unicode(__jupyter_widgets_base_version__).tag(sync=True)
81+
_model_name = Unicode('GridLayoutModel').tag(sync=True)
82+
83+
# Keys
84+
auto_columns = Unicode(None, allow_none=True, help="The grid-auto-columns CSS attribute.").tag(sync=True)
85+
auto_flow = CaselessStrEnum(['column','row','row dense','column dense']+ CSS_PROPERTIES, allow_none=True, help="The grid-auto-flow CSS attribute.").tag(sync=True)
86+
auto_rows = Unicode(None, allow_none=True, help="The grid-auto-rows CSS attribute.").tag(sync=True)
87+
row_gap = Unicode(None, allow_none=True, help="The grid-row-gap CSS attribute.").tag(sync=True)
88+
column_gap = Unicode(None, allow_none=True, help="The grid-column-gap CSS attribute.").tag(sync=True)
89+
template_areas = Unicode(None, allow_none=True, help="The grid-template-areas CSS attribute.").tag(sync=True)
90+
template_columns = Unicode(None, allow_none=True, help="The grid-template-columns CSS attribute.").tag(sync=True)
91+
template_rows = Unicode(None, allow_none=True, help="The grid-template-rows CSS attribute.").tag(sync=True)
92+
93+
94+
@register
95+
class GridItemLayout(Widget):
96+
_view_name = Unicode('GridItemLayoutView').tag(sync=True)
97+
_view_module = Unicode('@jupyter-widgets/base').tag(sync=True)
98+
_view_module_version = Unicode(__jupyter_widgets_base_version__).tag(sync=True)
99+
_model_name = Unicode('GridItemLayoutModel').tag(sync=True)
100+
101+
row_start = Unicode(None, allow_none=True, help="The row-start CSS attribute.").tag(sync=True)
102+
column_start = Unicode(None, allow_none=True, help="The grid-column-start CSS attribute.").tag(sync=True)
103+
row_end = Unicode(None, allow_none=True, help="The grid-row-end CSS attribute.").tag(sync=True)
104+
column_end = Unicode(None, allow_none=True, help="The grid-column-end CSS attribute.").tag(sync=True)

packages/base/src/widget.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ class DOMWidgetModel extends WidgetModel {
566566
static serializers: ISerializers = {
567567
...WidgetModel.serializers,
568568
layout: {deserialize: unpack_models},
569+
grid_layout: {deserialize: unpack_models},
570+
grid_item_layout: {deserialize: unpack_models},
569571
style: {deserialize: unpack_models},
570572
};
571573

@@ -749,7 +751,17 @@ class DOMWidgetView extends WidgetView {
749751

750752
this.layoutPromise = Promise.resolve();
751753
this.listenTo(this.model, 'change:layout', (model, value) => {
752-
this.setLayout(value, model.previous('layout'));
754+
this.setLayout('layoutPromise', value, model.previous('layout'));
755+
});
756+
757+
this.gridLayoutPromise = Promise.resolve();
758+
this.listenTo(this.model, 'change:grid_layout', (model, value) => {
759+
this.setLayout('gridLayoutPromise', value, model.previous('grid_layout'));
760+
});
761+
762+
this.gridItemLayoutPromise = Promise.resolve();
763+
this.listenTo(this.model, 'change:grid_item_layout', (model, value) => {
764+
this.setLayout('gridItemLayoutPromise', value, model.previous('grid_item_layout'));
753765
});
754766

755767
this.stylePromise = Promise.resolve();
@@ -759,7 +771,9 @@ class DOMWidgetView extends WidgetView {
759771

760772
this.displayed.then(() => {
761773
this.update_classes([], this.model.get('_dom_classes'));
762-
this.setLayout(this.model.get('layout'));
774+
this.setLayout('layoutPromise', this.model.get('layout'));
775+
this.setLayout('gridLayoutPromise', this.model.get('grid_layout'));
776+
this.setLayout('gridItemLayoutPromise', this.model.get('grid_item_layout'));
763777
this.setStyle(this.model.get('style'));
764778
});
765779

@@ -769,9 +783,9 @@ class DOMWidgetView extends WidgetView {
769783
});
770784
}
771785

772-
setLayout(layout, oldLayout?) {
786+
setLayout(layoutAttrName, layout, oldLayout?) {
773787
if (layout) {
774-
this.layoutPromise = this.layoutPromise.then((oldLayoutView) => {
788+
this[layoutAttrName] = this[layoutAttrName].then((oldLayoutView) => {
775789
if (oldLayoutView) {
776790
oldLayoutView.unlayout();
777791
this.stopListening(oldLayoutView.model);
@@ -918,5 +932,7 @@ class DOMWidgetView extends WidgetView {
918932
'$el': any;
919933
pWidget: Widget;
920934
layoutPromise: Promise<any>;
935+
gridLayoutPromise: Promise<any>;
936+
gridItemLayoutPromise: Promise<any>;
921937
stylePromise: Promise<any>;
922938
}

packages/base/src/widget_layout.ts

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@ let css_properties = {
4141
width: null
4242
};
4343

44+
let grid_css_properties = {
45+
auto_columns: null,
46+
auto_flow: null,
47+
auto_rows: null,
48+
row_gap: null,
49+
column_gap: null,
50+
template_areas: null,
51+
template_columns: null,
52+
template_rows: null
53+
};
54+
55+
let grid_item_css_properties = {
56+
row_start: null,
57+
column_start: null,
58+
row_end: null,
59+
column_end: null
60+
}
61+
62+
4463
export
4564
class LayoutModel extends WidgetModel {
4665
defaults() {
@@ -53,14 +72,19 @@ class LayoutModel extends WidgetModel {
5372

5473
export
5574
class LayoutView extends WidgetView {
75+
constructor(options?: Backbone.ViewOptions<LayoutModel> & {options?: any}) {
76+
super(options);
77+
}
5678
/**
5779
* Public constructor
5880
*/
5981
initialize(parameters) {
6082
this._traitNames = [];
6183
super.initialize(parameters);
84+
// Allowing override of default css_properties
85+
const {options: {css_props = css_properties}} = parameters;
6286
// Register the traits that live on the Python side
63-
for (let key of Object.keys(css_properties)) {
87+
for (let key of Object.keys(css_props)) {
6488
this.registerTrait(key);
6589
}
6690
}
@@ -87,7 +111,7 @@ class LayoutView extends WidgetView {
87111
* @return css property name
88112
*/
89113
css_name(trait: string): string {
90-
return trait.replace('_', '-');
114+
return trait.replace(new RegExp('_', 'g'), '-');
91115
}
92116

93117
/**
@@ -121,5 +145,61 @@ class LayoutView extends WidgetView {
121145
}, this);
122146
}
123147

124-
private _traitNames: string[];
148+
protected _traitNames: string[];
149+
}
150+
151+
export
152+
class GridLayoutModel extends WidgetModel {
153+
defaults() {
154+
return assign(super.defaults(), {
155+
_model_name: 'GridLayoutModel',
156+
_view_name: 'GridLayoutView'
157+
}, grid_css_properties);
158+
}
159+
}
160+
161+
export
162+
class GridLayoutView extends LayoutView {
163+
/**
164+
* Public constructor
165+
*/
166+
initialize(parameters) {
167+
const options = {...parameters.options, css_props: grid_css_properties};
168+
super.initialize({...parameters, options});
169+
}
170+
171+
/**
172+
* Get the the name of the css property from the trait name
173+
* @param model attribute name
174+
* @return css property name
175+
*/
176+
css_name(trait: string): string {
177+
return `grid-${trait.replace(new RegExp('_', 'g'), '-')}`;
178+
}
179+
}
180+
181+
182+
export
183+
class GridItemLayoutModel extends WidgetModel {
184+
defaults() {
185+
return assign(super.defaults(), {
186+
_model_name: 'GridItemLayoutModel',
187+
_view_name: 'GridItemLayoutView'
188+
}, grid_item_css_properties);
189+
}
190+
}
191+
192+
export
193+
class GridItemLayoutView extends LayoutView {
194+
/**
195+
* Public constructor
196+
*/
197+
initialize(parameters) {
198+
const options = {...parameters.options, css_props: grid_item_css_properties};
199+
super.initialize({...parameters, options});
200+
}
201+
202+
css_name(trait: string): string {
203+
return `grid-${trait.replace(new RegExp('_', 'g'), '-')}`;
204+
}
125205
}

packages/controls/css/widgets-base.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@
9595
overflow: auto;
9696
}
9797

98+
.widget-gridbox {
99+
box-sizing: border-box;
100+
display: grid;
101+
margin: 0;
102+
overflow: auto;
103+
}
104+
98105
.widget-hbox {
99106
flex-direction: row;
100107
}

packages/controls/src/widget_box.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,26 @@ class VBoxView extends BoxView {
214214
this.pWidget.addClass('widget-vbox');
215215
}
216216
}
217+
218+
export
219+
class GridBoxView extends BoxView {
220+
/**
221+
* Public constructor
222+
*/
223+
initialize(parameters) {
224+
super.initialize(parameters);
225+
this.pWidget.addClass('widget-gridbox');
226+
// display needn't be set to flex and grid
227+
this.pWidget.removeClass('widget-box');
228+
}
229+
}
230+
231+
export
232+
class GridBoxModel extends BoxModel {
233+
defaults() {
234+
return _.extend(super.defaults(), {
235+
_view_name: 'GridBoxView',
236+
_model_name: 'GridBoxModel',
237+
});
238+
}
239+
}

0 commit comments

Comments
 (0)