@andreyshpigunov/x 0.3.89 → 0.4.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 (65) hide show
  1. package/.github/workflows/publish.yml +1 -1
  2. package/README.md +1 -1
  3. package/assets/css/app.css +1 -0
  4. package/assets/img/github-mark-white.png +0 -0
  5. package/assets/img/github-mark.png +0 -0
  6. package/assets/js/app.js +1 -0
  7. package/cheatsheet.html +427 -0
  8. package/dist/x.css +1 -1
  9. package/dist/x.js +1 -3
  10. package/index.html +34 -38
  11. package/package.json +52 -47
  12. package/postcss.config.cjs +0 -2
  13. package/src/components/x/animate.js +39 -45
  14. package/src/components/x/appear.js +19 -26
  15. package/src/components/x/autocomplete.js +22 -10
  16. package/src/components/x/buttons.css +38 -16
  17. package/src/components/x/colors.css +47 -41
  18. package/src/components/x/debug.css +2 -2
  19. package/src/components/x/device.js +39 -33
  20. package/src/components/x/dropdown.css +2 -3
  21. package/src/components/x/dropdown.js +16 -9
  22. package/src/components/x/flex.css +146 -109
  23. package/src/components/x/flow.css +12 -6
  24. package/src/components/x/form.css +3 -3
  25. package/src/components/x/form.js +12 -9
  26. package/src/components/x/grid.css +78 -42
  27. package/src/components/x/helpers.css +601 -438
  28. package/src/components/x/hover.js +20 -9
  29. package/src/components/x/icons.css +12 -12
  30. package/src/components/x/lazyload.js +17 -8
  31. package/src/components/x/lib.js +15 -1
  32. package/src/components/x/links.css +2 -6
  33. package/src/components/x/loadmore.js +17 -5
  34. package/src/components/x/modal.css +4 -22
  35. package/src/components/x/modal.js +14 -5
  36. package/src/components/x/reset.css +1 -15
  37. package/src/components/x/scroll.css +4 -9
  38. package/src/components/x/scroll.js +14 -1
  39. package/src/components/x/sheets.css +0 -3
  40. package/src/components/x/sheets.js +157 -37
  41. package/src/components/x/slider.css +10 -1
  42. package/src/components/x/slider.js +15 -0
  43. package/src/components/x/space.css +22 -2
  44. package/src/components/x/sticky.css +10 -15
  45. package/src/components/x/sticky.js +21 -4
  46. package/src/components/x/typo.css +14 -40
  47. package/src/css/app.css +92 -93
  48. package/src/css/x.css +191 -213
  49. package/src/js/app.js +9 -9
  50. package/src/js/x.js +37 -41
  51. package/assets/github-mark-white.png +0 -0
  52. package/assets/github-mark.png +0 -0
  53. package/assets/logo-inverse.png +0 -0
  54. package/babel.config.cjs +0 -4
  55. package/dist/app.css +0 -1
  56. package/dist/app.js +0 -1
  57. package/dist/index.html +0 -2182
  58. package/dist/logo.png +0 -0
  59. package/jest.config.mjs +0 -7
  60. package/jsdoc.json +0 -11
  61. package/vite.config.js +0 -31
  62. /package/assets/{alpha.png → img/alpha.png} +0 -0
  63. /package/assets/{apple-touch-icon.png → img/apple-touch-icon.png} +0 -0
  64. /package/assets/{logo.png → img/logo.png} +0 -0
  65. /package/assets/{logo.svg → img/logo.svg} +0 -0
@@ -59,7 +59,7 @@
59
59
  * Attaches event listeners to the document for mouseenter and mouseleave events.
60
60
  * Safe to call multiple times (no-op after first call). SSR-safe: no-op when document is undefined.
61
61
  *
62
- * @method destroy() - Removes document listeners and resets state. Use on SPA unmount / Next.js.
62
+ * @method destroy() - Removes document listeners and resets state. Use on SPA unmount / page change.
63
63
  *
64
64
  * @example
65
65
  * import { hover } from './hover.js';
@@ -85,14 +85,25 @@
85
85
  * - Efficient CSS selector matching with `CSS.escape()` for special characters
86
86
  * - Minimal DOM manipulation (only toggles class on matching elements)
87
87
  *
88
- * Next.js: call hover.init() in useEffect; in cleanup call hover.destroy().
89
- *
90
88
  * @example
91
- * // Next.jslayout or _app
92
- * useEffect(() => {
93
- * hover.init();
94
- * return () => hover.destroy();
95
- * }, []);
89
+ * // Vanilla JS plain HTML
90
+ * // index.html:
91
+ * // <nav>
92
+ * // <a href="/about" x-hover>About</a>
93
+ * // </nav>
94
+ * // <footer>
95
+ * // <a href="/about" x-hover>Learn More</a>
96
+ * // </footer>
97
+ * //
98
+ * // <style>
99
+ * // a.hover { text-decoration: underline; }
100
+ * // </style>
101
+ * //
102
+ * // <script type="module">
103
+ * // import { hover } from './src/components/x/hover.js';
104
+ * // window.addEventListener('DOMContentLoaded', () => hover.init());
105
+ * // window.addEventListener('pagehide', () => hover.destroy());
106
+ * // </script>
96
107
  *
97
108
  * @author Andrey Shpigunov
98
109
  * @version 0.3
@@ -129,7 +140,7 @@ export class Hover {
129
140
  }
130
141
 
131
142
  /**
132
- * Removes document listeners and resets state. Use on SPA unmount or Next.js cleanup.
143
+ * Removes document listeners and resets state. Use on SPA unmount / page change.
133
144
  * SSR-safe: no-op when document is undefined.
134
145
  */
135
146
  destroy() {
@@ -9,7 +9,7 @@ All right reserved.
9
9
 
10
10
  /*
11
11
  .icon - base icon style
12
- .icon--[10-60] step 2 (m,l) - icon size in px
12
+ .icon.s[10-60] step 2 (m,l) - icon size in px
13
13
  */
14
14
 
15
15
  .icon,
@@ -27,30 +27,30 @@ All right reserved.
27
27
  }
28
28
 
29
29
  @for $i from 10 to 60 by 2 {
30
- .icon--$(i),
31
- .icon--$(i) > svg,
32
- .icon--$(i) > img {
30
+ .icon.s$(i),
31
+ .icon.s$(i) > svg,
32
+ .icon.s$(i) > img {
33
33
  width: calc($(i)rem / 10);
34
34
  height: calc($(i)rem / 10);
35
35
  }
36
36
  }
37
37
 
38
- @media (--medium) {
38
+ @media (min-width: 768px) {
39
39
  @for $i from 10 to 60 by 2 {
40
- .m\:icon--$(i),
41
- .m\:icon--$(i) > svg,
42
- .m\:icon--$(i) > img {
40
+ .m\:icon.s$(i),
41
+ .m\:icon.s$(i) > svg,
42
+ .m\:icon.s$(i) > img {
43
43
  width: calc($(i)rem / 10);
44
44
  height: calc($(i)rem / 10);
45
45
  }
46
46
  }
47
47
  }
48
48
 
49
- @media (--large) {
49
+ @media (min-width: 1024px) {
50
50
  @for $i from 10 to 60 by 2 {
51
- .l\:icon--$(i),
52
- .l\:icon--$(i) > svg,
53
- .l\:icon--$(i) > img {
51
+ .l\:icon.s$(i),
52
+ .l\:icon.s$(i) > svg,
53
+ .l\:icon.s$(i) > img {
54
54
  width: calc($(i)rem / 10);
55
55
  height: calc($(i)rem / 10);
56
56
  }
@@ -89,14 +89,23 @@
89
89
  * - Use `data-srcset` for responsive images to reduce bandwidth
90
90
  * - Images are automatically unobserved after loading to reduce overhead
91
91
  *
92
- * Next.js: call lazyload.init() in useEffect; in cleanup call lazyload.destroy().
93
- *
94
92
  * @example
95
- * // Next.jslayout or _app
96
- * useEffect(() => {
97
- * lazyload.init();
98
- * return () => lazyload.destroy();
99
- * }, []);
93
+ * // Vanilla JS plain HTML
94
+ * // index.html:
95
+ * // <img
96
+ * // x-lazyload
97
+ * // width="800"
98
+ * // height="450"
99
+ * // src="placeholder.jpg"
100
+ * // data-src="image.jpg"
101
+ * // alt="Lazy image"
102
+ * // />
103
+ * //
104
+ * // <script type="module">
105
+ * // import { lazyload } from './src/components/x/lazyload.js';
106
+ * // window.addEventListener('DOMContentLoaded', () => lazyload.init());
107
+ * // window.addEventListener('pagehide', () => lazyload.destroy());
108
+ * // </script>
100
109
  *
101
110
  * @author Andrey Shpigunov
102
111
  * @version 0.2
@@ -149,7 +158,7 @@ class Lazyload {
149
158
  }
150
159
 
151
160
  /**
152
- * Disconnects observer and clears reference. Use on SPA unmount or Next.js cleanup.
161
+ * Disconnects observer and clears reference. Use on SPA unmount / page change.
153
162
  * SSR-safe: no-op when window is undefined.
154
163
  */
155
164
  destroy() {
@@ -156,7 +156,21 @@
156
156
  * - Implement Content Security Policy (CSP) headers
157
157
  * - Consider using a proper HTML sanitization library for complex cases
158
158
  *
159
- * Next.js: DOM methods no-op or return safe defaults when document/window undefined. Use lib on client (e.g. useEffect).
159
+ * SSR-safe: DOM methods no-op or return safe defaults when document/window undefined.
160
+ *
161
+ * @example
162
+ * // Vanilla JS — plain HTML
163
+ * // index.html:
164
+ * // <p id="out"></p>
165
+ * // <div x-render="'Hello from x-render'"></div>
166
+ * //
167
+ * // <script type="module">
168
+ * // import { lib } from './src/components/x/lib.js';
169
+ * // window.addEventListener('DOMContentLoaded', () => {
170
+ * // const el = lib.qs('#out');
171
+ * // if (el) el.textContent = lib.price(1234.56);
172
+ * // });
173
+ * // </script>
160
174
  *
161
175
  * @author Andrey Shpigunov
162
176
  * @version 0.3
@@ -9,7 +9,6 @@ All right reserved.
9
9
 
10
10
  /* !- Links styles */
11
11
 
12
-
13
12
  /*
14
13
  a
15
14
  a.link
@@ -22,7 +21,6 @@ All right reserved.
22
21
  a.link--wavy
23
22
  */
24
23
 
25
-
26
24
  :root {
27
25
  --link-color: #0060cc;
28
26
  --link-decoration-line: underline;
@@ -43,7 +41,6 @@ All right reserved.
43
41
  */
44
42
  }
45
43
 
46
-
47
44
  a {
48
45
 
49
46
  &[role=button] {
@@ -83,7 +80,6 @@ a {
83
80
  text-decoration-color: var(--link-decoration-color-hover, currentcolor);
84
81
  }
85
82
 
86
-
87
83
  /* Link predefined styles */
88
84
 
89
85
  &.link--noline {
@@ -109,6 +105,6 @@ a {
109
105
  a[href^="tel"],
110
106
  a[href^="tel"]:hover,
111
107
  a[href^="tel"]:active {
112
- color: inherit !important;
113
- text-decoration: inherit !important;
108
+ color: inherit;
109
+ text-decoration: inherit;
114
110
  }
@@ -120,14 +120,26 @@
120
120
  * - Handle errors gracefully in callback functions
121
121
  * - Consider rate limiting for API calls
122
122
  *
123
- * Next.js: call init() in useEffect; call destroy() in cleanup (e.g. on unmount).
124
123
  * SSR-safe: init/destroy no-op when window/document is undefined.
125
124
  *
126
125
  * @example
127
- * useEffect(() => {
128
- * loadmore.init();
129
- * return () => loadmore.destroy();
130
- * }, []);
126
+ * // Vanilla JS — plain HTML
127
+ * // index.html:
128
+ * // <div class="items"></div>
129
+ * // <div x-loadmore='{"functionName": "loadMoreData"}'></div>
130
+ * //
131
+ * // <script type="module">
132
+ * // import { loadmore } from './src/components/x/loadmore.js';
133
+ * // window.loadMoreData = async (page) => {
134
+ * // const r = await fetch(`/api/items?page=${page}`);
135
+ * // const items = await r.json();
136
+ * // const el = document.querySelector('.items');
137
+ * // items.forEach((item) => el.appendChild(Object.assign(document.createElement('div'), { textContent: item.name })));
138
+ * // return items.length > 0;
139
+ * // };
140
+ * // window.addEventListener('DOMContentLoaded', () => loadmore.init());
141
+ * // window.addEventListener('pagehide', () => loadmore.destroy());
142
+ * // </script>
131
143
  *
132
144
  * @author Andrey Shpigunov
133
145
  * @version 0.3
@@ -7,9 +7,6 @@ All right reserved.
7
7
  ----------------------------------------*/
8
8
 
9
9
 
10
- /* !- Modal windows */
11
-
12
-
13
10
  :root {
14
11
  --modal-margin-top: 3vw;
15
12
  --modal-margin-bottom: 3vw;
@@ -31,18 +28,15 @@ All right reserved.
31
28
  --modal-overlay-background-color: #00000066;
32
29
  }
33
30
 
34
-
35
31
  html.modal--active body {
36
32
  overflow: hidden;
37
33
  }
38
34
 
39
-
40
35
  /* !- Modal window content */
41
36
  [x-modal] {
42
37
  display: none;
43
38
  }
44
39
 
45
-
46
40
  /* !- Modal wrapper */
47
41
  .modal {
48
42
  position: fixed;
@@ -71,7 +65,6 @@ html.modal--active body {
71
65
  .modal--z9 { z-index: 10009 }
72
66
  .modal--z10 { z-index: 10010 }
73
67
 
74
-
75
68
  /* !- Overlay */
76
69
  .modal-overlay {
77
70
  position: absolute;
@@ -83,7 +76,6 @@ html.modal--active body {
83
76
  background-color: var(--modal-overlay-background-color);
84
77
  }
85
78
 
86
-
87
79
  /* ! -Main structure */
88
80
  .modal-outer {
89
81
  position: relative;
@@ -104,7 +96,6 @@ html.modal--active body {
104
96
  min-height: 100%;
105
97
  }
106
98
 
107
-
108
99
  /* !- Modal window */
109
100
  .modal-window {
110
101
  position: relative;
@@ -122,7 +113,6 @@ html.modal--active body {
122
113
  border-radius: var(--modal-border-radius);
123
114
  }
124
115
 
125
-
126
116
  /* !- Close cross */
127
117
  .modal-rail {
128
118
  position: absolute;
@@ -187,11 +177,10 @@ html.modal--active body {
187
177
  }
188
178
  }
189
179
 
190
-
191
- /* !- Effects
192
- https://github.com/codrops/ModalWindowEffects */
193
-
194
-
180
+ /*
181
+ !- Effects
182
+ https://github.com/codrops/ModalWindowEffects
183
+ */
195
184
  .modal {
196
185
  & .modal-overlay {
197
186
  will-change: opacity;
@@ -203,7 +192,6 @@ html.modal--active body {
203
192
  }
204
193
  }
205
194
 
206
-
207
195
  /* !- Default */
208
196
  .modal {
209
197
  & .modal-window {
@@ -219,7 +207,6 @@ html.modal--active body {
219
207
  }
220
208
  }
221
209
 
222
-
223
210
  /* !- Zoom in */
224
211
  .modal--effect-in {
225
212
  & .modal-window {
@@ -230,7 +217,6 @@ html.modal--active body {
230
217
  }
231
218
  }
232
219
 
233
-
234
220
  /* !- Zoom out */
235
221
  .modal--effect-out {
236
222
  & .modal-window {
@@ -241,7 +227,6 @@ html.modal--active body {
241
227
  }
242
228
  }
243
229
 
244
-
245
230
  /* !- Slide up */
246
231
  .modal--effect-up {
247
232
  & .modal-window {
@@ -252,7 +237,6 @@ html.modal--active body {
252
237
  }
253
238
  }
254
239
 
255
-
256
240
  /* !- Slide down */
257
241
  .modal--effect-down {
258
242
  & .modal-window {
@@ -263,7 +247,6 @@ html.modal--active body {
263
247
  }
264
248
  }
265
249
 
266
-
267
250
  /* !- Slide from left */
268
251
  .modal--effect-left {
269
252
  & .modal-window {
@@ -274,7 +257,6 @@ html.modal--active body {
274
257
  }
275
258
  }
276
259
 
277
-
278
260
  /* !- Slide from right */
279
261
  .modal--effect-right {
280
262
  & .modal-window {
@@ -131,14 +131,23 @@
131
131
  * - Test modal behavior with nested modals
132
132
  * - Handle errors in event listeners gracefully
133
133
  *
134
- * Next.js: call modal.init() in useEffect; call modal.destroy() in cleanup (e.g. on unmount).
135
134
  * SSR-safe: init/destroy and DOM methods no-op when document/window is undefined.
136
135
  *
137
136
  * @example
138
- * useEffect(() => {
139
- * modal.init();
140
- * return () => modal.destroy();
141
- * }, []);
137
+ * // Vanilla JS — plain HTML
138
+ * // index.html:
139
+ * // <div x-modal="myModal" class="modal--hash" data-window-class="max800">
140
+ * // <h2>Title</h2>
141
+ * // <p>Content</p>
142
+ * // <button type="button" class="modal-close">Close</button>
143
+ * // </div>
144
+ * // <a href="#myModal" x-modal-open="myModal">Open</a>
145
+ * //
146
+ * // <script type="module">
147
+ * // import { modal } from './src/components/x/modal.js';
148
+ * // window.addEventListener('DOMContentLoaded', () => modal.init());
149
+ * // window.addEventListener('pagehide', () => modal.destroy());
150
+ * // </script>
142
151
  *
143
152
  * @author Andrey Shpigunov
144
153
  * @version 0.3
@@ -14,12 +14,11 @@ All right reserved.
14
14
  ---------------------------
15
15
  | s | m | l | xl | - main breakpoints
16
16
  ---------------------------
17
- | 640 | 768 | 1024 | 1440 | - min viewport size
17
+ | 640 | 768 | 1024 | 1280 | - min viewport size
18
18
  ---------------------------
19
19
  All queries are mobile first.
20
20
  */
21
21
 
22
-
23
22
  :root {
24
23
  /* Base variables */
25
24
  --font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
@@ -46,19 +45,6 @@ All right reserved.
46
45
  --space-10: 6.4rem;
47
46
  }
48
47
 
49
-
50
- /* PostCSS custom media */
51
- /* https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media */
52
-
53
- /* Breakpoints */
54
- @custom-media --small (min-width: 640px); /* Small screen [s] */
55
- @custom-media --medium (min-width: 768px); /* Medium screen [m] */
56
- @custom-media --large (min-width: 1024px); /* Large screen [l] */
57
- @custom-media --xlarge (min-width: 1440px); /* XLarge screen [xl] */
58
- /* Dark mode */
59
- @custom-media --dark (prefers-color-scheme: dark);
60
-
61
-
62
48
  html, body, main, div, span, object, iframe,
63
49
  h1, h2, h3, h4, h5, h6, p, blockquote, pre,
64
50
  a, abbr, acronym, address, big, cite, code,
@@ -1,15 +1,12 @@
1
1
  /*----------------------------------------
2
2
  scroll.css / x
3
- Scroll
3
+ Scroll — native CSS horizontal scroller
4
4
 
5
5
  Created by Andrey Shpigunov at 20.03.2025
6
6
  All right reserved.
7
7
  ----------------------------------------*/
8
8
 
9
9
 
10
- /* !- Scroll - native CSS horizontal scroller */
11
-
12
-
13
10
  /*
14
11
  .scroll
15
12
  .scroll--contain
@@ -17,14 +14,12 @@ All right reserved.
17
14
  .scroll--x-mandatory
18
15
  */
19
16
 
20
-
21
17
  :root {
22
18
  --scroll-scrollbar-size: 6px;
23
19
  --scroll-scrollbar-thumb-background: #00000011;
24
20
  --scroll-scrollbar-thumb-background-hover: #00000022;
25
21
  }
26
22
 
27
-
28
23
  .scroll {
29
24
  flex-wrap: nowrap;
30
25
  overflow-x: scroll;
@@ -34,7 +29,7 @@ All right reserved.
34
29
  -ms-overflow-style: none;
35
30
  /* Hide scrollbar in Firefox */
36
31
  scrollbar-width: none;
37
- touch-action: pan-x !important;
32
+ touch-action: pan-x;
38
33
 
39
34
  &::-webkit-scrollbar {
40
35
  width: var(--scroll-scrollbar-size);
@@ -69,13 +64,13 @@ All right reserved.
69
64
  .scroll.scroll--x {
70
65
  overflow-x: scroll;
71
66
  overflow-y: hidden;
72
- touch-action: pan-x !important;
67
+ touch-action: pan-x;
73
68
  }
74
69
 
75
70
  .scroll.scroll--y {
76
71
  overflow-x: hidden;
77
72
  overflow-y: scroll;
78
- touch-action: pan-y !important;
73
+ touch-action: pan-y;
79
74
  }
80
75
 
81
76
  .scroll--contain {
@@ -47,7 +47,7 @@
47
47
  * @example
48
48
  * scroll.destroy();
49
49
  *
50
- * @method scrollTo(target | params) - Scrolls to element by id, selector, or element with options.
50
+ * @method scrollTo - Scrolls to element by id, selector, or options object.
51
51
  * @param {string|HTMLElement|Object} params - Target element, selector, or options object.
52
52
  * Options format:
53
53
  * {
@@ -98,6 +98,19 @@
98
98
  * - Handle errors gracefully in scroll callbacks
99
99
  * - Consider accessibility (keyboard navigation, focus management)
100
100
  *
101
+ * @example
102
+ * // Vanilla JS — plain HTML
103
+ * // index.html:
104
+ * // <a x-scrollto='{"target":"#section","offset":80,"hash":true,"classActive":"active"}'>Go</a>
105
+ * // <div style="height: 120vh"></div>
106
+ * // <section id="section" style="height: 50vh; border: 1px solid #ccc;">Target</section>
107
+ * //
108
+ * // <script type="module">
109
+ * // import { scroll } from './src/components/x/scroll.js';
110
+ * // window.addEventListener('DOMContentLoaded', () => scroll.init());
111
+ * // window.addEventListener('pagehide', () => scroll.destroy());
112
+ * // </script>
113
+ *
101
114
  * @author Andrey Shpigunov
102
115
  * @version 0.3
103
116
  * @since 2025-07-18
@@ -7,9 +7,6 @@ All right reserved.
7
7
  ----------------------------------------*/
8
8
 
9
9
 
10
- /* !- Sheets */
11
-
12
-
13
10
  [x-sheet]:not(.active) {
14
11
  display: none;
15
12
  }