Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 121 additions & 16 deletions src-theme/src/routes/clickgui/setting/MultiChooseSetting.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<script lang="ts">
import {createEventDispatcher} from "svelte";
import {createEventDispatcher, onDestroy} from "svelte";
import type {ModuleSetting, MultiChooseSetting,} from "../../../integration/types";
import {slide} from "svelte/transition";
import {convertToSpacedString, spaceSeperatedNames} from "../../../theme/theme_config";
import ExpandArrow from "./common/ExpandArrow.svelte";
import {setItem} from "../../../integration/persistent_storage";
import {flip} from "svelte/animate";
import {swap} from "../../../integration/util";

export let setting: ModuleSetting;
export let path: string;
Expand All @@ -17,14 +19,50 @@

const dispatch = createEventDispatcher();

function handleChange(v: string) {
if (cSetting.value.includes(v)) {
const isEnabled = (choice: string) => cSetting.value.includes(choice);

const getEnabledIndex = (choice: string) => {
if (!cSetting.isOrderSensitive || !isEnabled(choice)) return -1;
return cSetting.value.indexOf(choice);
}

$: choices = (() => {
if (cSetting.isOrderSensitive) {
return [...cSetting.value, ...cSetting.choices.filter(it => !isEnabled(it))];
} else {
return cSetting.choices;
}
})();

let hoveringChoice = -1;

const moveEnabledChoice = (index: number, direction: number) => {
if (!cSetting.isOrderSensitive) return;

const enabledCount = cSetting.value.length;
if (index < 0 || index >= enabledCount) return;

const newIndex = index + direction;
if (newIndex < 0 || newIndex >= enabledCount) return;

swap(cSetting.value, index, newIndex);
cSetting.value = cSetting.value;
fireChange();
}

const fireChange = () => {
setting = {...cSetting};
dispatch("change");
};

const handleChange = (v: string) => {
if (isEnabled(v)) {
const filtered = cSetting.value.filter(item => item !== v);

if (filtered.length === 0 && !cSetting.canBeNone) {
// Doesn't remove the element because in this case the value will be empty
// And indicate the value
errorValue = v
errorValue = v;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => errorValue = null, 300);

Expand All @@ -33,12 +71,12 @@

cSetting.value = filtered;
} else {
cSetting.value = [...cSetting.value, v]
// Append enabled value to tail
cSetting.value = [...cSetting.value, v];
}

setting = {...cSetting};
dispatch("change");
}
fireChange();
};

let expanded = localStorage.getItem(thisPath) === "true";

Expand All @@ -47,6 +85,8 @@
function toggleExpanded() {
expanded = !expanded;
}

onDestroy(() => clearTimeout(timeoutId));
</script>

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

{#if expanded}
<div class="choices" transition:slide|global={{duration: 200, axis: "y"}}>
{#each cSetting.choices as choice (choice)}
{#each choices as choice, i (choice)}
{@const enabledIndex = getEnabledIndex(choice)}
{@const showLeftArrow = cSetting.isOrderSensitive && enabledIndex > 0 && hoveringChoice === i}
{@const showRightArrow = cSetting.isOrderSensitive && enabledIndex >= 0 && enabledIndex < cSetting.value.length - 1 && hoveringChoice === i}
<div
class="choice"
class:active={cSetting.value.includes(choice)}
class:active={isEnabled(choice)}
class:error={errorValue === choice}
on:click={() => {
handleChange(choice)
}}
class:order-sensitive={cSetting.isOrderSensitive}
animate:flip={{duration: 200}}
on:mouseenter={() => hoveringChoice = i}
on:mouseleave={() => hoveringChoice = -1}
>
{$spaceSeperatedNames ? convertToSpacedString(choice) : choice}
<!-- Move left -->
<span
class="choice-action move-left"
class:visible={showLeftArrow}
on:click|stopPropagation={() => moveEnabledChoice(enabledIndex, -1)}
>
</span>

<!-- Toggle -->
<span class="choice-name" on:click={() => handleChange(choice)}>
{$spaceSeperatedNames ? convertToSpacedString(choice) : choice}
</span>

<!-- Move right -->
<span
class="choice-action move-right"
class:visible={showRightArrow}
on:click|stopPropagation={() => moveEnabledChoice(enabledIndex, 1)}
>
</span>
</div>
{/each}
</div>
Expand Down Expand Up @@ -97,8 +162,12 @@
padding: 3px 6px;
cursor: pointer;
font-weight: 500;
transition: ease color 0.2s;
transition: ease color 0.2s, ease background-color 0.2s;
overflow-wrap: anywhere;
position: relative;
display: inline-flex;
align-items: center;
gap: 3px;

&:hover {
color: $clickgui-text-color;
Expand All @@ -111,7 +180,43 @@

&.active {
background-color: rgba($accent-color, 0.1);
color: $accent-color;
.choice-name {
color: $accent-color;
}
}

&:not(.order-sensitive) {
gap: 0;

.choice-action {
display: none;
}
}

.choice-action {
color: $clickgui-text-dimmed-color;
font-size: 10px;
cursor: pointer;
padding: 1px 2px;
border-radius: 2px;
transition: ease color 0.2s, ease background-color 0.2s, ease width 0.2s, ease opacity 0.2s;
display: inline-flex;
align-items: center;
justify-content: center;
width: 0;
height: 14px;
opacity: 0;
overflow: hidden;

&.visible {
width: 14px;
opacity: 1;
}

&:hover {
color: $accent-color;
background-color: rgba($accent-color, 0.1);
}
}
}

Expand Down