11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/utils/isRGBA.ts:
--------------------------------------------------------------------------------
1 | const isRGBAColor = (str: string): boolean => {
2 | // Regular expression to match the pattern of an RGBA color code
3 | const rgbaColorPattern: RegExp = /^rgba?\(\s*(\d{1,3}\s*,\s*){3}(0(\.\d+)?|1(\.0+)?)\s*\)$/;
4 |
5 | // Check if the string matches the pattern
6 | return rgbaColorPattern.test(str);
7 | };
8 |
9 | export default isRGBAColor;
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true,
12 | "module": "NodeNext",
13 | "moduleResolution": "NodeNext"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /** @type { import("eslint").Linter.Config } */
2 | module.exports = {
3 | root: true,
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:svelte/recommended',
8 | 'prettier'
9 | ],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['@typescript-eslint'],
12 | parserOptions: {
13 | sourceType: 'module',
14 | ecmaVersion: 2020,
15 | extraFileExtensions: ['.svelte']
16 | },
17 | env: {
18 | browser: true,
19 | es2017: true,
20 | node: true
21 | },
22 | overrides: [
23 | {
24 | files: ['*.svelte'],
25 | parser: 'svelte-eslint-parser',
26 | parserOptions: {
27 | parser: '@typescript-eslint/parser'
28 | }
29 | }
30 | ]
31 | };
32 |
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
12 | // If your environment is not supported or you settled on a specific environment, switch out the adapter.
13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters.
14 | adapter: adapter()
15 | }
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/src/lib/Arrow.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
22 |
23 |
37 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | The Lil License v1
2 |
3 | Copyright (c) 2024 Themecloset [themecloset.com]
4 |
5 | Permission is hereby granted by the authors of this software, to any person,
6 | to use the software for any purpose, free of charge, including the rights
7 | to run, read, copy, change, distribute and sell it, and including usage rights
8 | to any patents the authors may hold on it, subject to the following conditions:
9 |
10 | This license, or a link to its text, must be included with all copies of
11 | the software and any derivative works.
12 |
13 | Any modification to the software submitted to the authors may be incorporated
14 | into the software under the terms of this license.
15 |
16 | The software is provided "as is", without warranty of any kind, including
17 | but not limited to the warranties of title, fitness, merchantability and
18 | non-infringement. The authors have no obligation to provide support or updates
19 | for the software, and may not be held liable for any damages, claims or other
20 | liability arising from its use.
21 |
--------------------------------------------------------------------------------
/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {
17 | console.log('d');
18 | }}
19 | on:dragEnd={() => {
20 | console.log('d');
21 | }}
22 | arrowPosition="outside-top-right"
23 | >
24 | {#each images as item}
25 |
26 |
28 |
29 |
30 | {/each}
31 |
32 |
33 |
41 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.3.2
2 |
3 | 1. Improvement to `drag event`
4 |
5 | ## 1.3.0
6 |
7 | 1. Stylesheet importing issue fixed
8 |
9 | ## 1.2.8
10 |
11 | 1. Fixed the bug where the link in the item didn't work.
12 | 2. Moved the style tag into a file.
13 | 3. Type check.
14 |
15 | ## 1.2.6
16 |
17 | 1. Changed the default arrow threshold to `window.innerWidth - 50` for narrow screens.
18 | 2. Added an explanation about the `Smooth Scrolling` option on Firefox.
19 |
20 | ## 1.2.4
21 |
22 | 1. Added a measurement to remove the content highlight (user select).
23 | 2. Added a measurement for the image and link ghosting on drag. To fix this, we moved the `drag` event inside the `mouse` event. `dragStart` and `dragEnd` still work the same.
24 | 3. Changed the `reachedEnd` threshold to 30ms
25 | 4. Bug where the scroll event hang leaving 100~200px left has been fixed.
26 |
27 | ## 1.2.0
28 |
29 | 1. Dispatch `dragStart` and `dragEnd` (mobile/desktop)
30 | 2. Added `reachedNearEnd` that fires when there's only one arrow to go.
31 | 3. Added `load` that fires when the component is mounted.
32 | 4. Fixed bug where the scrollbar appears on Firefox, Safari & Mobile Safari.
33 | 5. Changed the default scroll threshold to `1/2` from `1/3` on smaller devices.
34 |
35 | ## 1.1.4
36 |
37 | 1. Drag direction was set backwards.
38 |
39 | ## 1.1.3
40 |
41 | 1. `.scroll_view_shadow` class was mistakenly attached to container when it should be attahced to the arrow.
42 | 2. ID was not setting as `id == "" ? id : ""` should be `id != "" ? id : ""`.
43 | 3. Fixed the issue where the arrow gets triggered by a keyboard after a click on it.
44 |
45 | ## 1.1.0
46 |
47 | 1. ID was visible even when there's no value set up.
48 | 2. Added a new `drag event` for the desktop.
49 | 3. Added props for `___scrollTo` `(value: number, props?: { behavior: 'auto' | 'smooth' }) => void`
50 | 4. Changed arrow positions from `20px` to `15px` for each.
51 | 5. Fixed the remaining shadow block issue when the shadow is disabled.
52 | 6. Added `.scroll_view_shadow` to the container when `arrowShadow` is enabled.
53 |
--------------------------------------------------------------------------------
/src/lib/ScrollableArrowItem.svelte:
--------------------------------------------------------------------------------
1 |
61 |
62 |
12 |
13 | Svelte Scrollable Arrow effortlessly creates a horizontal navigation with sleek arrows for contents that require responsiveness. It aims to keep every child element as it is while wrapping with a scrollable container and a navigation.
14 |
15 | ## Problem It Solves
16 |
17 | Images, menus, and buttons can get unmanageable, requiring one to write a separate grid, list, or menus for wider and smaller devices. It solves this issue. You can still stuff all the menus or images in one line by making them scrollable.
18 |
19 | ## Compatibility
20 |
21 | This component works seamlessly on any device, including mobile, tablet, or desktop. It efficiently handles various scroll triggers, thanks to its utilization of the [scroll event](https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event),
22 |
23 | - Mouse wheels ✨
24 | - Keyboard arrow events ✨ (Only if mouse is over the container to prevent every instance from listening to this event)
25 | - Built-in arrows (←/→) ✨
26 | - Touchevents ✨
27 | - Version 1.1.0 now supports the drag event on Desktop.
28 |
29 | And of course, this component is type-safe.
30 |
31 | ## Use Cases
32 |
33 | Basically, everywhere. It works like SwiftUI's `ScrollView` except that it comes with an arrow navigation, which can be hidden with an option. Cards, images, menus, buttons, links, or even album covers and tags. For most cases, it just makes you forget how the web should be designed in terms of responsiveness.
34 |
35 | On the version [[1.2.0]](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/CHANGELOG.md#120), there was a new event added `reachNearEnd`. This opens the door for the horizontal endless scrolling.
36 |
37 | ## Demo
38 |
39 |
40 |
41 | [Try on a website✨ (Click the search input)](https://themecloset.com 'Try on our website').
42 |
43 | ## Mobile Preview
44 |
45 |
46 |
47 |
48 | ## Responsive
49 |
50 | Automatically hides/shows arrows depending on the container size.
51 |
52 |
53 | ## Installation
54 |
55 | ```bash
56 | npm install svelte-scrollable-arrow
57 | ```
58 |
59 | ```javascript
60 | import { ScrollableArrow, ScrollableArrowItem } from 'svelte-scrollable-arrow';
61 | ```
62 |
63 | ## Usage guide
64 |
65 | All you have to do is to wrap each of your images, buttons or texts with `` inside the `` container. Items can be multiple. You can use the loop `{#each}{/each}` block and it may be wise to assign a key for each item.
66 |
67 | ```html
68 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | ```
95 |
96 | ## Props for Container (ScrollableArrow)
97 |
98 | | Prop | Type | Default | Description |
99 | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
100 | | justifyContent | `"start"⎮"end"⎮"flex-start"⎮"flex-end"⎮"center"⎮"left"⎮"right"⎮"normal"⎮"space-between"⎮"space-around"⎮"space-evenly"⎮"stretch` | `"start"` | Basically the same as `justify-content`. Sets how the items are aligned horizontally. |
101 | | alignItems | `"normal"⎮ "flex-start"⎮"flex-end"⎮"center"⎮"start"⎮"end"⎮"self-start"⎮"self-end"⎮"baseline"⎮"first baseline"⎮"last baseline"⎮"stretch"⎮"safe"⎮"unsafe"` | `"center"` | Basically same as `align-items`. Sets how the items are aligned vertically. |
102 | | arrowShadow | `boolean` | `true` | Enables the shadow behind each arrow. If disabled, ShadowColor has no effect. |
103 | | arrowShadowColor | `string` | `"rgba(0,0,0,0.7)"` | Only accepts a RGBA color for the shadow behind each arrow. |
104 | | arrow | `boolean` | `true` | Show the arrow on and off |
105 | | arrowColor | `string` | `"#ffffff"` | Hex Color for the arrow |
106 | | arrowSize | `number` | `18` | Size of each arrow (px) |
107 | | arrowPosition | `"top" ⎮ "center" ⎮ "bottom" ⎮ "outside-top-left" ⎮ "outside-top-right" ⎮ "outside-top-center" ⎮ "outside-top-space-between" ` | `"center"` | The position for each arrow. |
108 | | threshold | `number` | `0` | The number of the scroll value required to move through container. If left unset, the default is set to 1/3 of the container's width. |
109 | | showArrowByDefault | `boolean` | `true` | Shows the `right arrow` button on load, and it remains if the container has overflowing contents, and gets removed if it doesn't. |
110 | | id | `string` | `""` | ID for container. |
111 | | class | `string` | `""` | Container classes |
112 | | style | `string` | `""` | Container styles |
113 |
114 | ## Props for Item (ScrollableArrow)
115 |
116 | | Prop | Type | Default | Description |
117 | | ----- | -------- | ------- | ---------------- |
118 | | id | `string` | `""` | ID for the item. |
119 | | class | `string` | `""` | Item classes |
120 | | style | `string` | `""` | Item styles |
121 |
122 | ## Event for Container (ScrollableArrow)
123 |
124 | | Prop | Type | Description |
125 | | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
126 | | load | `CustomEvent` | Fires when the component is mounted. |
127 | | scroll | `CustomEvent` | Fires when the scroll event in the container is fired. This also fires when a user scrolls through the container without clicking the arrow. (Keyboard arrows, touch gestures, and mouse wheels.) |
128 | | scrollStart | `CustomEvent` | Fires once when the scroll event is fired. This is different from `scroll` as `scroll` fires on scroll. |
129 | | scrollEnd | `CustomEvent` | Fires 30ms after the scroll event ends. |
130 | | reachedEnd | `CustomEvent` | Fires when the scroller reached at the end. |
131 | | reachedNearEnd | `CustomEvent` | Fires when there's only one arrow left to go. |
132 | | mouseenter | `CustomEvent` | Fires when the mouse enters the container |
133 | | mouseleave | `CustomEvent` | Fires when the mouse leaves the container |
134 | | dragStart | `CustomEvent` | Fires when `touchEvent` and `dragEvent` are triggered. |
135 | | dragEnd | `CustomEvent` | Fires when `touchEvent` and `dragEvent` ended. |
136 | | next | `CustomEvent` | Fires when the `next arrow` is fired. (Can be triggered with a keyboard arrow →.) |
137 | | prev | `CustomEvent` | Fires when the `prev arrow` is fired. (Can be triggered with a keyboard arrow ←.) |
138 | | nav | `CustomEvent` | Fires when either arrow is fired. (Can be triggered with keyboard arrows ←/→.) |
139 |
140 | ## Event for Item (ScrollableArrowItem)
141 |
142 | The event in `ScrollableArrowItem` is limited to two events `visible` and `invisible`. It doesn't handle any other event. You may consider adding the event to your item container, not this component.
143 |
144 | | Prop | Type | Description |
145 | | --------- | ------------- | ---------------------------------- |
146 | | visible | `CustomEvent` | Fires when the item is visible |
147 | | invisible | `CustomEvent` | Fires when the item is not visible |
148 |
149 | ### Note about the `showArrowByDefault` option.
150 |
151 | This option is enabled by default. If you see the `right arrow` button showing up and disappearing immediately after, it means there are not enough scrollable contents. As it relies on the `onMount` lifecycle, this flickering is inevitable. You can disable it (not completely, but only when there are not enough contents to scroll) with this option. The recommended way is to make it `true` for smaller window sizes such as mobile, or tablets, and `false` for the wider views such as desktop.
152 |
153 | ## Limitations & Things to do
154 |
155 | 1. The component does not support custom arrow images, even though you can change the color & size.
156 | 2. You should wrap each item by `` to make it work and be able to use the API of the component.
157 | 3. The layout may break if the items in the content don't have a fixed size (either width or height)
158 | 4. On Firefox, the `Smooth Scrolling` option may be turned off if you see the scrolling instantly move to another position. This can be fixed in `about:preferences`. On the `Browsers` tab, enable `Smooth Scrolling`.
159 | 5. We advise to refrain from using `window.location.href` or `goto` as this may be triggered while a user drags the scrollview container.
160 |
161 | ## Version Change LOG
162 |
163 | [[1.1.0]](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/CHANGELOG.md?plain=1#110) Moved to 1.1.0 as there was a new drag event added for desktop.
164 | [[1.2.0]](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/CHANGELOG.md#120) Added new events `dragEnd`, `dragStart`, `reachNearEnd`, `load`. Removed the scrollbar on Safari, Firefox, iPhone Safari, and Edge.
165 | [[1.2.4]](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/CHANGELOG.md#124) Ghosting issue has been resolved. Images, and links do not create ghosting anymore. See more details in `CHANGELOG.md`. New options for `arrowPositions` are added.
166 | [[1.2.8]](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/CHANGELOG.md#128) Change the bug where the link inside the item doesn't get triggered.
167 | [[1.3.2]](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/CHANGELOG.md#131) Improvement to `drag event`
168 |
169 | The new `outside-top-right` `arrowPosition` option on `v1.2.4` preview. (It comes with 4 new styles.) 👇
170 |
171 |
172 |
173 | ## License
174 |
175 | [LIL License](https://github.com/thingsneverchange/svelte-scrollable-arrow/blob/master/License).
176 |
177 | https://yongju.me
178 |
--------------------------------------------------------------------------------