@semantic-components/carousel 0.63.0 → 0.65.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/esm2022/index.js +2 -0
- package/esm2022/index.js.map +1 -0
- package/esm2022/lib/components/carousel/carousel-item.js +27 -0
- package/esm2022/lib/components/carousel/carousel-item.js.map +1 -0
- package/esm2022/lib/components/carousel/carousel-next.js +39 -0
- package/esm2022/lib/components/carousel/carousel-next.js.map +1 -0
- package/esm2022/lib/components/carousel/carousel-previous.js +39 -0
- package/esm2022/lib/components/carousel/carousel-previous.js.map +1 -0
- package/esm2022/lib/components/carousel/carousel-track.js +25 -0
- package/esm2022/lib/components/carousel/carousel-track.js.map +1 -0
- package/esm2022/lib/components/carousel/carousel-viewport.js +29 -0
- package/esm2022/lib/components/carousel/carousel-viewport.js.map +1 -0
- package/esm2022/lib/components/carousel/carousel.js +80 -0
- package/esm2022/lib/components/carousel/carousel.js.map +1 -0
- package/{src/lib/components/carousel/index.ts → esm2022/lib/components/carousel/index.js} +1 -6
- package/esm2022/lib/components/carousel/index.js.map +1 -0
- package/esm2022/lib/components/index.js +2 -0
- package/esm2022/lib/components/index.js.map +1 -0
- package/esm2022/semantic-components-carousel.js +5 -0
- package/esm2022/semantic-components-carousel.js.map +1 -0
- package/lib/components/carousel/carousel-item.d.ts +8 -0
- package/lib/components/carousel/carousel-next.d.ts +11 -0
- package/lib/components/carousel/carousel-previous.d.ts +11 -0
- package/lib/components/carousel/carousel-track.d.ts +8 -0
- package/lib/components/carousel/carousel-viewport.d.ts +9 -0
- package/lib/components/carousel/carousel.d.ts +25 -0
- package/lib/components/carousel/index.d.ts +7 -0
- package/package.json +17 -3
- package/semantic-components-carousel.d.ts +5 -0
- package/eslint.config.mjs +0 -48
- package/ng-package.json +0 -7
- package/project.json +0 -28
- package/src/lib/components/carousel/README.md +0 -342
- package/src/lib/components/carousel/carousel-item.ts +0 -27
- package/src/lib/components/carousel/carousel-next.ts +0 -46
- package/src/lib/components/carousel/carousel-previous.ts +0 -46
- package/src/lib/components/carousel/carousel-track.ts +0 -25
- package/src/lib/components/carousel/carousel-viewport.ts +0 -34
- package/src/lib/components/carousel/carousel.ts +0 -104
- package/tsconfig.json +0 -28
- package/tsconfig.lib.json +0 -12
- package/tsconfig.lib.prod.json +0 -7
- /package/{src/index.ts → index.d.ts} +0 -0
- /package/{src/lib/components/index.ts → lib/components/index.d.ts} +0 -0
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
# Carousel
|
|
2
|
-
|
|
3
|
-
A carousel component built with Embla Carousel, featuring smooth animations, touch/swipe gestures, and plugin support.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```html
|
|
8
|
-
<div sc-carousel class="w-full max-w-xs">
|
|
9
|
-
<div sc-carousel-viewport>
|
|
10
|
-
<div sc-carousel-track>
|
|
11
|
-
<div sc-carousel-item>Slide 1</div>
|
|
12
|
-
<div sc-carousel-item>Slide 2</div>
|
|
13
|
-
<div sc-carousel-item>Slide 3</div>
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
<button sc-carousel-previous>
|
|
17
|
-
<svg si-chevron-left-icon></svg>
|
|
18
|
-
<span class="sr-only">Previous slide</span>
|
|
19
|
-
</button>
|
|
20
|
-
<button sc-carousel-next>
|
|
21
|
-
<svg si-chevron-right-icon></svg>
|
|
22
|
-
<span class="sr-only">Next slide</span>
|
|
23
|
-
</button>
|
|
24
|
-
</div>
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Architecture
|
|
28
|
-
|
|
29
|
-
The carousel uses a three-layer structure inspired by Embla Carousel's architecture:
|
|
30
|
-
|
|
31
|
-
1. **ScCarousel** - The outer container that manages state, API, and keyboard navigation
|
|
32
|
-
2. **ScCarouselViewport** - The overflow container (`overflow-hidden`) that Embla binds to
|
|
33
|
-
3. **ScCarouselTrack** - The flex container that slides within the viewport
|
|
34
|
-
4. **ScCarouselItem** - Individual slides on the track
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
┌─ ScCarousel (relative positioning, manages state)
|
|
38
|
-
│ ├─ ScCarouselViewport (overflow-hidden, viewport for Embla)
|
|
39
|
-
│ │ └─ ScCarouselTrack (flex container, the sliding track)
|
|
40
|
-
│ │ ├─ ScCarouselItem (slide 1)
|
|
41
|
-
│ │ ├─ ScCarouselItem (slide 2)
|
|
42
|
-
│ │ └─ ScCarouselItem (slide 3)
|
|
43
|
-
│ ├─ ScCarouselPrevious (navigation button)
|
|
44
|
-
│ └─ ScCarouselNext (navigation button)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Components
|
|
48
|
-
|
|
49
|
-
### ScCarousel
|
|
50
|
-
|
|
51
|
-
Main carousel container that manages state and keyboard navigation.
|
|
52
|
-
|
|
53
|
-
**Selector:** `div[sc-carousel]`
|
|
54
|
-
|
|
55
|
-
**Inputs:**
|
|
56
|
-
|
|
57
|
-
| Input | Type | Default | Description |
|
|
58
|
-
| ------------- | ---------------------------- | -------------- | ------------------------------ |
|
|
59
|
-
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Scroll direction |
|
|
60
|
-
| `options` | `ScCarouselOptions` | `{}` | Embla carousel options |
|
|
61
|
-
| `plugins` | `ScCarouselPlugin[]` | `[]` | Embla plugins (e.g., autoplay) |
|
|
62
|
-
| `class` | `string` | `''` | Additional CSS classes |
|
|
63
|
-
|
|
64
|
-
### ScCarouselViewport
|
|
65
|
-
|
|
66
|
-
The overflow container (viewport) that Embla binds to. This provides the visible window for the carousel.
|
|
67
|
-
|
|
68
|
-
**Selector:** `div[sc-carousel-viewport]`
|
|
69
|
-
|
|
70
|
-
**Inputs:**
|
|
71
|
-
|
|
72
|
-
| Input | Type | Default | Description |
|
|
73
|
-
| ------- | -------- | ------- | ---------------------- |
|
|
74
|
-
| `class` | `string` | `''` | Additional CSS classes |
|
|
75
|
-
|
|
76
|
-
### ScCarouselTrack
|
|
77
|
-
|
|
78
|
-
The flex container (track) that moves within the viewport and holds carousel items. This is the element that slides left/right or up/down.
|
|
79
|
-
|
|
80
|
-
**Selector:** `div[sc-carousel-track]`
|
|
81
|
-
|
|
82
|
-
**Inputs:**
|
|
83
|
-
|
|
84
|
-
| Input | Type | Default | Description |
|
|
85
|
-
| ------- | -------- | ------- | ---------------------- |
|
|
86
|
-
| `class` | `string` | `''` | Additional CSS classes |
|
|
87
|
-
|
|
88
|
-
### ScCarouselItem
|
|
89
|
-
|
|
90
|
-
Individual carousel slide.
|
|
91
|
-
|
|
92
|
-
**Selector:** `div[sc-carousel-item]`
|
|
93
|
-
|
|
94
|
-
**Inputs:**
|
|
95
|
-
|
|
96
|
-
| Input | Type | Default | Description |
|
|
97
|
-
| ------- | -------- | ------- | ---------------------- |
|
|
98
|
-
| `class` | `string` | `''` | Additional CSS classes |
|
|
99
|
-
|
|
100
|
-
### ScCarouselPrevious
|
|
101
|
-
|
|
102
|
-
Previous slide navigation button. Content must be provided by the user (icon + screen reader text).
|
|
103
|
-
|
|
104
|
-
**Selector:** `button[sc-carousel-previous]`
|
|
105
|
-
|
|
106
|
-
**Inputs:**
|
|
107
|
-
|
|
108
|
-
| Input | Type | Default | Description |
|
|
109
|
-
| ------- | -------- | ------- | ---------------------- |
|
|
110
|
-
| `class` | `string` | `''` | Additional CSS classes |
|
|
111
|
-
|
|
112
|
-
**Example:**
|
|
113
|
-
|
|
114
|
-
```html
|
|
115
|
-
<button sc-carousel-previous>
|
|
116
|
-
<svg si-chevron-left-icon></svg>
|
|
117
|
-
<span class="sr-only">Previous slide</span>
|
|
118
|
-
</button>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### ScCarouselNext
|
|
122
|
-
|
|
123
|
-
Next slide navigation button. Content must be provided by the user (icon + screen reader text).
|
|
124
|
-
|
|
125
|
-
**Selector:** `button[sc-carousel-next]`
|
|
126
|
-
|
|
127
|
-
**Inputs:**
|
|
128
|
-
|
|
129
|
-
| Input | Type | Default | Description |
|
|
130
|
-
| ------- | -------- | ------- | ---------------------- |
|
|
131
|
-
| `class` | `string` | `''` | Additional CSS classes |
|
|
132
|
-
|
|
133
|
-
**Example:**
|
|
134
|
-
|
|
135
|
-
```html
|
|
136
|
-
<button sc-carousel-next>
|
|
137
|
-
<svg si-chevron-right-icon></svg>
|
|
138
|
-
<span class="sr-only">Next slide</span>
|
|
139
|
-
</button>
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Examples
|
|
143
|
-
|
|
144
|
-
### Default
|
|
145
|
-
|
|
146
|
-
```html
|
|
147
|
-
<div sc-carousel class="w-full max-w-xs">
|
|
148
|
-
<div sc-carousel-viewport>
|
|
149
|
-
<div sc-carousel-track>
|
|
150
|
-
@for (i of [1, 2, 3, 4, 5]; track i) {
|
|
151
|
-
<div sc-carousel-item>
|
|
152
|
-
<div class="p-1">
|
|
153
|
-
<div class="flex aspect-square items-center justify-center rounded-lg border">{{ i }}</div>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
}
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
<button sc-carousel-previous>
|
|
160
|
-
<svg si-chevron-left-icon></svg>
|
|
161
|
-
<span class="sr-only">Previous slide</span>
|
|
162
|
-
</button>
|
|
163
|
-
<button sc-carousel-next>
|
|
164
|
-
<svg si-chevron-right-icon></svg>
|
|
165
|
-
<span class="sr-only">Next slide</span>
|
|
166
|
-
</button>
|
|
167
|
-
</div>
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### With Loop
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
import { Component } from '@angular/core';
|
|
174
|
-
|
|
175
|
-
@Component({
|
|
176
|
-
template: `
|
|
177
|
-
<div sc-carousel [options]="{ loop: true }" class="w-full max-w-xs">
|
|
178
|
-
<div sc-carousel-viewport>
|
|
179
|
-
<div sc-carousel-track>
|
|
180
|
-
@for (i of [1, 2, 3, 4, 5]; track i) {
|
|
181
|
-
<div sc-carousel-item>{{ i }}</div>
|
|
182
|
-
}
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
<button sc-carousel-previous>
|
|
186
|
-
<svg si-chevron-left-icon></svg>
|
|
187
|
-
<span class="sr-only">Previous slide</span>
|
|
188
|
-
</button>
|
|
189
|
-
<button sc-carousel-next>
|
|
190
|
-
<svg si-chevron-right-icon></svg>
|
|
191
|
-
<span class="sr-only">Next slide</span>
|
|
192
|
-
</button>
|
|
193
|
-
</div>
|
|
194
|
-
`,
|
|
195
|
-
})
|
|
196
|
-
export class MyComponent {}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### With Autoplay
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
import { Component } from '@angular/core';
|
|
203
|
-
import Autoplay from 'embla-carousel-autoplay';
|
|
204
|
-
|
|
205
|
-
@Component({
|
|
206
|
-
template: `
|
|
207
|
-
<div sc-carousel [plugins]="plugins" class="w-full max-w-xs">
|
|
208
|
-
<div sc-carousel-viewport>
|
|
209
|
-
<div sc-carousel-track>
|
|
210
|
-
@for (i of items; track i) {
|
|
211
|
-
<div sc-carousel-item>{{ i }}</div>
|
|
212
|
-
}
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
215
|
-
<button sc-carousel-previous>
|
|
216
|
-
<svg si-chevron-left-icon></svg>
|
|
217
|
-
<span class="sr-only">Previous slide</span>
|
|
218
|
-
</button>
|
|
219
|
-
<button sc-carousel-next>
|
|
220
|
-
<svg si-chevron-right-icon></svg>
|
|
221
|
-
<span class="sr-only">Next slide</span>
|
|
222
|
-
</button>
|
|
223
|
-
</div>
|
|
224
|
-
`,
|
|
225
|
-
})
|
|
226
|
-
export class MyComponent {
|
|
227
|
-
plugins = [Autoplay({ delay: 2000, stopOnInteraction: true })];
|
|
228
|
-
items = [1, 2, 3, 4, 5];
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Partial Items (Multiple Visible)
|
|
233
|
-
|
|
234
|
-
```html
|
|
235
|
-
<div sc-carousel [options]="{ align: 'start' }" class="w-full max-w-lg">
|
|
236
|
-
<div sc-carousel-viewport>
|
|
237
|
-
<div sc-carousel-track>
|
|
238
|
-
@for (i of items; track i) {
|
|
239
|
-
<div sc-carousel-item class="basis-1/3">
|
|
240
|
-
<!-- Shows 3 items at a time -->
|
|
241
|
-
</div>
|
|
242
|
-
}
|
|
243
|
-
</div>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### Vertical Orientation
|
|
249
|
-
|
|
250
|
-
```html
|
|
251
|
-
<div sc-carousel orientation="vertical" [options]="{ align: 'start' }" class="w-full max-w-xs">
|
|
252
|
-
<div sc-carousel-viewport>
|
|
253
|
-
<div sc-carousel-track class="-mt-1 h-[270px]">
|
|
254
|
-
@for (i of items; track i) {
|
|
255
|
-
<div sc-carousel-item class="basis-1/2 pt-1">
|
|
256
|
-
<div class="p-1">
|
|
257
|
-
<div class="flex items-center justify-center rounded-lg border bg-card p-6">
|
|
258
|
-
<span class="text-3xl font-semibold">{{ i }}</span>
|
|
259
|
-
</div>
|
|
260
|
-
</div>
|
|
261
|
-
</div>
|
|
262
|
-
}
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
|
-
<button sc-carousel-previous>
|
|
266
|
-
<svg si-chevron-left-icon></svg>
|
|
267
|
-
<span class="sr-only">Previous slide</span>
|
|
268
|
-
</button>
|
|
269
|
-
<button sc-carousel-next>
|
|
270
|
-
<svg si-chevron-right-icon></svg>
|
|
271
|
-
<span class="sr-only">Next slide</span>
|
|
272
|
-
</button>
|
|
273
|
-
</div>
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Features
|
|
277
|
-
|
|
278
|
-
- **Embla Carousel**: Powered by Embla for smooth, physics-based animations
|
|
279
|
-
- **Touch/Swipe**: Native touch and mouse drag support
|
|
280
|
-
- **Horizontal/Vertical**: Supports both orientations
|
|
281
|
-
- **Keyboard Navigation**: Arrow keys for navigation
|
|
282
|
-
- **Plugin System**: Extend with plugins (autoplay, auto-scroll, etc.)
|
|
283
|
-
- **Flexible Options**: Loop, alignment, slide spacing, and more
|
|
284
|
-
- **Responsive**: Flexible sizing with basis classes
|
|
285
|
-
- **Navigation Buttons**: Prev/next with auto-disable at boundaries
|
|
286
|
-
- **Accessible**: ARIA roles and roledescription
|
|
287
|
-
|
|
288
|
-
## Keyboard Navigation
|
|
289
|
-
|
|
290
|
-
- `ArrowLeft`: Navigate to previous slide
|
|
291
|
-
- `ArrowRight`: Navigate to next slide
|
|
292
|
-
|
|
293
|
-
## Accessibility
|
|
294
|
-
|
|
295
|
-
- `role="region"` with `aria-roledescription="carousel"` on container
|
|
296
|
-
- `role="group"` with `aria-roledescription="slide"` on items
|
|
297
|
-
- Screen reader text on navigation buttons
|
|
298
|
-
- Disabled state for navigation at boundaries
|
|
299
|
-
|
|
300
|
-
## Carousel Options
|
|
301
|
-
|
|
302
|
-
The `options` input accepts all [Embla Carousel options](https://www.embla-carousel.com/api/options/):
|
|
303
|
-
|
|
304
|
-
- `align`: `'start' | 'center' | 'end'` - Slide alignment
|
|
305
|
-
- `loop`: `boolean` - Enable infinite loop
|
|
306
|
-
- `skipSnaps`: `boolean` - Skip slides that can't be scrolled to
|
|
307
|
-
- `dragFree`: `boolean` - Enable free scroll without snap points
|
|
308
|
-
- `slidesToScroll`: `number | 'auto'` - Number of slides to scroll
|
|
309
|
-
- `containScroll`: `'trimSnaps' | 'keepSnaps'` - Contain scroll behavior
|
|
310
|
-
- And many more...
|
|
311
|
-
|
|
312
|
-
## Plugins
|
|
313
|
-
|
|
314
|
-
The carousel supports all Embla plugins via the `plugins` input:
|
|
315
|
-
|
|
316
|
-
### Autoplay
|
|
317
|
-
|
|
318
|
-
```bash
|
|
319
|
-
npm install embla-carousel-autoplay
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
import Autoplay from 'embla-carousel-autoplay';
|
|
324
|
-
|
|
325
|
-
export class MyComponent {
|
|
326
|
-
plugins = [
|
|
327
|
-
Autoplay({
|
|
328
|
-
delay: 3000,
|
|
329
|
-
stopOnInteraction: true,
|
|
330
|
-
stopOnMouseEnter: true,
|
|
331
|
-
}),
|
|
332
|
-
];
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Other Available Plugins
|
|
337
|
-
|
|
338
|
-
- **embla-carousel-auto-scroll**: Automatic scrolling
|
|
339
|
-
- **embla-carousel-class-names**: Dynamic class names on slides
|
|
340
|
-
- **embla-carousel-fade**: Fade transition effect
|
|
341
|
-
|
|
342
|
-
See [Embla Carousel Plugins](https://www.embla-carousel.com/plugins/) for more.
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { computed, Directive, inject, input } from '@angular/core';
|
|
2
|
-
import { cn } from '@semantic-components/ui';
|
|
3
|
-
import { ScCarousel } from './carousel';
|
|
4
|
-
|
|
5
|
-
@Directive({
|
|
6
|
-
selector: 'div[sc-carousel-item]',
|
|
7
|
-
host: {
|
|
8
|
-
'data-slot': 'carousel-item',
|
|
9
|
-
role: 'group',
|
|
10
|
-
'aria-roledescription': 'slide',
|
|
11
|
-
'[class]': 'class()',
|
|
12
|
-
},
|
|
13
|
-
})
|
|
14
|
-
export class ScCarouselItem {
|
|
15
|
-
private readonly carousel = inject(ScCarousel);
|
|
16
|
-
|
|
17
|
-
readonly classInput = input<string>('', { alias: 'class' });
|
|
18
|
-
|
|
19
|
-
protected readonly class = computed(() => {
|
|
20
|
-
const isHorizontal = this.carousel.orientation() === 'horizontal';
|
|
21
|
-
return cn(
|
|
22
|
-
'min-w-0 shrink-0 grow-0 basis-full',
|
|
23
|
-
isHorizontal ? 'pl-4' : 'pt-4',
|
|
24
|
-
this.classInput(),
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectionStrategy,
|
|
3
|
-
Component,
|
|
4
|
-
computed,
|
|
5
|
-
inject,
|
|
6
|
-
input,
|
|
7
|
-
ViewEncapsulation,
|
|
8
|
-
} from '@angular/core';
|
|
9
|
-
import { buttonVariants, cn, ScButtonVariants } from '@semantic-components/ui';
|
|
10
|
-
import { ScCarousel } from './carousel';
|
|
11
|
-
|
|
12
|
-
@Component({
|
|
13
|
-
selector: 'button[sc-carousel-next]',
|
|
14
|
-
template: `
|
|
15
|
-
<ng-content />
|
|
16
|
-
`,
|
|
17
|
-
host: {
|
|
18
|
-
'data-slot': 'carousel-next',
|
|
19
|
-
type: 'button',
|
|
20
|
-
'[class]': 'class()',
|
|
21
|
-
'[disabled]': '!carousel.canScrollNext()',
|
|
22
|
-
'(click)': 'carousel.scrollNext()',
|
|
23
|
-
},
|
|
24
|
-
encapsulation: ViewEncapsulation.None,
|
|
25
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
26
|
-
})
|
|
27
|
-
export class ScCarouselNext {
|
|
28
|
-
readonly carousel = inject(ScCarousel);
|
|
29
|
-
|
|
30
|
-
readonly classInput = input<string>('', { alias: 'class' });
|
|
31
|
-
|
|
32
|
-
readonly variant = input<ScButtonVariants['variant']>('outline');
|
|
33
|
-
readonly size = input<ScButtonVariants['size']>('icon-sm');
|
|
34
|
-
|
|
35
|
-
protected readonly class = computed(() => {
|
|
36
|
-
const isHorizontal = this.carousel.orientation() === 'horizontal';
|
|
37
|
-
return cn(
|
|
38
|
-
buttonVariants({ variant: this.variant(), size: this.size() }),
|
|
39
|
-
'rounded-full absolute touch-manipulation',
|
|
40
|
-
isHorizontal
|
|
41
|
-
? 'top-1/2 -right-12 -translate-y-1/2'
|
|
42
|
-
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
|
|
43
|
-
this.classInput(),
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectionStrategy,
|
|
3
|
-
Component,
|
|
4
|
-
computed,
|
|
5
|
-
inject,
|
|
6
|
-
input,
|
|
7
|
-
ViewEncapsulation,
|
|
8
|
-
} from '@angular/core';
|
|
9
|
-
import { buttonVariants, cn, ScButtonVariants } from '@semantic-components/ui';
|
|
10
|
-
import { ScCarousel } from './carousel';
|
|
11
|
-
|
|
12
|
-
@Component({
|
|
13
|
-
selector: 'button[sc-carousel-previous]',
|
|
14
|
-
template: `
|
|
15
|
-
<ng-content />
|
|
16
|
-
`,
|
|
17
|
-
host: {
|
|
18
|
-
'data-slot': 'carousel-previous',
|
|
19
|
-
type: 'button',
|
|
20
|
-
'[class]': 'class()',
|
|
21
|
-
'[disabled]': '!carousel.canScrollPrev()',
|
|
22
|
-
'(click)': 'carousel.scrollPrev()',
|
|
23
|
-
},
|
|
24
|
-
encapsulation: ViewEncapsulation.None,
|
|
25
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
26
|
-
})
|
|
27
|
-
export class ScCarouselPrevious {
|
|
28
|
-
readonly carousel = inject(ScCarousel);
|
|
29
|
-
|
|
30
|
-
readonly classInput = input<string>('', { alias: 'class' });
|
|
31
|
-
|
|
32
|
-
readonly variant = input<ScButtonVariants['variant']>('outline');
|
|
33
|
-
readonly size = input<ScButtonVariants['size']>('icon-sm');
|
|
34
|
-
|
|
35
|
-
protected readonly class = computed(() => {
|
|
36
|
-
const isHorizontal = this.carousel.orientation() === 'horizontal';
|
|
37
|
-
return cn(
|
|
38
|
-
buttonVariants({ variant: this.variant(), size: this.size() }),
|
|
39
|
-
'rounded-full absolute touch-manipulation',
|
|
40
|
-
isHorizontal
|
|
41
|
-
? 'top-1/2 -left-12 -translate-y-1/2'
|
|
42
|
-
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
|
|
43
|
-
this.classInput(),
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { computed, Directive, inject, input } from '@angular/core';
|
|
2
|
-
import { cn } from '@semantic-components/ui';
|
|
3
|
-
import { ScCarousel } from './carousel';
|
|
4
|
-
|
|
5
|
-
@Directive({
|
|
6
|
-
selector: 'div[sc-carousel-track]',
|
|
7
|
-
host: {
|
|
8
|
-
'data-slot': 'carousel-track',
|
|
9
|
-
'[class]': 'class()',
|
|
10
|
-
},
|
|
11
|
-
})
|
|
12
|
-
export class ScCarouselTrack {
|
|
13
|
-
private readonly carousel = inject(ScCarousel);
|
|
14
|
-
|
|
15
|
-
readonly classInput = input<string>('', { alias: 'class' });
|
|
16
|
-
|
|
17
|
-
protected readonly class = computed(() => {
|
|
18
|
-
const isHorizontal = this.carousel.orientation() === 'horizontal';
|
|
19
|
-
return cn(
|
|
20
|
-
'flex',
|
|
21
|
-
isHorizontal ? '-ml-4' : '-mt-4 flex-col',
|
|
22
|
-
this.classInput(),
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computed,
|
|
3
|
-
Component,
|
|
4
|
-
ElementRef,
|
|
5
|
-
inject,
|
|
6
|
-
input,
|
|
7
|
-
ViewEncapsulation,
|
|
8
|
-
ChangeDetectionStrategy,
|
|
9
|
-
} from '@angular/core';
|
|
10
|
-
import { cn } from '@semantic-components/ui';
|
|
11
|
-
|
|
12
|
-
@Component({
|
|
13
|
-
selector: 'div[sc-carousel-viewport]',
|
|
14
|
-
template: `
|
|
15
|
-
<ng-content />
|
|
16
|
-
`,
|
|
17
|
-
host: {
|
|
18
|
-
'data-slot': 'carousel-viewport',
|
|
19
|
-
'[class]': 'class()',
|
|
20
|
-
},
|
|
21
|
-
encapsulation: ViewEncapsulation.None,
|
|
22
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
23
|
-
})
|
|
24
|
-
export class ScCarouselViewport {
|
|
25
|
-
private readonly elementRef = inject(ElementRef<HTMLElement>);
|
|
26
|
-
|
|
27
|
-
readonly classInput = input<string>('', { alias: 'class' });
|
|
28
|
-
|
|
29
|
-
readonly viewportElement = computed(() => this.elementRef.nativeElement);
|
|
30
|
-
|
|
31
|
-
protected readonly class = computed(() =>
|
|
32
|
-
cn('overflow-hidden', this.classInput()),
|
|
33
|
-
);
|
|
34
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectionStrategy,
|
|
3
|
-
Component,
|
|
4
|
-
computed,
|
|
5
|
-
contentChild,
|
|
6
|
-
input,
|
|
7
|
-
signal,
|
|
8
|
-
ViewEncapsulation,
|
|
9
|
-
DestroyRef,
|
|
10
|
-
inject,
|
|
11
|
-
afterNextRender,
|
|
12
|
-
} from '@angular/core';
|
|
13
|
-
import EmblaCarousel, {
|
|
14
|
-
type EmblaCarouselType,
|
|
15
|
-
EmblaOptionsType,
|
|
16
|
-
EmblaPluginType,
|
|
17
|
-
} from 'embla-carousel';
|
|
18
|
-
import { cn } from '@semantic-components/ui';
|
|
19
|
-
import { ScCarouselViewport } from './carousel-viewport';
|
|
20
|
-
|
|
21
|
-
export type ScCarouselOrientation = 'horizontal' | 'vertical';
|
|
22
|
-
export type ScCarouselApi = EmblaCarouselType;
|
|
23
|
-
export type ScCarouselOptions = EmblaOptionsType;
|
|
24
|
-
export type ScCarouselPlugin = EmblaPluginType;
|
|
25
|
-
|
|
26
|
-
@Component({
|
|
27
|
-
selector: 'div[sc-carousel]',
|
|
28
|
-
template: `
|
|
29
|
-
<ng-content />
|
|
30
|
-
`,
|
|
31
|
-
host: {
|
|
32
|
-
'data-slot': 'carousel',
|
|
33
|
-
role: 'region',
|
|
34
|
-
'aria-roledescription': 'carousel',
|
|
35
|
-
'[class]': 'class()',
|
|
36
|
-
'(keydown)': 'onKeyDown($event)',
|
|
37
|
-
},
|
|
38
|
-
encapsulation: ViewEncapsulation.None,
|
|
39
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
40
|
-
})
|
|
41
|
-
export class ScCarousel {
|
|
42
|
-
private readonly destroyRef = inject(DestroyRef);
|
|
43
|
-
|
|
44
|
-
readonly classInput = input<string>('', { alias: 'class' });
|
|
45
|
-
readonly orientation = input<ScCarouselOrientation>('horizontal');
|
|
46
|
-
readonly options = input<ScCarouselOptions>({});
|
|
47
|
-
readonly plugins = input<ScCarouselPlugin[]>([]);
|
|
48
|
-
|
|
49
|
-
private readonly viewport = contentChild(ScCarouselViewport);
|
|
50
|
-
|
|
51
|
-
readonly canScrollPrev = signal(false);
|
|
52
|
-
readonly canScrollNext = signal(false);
|
|
53
|
-
|
|
54
|
-
private api: ScCarouselApi | null = null;
|
|
55
|
-
|
|
56
|
-
protected readonly class = computed(() => cn('relative', this.classInput()));
|
|
57
|
-
|
|
58
|
-
constructor() {
|
|
59
|
-
afterNextRender(() => {
|
|
60
|
-
const viewportEl = this.viewport()?.viewportElement();
|
|
61
|
-
if (!viewportEl) return;
|
|
62
|
-
|
|
63
|
-
const opts = {
|
|
64
|
-
...this.options(),
|
|
65
|
-
axis: (this.orientation() === 'horizontal' ? 'x' : 'y') as 'x' | 'y',
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
this.api = EmblaCarousel(viewportEl, opts, this.plugins());
|
|
69
|
-
|
|
70
|
-
this.updateScrollState();
|
|
71
|
-
|
|
72
|
-
this.api.on('select', () => this.updateScrollState());
|
|
73
|
-
this.api.on('reInit', () => this.updateScrollState());
|
|
74
|
-
|
|
75
|
-
this.destroyRef.onDestroy(() => {
|
|
76
|
-
this.api?.destroy();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
scrollPrev(): void {
|
|
82
|
-
this.api?.scrollPrev();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
scrollNext(): void {
|
|
86
|
-
this.api?.scrollNext();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private updateScrollState(): void {
|
|
90
|
-
if (!this.api) return;
|
|
91
|
-
this.canScrollPrev.set(this.api.canScrollPrev());
|
|
92
|
-
this.canScrollNext.set(this.api.canScrollNext());
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
protected onKeyDown(event: KeyboardEvent): void {
|
|
96
|
-
if (event.key === 'ArrowLeft') {
|
|
97
|
-
event.preventDefault();
|
|
98
|
-
this.scrollPrev();
|
|
99
|
-
} else if (event.key === 'ArrowRight') {
|
|
100
|
-
event.preventDefault();
|
|
101
|
-
this.scrollNext();
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"isolatedModules": true,
|
|
5
|
-
"target": "es2022",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"strict": true,
|
|
8
|
-
"noImplicitOverride": true,
|
|
9
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
10
|
-
"noImplicitReturns": true,
|
|
11
|
-
"noFallthroughCasesInSwitch": true,
|
|
12
|
-
"emitDecoratorMetadata": false,
|
|
13
|
-
"module": "preserve"
|
|
14
|
-
},
|
|
15
|
-
"angularCompilerOptions": {
|
|
16
|
-
"enableI18nLegacyMessageIdFormat": false,
|
|
17
|
-
"strictInjectionParameters": true,
|
|
18
|
-
"strictInputAccessModifiers": true,
|
|
19
|
-
"strictTemplates": true
|
|
20
|
-
},
|
|
21
|
-
"files": [],
|
|
22
|
-
"include": [],
|
|
23
|
-
"references": [
|
|
24
|
-
{
|
|
25
|
-
"path": "./tsconfig.lib.json"
|
|
26
|
-
}
|
|
27
|
-
]
|
|
28
|
-
}
|
package/tsconfig.lib.json
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../dist/out-tsc",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"declarationMap": true,
|
|
7
|
-
"inlineSources": true,
|
|
8
|
-
"types": []
|
|
9
|
-
},
|
|
10
|
-
"include": ["src/**/*.ts"],
|
|
11
|
-
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
|
|
12
|
-
}
|
package/tsconfig.lib.prod.json
DELETED
|
File without changes
|
|
File without changes
|