@sensefolks/fastpoll 0.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/dist/cjs/app-globals-V2Kpy_OQ.js +8 -0
  4. package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
  5. package/dist/cjs/index-CC5IS5t8.js +1437 -0
  6. package/dist/cjs/index-CC5IS5t8.js.map +1 -0
  7. package/dist/cjs/index-D8TNlmQq.js +201 -0
  8. package/dist/cjs/index-D8TNlmQq.js.map +1 -0
  9. package/dist/cjs/index.cjs.js +11 -0
  10. package/dist/cjs/index.cjs.js.map +1 -0
  11. package/dist/cjs/loader.cjs.js +16 -0
  12. package/dist/cjs/loader.cjs.js.map +1 -0
  13. package/dist/cjs/sf-fastpoll.cjs.entry.js +395 -0
  14. package/dist/cjs/sf-fastpoll.cjs.entry.js.map +1 -0
  15. package/dist/cjs/sf-fastpoll.cjs.js +28 -0
  16. package/dist/cjs/sf-fastpoll.cjs.js.map +1 -0
  17. package/dist/cjs/sf-fastpoll.entry.cjs.js.map +1 -0
  18. package/dist/collection/collection-manifest.json +12 -0
  19. package/dist/collection/components/sf-fastpoll/sf-fastpoll.css +76 -0
  20. package/dist/collection/components/sf-fastpoll/sf-fastpoll.js +454 -0
  21. package/dist/collection/components/sf-fastpoll/sf-fastpoll.js.map +1 -0
  22. package/dist/collection/index.js +11 -0
  23. package/dist/collection/index.js.map +1 -0
  24. package/dist/collection/utils/utils.js +189 -0
  25. package/dist/collection/utils/utils.js.map +1 -0
  26. package/dist/components/index.d.ts +33 -0
  27. package/dist/components/index.js +1427 -0
  28. package/dist/components/index.js.map +1 -0
  29. package/dist/components/sf-fastpoll.d.ts +11 -0
  30. package/dist/components/sf-fastpoll.js +425 -0
  31. package/dist/components/sf-fastpoll.js.map +1 -0
  32. package/dist/esm/app-globals-DQuL1Twl.js +6 -0
  33. package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
  34. package/dist/esm/index-CfdIRf0W.js +193 -0
  35. package/dist/esm/index-CfdIRf0W.js.map +1 -0
  36. package/dist/esm/index-XYfqntZe.js +1428 -0
  37. package/dist/esm/index-XYfqntZe.js.map +1 -0
  38. package/dist/esm/index.js +4 -0
  39. package/dist/esm/index.js.map +1 -0
  40. package/dist/esm/loader.js +14 -0
  41. package/dist/esm/loader.js.map +1 -0
  42. package/dist/esm/polyfills/core-js.js +11 -0
  43. package/dist/esm/polyfills/dom.js +79 -0
  44. package/dist/esm/polyfills/es5-html-element.js +1 -0
  45. package/dist/esm/polyfills/index.js +34 -0
  46. package/dist/esm/polyfills/system.js +6 -0
  47. package/dist/esm/sf-fastpoll.entry.js +393 -0
  48. package/dist/esm/sf-fastpoll.entry.js.map +1 -0
  49. package/dist/esm/sf-fastpoll.js +24 -0
  50. package/dist/esm/sf-fastpoll.js.map +1 -0
  51. package/dist/esm-es5/app-globals-DQuL1Twl.js +2 -0
  52. package/dist/esm-es5/app-globals-DQuL1Twl.js.map +1 -0
  53. package/dist/esm-es5/index-CfdIRf0W.js +2 -0
  54. package/dist/esm-es5/index-CfdIRf0W.js.map +1 -0
  55. package/dist/esm-es5/index-XYfqntZe.js +3 -0
  56. package/dist/esm-es5/index-XYfqntZe.js.map +1 -0
  57. package/dist/esm-es5/index.js +2 -0
  58. package/dist/esm-es5/index.js.map +1 -0
  59. package/dist/esm-es5/loader.js +2 -0
  60. package/dist/esm-es5/loader.js.map +1 -0
  61. package/dist/esm-es5/sf-fastpoll.entry.js +2 -0
  62. package/dist/esm-es5/sf-fastpoll.entry.js.map +1 -0
  63. package/dist/esm-es5/sf-fastpoll.js +2 -0
  64. package/dist/esm-es5/sf-fastpoll.js.map +1 -0
  65. package/dist/index.cjs.js +1 -0
  66. package/dist/index.js +1 -0
  67. package/dist/sf-fastpoll/index.esm.js +2 -0
  68. package/dist/sf-fastpoll/index.esm.js.map +1 -0
  69. package/dist/sf-fastpoll/loader.esm.js.map +1 -0
  70. package/dist/sf-fastpoll/p-1f6dca2a.system.entry.js +2 -0
  71. package/dist/sf-fastpoll/p-1f6dca2a.system.entry.js.map +1 -0
  72. package/dist/sf-fastpoll/p-4648bca3.entry.js +2 -0
  73. package/dist/sf-fastpoll/p-4648bca3.entry.js.map +1 -0
  74. package/dist/sf-fastpoll/p-BbPAtVJG.system.js +2 -0
  75. package/dist/sf-fastpoll/p-BbPAtVJG.system.js.map +1 -0
  76. package/dist/sf-fastpoll/p-C7EMppj8.system.js.map +1 -0
  77. package/dist/sf-fastpoll/p-C9ESvisV.system.js +3 -0
  78. package/dist/sf-fastpoll/p-C9ESvisV.system.js.map +1 -0
  79. package/dist/sf-fastpoll/p-CfdIRf0W.js +2 -0
  80. package/dist/sf-fastpoll/p-CfdIRf0W.js.map +1 -0
  81. package/dist/sf-fastpoll/p-CpmSDeqe.system.js +2 -0
  82. package/dist/sf-fastpoll/p-CpmSDeqe.system.js.map +1 -0
  83. package/dist/sf-fastpoll/p-DQuL1Twl.js +2 -0
  84. package/dist/sf-fastpoll/p-DQuL1Twl.js.map +1 -0
  85. package/dist/sf-fastpoll/p-JC66e5NR.system.js.map +1 -0
  86. package/dist/sf-fastpoll/p-S-cJYJS7.system.js +2 -0
  87. package/dist/sf-fastpoll/p-S-cJYJS7.system.js.map +1 -0
  88. package/dist/sf-fastpoll/p-XYfqntZe.js +3 -0
  89. package/dist/sf-fastpoll/p-XYfqntZe.js.map +1 -0
  90. package/dist/sf-fastpoll/p-zRZYYxiz.system.js +2 -0
  91. package/dist/sf-fastpoll/p-zRZYYxiz.system.js.map +1 -0
  92. package/dist/sf-fastpoll/sf-fastpoll.entry.esm.js.map +1 -0
  93. package/dist/sf-fastpoll/sf-fastpoll.esm.js +2 -0
  94. package/dist/sf-fastpoll/sf-fastpoll.esm.js.map +1 -0
  95. package/dist/sf-fastpoll/sf-fastpoll.js +127 -0
  96. package/dist/types/components/sf-fastpoll/sf-fastpoll.d.ts +77 -0
  97. package/dist/types/components.d.ts +47 -0
  98. package/dist/types/index.d.ts +11 -0
  99. package/dist/types/stencil-public-runtime.d.ts +1709 -0
  100. package/dist/types/utils/utils.d.ts +86 -0
  101. package/loader/cdn.js +2 -0
  102. package/loader/index.cjs.js +2 -0
  103. package/loader/index.d.ts +24 -0
  104. package/loader/index.es2017.js +2 -0
  105. package/loader/index.js +3 -0
  106. package/package.json +86 -0
  107. package/readme.md +239 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Utility functions for sf-fastpoll
3
+ */
4
+ /**
5
+ * Sanitize user input to prevent XSS and limit length
6
+ * @param input The raw user input
7
+ * @param maxLength Maximum allowed length (default: 500)
8
+ * @returns Sanitized string
9
+ */
10
+ export declare function sanitizeInput(input: string, maxLength?: number): string;
11
+ /**
12
+ * Sanitize email input with email-specific rules
13
+ * @param email The raw email input
14
+ * @returns Sanitized email string
15
+ */
16
+ export declare function sanitizeEmail(email: string): string;
17
+ /**
18
+ * Sanitize a value based on field type
19
+ * @param value The raw value
20
+ * @param inputType The type of input field
21
+ * @returns Sanitized value
22
+ */
23
+ export declare function sanitizeByType(value: string, inputType: string): string;
24
+ /**
25
+ * Interface for respondent detail options
26
+ */
27
+ interface RespondentDetailOption {
28
+ value: string;
29
+ label: string;
30
+ }
31
+ /**
32
+ * Interface for respondent detail configuration
33
+ */
34
+ interface RespondentDetail {
35
+ label: string;
36
+ value: string;
37
+ inputType: string;
38
+ required?: boolean;
39
+ placeholder?: string;
40
+ options?: RespondentDetailOption[];
41
+ defaultValue?: any;
42
+ }
43
+ /**
44
+ * Check if a survey key is valid UUID
45
+ * @param key The survey key to validate
46
+ * @returns True if the key is a valid UUID, false otherwise
47
+ */
48
+ export declare function isValidKey(key: string | undefined | null): boolean;
49
+ /**
50
+ * Format error messages for display
51
+ * @param error The error object or message
52
+ * @returns A formatted error message string
53
+ */
54
+ export declare function formatErrorMessage(error: any): string;
55
+ /**
56
+ * Get field configuration for rendering
57
+ * @param detail The respondent detail configuration
58
+ * @param value The current value of the field
59
+ * @returns Field configuration object
60
+ */
61
+ export declare function getFieldConfig(detail: RespondentDetail, value: string): {
62
+ inputType: string;
63
+ placeholder: string;
64
+ required: boolean;
65
+ options: RespondentDetailOption[];
66
+ defaultValue: any;
67
+ hasOptions: boolean;
68
+ selectedValues: string[];
69
+ fieldValue: string;
70
+ currentValue: string;
71
+ };
72
+ /**
73
+ * Browser-compatible helper to add value to comma-separated list
74
+ * @param currentValue The current comma-separated string
75
+ * @param valueToAdd The value to add
76
+ * @returns Updated comma-separated string
77
+ */
78
+ export declare function addToCommaSeparatedList(currentValue: string, valueToAdd: string): string;
79
+ /**
80
+ * Browser-compatible helper to remove value from comma-separated list
81
+ * @param currentValue The current comma-separated string
82
+ * @param valueToRemove The value to remove
83
+ * @returns Updated comma-separated string
84
+ */
85
+ export declare function removeFromCommaSeparatedList(currentValue: string, valueToRemove: string): string;
86
+ export {};
package/loader/cdn.js ADDED
@@ -0,0 +1,2 @@
1
+ module.exports = require('../dist/cjs/loader.cjs.js');
2
+ module.exports.applyPolyfills = function() { return Promise.resolve() };
@@ -0,0 +1,2 @@
1
+ module.exports = require('../dist/cjs/loader.cjs.js');
2
+ module.exports.applyPolyfills = function() { return Promise.resolve() };
@@ -0,0 +1,24 @@
1
+ export * from '../dist/types/components';
2
+ export interface CustomElementsDefineOptions {
3
+ exclude?: string[];
4
+ resourcesUrl?: string;
5
+ syncQueue?: boolean;
6
+ jmp?: (c: Function) => any;
7
+ raf?: (c: FrameRequestCallback) => number;
8
+ ael?: (el: EventTarget, eventName: string, listener: EventListenerOrEventListenerObject, options: boolean | AddEventListenerOptions) => void;
9
+ rel?: (el: EventTarget, eventName: string, listener: EventListenerOrEventListenerObject, options: boolean | AddEventListenerOptions) => void;
10
+ }
11
+ export declare function defineCustomElements(win?: Window, opts?: CustomElementsDefineOptions): void;
12
+ /**
13
+ * @deprecated
14
+ */
15
+ export declare function applyPolyfills(): Promise<void>;
16
+
17
+ /**
18
+ * Used to specify a nonce value that corresponds with an application's CSP.
19
+ * When set, the nonce will be added to all dynamically created script and style tags at runtime.
20
+ * Alternatively, the nonce value can be set on a meta tag in the DOM head
21
+ * (<meta name="csp-nonce" content="{ nonce value here }" />) which
22
+ * will result in the same behavior.
23
+ */
24
+ export declare function setNonce(nonce: string): void;
@@ -0,0 +1,2 @@
1
+ export * from '../dist/esm/polyfills/index.js';
2
+ export * from '../dist/esm/loader.js';
@@ -0,0 +1,3 @@
1
+ (function(){if("undefined"!==typeof window&&void 0!==window.Reflect&&void 0!==window.customElements){var a=HTMLElement;window.HTMLElement=function(){return Reflect.construct(a,[],this.constructor)};HTMLElement.prototype=a.prototype;HTMLElement.prototype.constructor=HTMLElement;Object.setPrototypeOf(HTMLElement,a)}})();
2
+ export * from '../dist/esm/polyfills/index.js';
3
+ export * from '../dist/esm-es5/loader.js';
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@sensefolks/fastpoll",
3
+ "version": "0.1.0",
4
+ "description": "Conduct single or multi-choice polls to understand user opinions in a snap",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/types/index.d.ts",
8
+ "collection": "dist/collection/collection-manifest.json",
9
+ "collection:main": "dist/collection/index.js",
10
+ "unpkg": "dist/sf-fastpoll/sf-fastpoll.esm.js",
11
+ "jsdelivr": "dist/sf-fastpoll/sf-fastpoll.esm.js",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/sf-fastpoll/sf-fastpoll.esm.js",
15
+ "require": "./dist/sf-fastpoll/sf-fastpoll.cjs.js",
16
+ "types": "./dist/types/index.d.ts"
17
+ },
18
+ "./sf-fastpoll": {
19
+ "import": "./dist/components/sf-fastpoll.js",
20
+ "types": "./dist/components/sf-fastpoll.d.ts"
21
+ },
22
+ "./loader": {
23
+ "import": "./loader/index.js",
24
+ "require": "./loader/index.cjs",
25
+ "types": "./loader/index.d.ts"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist/",
30
+ "loader/",
31
+ "CHANGELOG.md"
32
+ ],
33
+ "scripts": {
34
+ "build": "stencil build",
35
+ "build:watch": "stencil build --watch",
36
+ "start": "stencil build --dev --watch --serve",
37
+ "test": "stencil test --spec --e2e",
38
+ "test:watch": "stencil test --spec --e2e --watchAll",
39
+ "test:unit": "stencil test --spec",
40
+ "test:e2e": "stencil test --e2e",
41
+ "generate": "stencil generate",
42
+ "prepublishOnly": "npm run build",
43
+ "version": "npm run build"
44
+ },
45
+ "keywords": [
46
+ "survey",
47
+ "poll",
48
+ "fastpoll",
49
+ "web-component",
50
+ "stencil",
51
+ "custom-element",
52
+ "feedback",
53
+ "questionnaire",
54
+ "form",
55
+ "accessible",
56
+ "wcag",
57
+ "a11y",
58
+ "sensefolks"
59
+ ],
60
+ "author": "SenseFolks <support@sensefolks.com> (https://sensefolks.com)",
61
+ "license": "MIT",
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "https://github.com/sensefolks/survey-components.git",
65
+ "directory": "fastpoll"
66
+ },
67
+ "bugs": {
68
+ "url": "https://github.com/sensefolks/survey-components/issues"
69
+ },
70
+ "homepage": "https://sensefolks.com/surveys/fastpoll",
71
+ "publishConfig": {
72
+ "access": "public"
73
+ },
74
+ "engines": {
75
+ "node": ">=16.0.0"
76
+ },
77
+ "sideEffects": false,
78
+ "devDependencies": {
79
+ "@stencil/core": "^4.27.1",
80
+ "@types/jest": "^29.5.14",
81
+ "@types/node": "^22.13.5",
82
+ "jest": "^29.7.0",
83
+ "jest-cli": "^29.7.0",
84
+ "puppeteer": "^24.3.0"
85
+ }
86
+ }
package/readme.md ADDED
@@ -0,0 +1,239 @@
1
+ # sf-fastpoll
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@sensefolks/fastpoll.svg)](https://www.npmjs.com/package/@sensefolks/fastpoll)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@sensefolks/fastpoll.svg)](https://www.npmjs.com/package/@sensefolks/fastpoll)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ > Fast poll survey web component for collecting user feedback with single/multiple choice options. WCAG 2.1 AA accessible.
8
+
9
+ ## Features
10
+
11
+ - 🎯 Single and multiple choice polls
12
+ - 📝 Follow-up questions support
13
+ - ♿ WCAG 2.1 AA accessible
14
+ - ⌨️ Full keyboard navigation
15
+ - 🔊 Screen reader optimized
16
+ - 🎨 Customizable via CSS Parts
17
+ - 📱 Responsive design
18
+ - 🌐 Works with any framework
19
+
20
+ ## Installation
21
+
22
+ ### NPM
23
+
24
+ ```bash
25
+ npm install @sensefolks/fastpoll
26
+ ```
27
+
28
+ ### CDN (Script Tag)
29
+
30
+ ```html
31
+ <!-- Modern browsers (ES modules) -->
32
+ <script type="module" src="https://unpkg.com/@sensefolks/fastpoll/dist/sf-fastpoll/sf-fastpoll.esm.js"></script>
33
+
34
+ <!-- Legacy browsers -->
35
+ <script nomodule src="https://unpkg.com/@sensefolks/fastpoll/dist/sf-fastpoll/sf-fastpoll.js"></script>
36
+ ```
37
+
38
+ Or via jsDelivr:
39
+
40
+ ```html
41
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@sensefolks/fastpoll/dist/sf-fastpoll/sf-fastpoll.esm.js"></script>
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ### HTML
47
+
48
+ ```html
49
+ <sf-fastpoll survey-key="your-survey-uuid" completion-message="Thank you for your feedback!"> </sf-fastpoll>
50
+ ```
51
+
52
+ ### React
53
+
54
+ ```jsx
55
+ import '@sensefolks/fastpoll';
56
+
57
+ function App() {
58
+ return <sf-fastpoll survey-key="your-survey-uuid" completion-message="Thank you!"></sf-fastpoll>;
59
+ }
60
+ ```
61
+
62
+ ### Vue
63
+
64
+ ```vue
65
+ <template>
66
+ <sf-fastpoll survey-key="your-survey-uuid" completion-message="Thank you!"> </sf-fastpoll>
67
+ </template>
68
+
69
+ <script>
70
+ import '@sensefolks/fastpoll';
71
+ export default { name: 'App' };
72
+ </script>
73
+ ```
74
+
75
+ ### Angular
76
+
77
+ ```typescript
78
+ // app.module.ts
79
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
80
+ import '@sensefolks/fastpoll';
81
+
82
+ @NgModule({
83
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
84
+ })
85
+ export class AppModule {}
86
+ ```
87
+
88
+ ```html
89
+ <!-- component.html -->
90
+ <sf-fastpoll survey-key="your-survey-uuid" completion-message="Thank you!"> </sf-fastpoll>
91
+ ```
92
+
93
+ ## API
94
+
95
+ ### Properties
96
+
97
+ | Property | Attribute | Type | Default | Description |
98
+ | ------------------- | -------------------- | -------- | -------------------------------- | ------------------------------ |
99
+ | `surveyKey` | `survey-key` | `string` | - | UUID of the survey to load |
100
+ | `completionMessage` | `completion-message` | `string` | `'Thank you for your response!'` | Message shown after submission |
101
+
102
+ ### CSS Parts
103
+
104
+ Style the component using CSS Parts:
105
+
106
+ ```css
107
+ sf-fastpoll::part(survey-container) {
108
+ /* Main container */
109
+ }
110
+ sf-fastpoll::part(heading) {
111
+ /* Step headings */
112
+ }
113
+ sf-fastpoll::part(choices-container) {
114
+ /* Poll options container */
115
+ }
116
+ sf-fastpoll::part(choice-option) {
117
+ /* Individual choice */
118
+ }
119
+ sf-fastpoll::part(button) {
120
+ /* All buttons */
121
+ }
122
+ sf-fastpoll::part(next-button) {
123
+ /* Next/Submit button */
124
+ }
125
+ sf-fastpoll::part(back-button) {
126
+ /* Back button */
127
+ }
128
+ sf-fastpoll::part(error-message) {
129
+ /* Error messages */
130
+ }
131
+ sf-fastpoll::part(form-field) {
132
+ /* Form fields */
133
+ }
134
+ sf-fastpoll::part(form-label) {
135
+ /* Form labels */
136
+ }
137
+ sf-fastpoll::part(input) {
138
+ /* Input elements */
139
+ }
140
+ ```
141
+
142
+ ## Accessibility
143
+
144
+ - Full keyboard navigation (Tab, Arrow keys, Enter/Space)
145
+ - ARIA labels and live regions for screen readers
146
+ - Focus indicators and high contrast mode support
147
+ - Respects `prefers-reduced-motion`
148
+
149
+ ## Development
150
+
151
+ ### Building
152
+
153
+ ```bash
154
+ # Install dependencies
155
+ npm install
156
+
157
+ # Build the component
158
+ npm run build
159
+
160
+ # Build with watch mode (for development)
161
+ npm run build:watch
162
+
163
+ # Start dev server with hot reload
164
+ npm start
165
+ ```
166
+
167
+ ### Testing
168
+
169
+ ```bash
170
+ # Run all tests (unit + e2e)
171
+ npm test
172
+
173
+ # Run unit tests only
174
+ npm run test:unit
175
+
176
+ # Run e2e tests only
177
+ npm run test:e2e
178
+
179
+ # Run tests in watch mode
180
+ npm run test:watch
181
+ ```
182
+
183
+ #### Test Coverage
184
+
185
+ The component includes 146 tests covering:
186
+
187
+ - **Utility functions** (68 tests): UUID validation, error formatting, field configuration, comma-separated list operations
188
+ - **Component spec tests** (65 tests): Initialization, error handling, poll steps, navigation, submission, accessibility, form validation
189
+ - **E2E tests** (13 tests): Rendering, shadow DOM, props, styling, accessibility features
190
+
191
+ ### Publishing to NPM
192
+
193
+ #### Prerequisites
194
+
195
+ 1. Create an NPM account at [npmjs.com](https://www.npmjs.com/signup) if you don't have one
196
+ 2. Ensure you have publish access to the `@sensefolks` organization
197
+
198
+ #### NPM Login
199
+
200
+ ```bash
201
+ # Login to NPM
202
+ npm login
203
+
204
+ # Verify you're logged in
205
+ npm whoami
206
+ ```
207
+
208
+ #### Publishing Workflow
209
+
210
+ ```bash
211
+ # 1. Build the component
212
+ npm run build
213
+
214
+ # 2. Run tests
215
+ npm test
216
+
217
+ # 3. Update version (choose one)
218
+ npm version patch # for bug fixes (1.0.0 -> 1.0.1)
219
+ npm version minor # for new features (1.0.0 -> 1.1.0)
220
+ npm version major # for breaking changes (1.0.0 -> 2.0.0)
221
+
222
+ # 4. Dry run to verify what will be published
223
+ npm publish --dry-run
224
+
225
+ # 5. Publish to NPM
226
+ npm publish
227
+ ```
228
+
229
+ ## Browser Support
230
+
231
+ - Chrome 88+
232
+ - Firefox 85+
233
+ - Safari 14+
234
+ - Edge 88+
235
+ - IE11 (with ES5 build)
236
+
237
+ ## License
238
+
239
+ MIT © [SenseFolks](https://sensefolks.com)