canvas-datagrid 0.25.4 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/button.js ADDED
@@ -0,0 +1,326 @@
1
+ /*jslint browser: true, unparam: true, todo: true*/
2
+ /*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false, Event: false*/
3
+ 'use strict';
4
+
5
+ export default function (self) {
6
+ let zIndexTop;
7
+
8
+ function applyButtonMenuItemStyle(buttonMenuItemContainer) {
9
+ self.createInlineStyle(
10
+ buttonMenuItemContainer,
11
+ 'canvas-datagrid-button-menu-item' + (self.mobile ? '-mobile' : ''),
12
+ );
13
+ buttonMenuItemContainer.addEventListener('mouseover', function () {
14
+ self.createInlineStyle(
15
+ buttonMenuItemContainer,
16
+ 'canvas-datagrid-button-menu-item:hover',
17
+ );
18
+ });
19
+ buttonMenuItemContainer.addEventListener('mouseout', function () {
20
+ self.createInlineStyle(
21
+ buttonMenuItemContainer,
22
+ 'canvas-datagrid-button-menu-item',
23
+ );
24
+ });
25
+ }
26
+
27
+ function applyButtonStyle(button) {
28
+ self.createInlineStyle(button, 'canvas-datagrid-button-wrapper');
29
+ button.addEventListener('mouseover', function () {
30
+ if (!self.buttonMenu) {
31
+ self.createInlineStyle(button, 'canvas-datagrid-button-wrapper:hover');
32
+ }
33
+ });
34
+ button.addEventListener('mouseout', function () {
35
+ if (!self.buttonMenu) {
36
+ self.createInlineStyle(button, 'canvas-datagrid-button-wrapper');
37
+ }
38
+ });
39
+ }
40
+
41
+ function createButton(pos, items, imgSrc) {
42
+ var wrapper = document.createElement('div'),
43
+ buttonArrow = document.createElement('div'),
44
+ buttonIcon = document.createElement('div'),
45
+ intf = {};
46
+
47
+ if (!Array.isArray(items)) {
48
+ throw new Error('createButton expects an array.');
49
+ }
50
+
51
+ function init() {
52
+ var loc = {},
53
+ s = self.scrollOffset(self.canvas);
54
+
55
+ if (zIndexTop === undefined) {
56
+ zIndexTop = self.style.buttonZIndex;
57
+ }
58
+
59
+ applyButtonStyle(wrapper);
60
+ self.createInlineStyle(buttonIcon, 'canvas-datagrid-button-icon');
61
+ self.createInlineStyle(buttonArrow, 'canvas-datagrid-button-arrow');
62
+
63
+ loc.x = pos.left - s.left;
64
+ loc.y = pos.top - s.top;
65
+ loc.height = 0;
66
+ zIndexTop += 1;
67
+ wrapper.style.position = 'absolute';
68
+ wrapper.style.zIndex = zIndexTop;
69
+ wrapper.style.left = loc.x + 'px';
70
+ wrapper.style.top = loc.y + 'px';
71
+ wrapper.left = pos.left + self.scrollBox.scrollLeft;
72
+ wrapper.top = pos.top + self.scrollBox.scrollTop;
73
+ buttonArrow.innerHTML = self.style.buttonArrowDownHTML;
74
+ if (imgSrc) {
75
+ let img = document.createElement('img');
76
+ img.setAttribute('src', imgSrc);
77
+ img.style.maxWidth = '100%';
78
+ img.style.height = '100%';
79
+ buttonIcon.appendChild(img);
80
+ }
81
+ wrapper.appendChild(buttonIcon);
82
+ wrapper.appendChild(buttonArrow);
83
+ document.body.appendChild(wrapper);
84
+ wrapper.addEventListener('click', toggleButtonMenu);
85
+ }
86
+
87
+ intf.wrapper = wrapper;
88
+ intf.items = items;
89
+ init();
90
+
91
+ intf.dispose = function () {
92
+ if (wrapper.parentNode) {
93
+ wrapper.parentNode.removeChild(wrapper);
94
+ }
95
+ };
96
+ return intf;
97
+ }
98
+
99
+ function toggleButtonMenu() {
100
+ function createDisposeEvent() {
101
+ requestAnimationFrame(function () {
102
+ document.addEventListener('click', self.disposeButtonMenu);
103
+ });
104
+ }
105
+ if (self.buttonMenu) {
106
+ self.disposeButtonMenu();
107
+ } else {
108
+ let pos = {
109
+ left: self.button.wrapper.left - self.scrollBox.scrollLeft,
110
+ top:
111
+ self.button.wrapper.top +
112
+ self.button.wrapper.offsetHeight -
113
+ self.scrollBox.scrollTop,
114
+ };
115
+ self.buttonMenu = createButtonMenu(pos, self.button.items);
116
+ self.createInlineStyle(
117
+ self.button.wrapper,
118
+ 'canvas-datagrid-button-wrapper:active',
119
+ );
120
+ createDisposeEvent();
121
+ }
122
+ }
123
+
124
+ function createButtonMenu(pos, items) {
125
+ var buttonMenu = document.createElement('div'),
126
+ selectedIndex = -1,
127
+ intf = {},
128
+ rect;
129
+
130
+ function createItems() {
131
+ function addItem(item, menuItemContainer) {
132
+ function addContent(content) {
133
+ if (content === null) {
134
+ return;
135
+ }
136
+
137
+ if (typeof content === 'object') {
138
+ menuItemContainer.appendChild(content);
139
+ return;
140
+ }
141
+
142
+ applyButtonMenuItemStyle(menuItemContainer);
143
+ menuItemContainer.innerHTML = content;
144
+ return;
145
+ }
146
+
147
+ addContent(item.title);
148
+ item.buttonMenuItemContainer = menuItemContainer;
149
+
150
+ if (item.click) {
151
+ menuItemContainer.addEventListener('click', function (ev) {
152
+ item.click.apply(self, [ev]);
153
+ self.disposeButton();
154
+ });
155
+ }
156
+ }
157
+
158
+ for (let item of items) {
159
+ var buttonMenuItemContainer = document.createElement('div');
160
+ addItem(item, buttonMenuItemContainer);
161
+ buttonMenu.appendChild(buttonMenuItemContainer);
162
+ }
163
+ }
164
+
165
+ function clickIndex(idx) {
166
+ items[idx].buttonMenuItemContainer.dispatchEvent(new Event('click'));
167
+ }
168
+
169
+ function init() {
170
+ var loc = {},
171
+ s = self.scrollOffset(self.canvas);
172
+
173
+ if (zIndexTop === undefined) {
174
+ zIndexTop = self.style.buttonZIndex;
175
+ }
176
+
177
+ createItems();
178
+ self.createInlineStyle(
179
+ buttonMenu,
180
+ 'canvas-datagrid-button-menu' + (self.mobile ? '-mobile' : ''),
181
+ );
182
+
183
+ loc.x = pos.left - s.left;
184
+ loc.y = pos.top - s.top;
185
+ loc.height = 0;
186
+ zIndexTop += 1;
187
+ buttonMenu.style.position = 'absolute';
188
+ buttonMenu.style.zIndex = zIndexTop;
189
+ buttonMenu.style.left = loc.x + 'px';
190
+ buttonMenu.style.top = loc.y + 'px';
191
+ document.body.appendChild(buttonMenu);
192
+ rect = buttonMenu.getBoundingClientRect();
193
+
194
+ if (rect.bottom > window.innerHeight) {
195
+ loc.y =
196
+ self.button.wrapper.top -
197
+ buttonMenu.offsetHeight -
198
+ self.scrollBox.scrollTop;
199
+ if (loc.y < 0) {
200
+ loc.y = self.style.buttonMenuWindowMargin;
201
+ }
202
+
203
+ if (
204
+ buttonMenu.offsetHeight >
205
+ window.innerHeight - self.style.buttonMenuWindowMargin
206
+ ) {
207
+ buttonMenu.style.height =
208
+ window.innerHeight - self.style.buttonMenuWindowMargin * 2 + 'px';
209
+ }
210
+ }
211
+
212
+ if (rect.right > window.innerWidth) {
213
+ loc.x -=
214
+ rect.right - window.innerWidth + self.style.buttonMenuWindowMargin;
215
+ }
216
+
217
+ if (loc.x < 0) {
218
+ loc.x = self.style.buttonMenuWindowMargin;
219
+ }
220
+
221
+ if (loc.y < 0) {
222
+ loc.y = self.style.buttonMenuWindowMargin;
223
+ }
224
+
225
+ buttonMenu.style.left = loc.x + 'px';
226
+ buttonMenu.style.top = loc.y + 'px';
227
+ }
228
+
229
+ intf.buttonMenu = buttonMenu;
230
+ init();
231
+ intf.clickIndex = clickIndex;
232
+ intf.rect = rect;
233
+ intf.items = items;
234
+
235
+ intf.dispose = function () {
236
+ if (buttonMenu.parentNode) {
237
+ buttonMenu.parentNode.removeChild(buttonMenu);
238
+ }
239
+ };
240
+
241
+ Object.defineProperty(intf, 'selectedIndex', {
242
+ get: function get() {
243
+ return selectedIndex;
244
+ },
245
+ set: function set(value) {
246
+ if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
247
+ throw new Error('Button menu selected index must be a sane number.');
248
+ }
249
+
250
+ selectedIndex = value;
251
+
252
+ if (selectedIndex > items.length - 1) {
253
+ selectedIndex = items.length - 1;
254
+ }
255
+
256
+ if (selectedIndex < 0) {
257
+ selectedIndex = 0;
258
+ }
259
+
260
+ items.forEach(function (item, index) {
261
+ if (index === selectedIndex) {
262
+ return self.createInlineStyle(
263
+ item.buttonMenuItemContainer,
264
+ 'canvas-datagrid-button-menu-item:hover',
265
+ );
266
+ }
267
+
268
+ self.createInlineStyle(
269
+ item.buttonMenuItemContainer,
270
+ 'canvas-datagrid-button-menu-item',
271
+ );
272
+ });
273
+ },
274
+ });
275
+ return intf;
276
+ }
277
+
278
+ self.disposeButtonMenu = function () {
279
+ if (self.buttonMenu) {
280
+ document.removeEventListener('click', self.disposeButtonMenu);
281
+ self.buttonMenu.dispose();
282
+ self.buttonMenu = undefined;
283
+ self.createInlineStyle(
284
+ self.button.wrapper,
285
+ 'canvas-datagrid-button-wrapper:hover',
286
+ );
287
+ }
288
+ };
289
+
290
+ self.disposeButton = function (e) {
291
+ if (e && e.keyCode !== 27) return;
292
+ document.removeEventListener('keydown', self.disposeButton);
293
+ zIndexTop = self.style.buttonZIndex;
294
+ self.disposeButtonMenu();
295
+
296
+ if (self.button) {
297
+ self.button.dispose();
298
+ }
299
+
300
+ self.button = undefined;
301
+ };
302
+
303
+ self.moveButtonPos = function () {
304
+ self.button.wrapper.style.left =
305
+ self.button.wrapper.left - self.scrollBox.scrollLeft + 'px';
306
+ self.button.wrapper.style.top =
307
+ self.button.wrapper.top - self.scrollBox.scrollTop + 'px';
308
+ self.disposeButtonMenu();
309
+ };
310
+
311
+ self.attachButton = function (pos, items, imgSrc) {
312
+ function createDisposeEvent() {
313
+ requestAnimationFrame(function () {
314
+ document.addEventListener('keydown', self.disposeButton);
315
+ });
316
+ }
317
+
318
+ if (self.button) {
319
+ self.disposeButton();
320
+ }
321
+
322
+ self.button = createButton(pos, items, imgSrc);
323
+ createDisposeEvent();
324
+ };
325
+ return;
326
+ }
package/lib/component.js CHANGED
@@ -60,8 +60,7 @@ export default function () {
60
60
  defs = {};
61
61
  intf.computedStyle = cStyle;
62
62
  defaults(defs);
63
- defs = defs.defaults.styles;
64
- defs.forEach(function (def) {
63
+ defs.defaults.styles.forEach(function (def) {
65
64
  var val;
66
65
  val = cStyle.getPropertyValue(component.hyphenateProperty(def[0], true));
67
66
  if (val === '') {
@@ -69,6 +68,16 @@ export default function () {
69
68
  component.hyphenateProperty(def[0], false),
70
69
  );
71
70
  }
71
+ if (
72
+ /^overflow[XY]$/.test(def[0]) &&
73
+ typeof val === 'string' &&
74
+ val.trim() === 'visible'
75
+ ) {
76
+ // 'visible' is the CSS default, not something the page asked for,
77
+ // and not a grid value: keep the overflow set through grid.style
78
+ // instead of overwriting it on every style pass (#248)
79
+ return;
80
+ }
72
81
  if (val !== '' && typeof val === 'string') {
73
82
  intf.setStyleProperty(
74
83
  def[0],
@@ -126,6 +135,14 @@ export default function () {
126
135
  };
127
136
  component.disconnectedCallback = function () {
128
137
  this.connected = false;
138
+ // stop observing while detached so a removed grid can be collected (#338)
139
+ if (this.observer && this.observer.disconnect) {
140
+ this.observer.disconnect();
141
+ this.observer = undefined;
142
+ }
143
+ if (typeof this.unobserveResizeTargets === 'function') {
144
+ this.unobserveResizeTargets();
145
+ }
129
146
  };
130
147
  component.connectedCallback = function () {
131
148
  var intf = this;
@@ -133,6 +150,9 @@ export default function () {
133
150
  intf.parentDOMNode.appendChild(intf.canvas);
134
151
  intf.connected = true;
135
152
  component.observe(intf);
153
+ if (typeof intf.observeResizeTargets === 'function') {
154
+ intf.observeResizeTargets();
155
+ }
136
156
  component.applyComponentStyle(true, intf);
137
157
  intf.resize(true);
138
158
  };
@@ -172,7 +192,19 @@ export default function () {
172
192
  return;
173
193
  }
174
194
  if (/^on/.test(attrName)) {
175
- intf.addEventListener('on' + attrName, Function('e', newVal));
195
+ // inline handler attributes need the Function constructor, which a
196
+ // strict Content Security Policy forbids; degrade with a warning (#311)
197
+ var handler;
198
+ try {
199
+ handler = new Function('e', newVal);
200
+ } catch (err) {
201
+ console.warn(
202
+ 'canvas-datagrid: cannot bind the %s attribute under the current Content Security Policy; use addEventListener instead.',
203
+ attrName,
204
+ );
205
+ return;
206
+ }
207
+ intf.addEventListener('on' + attrName, handler);
176
208
  }
177
209
  return;
178
210
  };
@@ -181,6 +213,10 @@ export default function () {
181
213
  if (!window.MutationObserver) {
182
214
  return;
183
215
  }
216
+ // connectedCallback runs on every (re)attach: never stack observers (#338)
217
+ if (intf.observer && intf.observer.disconnect) {
218
+ intf.observer.disconnect();
219
+ }
184
220
  intf.applyComponentStyle = function () {
185
221
  component.applyComponentStyle(false, intf);
186
222
  intf.resize();
@@ -229,6 +265,7 @@ export default function () {
229
265
  intf.data = intf.innerHTML;
230
266
  }
231
267
  });
268
+ intf.observer = observer;
232
269
  observer.observe(intf, {
233
270
  characterData: true,
234
271
  childList: true,