astro-swiper 2.3.0 → 2.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  <div align="center" style="background-color: dark-grey; padding: 1rem;">
2
2
  <a href="https://swiperjs.com" target="_blank"><img width="70" width="auto" src="images/swiper-logo.svg"></a>
3
3
  <a href="https://astro.build/" target="_blank"><img height="68" width="auto" src="images/astro-logo.svg"></a>
4
-
4
+
5
5
  # Astro Swiper
6
6
 
7
7
  > Astro Swiper - native component for [Swiper](https://github.com/nolimits4web/swiper).
@@ -74,7 +74,7 @@ You can also look at how others are using `astro-swiper` in public github repo:
74
74
  - the famous astro template [astroplate](https://github.com/zeon-studio/astroplate) in the
75
75
  [testominial section](https://zeon.studio/preview?project=astroplate)
76
76
  (cf. **_What Users Are Saying About Astroplate_**):
77
- autoplay, pagination and breakpoints are used
77
+ autoplay, pagination and breakpoints are used.
78
78
  - the popular astro template [pinwheel-astro](https://github.com/themefisher/pinwheel-astro)
79
79
  is using `astro-swiper` in several places:
80
80
  [testimonial section](https://pinwheel-astro.vercel.app/),
@@ -91,14 +91,18 @@ You can also look at how others are using `astro-swiper` in public github repo:
91
91
  [main page](https://tf-bigspring-light-astro.vercel.app/) with customized pagination.
92
92
  - [Women Techmakers organized by GDG Madrid](https://github.com/wtmgdgmadrid/wtmgdgmadrid.github.io)
93
93
  is using pagination and autoplay at different places in their
94
- [page](http://wtmgdgmadrid.github.io/)
94
+ [page](http://wtmgdgmadrid.github.io/).
95
95
  - [kando-menu](https://github.com/make-42/kando-menu.github.io) is using `astro-swiper`
96
- with pagination, card effect, and coverflow effect as displayed in [kando.menu](https://kando.menu/)
97
- - [astroimagej](https://github.com/AstroImageJ/astroimagej) is using pagination with progress bar
96
+ with pagination, card effect, and coverflow effect as displayed in [kando.menu](https://kando.menu/).
97
+ - [astroimagej](https://github.com/AstroImageJ/astroimagej) is using pagination with progress bar.
98
+ - [rustdesk.com](https://github.com/rustdesk/doc.rustdesk.com) makes use of `<SwiperLazyPreloader/>`
99
+ to add a preloader element.
98
100
  - ... and many others such as
99
101
  [Cinerama](https://github.com/RaiderMr3003/Cinerama) and
100
102
  [pfm-landing-page](https://github.com/RichardAgain/pfm-landing-page)
101
- using `astro-swiper` in the hero section
103
+ using `astro-swiper` in the hero section,
104
+ [folex-lite-astro](https://github.com/getastrothemes/folex-lite-astro)
105
+ using it in the portfolio page,...
102
106
 
103
107
  ## Help needed?
104
108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-swiper",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Astro component for swiper, dedicated to slider / carousel / photo swiper / slide, including thumbnails",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -18,7 +18,7 @@
18
18
  "bugs": {
19
19
  "url": "https://github.com/pascal-brand38/astro-swiper/issues"
20
20
  },
21
- "homepage": "https://github.com/pascal-brand38/astro-swiper",
21
+ "homepage": "https://pascal-brand38.github.io/astro-dev/packages/astro-swiper/",
22
22
  "exports": {
23
23
  ".": {
24
24
  "types": "./index.ts",
@@ -35,9 +35,9 @@
35
35
  },
36
36
  "packageManager": "pnpm@10.30.3",
37
37
  "devDependencies": {
38
- "@biomejs/biome": "^2.4.4",
39
- "@types/node": "^24.11.0",
40
- "astro": "^5.18.0",
38
+ "@biomejs/biome": "^2.4.9",
39
+ "@types/node": "^24.12.0",
40
+ "astro": "^5.18.1",
41
41
  "prettier": "^3.8.1",
42
42
  "prettier-plugin-astro": "^0.14.1",
43
43
  "typescript": "^5.9.3"
@@ -58,6 +58,6 @@
58
58
  "thumbnail"
59
59
  ],
60
60
  "dependencies": {
61
- "swiper": "^12.1.2"
61
+ "swiper": "^12.1.3"
62
62
  }
63
63
  }
@@ -9,20 +9,40 @@ type Props = AstroSwiperType;
9
9
 
10
10
  const {
11
11
  options = {},
12
- uniqueClass = `astro-swiper-${Math.random().toString(36).slice(2, 11)}`,
12
+ uniqueClass: removedUniqueClass, // remove it once deprecated uniqueClass prop is removed
13
13
  class: className = null,
14
- linkToThumbUniqueClass = null,
14
+ linkToThumbUniqueClass: removedLinkToThumbUniqueClass, // remove it once deprecated uniqueClass prop is removed
15
15
  addDefaultClass = true,
16
- useCustomElement = true,
17
16
  ...props
18
17
  } = Astro.props;
18
+
19
+ // check for deprecated useCustomElement prop and set the options.astro.useCustomElement
20
+ // option accordingly if not set, to keep legacy
21
+ if (options?.astro?.useCustomElement === undefined && Astro.props.useCustomElement === false) {
22
+ if (!options.astro) {
23
+ options.astro = {};
24
+ }
25
+ options.astro.useCustomElement = false;
26
+ }
27
+
28
+ // once Astro.props.uniqueClass is removed, uniqueClass can be directly set to the generated unique class name
29
+ const uniqueClass =
30
+ Astro.props.uniqueClass || `astro-swiper-${Math.random().toString(36).slice(2, 11)}`;
31
+
32
+ // check for deprecated useCustomElement prop and set the options.astro.useCustomElement
33
+ // option accordingly if not set, to keep legacy
34
+ if (options?.astro?.thumbSwiperUniqueSelector === undefined && Astro.props.linkToThumbUniqueClass) {
35
+ if (!options.astro) {
36
+ options.astro = {};
37
+ }
38
+ options.astro.thumbSwiperUniqueSelector = `.${Astro.props.linkToThumbUniqueClass}`;
39
+ }
19
40
  ---
20
41
 
21
42
  <!-- Slider main container -->{
22
- useCustomElement ? (
43
+ options?.astro?.useCustomElement !== false ? (
23
44
  <astro-swiper
24
45
  data-options={JSON.stringify(options)}
25
- data-linktothumbuniqueclass={linkToThumbUniqueClass}
26
46
  data-uniqueclass={uniqueClass}
27
47
  class:list={[addDefaultClass ? 'swiper' : null, uniqueClass, className]}
28
48
  {...props}
@@ -31,11 +51,7 @@ const {
31
51
  </astro-swiper>
32
52
  ) : (
33
53
  <div class:list={[addDefaultClass ? 'swiper' : null, uniqueClass, className]} {...props}>
34
- <astro-swiper
35
- data-options={JSON.stringify(options)}
36
- data-linktothumbuniqueclass={linkToThumbUniqueClass}
37
- data-uniqueclass={uniqueClass}
38
- />
54
+ <astro-swiper data-options={JSON.stringify(options)} data-uniqueclass={uniqueClass} />
39
55
  <slot />
40
56
  </div>
41
57
  )
@@ -43,47 +59,62 @@ const {
43
59
 
44
60
  <script>
45
61
  import Swiper from 'swiper/bundle';
46
- import type { AstroSwiperOptions } from 'astro-swiper';
62
+ import { getSwiperFromUniqueSelector } from '../index';
63
+ import type { AstroSwiperOptions, selectorStringType } from '../index';
47
64
 
48
- /** contains the swiper json object once created, for each uniqueClass */
49
- const _useSwiper: { [uniqueClass: string]: Swiper } = {};
65
+ const getSwiperFromUniqueSelectorOptions = {
66
+ // to avoid warning when the swiper is not initialized yet, but will be in the future
67
+ // (for example when using thumbnails, the main swiper is created before the thumbnail
68
+ // swiper, so it is normal that it is not found at this moment)
69
+ mayBeUndefined: true,
70
+ };
50
71
 
51
- /** contains the option used when creating the swiper object for each uniqueClass */
52
- const _useOptions: { [uniqueClass: string]: AstroSwiperOptions } = {};
72
+ /** contains the option used when creating the swiper object for each uniqueSelector */
73
+ const _useOptions: { [uniqueSelector: string]: AstroSwiperOptions } = {};
53
74
 
54
- /** contains all the uniqueClass that have their swiper object delayed because the
75
+ /** contains all the uniqueSelector that have their swiper object delayed because the
55
76
  * related thumbnail swiper is not created yet
56
- * For each uniqueClass, is equal to the thumbnail slider uniqueClass
77
+ * For each uniqueSelector, is equal to the thumbnail slider uniqueSelector
57
78
  */
58
- const _useDelaySwiper: { [uniqueClass: string]: string } = {};
79
+ const _useDelaySwiper: { [uniqueSelector: string]: string } = {};
59
80
 
60
81
  // Init swipers that have delayed initialization because the thumbnail swiper is not created yet
61
82
  // If this is now the case, then it is intialized, as well as its observer if needed
62
83
  function _initDelayedSwipers() {
63
- Object.keys(_useDelaySwiper).forEach((delayedClass) => {
64
- // swiper of uniqueClass "delayedClass" is delayed because it needs the swiper of uniqueClass
65
- // "delayedThumbClass" to be created first
66
- const delayedThumbClass = _useDelaySwiper[delayedClass];
67
- if (_useSwiper[delayedThumbClass]) {
84
+ Object.keys(_useDelaySwiper).forEach((delayedSelector) => {
85
+ // swiper of uniqueSelector "delayedSelector" is delayed because it needs the swiper of uniqueSelector
86
+ // "delayedThumbSelector" to be created first
87
+ const delayedThumbSelector = _useDelaySwiper[delayedSelector];
88
+ const swiper = getSwiperFromUniqueSelector(
89
+ delayedThumbSelector,
90
+ getSwiperFromUniqueSelectorOptions,
91
+ );
92
+ if (swiper) {
68
93
  // the thumbnail swiper is created, we can create the swiper
69
- delete _useDelaySwiper[delayedClass]; // remove it from the delayed list
70
- const delayedOptions = _useOptions[delayedClass];
94
+ delete _useDelaySwiper[delayedSelector]; // remove it from the delayed list
95
+ const delayedOptions = _useOptions[delayedSelector];
71
96
  delayedOptions.thumbs = {
72
- swiper: _useSwiper[delayedThumbClass], // add the thumbnail swiper link to the options
97
+ swiper: swiper, // add the thumbnail swiper link to the options
73
98
  ...delayedOptions.thumbs,
74
99
  };
75
100
 
76
- _createSwiperAndObserver(delayedClass, delayedOptions, (document.querySelector(`.${delayedClass}`) as AstroSwiper));
77
- _useOptions[delayedClass] = delayedOptions; // update the options with the thumbnail link
101
+ _createSwiperAndObserver(
102
+ delayedSelector,
103
+ delayedOptions,
104
+ document.querySelector(delayedSelector) as AstroSwiper,
105
+ );
78
106
  }
79
107
  });
80
108
  }
81
109
 
82
110
  // initialize the swiper instance
83
111
  // the autoplay may be stopped manually if it should be started by the observer
84
- function _initSwiper(uniqueClass: string, options: AstroSwiperOptions, element: AstroSwiper) {
85
- const swiper = new Swiper(`.${uniqueClass}`, options);
86
- _useSwiper[uniqueClass] = swiper;
112
+ function _initSwiper(
113
+ uniqueSelector: selectorStringType,
114
+ options: AstroSwiperOptions,
115
+ element: AstroSwiper,
116
+ ) {
117
+ const swiper = new Swiper(uniqueSelector, options);
87
118
  element.astroSwiper = swiper;
88
119
 
89
120
  // stop the autoplay if it will be started by the observer
@@ -95,51 +126,56 @@ const {
95
126
  return swiper;
96
127
  }
97
128
 
98
- function _createObserver(uniqueClass: string, options: AstroSwiperOptions, element: AstroSwiper) {
129
+ function _createObserver(
130
+ uniqueSelector: selectorStringType,
131
+ options: AstroSwiperOptions,
132
+ element: AstroSwiper,
133
+ ) {
99
134
  const observerOptions = options.astro?.intersectionObserver || {};
100
- const observer = new IntersectionObserver(
101
- (entries) => {
102
- if (entries[0].isIntersecting) {
103
- if (observerOptions?.initSwiper && !element.astroSwiper) {
104
- _initSwiper(uniqueClass, options, element);
105
- if (observerOptions?.disconnectOnInit) {
106
- observer.disconnect();
107
- }
135
+ const observer = new IntersectionObserver((entries) => {
136
+ if (entries[0].isIntersecting) {
137
+ if (observerOptions?.initSwiper && !element.astroSwiper) {
138
+ _initSwiper(uniqueSelector, options, element);
139
+ if (observerOptions?.disconnectOnInit) {
140
+ observer.disconnect();
108
141
  }
109
142
  }
143
+ }
110
144
 
111
- const swiper = element?.astroSwiper
112
- if (!swiper) {
113
- return;
114
- }
145
+ const swiper = element?.astroSwiper;
146
+ if (!swiper) {
147
+ return;
148
+ }
115
149
 
116
- if (observerOptions?.controlAutoplay && options.autoplay) {
117
- if (entries[0].isIntersecting) {
118
- swiper.autoplay.start();
119
- } else {
120
- swiper.autoplay.stop();
121
- }
150
+ if (observerOptions?.controlAutoplay && options.autoplay) {
151
+ if (entries[0].isIntersecting) {
152
+ swiper.autoplay.start();
153
+ } else {
154
+ swiper.autoplay.stop();
122
155
  }
123
- },
124
- observerOptions.options,
125
- );
156
+ }
157
+ }, observerOptions.options);
126
158
 
127
159
  observer.observe(element);
128
- window.addEventListener("unload", observer.disconnect);
160
+ window.addEventListener('unload', observer.disconnect);
129
161
  }
130
162
 
131
163
  // create the swiper and the observer
132
164
  // - init the swiper if it should not be done by the observer
133
165
  // - create the observer if required
134
- function _createSwiperAndObserver(uniqueClass: string, options: AstroSwiperOptions, element: AstroSwiper) {
135
- if (!(options.astro?.intersectionObserver?.initSwiper)) {
166
+ function _createSwiperAndObserver(
167
+ uniqueSelector: selectorStringType,
168
+ options: AstroSwiperOptions,
169
+ element: AstroSwiper,
170
+ ) {
171
+ if (!options.astro?.intersectionObserver?.initSwiper) {
136
172
  // create the swiper immediatly
137
- _initSwiper(uniqueClass, options, element);
173
+ _initSwiper(uniqueSelector, options, element);
138
174
  }
139
175
 
140
176
  // create the observer. It may init the swiper
141
177
  if (options.astro?.intersectionObserver) {
142
- _createObserver(uniqueClass, options, element);
178
+ _createObserver(uniqueSelector, options, element);
143
179
  }
144
180
  }
145
181
 
@@ -155,24 +191,32 @@ const {
155
191
  // Read the message from the data attribute.
156
192
  const options: AstroSwiperOptions = JSON.parse(this.dataset.options || '{}');
157
193
  // to have more than 1 swiper in a single page
158
- const uniqueClass = this.dataset.uniqueclass || '';
159
- const linkToThumbUniqueClass = this.dataset.linktothumbuniqueclass || '';
194
+ const uniqueSelector: selectorStringType = `.${this.dataset.uniqueclass}`;
195
+ const thumbSwiperUniqueSelector = options.astro?.thumbSwiperUniqueSelector;
160
196
 
161
197
  // no thumbnail link, or link to thumbnail swiper already created,
162
198
  // the swiper can be created or not immediatly depending on the observer option
163
- if (!linkToThumbUniqueClass || _useSwiper[linkToThumbUniqueClass]) {
164
- if (linkToThumbUniqueClass) {
165
- options.thumbs = { swiper: _useSwiper[linkToThumbUniqueClass], ...options.thumbs };
199
+ if (
200
+ !thumbSwiperUniqueSelector ||
201
+ getSwiperFromUniqueSelector(thumbSwiperUniqueSelector, getSwiperFromUniqueSelectorOptions)
202
+ ) {
203
+ if (thumbSwiperUniqueSelector) {
204
+ options.thumbs = {
205
+ swiper: getSwiperFromUniqueSelector(
206
+ thumbSwiperUniqueSelector,
207
+ getSwiperFromUniqueSelectorOptions,
208
+ ),
209
+ ...options.thumbs,
210
+ };
166
211
  }
167
212
  // create the swiper and the observer if needed
168
- _createSwiperAndObserver(uniqueClass, options, this);
213
+ _createSwiperAndObserver(uniqueSelector, options, this);
169
214
  } else {
170
215
  // cannot create the swiper as the thumbnail swiper not created yet
171
216
  // is delayed until the thumbnail swiper is initialized.
172
- _useDelaySwiper[uniqueClass] = linkToThumbUniqueClass;
217
+ _useDelaySwiper[uniqueSelector] = thumbSwiperUniqueSelector;
218
+ _useOptions[uniqueSelector] = options;
173
219
  }
174
-
175
- _useOptions[uniqueClass] = options;
176
220
  }
177
221
  }
178
222
 
@@ -0,0 +1,19 @@
1
+ ---
2
+ // Copyright (c) Pascal Brand
3
+ // MIT License
4
+
5
+ import 'swiper/css/bundle';
6
+
7
+ import type { HTMLAttributes } from 'astro/types';
8
+
9
+ interface Props extends HTMLAttributes<'div'> {
10
+ /** add the default swiper class, true by default */
11
+ addDefaultClass?: boolean;
12
+ }
13
+
14
+ const { class: className = null, addDefaultClass = true, ...props } = Astro.props;
15
+ ---
16
+
17
+ <div class:list={[addDefaultClass ? 'swiper-lazy-preloader' : null, className]} {...props}>
18
+ <slot />
19
+ </div>
package/src/index.ts CHANGED
@@ -4,10 +4,13 @@
4
4
  import type { Swiper, SwiperOptions } from 'swiper/types';
5
5
  import type { HTMLAttributes } from 'astro/types';
6
6
 
7
+ // type of a string that starts with a dot (class selector) or a hash (id selector), followed by any string
8
+ export type selectorStringType = `.${string}` | `#${string}`;
9
+
7
10
  /** Swiper options for the Astro component.
8
11
  * Basically the same as the original SwiperOptions, but extended
9
12
  * with new capabilities
10
- */
13
+ */
11
14
  export interface AstroSwiperOptions extends SwiperOptions {
12
15
  /** options specific to astro-swiper component */
13
16
  astro?: {
@@ -25,10 +28,26 @@ export interface AstroSwiperOptions extends SwiperOptions {
25
28
  controlAutoplay?: boolean;
26
29
  /** options for the IntersectionObserver */
27
30
  options?: IntersectionObserverInit;
28
- }
29
- /** TODO: uniqueClass and linkToThumbUniqueClass may be part of it */
30
- }
31
- };
31
+ };
32
+
33
+ /** useCustomElement, if true, the component will be rendered as a custom element, otherwise as a div.
34
+ * This option is true by default to keep legacy.
35
+ * It is there to be as close as possible to the original swiper structure, that is a div with class "swiper"
36
+ * and not a custom element.
37
+ * It is also to avoid issues with some swiper modules that are looking for the "swiper" class on the parent
38
+ * element, and not on the custom element.
39
+ */
40
+ useCustomElement?: boolean;
41
+
42
+ /** unique selector of the thumbnail swiper to link with, when using the thumbs module.
43
+ * when a thumbnail swiper is build, this parameter is provided on the main slider
44
+ * (the one with big slides, not the one to track the progress) and equal
45
+ * the unique selector of the thumbnail swiper (the one to track the progress).
46
+ * It is used to link the main swiper with the thumbnail swiper when using the thumbs module.
47
+ */
48
+ thumbSwiperUniqueSelector?: selectorStringType;
49
+ };
50
+ }
32
51
 
33
52
  /** properties passed to the <Swiper> component
34
53
  * It extends a div (that is may have class, style,...), plus other attributes
@@ -40,28 +59,17 @@ export interface AstroSwiperType extends HTMLAttributes<'div'> {
40
59
  */
41
60
  options?: AstroSwiperOptions;
42
61
 
43
- /** unique class to be able to retrieve the swiper instance, if required
44
- * Mandatory on thumbnail for example
45
- * When undefined, an automatic unique class name is provided
46
- */
62
+ /** add the default swiper class, true by default */
63
+ addDefaultClass?: boolean;
64
+
65
+ /** @deprecated is not really usefull anymore. If a specific unique class or id is needed,
66
+ * add it as an id or or a class directly */
47
67
  uniqueClass?: string;
48
68
 
49
- /** a thumbnail slider is build, this parameter is provided on the main slider
50
- * (the one with big slides, not the one to track the progress) and equal
51
- * the unique class of the thumbnail slider
52
- */
69
+ /** @deprecated use astro.options.astro.thumbsSwiperUniqueSelector instead */
53
70
  linkToThumbUniqueClass?: string;
54
71
 
55
- /** add the default swiper class, true by default */
56
- addDefaultClass?: boolean;
57
-
58
- /** useCustomElement, if true, the component will be rendered as a custom element, otherwise as a div.
59
- * This option is true by default to keep legacy.
60
- * It is there to be as close as possible to the original swiper structure, that is a div with class "swiper"
61
- * and not a custom element.
62
- * It is also to avoid issues with some swiper modules that are looking for the "swiper" class on the parent
63
- * element, and not on the custom element.
64
- */
72
+ /** @deprecated use astro.useCustomElement instead */
65
73
  useCustomElement?: boolean;
66
74
  }
67
75
 
@@ -69,6 +77,7 @@ export interface AstroSwiperType extends HTMLAttributes<'div'> {
69
77
  export { default as Swiper } from './components/Swiper.astro';
70
78
  export { default as SwiperButtonNext } from './components/SwiperButtonNext.astro';
71
79
  export { default as SwiperButtonPrev } from './components/SwiperButtonPrev.astro';
80
+ export { default as SwiperLazyPreloader } from './components/SwiperLazyPreloader.astro';
72
81
  export { default as SwiperPagination } from './components/SwiperPagination.astro';
73
82
  export { default as SwiperScrollbar } from './components/SwiperScrollbar.astro';
74
83
  export { default as SwiperSlide } from './components/SwiperSlide.astro';
@@ -90,7 +99,10 @@ export function getSwiperFromUniqueClass(uniqueClass: string): Swiper | undefine
90
99
  * @example const swiper = getSwiperFromUniqueSelector('.my-unique-class')
91
100
  * const swiper = getSwiperFromUniqueSelector('#my-unique-id')
92
101
  */
93
- export function getSwiperFromUniqueSelector(uniqueSelector: string): Swiper | undefined {
102
+ export function getSwiperFromUniqueSelector(
103
+ uniqueSelector: selectorStringType,
104
+ options?: { mayBeUndefined?: boolean },
105
+ ): Swiper | undefined {
94
106
  if (!/^[.#]/.test(uniqueSelector)) {
95
107
  console.warn("Used selector doesn't contain class or ID selector sign");
96
108
  }
@@ -110,10 +122,16 @@ export function getSwiperFromUniqueSelector(uniqueSelector: string): Swiper | un
110
122
  const childElement = element.firstElementChild as AstroSwiper;
111
123
  if (childElement?.astroSwiper) return childElement.astroSwiper;
112
124
 
113
- console.warn(
114
- `astro-swiper: element found with selector "${uniqueSelector}" but no swiper instance found. ` +
115
- `Expected either a custom element with astroSwiper property or a <div/> containing such an element.`,
116
- );
125
+ if (!options?.mayBeUndefined) {
126
+ // set options.mayBeUndefined to true to avoid this warning when the swiper is not initialized yet,
127
+ // but will be in the future:
128
+ // for example when using thumbnails, the main swiper is created before the thumbnail swiper,
129
+ // so it is normal that it is not found at this moment)
130
+ console.warn(
131
+ `astro-swiper: element found with selector "${uniqueSelector}" but no swiper instance found. ` +
132
+ `Expected either a custom element with astroSwiper property or a <div/> containing such an element.`,
133
+ );
134
+ }
117
135
 
118
136
  return undefined;
119
137
  }