-
Notifications
You must be signed in to change notification settings - Fork 947
/
Copy pathwidget_style.ts
133 lines (119 loc) · 3.7 KB
/
widget_style.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { assign } from './utils';
import { WidgetModel, WidgetView, DOMWidgetView } from './widget';
/**
* Three functions to deal with some CSS attributes
* to make them easier to use.
*/
export class StyleModel extends WidgetModel {
defaults(): Backbone.ObjectHash {
const Derived = this.constructor as typeof StyleModel;
return assign(
super.defaults(),
{
_model_name: 'StyleModel',
_view_name: 'StyleView',
},
Object.keys(Derived.styleProperties).reduce((obj: any, key: string) => {
obj[key] = Derived.styleProperties[key].default;
return obj;
}, {})
);
}
public static styleProperties: { [s: string]: IStyleProperty } = {};
}
interface IStyleProperty {
attribute: string;
selector: string;
default: string;
}
export class StyleView extends WidgetView {
/**
* Public constructor
*/
initialize(parameters: WidgetView.IInitializeParameters): void {
this._traitNames = [];
super.initialize(parameters);
// Register the traits that live on the Python side
const ModelType = this.model.constructor as typeof StyleModel;
for (const key of Object.keys(ModelType.styleProperties)) {
this.registerTrait(key);
}
// Set the initial styles
this.style();
}
/**
* Register a CSS trait that is known by the model
* @param trait
*/
registerTrait(trait: string): void {
this._traitNames.push(trait);
// Listen to changes, and set the value on change.
this.listenTo(
this.model,
'change:' + trait,
(model: StyleModel, value: string) => {
this.handleChange(trait, value);
}
);
}
/**
* Handles when a trait value changes
*/
handleChange(trait: string, value: any): void {
// should be synchronous so that we can measure later.
const parent = this.options.parent as DOMWidgetView;
if (parent) {
const ModelType = this.model.constructor as typeof StyleModel;
const styleProperties = ModelType.styleProperties;
const attribute = styleProperties[trait].attribute;
const selector = styleProperties[trait].selector;
const elements = selector
? parent.el.querySelectorAll<HTMLElement>(selector)
: [parent.el];
if (value === null) {
for (let i = 0; i !== elements.length; ++i) {
elements[i].style.removeProperty(attribute);
}
} else {
for (let i = 0; i !== elements.length; ++i) {
elements[i].style.setProperty(attribute, value);
}
}
} else {
console.warn('Style not applied because a parent view does not exist');
}
}
/**
* Apply styles for all registered traits
*/
style(): void {
for (const trait of this._traitNames) {
this.handleChange(trait, this.model.get(trait));
}
}
/**
* Remove the styling from the parent view.
*/
unstyle(): void {
const parent = this.options.parent as DOMWidgetView;
const ModelType = this.model.constructor as typeof StyleModel;
const styleProperties = ModelType.styleProperties;
this._traitNames.forEach((trait) => {
if (parent) {
const attribute = styleProperties[trait].attribute;
const selector = styleProperties[trait].selector;
const elements = selector
? parent.el.querySelectorAll<HTMLElement>(selector)
: [parent.el];
for (let i = 0; i !== elements.length; ++i) {
elements[i].style.removeProperty(attribute);
}
} else {
console.warn('Style not removed because a parent view does not exist');
}
}, this);
}
private _traitNames: string[];
}