1 | import angular from 'angular';
|
2 |
|
3 | import 'dom4';
|
4 | import debounce from 'just-debounce-it';
|
5 |
|
6 | import {getRect, getStyles, getWindowHeight} from '../global/dom';
|
7 | import PlaceUnder from '../place-under-ng/place-under-ng';
|
8 | import Checkbox from '../checkbox-ng/checkbox-ng';
|
9 |
|
10 | import Selection from './table-legacy-ng__selection';
|
11 | import SelectionNavigateActions from './table-legacy-ng__selection-navigate-actions';
|
12 | import TableToolbar from './table-legacy-ng__toolbar';
|
13 | import TablePager from './table-legacy-ng__pager';
|
14 |
|
15 | import '../table-legacy/table-legacy.scss';
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | const angularModule = angular.module(
|
22 | 'Ring.table-legacy',
|
23 | [TableToolbar, TablePager, Checkbox, PlaceUnder]
|
24 | );
|
25 |
|
26 | angularModule.directive('rgLegacyTable', function rgLegacyTableDirective() {
|
27 | return {
|
28 | restrict: 'E',
|
29 | transclude: true,
|
30 | template: require('./table-legacy-ng.html'),
|
31 | controllerAs: 'ctrl',
|
32 |
|
33 | |
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | scope: {
|
40 | items: '=',
|
41 | selection: '=?',
|
42 | disableSelection: '@'
|
43 | },
|
44 |
|
45 | bindToController: true,
|
46 |
|
47 | controller: function controller($scope) {
|
48 | this.$onInit = () => {
|
49 | if (this.disableSelection) {
|
50 | return;
|
51 | }
|
52 |
|
53 | |
54 |
|
55 |
|
56 |
|
57 | this.selection = new Selection(this.items, (name, item, index) => {
|
58 | $scope.$emit(name, item, index);
|
59 | $scope.$broadcast(name, item, index);
|
60 | });
|
61 |
|
62 | |
63 |
|
64 |
|
65 | $scope.$watch(() => this.items, newItems => {
|
66 | if (newItems) {
|
67 | this.selection.setItems(newItems);
|
68 | }
|
69 | });
|
70 | };
|
71 | }
|
72 | };
|
73 | });
|
74 |
|
75 | angularModule.directive('rgLegacyTableHeader',
|
76 | function rgLegacyTableHeaderDirective(getClosestElementWithCommonParent) {
|
77 | const HEADER_RESIZE_DEBOUNCE = 50;
|
78 | const HEADER_SCROLL_DEBOUNCE = 10;
|
79 | const TOOLBAR_FIXED_CLASSNAME = 'ring-table__toolbar-controls_fixed';
|
80 |
|
81 | return {
|
82 | restrict: 'E',
|
83 | template: require('./table-legacy-ng__header.html'),
|
84 | transclude: true,
|
85 | replace: true,
|
86 | link: function link(scope, iElement, iAttrs) {
|
87 | const element = iElement[0];
|
88 | let stickToElement = null;
|
89 |
|
90 | scope.stickToSelector = iAttrs.stickTo;
|
91 |
|
92 |
|
93 | if (iAttrs.stickToToolbar !== undefined) {
|
94 | scope.stickToSelector = '.ring-table__toolbar';
|
95 | }
|
96 |
|
97 | const scrollableHeader = element.query('.ring-table__header:not(.ring-table__header_sticky)');
|
98 | const fixedHeader = element.query('.ring-table__header_sticky');
|
99 |
|
100 | const toolbarFixed = () => stickToElement.query(`.${TOOLBAR_FIXED_CLASSNAME}`) !== null;
|
101 |
|
102 | |
103 |
|
104 |
|
105 | const resizeFixedHeader = debounce(() => {
|
106 | fixedHeader.style.width = `${scrollableHeader.offsetWidth}px`;
|
107 | const titles = fixedHeader.queryAll('.ring-table__title');
|
108 |
|
109 | titles.forEach((titleElement, index) => {
|
110 | const targetHeaderTitle = scrollableHeader.queryAll('.ring-table__title')[index];
|
111 | titleElement.style.width = getStyles(targetHeaderTitle).width;
|
112 | });
|
113 |
|
114 | }, HEADER_RESIZE_DEBOUNCE, true);
|
115 |
|
116 | |
117 |
|
118 |
|
119 | const scrollListener = debounce(() => {
|
120 | if (toolbarFixed()) {
|
121 | fixedHeader.style.display = 'block';
|
122 | scrollableHeader.style.visibility = 'hidden';
|
123 | } else {
|
124 | fixedHeader.style.display = 'none';
|
125 | scrollableHeader.style.visibility = 'visible';
|
126 | }
|
127 |
|
128 | resizeFixedHeader();
|
129 | }, HEADER_SCROLL_DEBOUNCE);
|
130 |
|
131 | function startSticking() {
|
132 | scope.$evalAsync(() => {
|
133 | window.addEventListener('resize', resizeFixedHeader);
|
134 | window.addEventListener('scroll', scrollListener);
|
135 | scope.$on('rgLegacyTable:itemsChanged', scrollListener);
|
136 | });
|
137 | }
|
138 |
|
139 | if (scope.stickToSelector) {
|
140 | stickToElement = getClosestElementWithCommonParent(element, scope.stickToSelector);
|
141 | startSticking();
|
142 | }
|
143 | }
|
144 | };
|
145 | }
|
146 | );
|
147 |
|
148 | angularModule.directive('rgLegacyTableBody', function rgLegacyTableBodyDirective() {
|
149 | return {
|
150 | restrict: 'E',
|
151 | template: '<tbody ng-transclude></tbody>',
|
152 | transclude: true,
|
153 | replace: true
|
154 | };
|
155 | });
|
156 |
|
157 | angularModule.directive('rgLegacyTableRow', function rgLegacyTableRowDirective() {
|
158 | return {
|
159 | template: require('./table-legacy-ng__row.html'),
|
160 | restrict: 'E',
|
161 | transclude: true,
|
162 | replace: true,
|
163 | require: ['^rgLegacyTable', 'rgLegacyTableRow'],
|
164 |
|
165 | scope: {
|
166 | rowItem: '='
|
167 | },
|
168 |
|
169 | link: function link(scope, iElement, iAttrs, ctrls) {
|
170 | const rgLegacyTableCtrl = ctrls[0];
|
171 | const rgLegacyTableRowCtrl = ctrls[1];
|
172 | rgLegacyTableRowCtrl.setSelection(rgLegacyTableCtrl.selection);
|
173 | },
|
174 |
|
175 | controllerAs: 'rowCtrl',
|
176 | bindToController: true,
|
177 |
|
178 | controller: function controller($scope, $element) {
|
179 | const element = $element[0];
|
180 |
|
181 | let watchRowCheckFlag;
|
182 | this.setSelection = selection => {
|
183 | if (!selection) {
|
184 | return;
|
185 | }
|
186 |
|
187 | this.selection = selection;
|
188 |
|
189 | if (!watchRowCheckFlag) {
|
190 | watchRowCheckFlag = $scope.$watch('rowCtrl.rowItem.checked', newValue => {
|
191 | if (newValue !== undefined) {
|
192 | this.selection.triggerSelectionChanged(this.rowItem);
|
193 | }
|
194 | });
|
195 | }
|
196 | };
|
197 |
|
198 | this.setActiveItem = item => {
|
199 | item && !item.unselectable && this.selection && this.selection.activateItem(item);
|
200 | };
|
201 |
|
202 | this.onMouseOver = item => {
|
203 | item && !item.unselectable && this.selection && this.selection.setSuggestedItem(item);
|
204 | };
|
205 |
|
206 | this.onMouseOut = item => {
|
207 | item &&
|
208 | this.selection &&
|
209 | item === this.selection.suggestedItem && this.selection.setSuggestedItem(null);
|
210 | };
|
211 |
|
212 | this.hasCheckedItems = () => {
|
213 | if (!this.selection) {
|
214 | return false;
|
215 | }
|
216 |
|
217 | const checkedItems = this.selection.getCheckedItems();
|
218 | return checkedItems && checkedItems.length > 0;
|
219 | };
|
220 |
|
221 | function getRowOutOfViewInfo(el, offsetInRows) {
|
222 | const rect = getRect(el);
|
223 | const offset = rect.height * offsetInRows;
|
224 |
|
225 | const isGoneUp = rect.top < offset;
|
226 | const isGoneDown = rect.bottom > (getWindowHeight() - offset);
|
227 |
|
228 | return {
|
229 | offset,
|
230 | isOutOfView: isGoneDown || isGoneUp,
|
231 | isGoneUp,
|
232 | isGoneDown
|
233 | };
|
234 | }
|
235 |
|
236 | function addSpacingAfterScroll(offset) {
|
237 | if (window.scrollY) {
|
238 | window.scrollBy(0, offset);
|
239 | }
|
240 | }
|
241 |
|
242 | $scope.$on('rgLegacyTable:activateItem', (e, item) => {
|
243 | if (item === this.rowItem) {
|
244 | const scrollInfo = getRowOutOfViewInfo(element, 2);
|
245 | if (scrollInfo.isOutOfView) {
|
246 | element.scrollIntoView(scrollInfo.isGoneUp);
|
247 | addSpacingAfterScroll(scrollInfo.isGoneDown ? scrollInfo.offset : -scrollInfo.offset);
|
248 | }
|
249 | }
|
250 | });
|
251 | }
|
252 | };
|
253 | });
|
254 |
|
255 | angularModule.
|
256 | directive('rgLegacyTableHeaderCheckbox', function rgLegacyTableHeaderCheckboxDirective() {
|
257 | return {
|
258 | restrict: 'E',
|
259 | require: '^rgLegacyTable',
|
260 | replace: true,
|
261 | template: '<span class="ring-table__header-checkbox"><rg-checkbox ng-click="onClickChange()" ng-model="allChecked"/></span>',
|
262 |
|
263 | link: function link(scope, iElement, iAttrs, tableCtrl) {
|
264 |
|
265 | scope.allChecked = false;
|
266 |
|
267 | function recheckSelection() {
|
268 | if (tableCtrl.items && tableCtrl.items.length) {
|
269 | scope.allChecked = tableCtrl.items.every(item => item.checked);
|
270 | } else {
|
271 | scope.allChecked = false;
|
272 | }
|
273 | }
|
274 |
|
275 | function markAllItemsAs(state) {
|
276 | tableCtrl.items.forEach(item => {
|
277 | item.checked = state;
|
278 | });
|
279 | }
|
280 |
|
281 | scope.$on('rgLegacyTable:itemsChanged', () => {
|
282 | if (scope.allChecked) {
|
283 | markAllItemsAs(true);
|
284 | }
|
285 | recheckSelection();
|
286 | });
|
287 | scope.$on('rgLegacyTable:selectionChanged', recheckSelection);
|
288 |
|
289 | scope.onClickChange = () => {
|
290 | markAllItemsAs(scope.allChecked);
|
291 | };
|
292 | }
|
293 | };
|
294 | });
|
295 |
|
296 |
|
297 |
|
298 |
|
299 | angularModule.directive('rgLegacyTableCheckboxCell', function rgLegacyTableCheckboxCellDirective() {
|
300 | return {
|
301 | restrict: 'E',
|
302 | transclude: true,
|
303 | require: '^rgLegacyTableRow',
|
304 | replace: true,
|
305 | template: '<td class="ring-table__selector ring-table__column_selector" ng-class="{\'ring-table__column\': !isEmbedded}"><rg-checkbox ng-model="getRowItem().checked"/></td>',
|
306 |
|
307 | link: function link(scope, iElement, iAttrs, rowCtrl) {
|
308 | |
309 |
|
310 |
|
311 | scope.getRowItem = () => rowCtrl.rowItem;
|
312 | scope.isEmbedded = angular.isDefined(iAttrs.embedded);
|
313 | }
|
314 | };
|
315 | });
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 | angularModule.directive('rgLegacyTableTitle', function rgLegacyTableTitleDirective() {
|
325 | return {
|
326 | restrict: 'E',
|
327 | transclude: true,
|
328 | replace: true,
|
329 | scope: true,
|
330 | template: require('./table-legacy-ng__title.html'),
|
331 |
|
332 | link: function link(scope, iElement, iAttrs) {
|
333 | |
334 |
|
335 |
|
336 | scope.isBorder = angular.isDefined(iAttrs.border);
|
337 | scope.isActive = angular.isDefined(iAttrs.active);
|
338 | scope.isPullRight = angular.isDefined(iAttrs.pullRight);
|
339 | scope.isAlignRight = angular.isDefined(iAttrs.alignRight);
|
340 | scope.isPullLeft = angular.isDefined(iAttrs.pullLeft);
|
341 | }
|
342 | };
|
343 | });
|
344 |
|
345 |
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 | angularModule.directive('rgLegacyTableColumn', function rgLegacyTableColumnDirective() {
|
354 | return {
|
355 | restrict: 'E',
|
356 | transclude: true,
|
357 | replace: true,
|
358 | scope: true,
|
359 | template: require('./table-legacy-ng__column.html'),
|
360 |
|
361 | link: function link(scope, iElement, iAttrs) {
|
362 | const element = iElement[0];
|
363 | const FULL_WIDTH = 100;
|
364 |
|
365 | scope.isLimited = angular.isDefined(iAttrs.limited);
|
366 | scope.isUnlimited = angular.isDefined(iAttrs.unlimited);
|
367 | scope.isAvatar = angular.isDefined(iAttrs.avatar);
|
368 | scope.isWide = angular.isDefined(iAttrs.wide);
|
369 | scope.isAlignRight = angular.isDefined(iAttrs.alignRight);
|
370 | scope.isGray = angular.isDefined(iAttrs.gray);
|
371 | scope.isPullRight = angular.isDefined(iAttrs.pullRight);
|
372 | scope.isPullLeft = angular.isDefined(iAttrs.pullLeft);
|
373 |
|
374 | function adjustUnlimitedColumnWidths() {
|
375 | const unlimitedColumnsCount = element.parentNode.
|
376 | queryAll('.ring-table__column[unlimited]').length;
|
377 | if (unlimitedColumnsCount > 1) {
|
378 | element.style.width = `${(FULL_WIDTH / unlimitedColumnsCount).toFixed()}%`;
|
379 | }
|
380 | }
|
381 |
|
382 | if (scope.isUnlimited) {
|
383 | adjustUnlimitedColumnWidths();
|
384 | }
|
385 | }
|
386 | };
|
387 | });
|
388 |
|
389 |
|
390 |
|
391 |
|
392 | angularModule.constant('SelectionNavigateActions', SelectionNavigateActions);
|
393 |
|
394 | export default angularModule.name;
|