@stackoverflow/stacks 1.0.0 → 1.0.1

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 (71) hide show
  1. package/README.md +47 -47
  2. package/dist/css/stacks.css +108 -0
  3. package/dist/css/stacks.min.css +1 -1
  4. package/lib/css/atomic/borders.less +378 -378
  5. package/lib/css/atomic/colors.less +209 -209
  6. package/lib/css/atomic/flex.less +375 -375
  7. package/lib/css/atomic/grid.less +174 -174
  8. package/lib/css/atomic/misc.less +343 -343
  9. package/lib/css/atomic/spacing.less +332 -314
  10. package/lib/css/atomic/typography.less +273 -273
  11. package/lib/css/atomic/width-height.less +194 -194
  12. package/lib/css/base/body.less +44 -44
  13. package/lib/css/base/configuration-static.less +61 -61
  14. package/lib/css/base/icons.less +20 -20
  15. package/lib/css/base/internals.less +220 -220
  16. package/lib/css/base/reset-meyer.less +64 -64
  17. package/lib/css/base/reset-normalize.less +449 -449
  18. package/lib/css/base/reset.less +20 -20
  19. package/lib/css/components/activity-indicator.less +45 -45
  20. package/lib/css/components/avatars.less +189 -189
  21. package/lib/css/components/badges.less +209 -209
  22. package/lib/css/components/banners.less +80 -80
  23. package/lib/css/components/blank-states.less +26 -26
  24. package/lib/css/components/breadcrumbs.less +44 -44
  25. package/lib/css/components/button-groups.less +104 -104
  26. package/lib/css/components/buttons.less +665 -665
  27. package/lib/css/components/cards.less +44 -44
  28. package/lib/css/components/code-blocks.less +130 -130
  29. package/lib/css/components/collapsible.less +104 -104
  30. package/lib/css/components/inputs.less +728 -728
  31. package/lib/css/components/link-previews.less +136 -136
  32. package/lib/css/components/links.less +218 -218
  33. package/lib/css/components/menu.less +47 -47
  34. package/lib/css/components/modals.less +133 -133
  35. package/lib/css/components/navigation.less +146 -146
  36. package/lib/css/components/notices.less +233 -233
  37. package/lib/css/components/page-titles.less +60 -60
  38. package/lib/css/components/pagination.less +55 -55
  39. package/lib/css/components/popovers.less +197 -197
  40. package/lib/css/components/post-summary.less +425 -425
  41. package/lib/css/components/progress-bars.less +330 -330
  42. package/lib/css/components/prose.less +503 -503
  43. package/lib/css/components/spinner.less +107 -107
  44. package/lib/css/components/tables.less +341 -341
  45. package/lib/css/components/tags.less +236 -236
  46. package/lib/css/components/toggle-switches.less +144 -144
  47. package/lib/css/components/topbar.less +427 -427
  48. package/lib/css/components/uploader.less +210 -210
  49. package/lib/css/components/user-cards.less +169 -169
  50. package/lib/css/components/widget-dynamic.less +33 -33
  51. package/lib/css/components/widget-static.less +273 -273
  52. package/lib/css/exports/constants-colors.less +1092 -1092
  53. package/lib/css/exports/constants-helpers.less +108 -108
  54. package/lib/css/exports/constants-type.less +153 -153
  55. package/lib/css/exports/exports.less +15 -15
  56. package/lib/css/exports/mixins.less +237 -237
  57. package/lib/css/stacks-dynamic.less +35 -35
  58. package/lib/css/stacks-static.less +86 -86
  59. package/lib/css/stacks.less +13 -13
  60. package/lib/ts/controllers/index.ts +7 -7
  61. package/lib/ts/controllers/s-expandable-control.ts +188 -188
  62. package/lib/ts/controllers/s-modal.ts +321 -321
  63. package/lib/ts/controllers/s-navigation-tablist.ts +117 -117
  64. package/lib/ts/controllers/s-popover.ts +547 -547
  65. package/lib/ts/controllers/s-table.ts +220 -220
  66. package/lib/ts/controllers/s-tooltip.ts +246 -246
  67. package/lib/ts/controllers/s-uploader.ts +172 -172
  68. package/lib/ts/index.ts +20 -20
  69. package/lib/ts/stacks.ts +88 -88
  70. package/lib/tsconfig.json +13 -13
  71. package/package.json +87 -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
+ }