@stackoverflow/stacks 1.0.0-beta.0 → 1.1.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/README.md +47 -47
- package/dist/controllers/s-popover.d.ts +11 -2
- package/dist/css/stacks.css +214 -3
- package/dist/css/stacks.min.css +1 -1
- package/dist/js/stacks.js +18 -2
- package/dist/js/stacks.min.js +1 -1
- package/lib/css/atomic/borders.less +378 -378
- package/lib/css/atomic/colors.less +209 -209
- package/lib/css/atomic/flex.less +375 -375
- package/lib/css/atomic/grid.less +174 -174
- package/lib/css/atomic/misc.less +343 -343
- package/lib/css/atomic/spacing.less +342 -314
- package/lib/css/atomic/typography.less +273 -273
- package/lib/css/atomic/width-height.less +194 -194
- package/lib/css/base/body.less +44 -44
- package/lib/css/base/configuration-static.less +61 -61
- package/lib/css/base/icons.less +20 -20
- package/lib/css/base/internals.less +220 -220
- package/lib/css/base/reset-meyer.less +64 -64
- package/lib/css/base/reset-normalize.less +449 -449
- package/lib/css/base/reset.less +20 -20
- package/lib/css/components/activity-indicator.less +44 -45
- package/lib/css/components/avatars.less +189 -189
- package/lib/css/components/badges.less +209 -209
- package/lib/css/components/banners.less +80 -80
- package/lib/css/components/blank-states.less +26 -26
- package/lib/css/components/breadcrumbs.less +44 -44
- package/lib/css/components/button-groups.less +104 -104
- package/lib/css/components/buttons.less +665 -665
- package/lib/css/components/cards.less +44 -44
- package/lib/css/components/code-blocks.less +130 -130
- package/lib/css/components/collapsible.less +104 -104
- package/lib/css/components/inputs.less +728 -728
- package/lib/css/components/link-previews.less +136 -136
- package/lib/css/components/links.less +218 -218
- package/lib/css/components/menu.less +47 -47
- package/lib/css/components/modals.less +133 -133
- package/lib/css/components/navigation.less +146 -146
- package/lib/css/components/notices.less +233 -233
- package/lib/css/components/page-titles.less +60 -60
- package/lib/css/components/pagination.less +55 -55
- package/lib/css/components/popovers.less +197 -197
- package/lib/css/components/post-summary.less +425 -425
- package/lib/css/components/progress-bars.less +330 -330
- package/lib/css/components/prose.less +503 -503
- package/lib/css/components/spinner.less +107 -107
- package/lib/css/components/tables.less +341 -341
- package/lib/css/components/tags.less +236 -236
- package/lib/css/components/toggle-switches.less +144 -144
- package/lib/css/components/topbar.less +427 -427
- package/lib/css/components/uploader.less +210 -210
- package/lib/css/components/user-cards.less +169 -169
- package/lib/css/components/widget-dynamic.less +33 -33
- package/lib/css/components/widget-static.less +273 -273
- package/lib/css/exports/constants-colors.less +1092 -1092
- package/lib/css/exports/constants-helpers.less +108 -108
- package/lib/css/exports/constants-type.less +153 -153
- package/lib/css/exports/exports.less +15 -15
- package/lib/css/exports/mixins.less +237 -237
- package/lib/css/stacks-dynamic.less +35 -35
- package/lib/css/stacks-static.less +86 -86
- package/lib/css/stacks.less +13 -13
- package/lib/ts/controllers/index.ts +7 -7
- package/lib/ts/controllers/s-expandable-control.ts +188 -188
- package/lib/ts/controllers/s-modal.ts +321 -321
- package/lib/ts/controllers/s-navigation-tablist.ts +117 -117
- package/lib/ts/controllers/s-popover.ts +567 -547
- package/lib/ts/controllers/s-table.ts +220 -220
- package/lib/ts/controllers/s-tooltip.ts +246 -246
- package/lib/ts/controllers/s-uploader.ts +172 -172
- package/lib/ts/index.ts +20 -20
- package/lib/ts/stacks.ts +88 -88
- package/lib/tsconfig.json +13 -13
- package/package.json +86 -87
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
import * as Stacks from "../stacks";
|
|
2
|
-
|
|
3
|
-
interface FilePreview {
|
|
4
|
-
data?: string | ArrayBuffer;
|
|
5
|
-
name: string;
|
|
6
|
-
type: string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export class UploaderController extends Stacks.StacksController {
|
|
10
|
-
static targets = ["input", "previews", "uploader"];
|
|
11
|
-
private inputTarget!: HTMLInputElement;
|
|
12
|
-
private previewsTarget!: HTMLElement;
|
|
13
|
-
private uploaderTarget!: HTMLElement;
|
|
14
|
-
|
|
15
|
-
private boundDragEnter!: any;
|
|
16
|
-
private boundDragLeave!: any;
|
|
17
|
-
|
|
18
|
-
private static readonly FILE_DISPLAY_LIMIT = 10;
|
|
19
|
-
private static readonly MAX_FILE_SIZE = 1024 * 1024 * 10; // 10 MB
|
|
20
|
-
|
|
21
|
-
connect() {
|
|
22
|
-
super.connect();
|
|
23
|
-
this.boundDragEnter = this.handleUploaderActive.bind(this, true);
|
|
24
|
-
this.boundDragLeave = this.handleUploaderActive.bind(this, false);
|
|
25
|
-
|
|
26
|
-
this.inputTarget.addEventListener("dragenter", this.boundDragEnter);
|
|
27
|
-
this.inputTarget.addEventListener("dragleave", this.boundDragLeave);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
disconnect() {
|
|
31
|
-
this.inputTarget.removeEventListener("dragenter", this.boundDragEnter);
|
|
32
|
-
this.inputTarget.removeEventListener("dragleave", this.boundDragLeave);
|
|
33
|
-
super.disconnect();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Handles rendering the file preview state on input change
|
|
38
|
-
*/
|
|
39
|
-
handleInput() {
|
|
40
|
-
this.previewsTarget.innerHTML = "";
|
|
41
|
-
|
|
42
|
-
if (!this.inputTarget.files) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const count = this.inputTarget.files.length;
|
|
47
|
-
this.getDataURLs(this.inputTarget.files, UploaderController.FILE_DISPLAY_LIMIT)
|
|
48
|
-
.then((res: FilePreview[]) => {
|
|
49
|
-
this.handleVisible(true);
|
|
50
|
-
const hasMultipleFiles = res.length > 1;
|
|
51
|
-
|
|
52
|
-
if (hasMultipleFiles) {
|
|
53
|
-
let headingElement = document.createElement("div");
|
|
54
|
-
headingElement.classList.add("s-uploader--previews-heading");
|
|
55
|
-
headingElement.innerText = res.length < count ?
|
|
56
|
-
`Showing ${res.length} of ${count} files` : `${count} items`;
|
|
57
|
-
this.previewsTarget.appendChild(headingElement);
|
|
58
|
-
this.previewsTarget.classList.add("has-multiple");
|
|
59
|
-
} else {
|
|
60
|
-
this.previewsTarget.classList.remove("has-multiple");
|
|
61
|
-
}
|
|
62
|
-
res.forEach((file) => this.addFilePreview(file));
|
|
63
|
-
this.handleUploaderActive(true);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Resets the Uploader to initial state
|
|
69
|
-
*/
|
|
70
|
-
reset() {
|
|
71
|
-
this.inputTarget.value = '';
|
|
72
|
-
this.previewsTarget.innerHTML = "";
|
|
73
|
-
this.handleVisible(false);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Set hide/show and disabled state on elements depending on preview state
|
|
78
|
-
* @param {boolean} shouldPreview - Uploader is entering a preview state
|
|
79
|
-
*/
|
|
80
|
-
private handleVisible(shouldPreview: boolean) {
|
|
81
|
-
const { scope } = this.targets;
|
|
82
|
-
const hideElements = scope.findAllElements('[data-s-uploader-hide-on-input]');
|
|
83
|
-
const showElements = scope.findAllElements('[data-s-uploader-show-on-input]');
|
|
84
|
-
const enableElements = scope.findAllElements('[data-s-uploader-enable-on-input]');
|
|
85
|
-
|
|
86
|
-
if (shouldPreview) {
|
|
87
|
-
hideElements.map(el => el.classList.add("d-none"));
|
|
88
|
-
showElements.map(el => el.classList.remove("d-none"));
|
|
89
|
-
enableElements.map(el => el.removeAttribute("disabled"));
|
|
90
|
-
} else {
|
|
91
|
-
hideElements.map(el => el.classList.remove("d-none"));
|
|
92
|
-
showElements.map(el => el.classList.add("d-none"));
|
|
93
|
-
enableElements.map(el => el.setAttribute("disabled", "true"))
|
|
94
|
-
this.handleUploaderActive(false);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Adds a DOM element to preview a selected file
|
|
100
|
-
* @param {FilePreview} file
|
|
101
|
-
*/
|
|
102
|
-
private addFilePreview(file: FilePreview) {
|
|
103
|
-
if (!file) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
let previewElement = document.createElement("div");
|
|
108
|
-
let thumbElement;
|
|
109
|
-
|
|
110
|
-
if (file.type.match('image/*') && file.data) {
|
|
111
|
-
thumbElement = document.createElement("img");
|
|
112
|
-
thumbElement.src = file.data.toString();
|
|
113
|
-
thumbElement.alt = file.name;
|
|
114
|
-
} else {
|
|
115
|
-
thumbElement = document.createElement("div");
|
|
116
|
-
thumbElement.innerText = file.name;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
thumbElement.classList.add("s-uploader--preview-thumbnail");
|
|
120
|
-
previewElement.appendChild(thumbElement);
|
|
121
|
-
previewElement.classList.add("s-uploader--preview");
|
|
122
|
-
previewElement.setAttribute('data-filename', file.name);
|
|
123
|
-
this.previewsTarget.appendChild(previewElement);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Toggles display and disabled state for select elements on valid input
|
|
128
|
-
* @param {boolean} active - Uploader is in active state (typically on 'dragenter')
|
|
129
|
-
*/
|
|
130
|
-
private handleUploaderActive(active: boolean) {
|
|
131
|
-
this.uploaderTarget.classList.toggle("is-active", active);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Converts the file data into a data URL
|
|
136
|
-
* @param {File} file
|
|
137
|
-
* @returns an object containing a FilePreview object
|
|
138
|
-
*/
|
|
139
|
-
private fileToDataURL(file: File): Promise<FilePreview> {
|
|
140
|
-
var reader = new FileReader();
|
|
141
|
-
const { name, size, type } = file;
|
|
142
|
-
|
|
143
|
-
if (size < UploaderController.MAX_FILE_SIZE && type.indexOf("image") > -1) {
|
|
144
|
-
return new Promise((resolve, reject) => {
|
|
145
|
-
reader.onload = evt => {
|
|
146
|
-
const res = evt?.target?.result;
|
|
147
|
-
if (res) {
|
|
148
|
-
resolve({ data: res, name, type });
|
|
149
|
-
} else {
|
|
150
|
-
reject();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
reader.readAsDataURL(file);
|
|
154
|
-
});
|
|
155
|
-
} else {
|
|
156
|
-
return Promise.resolve({ name, type });
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Gets an array of FilePreviews from a FileList
|
|
162
|
-
* @param {FileList|[]} files
|
|
163
|
-
* @returns an array of FilePreview objects from a FileList
|
|
164
|
-
*/
|
|
165
|
-
private getDataURLs(files: FileList, limit: number): Promise<FilePreview[]> {
|
|
166
|
-
const promises = Array.from(files)
|
|
167
|
-
.slice(0, Math.min(limit, files.length))
|
|
168
|
-
.map(f => this.fileToDataURL(f));
|
|
169
|
-
|
|
170
|
-
return Promise.all(promises);
|
|
171
|
-
}
|
|
172
|
-
};
|
|
1
|
+
import * as Stacks from "../stacks";
|
|
2
|
+
|
|
3
|
+
interface FilePreview {
|
|
4
|
+
data?: string | ArrayBuffer;
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export class UploaderController extends Stacks.StacksController {
|
|
10
|
+
static targets = ["input", "previews", "uploader"];
|
|
11
|
+
private inputTarget!: HTMLInputElement;
|
|
12
|
+
private previewsTarget!: HTMLElement;
|
|
13
|
+
private uploaderTarget!: HTMLElement;
|
|
14
|
+
|
|
15
|
+
private boundDragEnter!: any;
|
|
16
|
+
private boundDragLeave!: any;
|
|
17
|
+
|
|
18
|
+
private static readonly FILE_DISPLAY_LIMIT = 10;
|
|
19
|
+
private static readonly MAX_FILE_SIZE = 1024 * 1024 * 10; // 10 MB
|
|
20
|
+
|
|
21
|
+
connect() {
|
|
22
|
+
super.connect();
|
|
23
|
+
this.boundDragEnter = this.handleUploaderActive.bind(this, true);
|
|
24
|
+
this.boundDragLeave = this.handleUploaderActive.bind(this, false);
|
|
25
|
+
|
|
26
|
+
this.inputTarget.addEventListener("dragenter", this.boundDragEnter);
|
|
27
|
+
this.inputTarget.addEventListener("dragleave", this.boundDragLeave);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
disconnect() {
|
|
31
|
+
this.inputTarget.removeEventListener("dragenter", this.boundDragEnter);
|
|
32
|
+
this.inputTarget.removeEventListener("dragleave", this.boundDragLeave);
|
|
33
|
+
super.disconnect();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Handles rendering the file preview state on input change
|
|
38
|
+
*/
|
|
39
|
+
handleInput() {
|
|
40
|
+
this.previewsTarget.innerHTML = "";
|
|
41
|
+
|
|
42
|
+
if (!this.inputTarget.files) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const count = this.inputTarget.files.length;
|
|
47
|
+
this.getDataURLs(this.inputTarget.files, UploaderController.FILE_DISPLAY_LIMIT)
|
|
48
|
+
.then((res: FilePreview[]) => {
|
|
49
|
+
this.handleVisible(true);
|
|
50
|
+
const hasMultipleFiles = res.length > 1;
|
|
51
|
+
|
|
52
|
+
if (hasMultipleFiles) {
|
|
53
|
+
let headingElement = document.createElement("div");
|
|
54
|
+
headingElement.classList.add("s-uploader--previews-heading");
|
|
55
|
+
headingElement.innerText = res.length < count ?
|
|
56
|
+
`Showing ${res.length} of ${count} files` : `${count} items`;
|
|
57
|
+
this.previewsTarget.appendChild(headingElement);
|
|
58
|
+
this.previewsTarget.classList.add("has-multiple");
|
|
59
|
+
} else {
|
|
60
|
+
this.previewsTarget.classList.remove("has-multiple");
|
|
61
|
+
}
|
|
62
|
+
res.forEach((file) => this.addFilePreview(file));
|
|
63
|
+
this.handleUploaderActive(true);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resets the Uploader to initial state
|
|
69
|
+
*/
|
|
70
|
+
reset() {
|
|
71
|
+
this.inputTarget.value = '';
|
|
72
|
+
this.previewsTarget.innerHTML = "";
|
|
73
|
+
this.handleVisible(false);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Set hide/show and disabled state on elements depending on preview state
|
|
78
|
+
* @param {boolean} shouldPreview - Uploader is entering a preview state
|
|
79
|
+
*/
|
|
80
|
+
private handleVisible(shouldPreview: boolean) {
|
|
81
|
+
const { scope } = this.targets;
|
|
82
|
+
const hideElements = scope.findAllElements('[data-s-uploader-hide-on-input]');
|
|
83
|
+
const showElements = scope.findAllElements('[data-s-uploader-show-on-input]');
|
|
84
|
+
const enableElements = scope.findAllElements('[data-s-uploader-enable-on-input]');
|
|
85
|
+
|
|
86
|
+
if (shouldPreview) {
|
|
87
|
+
hideElements.map(el => el.classList.add("d-none"));
|
|
88
|
+
showElements.map(el => el.classList.remove("d-none"));
|
|
89
|
+
enableElements.map(el => el.removeAttribute("disabled"));
|
|
90
|
+
} else {
|
|
91
|
+
hideElements.map(el => el.classList.remove("d-none"));
|
|
92
|
+
showElements.map(el => el.classList.add("d-none"));
|
|
93
|
+
enableElements.map(el => el.setAttribute("disabled", "true"))
|
|
94
|
+
this.handleUploaderActive(false);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Adds a DOM element to preview a selected file
|
|
100
|
+
* @param {FilePreview} file
|
|
101
|
+
*/
|
|
102
|
+
private addFilePreview(file: FilePreview) {
|
|
103
|
+
if (!file) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let previewElement = document.createElement("div");
|
|
108
|
+
let thumbElement;
|
|
109
|
+
|
|
110
|
+
if (file.type.match('image/*') && file.data) {
|
|
111
|
+
thumbElement = document.createElement("img");
|
|
112
|
+
thumbElement.src = file.data.toString();
|
|
113
|
+
thumbElement.alt = file.name;
|
|
114
|
+
} else {
|
|
115
|
+
thumbElement = document.createElement("div");
|
|
116
|
+
thumbElement.innerText = file.name;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
thumbElement.classList.add("s-uploader--preview-thumbnail");
|
|
120
|
+
previewElement.appendChild(thumbElement);
|
|
121
|
+
previewElement.classList.add("s-uploader--preview");
|
|
122
|
+
previewElement.setAttribute('data-filename', file.name);
|
|
123
|
+
this.previewsTarget.appendChild(previewElement);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Toggles display and disabled state for select elements on valid input
|
|
128
|
+
* @param {boolean} active - Uploader is in active state (typically on 'dragenter')
|
|
129
|
+
*/
|
|
130
|
+
private handleUploaderActive(active: boolean) {
|
|
131
|
+
this.uploaderTarget.classList.toggle("is-active", active);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Converts the file data into a data URL
|
|
136
|
+
* @param {File} file
|
|
137
|
+
* @returns an object containing a FilePreview object
|
|
138
|
+
*/
|
|
139
|
+
private fileToDataURL(file: File): Promise<FilePreview> {
|
|
140
|
+
var reader = new FileReader();
|
|
141
|
+
const { name, size, type } = file;
|
|
142
|
+
|
|
143
|
+
if (size < UploaderController.MAX_FILE_SIZE && type.indexOf("image") > -1) {
|
|
144
|
+
return new Promise((resolve, reject) => {
|
|
145
|
+
reader.onload = evt => {
|
|
146
|
+
const res = evt?.target?.result;
|
|
147
|
+
if (res) {
|
|
148
|
+
resolve({ data: res, name, type });
|
|
149
|
+
} else {
|
|
150
|
+
reject();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
reader.readAsDataURL(file);
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
return Promise.resolve({ name, type });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Gets an array of FilePreviews from a FileList
|
|
162
|
+
* @param {FileList|[]} files
|
|
163
|
+
* @returns an array of FilePreview objects from a FileList
|
|
164
|
+
*/
|
|
165
|
+
private getDataURLs(files: FileList, limit: number): Promise<FilePreview[]> {
|
|
166
|
+
const promises = Array.from(files)
|
|
167
|
+
.slice(0, Math.min(limit, files.length))
|
|
168
|
+
.map(f => this.fileToDataURL(f));
|
|
169
|
+
|
|
170
|
+
return Promise.all(promises);
|
|
171
|
+
}
|
|
172
|
+
};
|
package/lib/ts/index.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import '../css/stacks.less';
|
|
2
|
-
import { ExpandableController, ModalController, PopoverController, TableController, TabListController, TooltipController, UploaderController } from './controllers';
|
|
3
|
-
import { application, StacksApplication } from './stacks';
|
|
4
|
-
|
|
5
|
-
// register all built-in controllers
|
|
6
|
-
application.register("s-expandable-control", ExpandableController);
|
|
7
|
-
application.register("s-modal", ModalController);
|
|
8
|
-
application.register("s-navigation-tablist", TabListController);
|
|
9
|
-
application.register("s-popover", PopoverController);
|
|
10
|
-
application.register("s-table", TableController);
|
|
11
|
-
application.register("s-tooltip", TooltipController);
|
|
12
|
-
application.register("s-uploader", UploaderController);
|
|
13
|
-
|
|
14
|
-
// finalize the application to guard our controller namespace
|
|
15
|
-
StacksApplication.finalize();
|
|
16
|
-
|
|
17
|
-
// export all controllers w/ helpers
|
|
18
|
-
export * from "./controllers";
|
|
19
|
-
// export the entirety of the contents of stacks.ts
|
|
20
|
-
export * from "./stacks";
|
|
1
|
+
import '../css/stacks.less';
|
|
2
|
+
import { ExpandableController, ModalController, PopoverController, TableController, TabListController, TooltipController, UploaderController } from './controllers';
|
|
3
|
+
import { application, StacksApplication } from './stacks';
|
|
4
|
+
|
|
5
|
+
// register all built-in controllers
|
|
6
|
+
application.register("s-expandable-control", ExpandableController);
|
|
7
|
+
application.register("s-modal", ModalController);
|
|
8
|
+
application.register("s-navigation-tablist", TabListController);
|
|
9
|
+
application.register("s-popover", PopoverController);
|
|
10
|
+
application.register("s-table", TableController);
|
|
11
|
+
application.register("s-tooltip", TooltipController);
|
|
12
|
+
application.register("s-uploader", UploaderController);
|
|
13
|
+
|
|
14
|
+
// finalize the application to guard our controller namespace
|
|
15
|
+
StacksApplication.finalize();
|
|
16
|
+
|
|
17
|
+
// export all controllers w/ helpers
|
|
18
|
+
export * from "./controllers";
|
|
19
|
+
// export the entirety of the contents of stacks.ts
|
|
20
|
+
export * from "./stacks";
|
package/lib/ts/stacks.ts
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import * as Stimulus from "stimulus";
|
|
2
|
-
|
|
3
|
-
export class StacksApplication extends Stimulus.Application {
|
|
4
|
-
static _initializing = true;
|
|
5
|
-
|
|
6
|
-
load(...definitions: Stimulus.Definition[]): void
|
|
7
|
-
load(definitions: Stimulus.Definition[]): void
|
|
8
|
-
load(head: Stimulus.Definition | Stimulus.Definition[], ...rest: Stimulus.Definition[]) {
|
|
9
|
-
const definitions = Array.isArray(head) ? head : [head, ...rest];
|
|
10
|
-
|
|
11
|
-
for (const definition of definitions) {
|
|
12
|
-
var hasPrefix = /^s-/.test(definition.identifier);
|
|
13
|
-
if (StacksApplication._initializing && !hasPrefix) {
|
|
14
|
-
throw "Stacks-created Stimulus controller names must start with \"s-\".";
|
|
15
|
-
}
|
|
16
|
-
if (!StacksApplication._initializing && hasPrefix) {
|
|
17
|
-
throw "The \"s-\" prefix on Stimulus controller names is reserved for Stacks-created controllers.";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
super.load(definitions);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
static start(element?: Element, schema?: Stimulus.Schema): StacksApplication {
|
|
25
|
-
const application = new StacksApplication(element, schema);
|
|
26
|
-
application.start();
|
|
27
|
-
return application;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static finalize() {
|
|
31
|
-
StacksApplication._initializing = false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const application: Stimulus.Application = StacksApplication.start();
|
|
36
|
-
|
|
37
|
-
export class StacksController extends Stimulus.Controller {
|
|
38
|
-
protected getElementData(element: Element, key: string) {
|
|
39
|
-
return element.getAttribute("data-" + this.identifier + "-" + key);
|
|
40
|
-
};
|
|
41
|
-
protected setElementData(element: Element, key: string, value: string) {
|
|
42
|
-
element.setAttribute("data-" + this.identifier + "-" + key, value);
|
|
43
|
-
};
|
|
44
|
-
protected removeElementData(element: Element, key: string) {
|
|
45
|
-
element.removeAttribute("data-" + this.identifier + "-" + key);
|
|
46
|
-
};
|
|
47
|
-
protected triggerEvent<T>(eventName: string, detail?: T, optionalElement?: Element) {
|
|
48
|
-
const namespacedName = this.identifier + ":" + eventName;
|
|
49
|
-
var event : CustomEvent<T>;
|
|
50
|
-
try {
|
|
51
|
-
event = new CustomEvent(namespacedName, {bubbles: true, cancelable: true, detail: detail});
|
|
52
|
-
} catch (ex) {
|
|
53
|
-
// Internet Explorer
|
|
54
|
-
event = document.createEvent("CustomEvent");
|
|
55
|
-
event.initCustomEvent(namespacedName, true, true, detail!);
|
|
56
|
-
}
|
|
57
|
-
(optionalElement || this.element).dispatchEvent(event);
|
|
58
|
-
return event;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ControllerDefinition/createController/addController is here to make
|
|
63
|
-
// it easier to consume Stimulus from ES5 files (without classes)
|
|
64
|
-
export interface ControllerDefinition {
|
|
65
|
-
[name: string]: any;
|
|
66
|
-
targets?: string[];
|
|
67
|
-
}
|
|
68
|
-
export function createController(controllerDefinition: ControllerDefinition): typeof StacksController {
|
|
69
|
-
const Controller = controllerDefinition.hasOwnProperty("targets")
|
|
70
|
-
? class Controller extends StacksController { static targets = controllerDefinition.targets! }
|
|
71
|
-
: class Controller extends StacksController {};
|
|
72
|
-
|
|
73
|
-
for (var prop in controllerDefinition) {
|
|
74
|
-
if (prop !== "targets" && controllerDefinition.hasOwnProperty(prop)) {
|
|
75
|
-
Object.defineProperty(
|
|
76
|
-
Controller.prototype,
|
|
77
|
-
prop,
|
|
78
|
-
Object.getOwnPropertyDescriptor(controllerDefinition, prop)!
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return Controller;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function addController(name: string, controller: ControllerDefinition) {
|
|
87
|
-
application.register(name, createController(controller));
|
|
88
|
-
}
|
|
1
|
+
import * as Stimulus from "stimulus";
|
|
2
|
+
|
|
3
|
+
export class StacksApplication extends Stimulus.Application {
|
|
4
|
+
static _initializing = true;
|
|
5
|
+
|
|
6
|
+
load(...definitions: Stimulus.Definition[]): void
|
|
7
|
+
load(definitions: Stimulus.Definition[]): void
|
|
8
|
+
load(head: Stimulus.Definition | Stimulus.Definition[], ...rest: Stimulus.Definition[]) {
|
|
9
|
+
const definitions = Array.isArray(head) ? head : [head, ...rest];
|
|
10
|
+
|
|
11
|
+
for (const definition of definitions) {
|
|
12
|
+
var hasPrefix = /^s-/.test(definition.identifier);
|
|
13
|
+
if (StacksApplication._initializing && !hasPrefix) {
|
|
14
|
+
throw "Stacks-created Stimulus controller names must start with \"s-\".";
|
|
15
|
+
}
|
|
16
|
+
if (!StacksApplication._initializing && hasPrefix) {
|
|
17
|
+
throw "The \"s-\" prefix on Stimulus controller names is reserved for Stacks-created controllers.";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
super.load(definitions);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static start(element?: Element, schema?: Stimulus.Schema): StacksApplication {
|
|
25
|
+
const application = new StacksApplication(element, schema);
|
|
26
|
+
application.start();
|
|
27
|
+
return application;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static finalize() {
|
|
31
|
+
StacksApplication._initializing = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const application: Stimulus.Application = StacksApplication.start();
|
|
36
|
+
|
|
37
|
+
export class StacksController extends Stimulus.Controller {
|
|
38
|
+
protected getElementData(element: Element, key: string) {
|
|
39
|
+
return element.getAttribute("data-" + this.identifier + "-" + key);
|
|
40
|
+
};
|
|
41
|
+
protected setElementData(element: Element, key: string, value: string) {
|
|
42
|
+
element.setAttribute("data-" + this.identifier + "-" + key, value);
|
|
43
|
+
};
|
|
44
|
+
protected removeElementData(element: Element, key: string) {
|
|
45
|
+
element.removeAttribute("data-" + this.identifier + "-" + key);
|
|
46
|
+
};
|
|
47
|
+
protected triggerEvent<T>(eventName: string, detail?: T, optionalElement?: Element) {
|
|
48
|
+
const namespacedName = this.identifier + ":" + eventName;
|
|
49
|
+
var event : CustomEvent<T>;
|
|
50
|
+
try {
|
|
51
|
+
event = new CustomEvent(namespacedName, {bubbles: true, cancelable: true, detail: detail});
|
|
52
|
+
} catch (ex) {
|
|
53
|
+
// Internet Explorer
|
|
54
|
+
event = document.createEvent("CustomEvent");
|
|
55
|
+
event.initCustomEvent(namespacedName, true, true, detail!);
|
|
56
|
+
}
|
|
57
|
+
(optionalElement || this.element).dispatchEvent(event);
|
|
58
|
+
return event;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ControllerDefinition/createController/addController is here to make
|
|
63
|
+
// it easier to consume Stimulus from ES5 files (without classes)
|
|
64
|
+
export interface ControllerDefinition {
|
|
65
|
+
[name: string]: any;
|
|
66
|
+
targets?: string[];
|
|
67
|
+
}
|
|
68
|
+
export function createController(controllerDefinition: ControllerDefinition): typeof StacksController {
|
|
69
|
+
const Controller = controllerDefinition.hasOwnProperty("targets")
|
|
70
|
+
? class Controller extends StacksController { static targets = controllerDefinition.targets! }
|
|
71
|
+
: class Controller extends StacksController {};
|
|
72
|
+
|
|
73
|
+
for (var prop in controllerDefinition) {
|
|
74
|
+
if (prop !== "targets" && controllerDefinition.hasOwnProperty(prop)) {
|
|
75
|
+
Object.defineProperty(
|
|
76
|
+
Controller.prototype,
|
|
77
|
+
prop,
|
|
78
|
+
Object.getOwnPropertyDescriptor(controllerDefinition, prop)!
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return Controller;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function addController(name: string, controller: ControllerDefinition) {
|
|
87
|
+
application.register(name, createController(controller));
|
|
88
|
+
}
|
package/lib/tsconfig.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"strict": true,
|
|
4
|
-
"target": "es5",
|
|
5
|
-
"lib": ["dom", "es6", "dom.iterable", "scripthost"], // es6 stuff is polyfilled by stimulus
|
|
6
|
-
"outDir": "../dist",
|
|
7
|
-
"sourceMap": true,
|
|
8
|
-
"moduleResolution": "node",
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"declaration": true
|
|
11
|
-
},
|
|
12
|
-
"include": ["ts/**/*"]
|
|
13
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"strict": true,
|
|
4
|
+
"target": "es5",
|
|
5
|
+
"lib": ["dom", "es6", "dom.iterable", "scripthost"], // es6 stuff is polyfilled by stimulus
|
|
6
|
+
"outDir": "../dist",
|
|
7
|
+
"sourceMap": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["ts/**/*"]
|
|
13
|
+
}
|