Skip to content

Commit b6f0164

Browse files
committed
feat: added drop zone to move categories into a new level
1 parent 95efe7e commit b6f0164

File tree

5 files changed

+82
-8
lines changed

5 files changed

+82
-8
lines changed

phpmyfaq/admin/assets/scss/layout/_category.scss

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,61 @@
7070
display: block;
7171
}
7272
}
73+
74+
// Drop zone styling for empty nested-sortable containers
75+
.nested-sortable {
76+
transition: all 0.2s ease;
77+
78+
&:empty {
79+
min-height: 0;
80+
height: 0;
81+
overflow: hidden;
82+
position: relative;
83+
transition: all 0.3s ease;
84+
85+
&::after {
86+
content: '';
87+
position: absolute;
88+
top: 0;
89+
left: 0;
90+
right: 0;
91+
height: 0;
92+
border: 0 dashed transparent;
93+
border-radius: 6px;
94+
transition: all 0.3s ease;
95+
pointer-events: none;
96+
opacity: 0;
97+
}
98+
}
99+
100+
// Show drop zone when dragging
101+
&.sortable-drag-active:empty {
102+
min-height: 50px;
103+
height: 50px;
104+
margin: 8px 0;
105+
106+
&::after {
107+
top: 8px;
108+
left: 30px;
109+
right: 30px;
110+
height: 34px;
111+
border: 3px dashed var(--bs-primary);
112+
background: linear-gradient(135deg, rgba(13, 110, 253, 0.08) 0%, rgba(13, 110, 253, 0.15) 100%);
113+
box-shadow: inset 0 0 20px rgba(13, 110, 253, 0.1);
114+
opacity: 1;
115+
}
116+
}
117+
}
118+
119+
// Visual feedback during drag
120+
.sortable-ghost {
121+
opacity: 0.3;
122+
background-color: rgba(0, 0, 0, 0.05);
123+
}
124+
125+
.sortable-drag {
126+
opacity: 1;
127+
box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.2);
128+
transform: scale(1.02);
129+
transition: all 0.2s ease;
130+
}

phpmyfaq/admin/assets/src/content/category.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,21 @@ export const handleCategories = (): void => {
3737
fallbackOnBody: true,
3838
swapThreshold: 0.65,
3939
dataIdAttr: identifier,
40+
emptyInsertThreshold: 10,
41+
onStart: (): void => {
42+
// Add class to all empty drop zones when drag starts
43+
const emptySortables = document.querySelectorAll<HTMLElement>(`${nestedQuery}:empty`);
44+
emptySortables.forEach((sortable: HTMLElement): void => {
45+
sortable.classList.add('sortable-drag-active');
46+
});
47+
},
4048
onEnd: async (event: SortableEvent): Promise<void> => {
49+
// Remove class from all drop zones when drag ends
50+
const allSortables = document.querySelectorAll<HTMLElement>(nestedQuery);
51+
allSortables.forEach((sortable: HTMLElement): void => {
52+
sortable.classList.remove('sortable-drag-active');
53+
});
54+
4155
const categoryId = event.item.getAttribute('data-pmf-catid') as string;
4256
const csrf: string = (document.querySelector('input[name=pmf-csrf-token]') as HTMLInputElement).value;
4357
const data: SerializedTree[] = serializedTree(root);

phpmyfaq/assets/templates/admin/content/category.overview.twig

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@
6666
{{ categoryInfo[key]['name'] | raw }}
6767

6868
{% if categoryInfo[key]['show_home'] == 1 %}
69-
<span class="badge bg-warning">
69+
<span class="badge bg-warning ms-1">
7070
<i class="bi bi-star" aria-hidden="true"></i>
7171
</span>
7272

7373
{% endif %}
7474

7575
{% if categoryInfo[key]['image'] is not empty %}
76-
<span class="badge bg-warning">
76+
<span class="badge bg-warning ms-1">
7777
<i class="bi bi-image" aria-hidden="true"></i>
7878
</span>
7979
{% endif %}
@@ -84,7 +84,7 @@
8484
{% endif %}
8585

8686
<div class="pmf-category-actions">
87-
<!-- Add FAQ to category -->
87+
<!-- Add FAQ to the category -->
8888
{% if categoryInfo[key]['id'] is not empty %}
8989
<a class="btn btn-info mb-1 mt-1"
9090
href="./faq/add/{{ categoryInfo[key]['id'] }}/{{ categoryInfo[key]['lang'] }}"
@@ -97,7 +97,7 @@
9797
{% if categoryInfo[key]['id'] is not empty %}
9898
<a class="btn btn-info"
9999
href="./category/add/{{ categoryInfo[key]['id'] }}/{{ categoryInfo[key]['lang'] }}"
100-
title="{{ 'ad_quick_category'|translate }}" data-bs-toggle="tooltip" data-bs-placement="top">
100+
title="{{ 'ad_quick_sub_category' | translate }}" data-bs-toggle="tooltip" data-bs-placement="top">
101101
<i aria-hidden="true" class="bi bi-plus-square"></i>
102102
</a>
103103
{% endif %}
@@ -138,11 +138,11 @@
138138
{% endif %}
139139
</div>
140140
</div>
141-
{% if children is not empty %}
142-
<div class="list-group nested-sortable">
141+
<div class="list-group nested-sortable">
142+
{% if children is not empty %}
143143
{{ _self.renderCategories(children, categoryInfo, depth + 1) }}
144-
</div>
145-
{% endif %}
144+
{% endif %}
145+
</div>
146146
</div>
147147
{% endfor %}
148148
{% endmacro %}

phpmyfaq/translations/language_de.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,5 +1524,6 @@
15241524
$PMF_LANG['msgMissingKeys'] = 'Fehlende Übersetzungen';
15251525
$PMF_LANG['msgCompletionPercentage'] = 'Umsetzung in %';
15261526
$PMF_LANG['msgInstalledNewerThanAvailable'] = 'Die installierte Version ist neuer als die verfügbare Version.';
1527+
$PMF_LANG['ad_quick_sub_category'] = "Neue Sub-Kategorie hinzufügen";
15271528

15281529
return $PMF_LANG;

phpmyfaq/translations/language_en.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,5 +1525,6 @@
15251525
$PMF_LANG['msgMissingKeys'] = 'Missing translation keys';
15261526
$PMF_LANG['msgCompletionPercentage'] = 'Completion percentage';
15271527
$PMF_LANG['msgInstalledNewerThanAvailable'] = 'The installed version is newer than the latest available version.';
1528+
$PMF_LANG['ad_quick_sub_category'] = "Add new sub-category";
15281529

15291530
return $PMF_LANG;

0 commit comments

Comments
 (0)