Skip to content

Commit 64c9d49

Browse files
authored
Merge pull request #301 from acelaya-forks/feature/rules-dnd
Feature/rules dnd
2 parents 5bf383d + 8106b80 commit 64c9d49

File tree

5 files changed

+42
-12
lines changed

5 files changed

+42
-12
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
66

7+
## [Unreleased]
8+
### Added
9+
* [#293](https://github.com/shlinkio/shlink-web-component/issues/293) Allow redirect rules to be ordered via drag'n'drop, when using a desktop device.
10+
11+
### Changed
12+
* *Nothing*
13+
14+
### Deprecated
15+
* *Nothing*
16+
17+
### Removed
18+
* *Nothing*
19+
20+
### Fixed
21+
* *Nothing*
22+
23+
724
## [0.6.0] - 2024-03-17
825
### Added
926
* [#271](https://github.com/shlinkio/shlink-web-component/issues/271) Add support for redirect rules when consuming Shlink 4.0.0.

package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
}
6060
},
6161
"dependencies": {
62+
"@formkit/drag-and-drop": "^0.0.36",
6263
"@json2csv/plainjs": "^7.0.6",
6364
"@shlinkio/data-manipulation": "^1.0.3",
6465
"bottlejs": "^2.0.1",

src/redirect-rules/ShortUrlRedirectRules.tsx

+14-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { useDragAndDrop } from '@formkit/drag-and-drop/react';
12
import { faPlus } from '@fortawesome/free-solid-svg-icons';
23
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
34
import { Message, Result, SimpleCard, useToggle } from '@shlinkio/shlink-frontend-kit';
45
import type { ShlinkRedirectRuleData } from '@shlinkio/shlink-js-sdk/api-contract';
56
import type { FC, FormEvent } from 'react';
6-
import { useCallback, useEffect, useState } from 'react';
7+
import { useCallback, useEffect } from 'react';
78
import { ExternalLink } from 'react-external-link';
89
import { Button, Card } from 'reactstrap';
910
import { ShlinkApiError } from '../common/ShlinkApiError';
@@ -41,24 +42,26 @@ export const ShortUrlRedirectRules: FC<ShortUrlRedirectRulesProps> = ({
4142
const identifier = useShortUrlIdentifier();
4243
const { shortUrls } = shortUrlsDetails;
4344
const shortUrl = identifier && shortUrls?.get(identifier);
44-
const [rules, setRules] = useState<ShlinkRedirectRuleData[]>();
45-
const hasRules = rules && rules.length > 0;
45+
const [rulesContainerRef, rules, setRules] = useDragAndDrop<HTMLDivElement, ShlinkRedirectRuleData>([], {
46+
dragHandle: '.drag-n-drop-handler',
47+
dropZoneClass: 'opacity-25',
48+
});
4649

4750
const { saving, saved, errorData } = shortUrlRedirectRulesSaving;
4851

4952
const [isModalOpen, toggleModal] = useToggle();
5053

51-
const pushRule = useCallback((rule: ShlinkRedirectRuleData) => setRules((prev = []) => [...prev, rule]), []);
54+
const pushRule = useCallback((rule: ShlinkRedirectRuleData) => setRules((prev = []) => [...prev, rule]), [setRules]);
5255
const removeRule = useCallback((index: number) => setRules((prev = []) => {
5356
const copy = [...prev];
5457
copy.splice(index, 1);
5558
return copy;
56-
}), []);
59+
}), [setRules]);
5760
const updateRule = useCallback((index: number, rule: ShlinkRedirectRuleData) => setRules((prev = []) => {
5861
const copy = [...prev];
5962
copy[index] = rule;
6063
return copy;
61-
}), []);
64+
}), [setRules]);
6265

6366
const moveRuleToNewPosition = useCallback((oldIndex: number, newIndex: number) => setRules((prev = []) => {
6467
if (!prev[newIndex]) {
@@ -71,7 +74,7 @@ export const ShortUrlRedirectRules: FC<ShortUrlRedirectRulesProps> = ({
7174
copy[oldIndex] = temp;
7275

7376
return copy;
74-
}), []);
77+
}), [setRules]);
7578
const moveRuleUp = useCallback((index: number) => moveRuleToNewPosition(index, index - 1), [moveRuleToNewPosition]);
7679
const moveRuleDown = useCallback((index: number) => moveRuleToNewPosition(index, index + 1), [moveRuleToNewPosition]);
7780

@@ -95,7 +98,7 @@ export const ShortUrlRedirectRules: FC<ShortUrlRedirectRulesProps> = ({
9598
if (shortUrlRedirectRules.redirectRules) {
9699
setRules(shortUrlRedirectRules.redirectRules);
97100
}
98-
}, [shortUrlRedirectRules.redirectRules]);
101+
}, [setRules, shortUrlRedirectRules.redirectRules]);
99102

100103
return (
101104
<div className="d-flex flex-column gap-3">
@@ -124,11 +127,11 @@ export const ShortUrlRedirectRules: FC<ShortUrlRedirectRulesProps> = ({
124127
</div>
125128
<form onSubmit={onSubmit}>
126129
{shortUrlRedirectRules.loading && <Message loading />}
127-
{!hasRules && !shortUrlRedirectRules.loading && (
130+
{rules.length === 0 && !shortUrlRedirectRules.loading && (
128131
<SimpleCard className="text-center"><i>This short URL has no dynamic redirect rules</i></SimpleCard>
129132
)}
130-
<div className="d-flex flex-column gap-2">
131-
{rules?.map((rule, index) => (
133+
<div className="d-flex flex-column gap-2" ref={rulesContainerRef}>
134+
{rules.map((rule, index) => (
132135
<RedirectRuleCard
133136
key={`${rule.longUrl}_${index}`}
134137
redirectRule={rule}

src/redirect-rules/helpers/RedirectRuleCard.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { faArrowDown, faArrowUp, faPencilAlt, faTrashCan } from '@fortawesome/free-solid-svg-icons';
1+
import { faArrowDown, faArrowUp, faGripVertical, faPencilAlt, faTrashCan } from '@fortawesome/free-solid-svg-icons';
22
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
33
import { SimpleCard, useToggle } from '@shlinkio/shlink-frontend-kit';
44
import type { ShlinkRedirectRuleData } from '@shlinkio/shlink-js-sdk/api-contract';
@@ -25,6 +25,9 @@ export const RedirectRuleCard: FC<RedirectRuleCardProps> = (
2525
return (
2626
<SimpleCard>
2727
<div className="d-flex align-content-center gap-3">
28+
<div className="d-flex flex-column my-auto drag-n-drop-handler d-none d-md-block" style={{ cursor: 'grab' }}>
29+
<FontAwesomeIcon icon={faGripVertical} />
30+
</div>
2831
<div className="d-flex flex-column my-auto">
2932
<Button
3033
outline

0 commit comments

Comments
 (0)