Skip to content

Commit a3891e2

Browse files
committed
feat(Theme/ClickGUI): ordered multi choose
1 parent e8c93c1 commit a3891e2

1 file changed

Lines changed: 121 additions & 16 deletions

File tree

src-theme/src/routes/clickgui/setting/MultiChooseSetting.svelte

Lines changed: 121 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<script lang="ts">
2-
import {createEventDispatcher} from "svelte";
2+
import {createEventDispatcher, onDestroy} from "svelte";
33
import type {ModuleSetting, MultiChooseSetting,} from "../../../integration/types";
44
import {slide} from "svelte/transition";
55
import {convertToSpacedString, spaceSeperatedNames} from "../../../theme/theme_config";
66
import ExpandArrow from "./common/ExpandArrow.svelte";
77
import {setItem} from "../../../integration/persistent_storage";
8+
import {flip} from "svelte/animate";
9+
import {swap} from "../../../integration/util";
810
911
export let setting: ModuleSetting;
1012
export let path: string;
@@ -17,14 +19,50 @@
1719
1820
const dispatch = createEventDispatcher();
1921
20-
function handleChange(v: string) {
21-
if (cSetting.value.includes(v)) {
22+
const isEnabled = (choice: string) => cSetting.value.includes(choice);
23+
24+
const getEnabledIndex = (choice: string) => {
25+
if (!cSetting.isOrderSensitive || !isEnabled(choice)) return -1;
26+
return cSetting.value.indexOf(choice);
27+
}
28+
29+
$: choices = (() => {
30+
if (cSetting.isOrderSensitive) {
31+
return [...cSetting.value, ...cSetting.choices.filter(it => !isEnabled(it))];
32+
} else {
33+
return cSetting.choices;
34+
}
35+
})();
36+
37+
let hoveringChoice = -1;
38+
39+
const moveEnabledChoice = (index: number, direction: number) => {
40+
if (!cSetting.isOrderSensitive) return;
41+
42+
const enabledCount = cSetting.value.length;
43+
if (index < 0 || index >= enabledCount) return;
44+
45+
const newIndex = index + direction;
46+
if (newIndex < 0 || newIndex >= enabledCount) return;
47+
48+
swap(cSetting.value, index, newIndex);
49+
cSetting.value = cSetting.value;
50+
fireChange();
51+
}
52+
53+
const fireChange = () => {
54+
setting = {...cSetting};
55+
dispatch("change");
56+
};
57+
58+
const handleChange = (v: string) => {
59+
if (isEnabled(v)) {
2260
const filtered = cSetting.value.filter(item => item !== v);
2361
2462
if (filtered.length === 0 && !cSetting.canBeNone) {
2563
// Doesn't remove the element because in this case the value will be empty
2664
// And indicate the value
27-
errorValue = v
65+
errorValue = v;
2866
clearTimeout(timeoutId);
2967
timeoutId = setTimeout(() => errorValue = null, 300);
3068
@@ -33,12 +71,12 @@
3371
3472
cSetting.value = filtered;
3573
} else {
36-
cSetting.value = [...cSetting.value, v]
74+
// Append enabled value to tail
75+
cSetting.value = [...cSetting.value, v];
3776
}
3877
39-
setting = {...cSetting};
40-
dispatch("change");
41-
}
78+
fireChange();
79+
};
4280
4381
let expanded = localStorage.getItem(thisPath) === "true";
4482
@@ -47,6 +85,8 @@
4785
function toggleExpanded() {
4886
expanded = !expanded;
4987
}
88+
89+
onDestroy(() => clearTimeout(timeoutId));
5090
</script>
5191

5292
<!-- svelte-ignore a11y-click-events-have-key-events -->
@@ -60,16 +100,41 @@
60100

61101
{#if expanded}
62102
<div class="choices" transition:slide|global={{duration: 200, axis: "y"}}>
63-
{#each cSetting.choices as choice (choice)}
103+
{#each choices as choice, i (choice)}
104+
{@const enabledIndex = getEnabledIndex(choice)}
105+
{@const showLeftArrow = cSetting.isOrderSensitive && enabledIndex > 0 && hoveringChoice === i}
106+
{@const showRightArrow = cSetting.isOrderSensitive && enabledIndex >= 0 && enabledIndex < cSetting.value.length - 1 && hoveringChoice === i}
64107
<div
65108
class="choice"
66-
class:active={cSetting.value.includes(choice)}
109+
class:active={isEnabled(choice)}
67110
class:error={errorValue === choice}
68-
on:click={() => {
69-
handleChange(choice)
70-
}}
111+
class:order-sensitive={cSetting.isOrderSensitive}
112+
animate:flip={{duration: 200}}
113+
on:mouseenter={() => hoveringChoice = i}
114+
on:mouseleave={() => hoveringChoice = -1}
71115
>
72-
{$spaceSeperatedNames ? convertToSpacedString(choice) : choice}
116+
<!-- Move left -->
117+
<span
118+
class="choice-action move-left"
119+
class:visible={showLeftArrow}
120+
on:click|stopPropagation={() => moveEnabledChoice(enabledIndex, -1)}
121+
>
122+
123+
</span>
124+
125+
<!-- Toggle -->
126+
<span class="choice-name" on:click={() => handleChange(choice)}>
127+
{$spaceSeperatedNames ? convertToSpacedString(choice) : choice}
128+
</span>
129+
130+
<!-- Move right -->
131+
<span
132+
class="choice-action move-right"
133+
class:visible={showRightArrow}
134+
on:click|stopPropagation={() => moveEnabledChoice(enabledIndex, 1)}
135+
>
136+
137+
</span>
73138
</div>
74139
{/each}
75140
</div>
@@ -97,8 +162,12 @@
97162
padding: 3px 6px;
98163
cursor: pointer;
99164
font-weight: 500;
100-
transition: ease color 0.2s;
165+
transition: ease color 0.2s, ease background-color 0.2s;
101166
overflow-wrap: anywhere;
167+
position: relative;
168+
display: inline-flex;
169+
align-items: center;
170+
gap: 3px;
102171
103172
&:hover {
104173
color: $clickgui-text-color;
@@ -111,7 +180,43 @@
111180
112181
&.active {
113182
background-color: rgba($accent-color, 0.1);
114-
color: $accent-color;
183+
.choice-name {
184+
color: $accent-color;
185+
}
186+
}
187+
188+
&:not(.order-sensitive) {
189+
gap: 0;
190+
191+
.choice-action {
192+
display: none;
193+
}
194+
}
195+
196+
.choice-action {
197+
color: $clickgui-text-dimmed-color;
198+
font-size: 10px;
199+
cursor: pointer;
200+
padding: 1px 2px;
201+
border-radius: 2px;
202+
transition: ease color 0.2s, ease background-color 0.2s, ease width 0.2s, ease opacity 0.2s;
203+
display: inline-flex;
204+
align-items: center;
205+
justify-content: center;
206+
width: 0;
207+
height: 14px;
208+
opacity: 0;
209+
overflow: hidden;
210+
211+
&.visible {
212+
width: 14px;
213+
opacity: 1;
214+
}
215+
216+
&:hover {
217+
color: $accent-color;
218+
background-color: rgba($accent-color, 0.1);
219+
}
115220
}
116221
}
117222

0 commit comments

Comments
 (0)