@stackoverflow/stacks 1.0.0 → 1.2.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.
Files changed (74) hide show
  1. package/README.md +47 -47
  2. package/dist/controllers/s-popover.d.ts +11 -2
  3. package/dist/css/stacks.css +257 -8
  4. package/dist/css/stacks.min.css +1 -1
  5. package/dist/js/stacks.js +18 -2
  6. package/dist/js/stacks.min.js +1 -1
  7. package/lib/css/atomic/borders.less +378 -378
  8. package/lib/css/atomic/colors.less +209 -209
  9. package/lib/css/atomic/flex.less +375 -375
  10. package/lib/css/atomic/grid.less +174 -174
  11. package/lib/css/atomic/misc.less +343 -343
  12. package/lib/css/atomic/spacing.less +342 -314
  13. package/lib/css/atomic/typography.less +273 -273
  14. package/lib/css/atomic/width-height.less +194 -194
  15. package/lib/css/base/body.less +44 -44
  16. package/lib/css/base/configuration-static.less +61 -61
  17. package/lib/css/base/icons.less +20 -20
  18. package/lib/css/base/internals.less +220 -220
  19. package/lib/css/base/reset-meyer.less +64 -64
  20. package/lib/css/base/reset-normalize.less +449 -449
  21. package/lib/css/base/reset.less +20 -20
  22. package/lib/css/components/activity-indicator.less +44 -45
  23. package/lib/css/components/avatars.less +189 -189
  24. package/lib/css/components/badges.less +254 -209
  25. package/lib/css/components/banners.less +80 -80
  26. package/lib/css/components/blank-states.less +26 -26
  27. package/lib/css/components/breadcrumbs.less +44 -44
  28. package/lib/css/components/button-groups.less +104 -104
  29. package/lib/css/components/buttons.less +665 -665
  30. package/lib/css/components/cards.less +44 -44
  31. package/lib/css/components/code-blocks.less +130 -130
  32. package/lib/css/components/collapsible.less +104 -104
  33. package/lib/css/components/inputs.less +728 -728
  34. package/lib/css/components/link-previews.less +136 -136
  35. package/lib/css/components/links.less +218 -218
  36. package/lib/css/components/menu.less +47 -47
  37. package/lib/css/components/modals.less +133 -133
  38. package/lib/css/components/navigation.less +146 -146
  39. package/lib/css/components/notices.less +233 -233
  40. package/lib/css/components/page-titles.less +60 -60
  41. package/lib/css/components/pagination.less +55 -55
  42. package/lib/css/components/popovers.less +197 -197
  43. package/lib/css/components/post-summary.less +436 -425
  44. package/lib/css/components/progress-bars.less +330 -330
  45. package/lib/css/components/prose.less +503 -503
  46. package/lib/css/components/spinner.less +107 -107
  47. package/lib/css/components/tables.less +341 -341
  48. package/lib/css/components/tags.less +236 -236
  49. package/lib/css/components/toggle-switches.less +144 -144
  50. package/lib/css/components/topbar.less +427 -427
  51. package/lib/css/components/uploader.less +210 -210
  52. package/lib/css/components/user-cards.less +169 -169
  53. package/lib/css/components/widget-dynamic.less +33 -33
  54. package/lib/css/components/widget-static.less +273 -273
  55. package/lib/css/exports/constants-colors.less +1092 -1092
  56. package/lib/css/exports/constants-helpers.less +108 -108
  57. package/lib/css/exports/constants-type.less +153 -153
  58. package/lib/css/exports/exports.less +15 -15
  59. package/lib/css/exports/mixins.less +237 -237
  60. package/lib/css/stacks-dynamic.less +35 -35
  61. package/lib/css/stacks-static.less +86 -86
  62. package/lib/css/stacks.less +13 -13
  63. package/lib/ts/controllers/index.ts +7 -7
  64. package/lib/ts/controllers/s-expandable-control.ts +188 -188
  65. package/lib/ts/controllers/s-modal.ts +321 -321
  66. package/lib/ts/controllers/s-navigation-tablist.ts +117 -117
  67. package/lib/ts/controllers/s-popover.ts +567 -547
  68. package/lib/ts/controllers/s-table.ts +220 -220
  69. package/lib/ts/controllers/s-tooltip.ts +246 -246
  70. package/lib/ts/controllers/s-uploader.ts +172 -172
  71. package/lib/ts/index.ts +20 -20
  72. package/lib/ts/stacks.ts +88 -88
  73. package/lib/tsconfig.json +13 -13
  74. package/package.json +86 -87
@@ -1,220 +1,220 @@
1
- import * as Stacks from "../stacks";
2
-
3
- export class TableController extends Stacks.StacksController {
4
- static targets = ["column"];
5
- readonly columnTarget!: Element;
6
- readonly columnTargets!: Element[];
7
-
8
- setCurrentSort(headElem: Element, direction: "asc" | "desc" | "none") {
9
- if (["asc", "desc", "none"].indexOf(direction) < 0) {
10
- throw "direction must be one of asc, desc, or none"
11
- }
12
- var controller = this;
13
- this.columnTargets.forEach(function (target) {
14
- var isCurrrent = target === headElem;
15
-
16
- target.classList.toggle("is-sorted", isCurrrent && direction !== "none");
17
-
18
- target.querySelectorAll(".js-sorting-indicator").forEach(function (icon) {
19
- var visible = isCurrrent ? direction : "none";
20
- icon.classList.toggle("d-none", !icon.classList.contains("js-sorting-indicator-" + visible));
21
- });
22
-
23
- if (!isCurrrent || direction === "none") {
24
- controller.removeElementData(target, "sort-direction");
25
- } else {
26
- controller.setElementData(target, "sort-direction", direction);
27
- }
28
- });
29
- };
30
-
31
- sort(evt: Event) {
32
- var controller = this;
33
- var colHead = evt.currentTarget;
34
- if (!(colHead instanceof HTMLTableCellElement)) {
35
- throw "invalid event target";
36
- }
37
- var table = this.element as HTMLTableElement;
38
- var tbody = table.tBodies[0];
39
-
40
- // the column slot number of the clicked header
41
- var colno = getCellSlot(colHead);
42
-
43
- if (colno < 0) { // this shouldn't happen if the clicked element is actually a column head
44
- return;
45
- }
46
-
47
- // an index of the <tbody>, so we can find out for each row which <td> element is
48
- // in the same column slot as the header
49
- var slotIndex = buildIndex(tbody);
50
-
51
- // the default behavior when clicking a header is to sort by this column in ascending
52
- // direction, *unless* it is already sorted that way
53
- var direction = this.getElementData(colHead, "sort-direction") === "asc" ? -1 : 1;
54
-
55
- var rows = Array.from(table.tBodies[0].rows);
56
-
57
- // if this is still false after traversing the data, that means all values are integers (or empty)
58
- // and thus we'll sort numerically.
59
- var anyNonInt = false;
60
-
61
- // data will be a list of tuples [value, rowNum], where value is what we're sorting by
62
- var data: [string | number, number][] = [];
63
- var firstBottomRow: HTMLTableRowElement;
64
- rows.forEach(function (row, index) {
65
- var force = controller.getElementData(row, "sort-to");
66
- if (force === "top") {
67
- return; // rows not added to the list will automatically end up at the top
68
- } else if (force === "bottom") {
69
- if (!firstBottomRow) {
70
- firstBottomRow = row;
71
- }
72
- return;
73
- }
74
- var cell = slotIndex[index][colno];
75
- if (!cell) {
76
- data.push(["", index]);
77
- return;
78
- }
79
-
80
- // unless the to-be-sorted-by value is explicitly provided on the element via this attribute,
81
- // the value we're using is the cell's text, trimmed of any whitespace
82
- var explicit = controller.getElementData(cell, "sort-val");
83
- var d = typeof explicit === "string" ? explicit : cell.textContent!.trim();
84
-
85
- if ((d !== "") && (parseInt(d, 10) + "" !== d)) {
86
- anyNonInt = true;
87
- }
88
- data.push([d, index]);
89
- });
90
-
91
- // If all values were integers (or empty cells), sort numerically, with empty cells treated as
92
- // having the lowest possible value (i.e. sorted to the top if ascending, bottom if descending)
93
- if (!anyNonInt) {
94
- data.forEach(function (tuple) {
95
- tuple[0] = tuple[0] === "" ? Number.MIN_VALUE : parseInt(tuple[0] as string, 10);
96
- });
97
- }
98
-
99
- // We don't sort an array of <tr>, but instead an arrays of row *numbers*, because this way we
100
- // can enforce stable sorting, i.e. rows that compare equal are guaranteed to remain in the same
101
- // order (the JS standard does not gurantee this for sort()).
102
- data.sort(function (a, b) {
103
- // first compare the values (a[0])
104
- if (a[0] > b[0]) {
105
- return 1 * direction;
106
- } else if (a[0] < b[0]) {
107
- return -1 * direction;
108
- } else {
109
- // if the values are equal, compare the row numbers (a[1]) to guarantee stable sorting
110
- // (note that this comparison is independent of the sorting direction)
111
- return a[1] > b[1] ? 1 : -1;
112
- }
113
- });
114
-
115
- // this is the actual reordering of the table rows
116
- data.forEach(function (tup) {
117
- var row = rows[tup[1]];
118
- row.parentElement!.removeChild(row);
119
- if (firstBottomRow) {
120
- tbody.insertBefore(row, firstBottomRow);
121
- } else {
122
- tbody.appendChild(row);
123
- }
124
- });
125
-
126
- // update the UI and set the `data-sort-direction` attribute if appropriate, so that the next click
127
- // will cause sorting in descending direction
128
- this.setCurrentSort(colHead, direction === 1 ? "asc" : "desc");
129
- }
130
-
131
- }
132
-
133
- function buildIndex(section: HTMLTableSectionElement): HTMLTableCellElement[][] {
134
- const result = buildIndexOrGetCellSlot(section);
135
- if (!(result instanceof Array)) {
136
- throw "shouldn't happen"
137
- }
138
- return result;
139
- }
140
-
141
- function getCellSlot(cell: HTMLTableCellElement): number {
142
- if (!(cell.parentElement && cell.parentElement.parentElement instanceof HTMLTableSectionElement)) {
143
- throw "invalid table"
144
- }
145
- const result = buildIndexOrGetCellSlot(cell.parentElement.parentElement, cell);
146
- if (typeof result !== "number") {
147
- throw "shouldn't happen"
148
- }
149
- return result
150
- }
151
-
152
- // Just because a <td> is the 4th *child* of its <tr> doesn't mean it belongs to the 4th *column*
153
- // of the table. Previous cells may have a colspan; cells in previous rows may have a rowspan.
154
- // Because we need to know which header cells and data cells belong together, we have to 1) find out
155
- // which column number (or "slot" as we call it here) the header cell has, and 2) for each row find
156
- // out which <td> cell corresponds to this slot (because those are the rows we're sorting by).
157
- //
158
- // That's what the following function does. If the second argument is not given, it returns an index
159
- // of the table, which is an array of arrays. Each of the sub-arrays corresponds to a table row. The
160
- // indices of the sub-array correspond to column slots; the values are the actual table cell elements.
161
- // For example index[4][3] is the <td> or <th> in row 4, column 3 of the table section (<tbody> or <thead>).
162
- // Note that this element is not necessarily even in the 4th (zero-based) <tr> -- if it has a rowSpan > 1,
163
- // it may also be in a previous <tr>.
164
- //
165
- // If the second argument is given, it's a <td> or <th> that we're trying to find, and the algorithm
166
- // stops as soon as it has found it and the function returns its slot number.
167
- function buildIndexOrGetCellSlot(section: HTMLTableSectionElement, findCell?: HTMLTableCellElement) {
168
- var index = [];
169
- var curRow = section.children[0];
170
-
171
- // the elements of these two arrays are synchronized; the first array contains table cell elements,
172
- // the second one contains a number that indicates for how many more rows this elements will
173
- // exist (i.e. the value is initially one less than the cell's rowspan, and will be decreased for each row)
174
- var growing: HTMLTableCellElement[] = [];
175
- var growingRowsLeft: number[] = [];
176
-
177
- // continue while we have actual <tr>'s left *or* we still have rowspan'ed elements that aren't done
178
- while (curRow || growingRowsLeft.some(function (e) { return e !== 0; })) {
179
- var curIndexRow: HTMLTableCellElement[] = [];
180
- index.push(curIndexRow);
181
-
182
- var curSlot = 0;
183
- if (curRow) {
184
- for (var curCellInd = 0; curCellInd < curRow.children.length; curCellInd++) {
185
- while (growingRowsLeft[curSlot]) {
186
- growingRowsLeft[curSlot]--;
187
- curIndexRow[curSlot] = growing[curSlot];
188
- curSlot++;
189
- }
190
- var cell = curRow.children[curCellInd];
191
- if (!(cell instanceof HTMLTableCellElement)) {
192
- throw "invalid table"
193
- }
194
- if (getComputedStyle(cell).display === "none") {
195
- continue;
196
- }
197
- if (cell === findCell) {
198
- return curSlot;
199
- }
200
- var nextFreeSlot = curSlot + cell.colSpan;
201
- for (; curSlot < nextFreeSlot; curSlot++) {
202
- growingRowsLeft[curSlot] = cell.rowSpan - 1; // if any of these is already growing, the table is broken -- no guarantees of anything
203
- growing[curSlot] = cell;
204
- curIndexRow[curSlot] = cell;
205
- }
206
- }
207
- }
208
- while (curSlot < growing.length) {
209
- if (growingRowsLeft[curSlot]) {
210
- growingRowsLeft[curSlot]--;
211
- curIndexRow[curSlot] = growing[curSlot];
212
- }
213
- curSlot++;
214
- }
215
- if (curRow) {
216
- curRow = curRow.nextElementSibling!;
217
- }
218
- }
219
- return findCell ? -1 : index; /* if findCell was given but we end up here, that means it isn't in this section */
220
- }
1
+ import * as Stacks from "../stacks";
2
+
3
+ export class TableController extends Stacks.StacksController {
4
+ static targets = ["column"];
5
+ readonly columnTarget!: Element;
6
+ readonly columnTargets!: Element[];
7
+
8
+ setCurrentSort(headElem: Element, direction: "asc" | "desc" | "none") {
9
+ if (["asc", "desc", "none"].indexOf(direction) < 0) {
10
+ throw "direction must be one of asc, desc, or none"
11
+ }
12
+ var controller = this;
13
+ this.columnTargets.forEach(function (target) {
14
+ var isCurrrent = target === headElem;
15
+
16
+ target.classList.toggle("is-sorted", isCurrrent && direction !== "none");
17
+
18
+ target.querySelectorAll(".js-sorting-indicator").forEach(function (icon) {
19
+ var visible = isCurrrent ? direction : "none";
20
+ icon.classList.toggle("d-none", !icon.classList.contains("js-sorting-indicator-" + visible));
21
+ });
22
+
23
+ if (!isCurrrent || direction === "none") {
24
+ controller.removeElementData(target, "sort-direction");
25
+ } else {
26
+ controller.setElementData(target, "sort-direction", direction);
27
+ }
28
+ });
29
+ };
30
+
31
+ sort(evt: Event) {
32
+ var controller = this;
33
+ var colHead = evt.currentTarget;
34
+ if (!(colHead instanceof HTMLTableCellElement)) {
35
+ throw "invalid event target";
36
+ }
37
+ var table = this.element as HTMLTableElement;
38
+ var tbody = table.tBodies[0];
39
+
40
+ // the column slot number of the clicked header
41
+ var colno = getCellSlot(colHead);
42
+
43
+ if (colno < 0) { // this shouldn't happen if the clicked element is actually a column head
44
+ return;
45
+ }
46
+
47
+ // an index of the <tbody>, so we can find out for each row which <td> element is
48
+ // in the same column slot as the header
49
+ var slotIndex = buildIndex(tbody);
50
+
51
+ // the default behavior when clicking a header is to sort by this column in ascending
52
+ // direction, *unless* it is already sorted that way
53
+ var direction = this.getElementData(colHead, "sort-direction") === "asc" ? -1 : 1;
54
+
55
+ var rows = Array.from(table.tBodies[0].rows);
56
+
57
+ // if this is still false after traversing the data, that means all values are integers (or empty)
58
+ // and thus we'll sort numerically.
59
+ var anyNonInt = false;
60
+
61
+ // data will be a list of tuples [value, rowNum], where value is what we're sorting by
62
+ var data: [string | number, number][] = [];
63
+ var firstBottomRow: HTMLTableRowElement;
64
+ rows.forEach(function (row, index) {
65
+ var force = controller.getElementData(row, "sort-to");
66
+ if (force === "top") {
67
+ return; // rows not added to the list will automatically end up at the top
68
+ } else if (force === "bottom") {
69
+ if (!firstBottomRow) {
70
+ firstBottomRow = row;
71
+ }
72
+ return;
73
+ }
74
+ var cell = slotIndex[index][colno];
75
+ if (!cell) {
76
+ data.push(["", index]);
77
+ return;
78
+ }
79
+
80
+ // unless the to-be-sorted-by value is explicitly provided on the element via this attribute,
81
+ // the value we're using is the cell's text, trimmed of any whitespace
82
+ var explicit = controller.getElementData(cell, "sort-val");
83
+ var d = typeof explicit === "string" ? explicit : cell.textContent!.trim();
84
+
85
+ if ((d !== "") && (parseInt(d, 10) + "" !== d)) {
86
+ anyNonInt = true;
87
+ }
88
+ data.push([d, index]);
89
+ });
90
+
91
+ // If all values were integers (or empty cells), sort numerically, with empty cells treated as
92
+ // having the lowest possible value (i.e. sorted to the top if ascending, bottom if descending)
93
+ if (!anyNonInt) {
94
+ data.forEach(function (tuple) {
95
+ tuple[0] = tuple[0] === "" ? Number.MIN_VALUE : parseInt(tuple[0] as string, 10);
96
+ });
97
+ }
98
+
99
+ // We don't sort an array of <tr>, but instead an arrays of row *numbers*, because this way we
100
+ // can enforce stable sorting, i.e. rows that compare equal are guaranteed to remain in the same
101
+ // order (the JS standard does not gurantee this for sort()).
102
+ data.sort(function (a, b) {
103
+ // first compare the values (a[0])
104
+ if (a[0] > b[0]) {
105
+ return 1 * direction;
106
+ } else if (a[0] < b[0]) {
107
+ return -1 * direction;
108
+ } else {
109
+ // if the values are equal, compare the row numbers (a[1]) to guarantee stable sorting
110
+ // (note that this comparison is independent of the sorting direction)
111
+ return a[1] > b[1] ? 1 : -1;
112
+ }
113
+ });
114
+
115
+ // this is the actual reordering of the table rows
116
+ data.forEach(function (tup) {
117
+ var row = rows[tup[1]];
118
+ row.parentElement!.removeChild(row);
119
+ if (firstBottomRow) {
120
+ tbody.insertBefore(row, firstBottomRow);
121
+ } else {
122
+ tbody.appendChild(row);
123
+ }
124
+ });
125
+
126
+ // update the UI and set the `data-sort-direction` attribute if appropriate, so that the next click
127
+ // will cause sorting in descending direction
128
+ this.setCurrentSort(colHead, direction === 1 ? "asc" : "desc");
129
+ }
130
+
131
+ }
132
+
133
+ function buildIndex(section: HTMLTableSectionElement): HTMLTableCellElement[][] {
134
+ const result = buildIndexOrGetCellSlot(section);
135
+ if (!(result instanceof Array)) {
136
+ throw "shouldn't happen"
137
+ }
138
+ return result;
139
+ }
140
+
141
+ function getCellSlot(cell: HTMLTableCellElement): number {
142
+ if (!(cell.parentElement && cell.parentElement.parentElement instanceof HTMLTableSectionElement)) {
143
+ throw "invalid table"
144
+ }
145
+ const result = buildIndexOrGetCellSlot(cell.parentElement.parentElement, cell);
146
+ if (typeof result !== "number") {
147
+ throw "shouldn't happen"
148
+ }
149
+ return result
150
+ }
151
+
152
+ // Just because a <td> is the 4th *child* of its <tr> doesn't mean it belongs to the 4th *column*
153
+ // of the table. Previous cells may have a colspan; cells in previous rows may have a rowspan.
154
+ // Because we need to know which header cells and data cells belong together, we have to 1) find out
155
+ // which column number (or "slot" as we call it here) the header cell has, and 2) for each row find
156
+ // out which <td> cell corresponds to this slot (because those are the rows we're sorting by).
157
+ //
158
+ // That's what the following function does. If the second argument is not given, it returns an index
159
+ // of the table, which is an array of arrays. Each of the sub-arrays corresponds to a table row. The
160
+ // indices of the sub-array correspond to column slots; the values are the actual table cell elements.
161
+ // For example index[4][3] is the <td> or <th> in row 4, column 3 of the table section (<tbody> or <thead>).
162
+ // Note that this element is not necessarily even in the 4th (zero-based) <tr> -- if it has a rowSpan > 1,
163
+ // it may also be in a previous <tr>.
164
+ //
165
+ // If the second argument is given, it's a <td> or <th> that we're trying to find, and the algorithm
166
+ // stops as soon as it has found it and the function returns its slot number.
167
+ function buildIndexOrGetCellSlot(section: HTMLTableSectionElement, findCell?: HTMLTableCellElement) {
168
+ var index = [];
169
+ var curRow = section.children[0];
170
+
171
+ // the elements of these two arrays are synchronized; the first array contains table cell elements,
172
+ // the second one contains a number that indicates for how many more rows this elements will
173
+ // exist (i.e. the value is initially one less than the cell's rowspan, and will be decreased for each row)
174
+ var growing: HTMLTableCellElement[] = [];
175
+ var growingRowsLeft: number[] = [];
176
+
177
+ // continue while we have actual <tr>'s left *or* we still have rowspan'ed elements that aren't done
178
+ while (curRow || growingRowsLeft.some(function (e) { return e !== 0; })) {
179
+ var curIndexRow: HTMLTableCellElement[] = [];
180
+ index.push(curIndexRow);
181
+
182
+ var curSlot = 0;
183
+ if (curRow) {
184
+ for (var curCellInd = 0; curCellInd < curRow.children.length; curCellInd++) {
185
+ while (growingRowsLeft[curSlot]) {
186
+ growingRowsLeft[curSlot]--;
187
+ curIndexRow[curSlot] = growing[curSlot];
188
+ curSlot++;
189
+ }
190
+ var cell = curRow.children[curCellInd];
191
+ if (!(cell instanceof HTMLTableCellElement)) {
192
+ throw "invalid table"
193
+ }
194
+ if (getComputedStyle(cell).display === "none") {
195
+ continue;
196
+ }
197
+ if (cell === findCell) {
198
+ return curSlot;
199
+ }
200
+ var nextFreeSlot = curSlot + cell.colSpan;
201
+ for (; curSlot < nextFreeSlot; curSlot++) {
202
+ growingRowsLeft[curSlot] = cell.rowSpan - 1; // if any of these is already growing, the table is broken -- no guarantees of anything
203
+ growing[curSlot] = cell;
204
+ curIndexRow[curSlot] = cell;
205
+ }
206
+ }
207
+ }
208
+ while (curSlot < growing.length) {
209
+ if (growingRowsLeft[curSlot]) {
210
+ growingRowsLeft[curSlot]--;
211
+ curIndexRow[curSlot] = growing[curSlot];
212
+ }
213
+ curSlot++;
214
+ }
215
+ if (curRow) {
216
+ curRow = curRow.nextElementSibling!;
217
+ }
218
+ }
219
+ return findCell ? -1 : index; /* if findCell was given but we end up here, that means it isn't in this section */
220
+ }