From bd3c6f4157366e34b41d35eefefce03d110dd415 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 26 Jun 2022 16:38:27 +0900
Subject: [PATCH] chore(client): tweak ui :art:

---
 .../client/src/components/form/checkbox.vue   | 143 ++++++++++++++++++
 .../client/src/components/form/switch.vue     |  47 +++---
 packages/client/src/themes/_dark.json5        |   4 +
 packages/client/src/themes/_light.json5       |   4 +
 4 files changed, 174 insertions(+), 24 deletions(-)
 create mode 100644 packages/client/src/components/form/checkbox.vue

diff --git a/packages/client/src/components/form/checkbox.vue b/packages/client/src/components/form/checkbox.vue
new file mode 100644
index 0000000000..fadb770aee
--- /dev/null
+++ b/packages/client/src/components/form/checkbox.vue
@@ -0,0 +1,143 @@
+<template>
+<div
+	class="ziffeoms"
+	:class="{ disabled, checked }"
+>
+	<input
+		ref="input"
+		type="checkbox"
+		:disabled="disabled"
+		@keydown.enter="toggle"
+	>
+	<span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
+		<i class="check fas fa-check"></i>
+	</span>
+	<span class="label">
+		<!-- TODO: 無名slotの方は廃止 -->
+		<span @click="toggle"><slot name="label"></slot><slot></slot></span>
+		<p class="caption"><slot name="caption"></slot></p>
+	</span>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { toRefs, Ref } from 'vue';
+import * as os from '@/os';
+import Ripple from '@/components/ripple.vue';
+
+const props = defineProps<{
+	modelValue: boolean | Ref<boolean>;
+	disabled?: boolean;
+}>();
+
+const emit = defineEmits<{
+	(ev: 'update:modelValue', v: boolean): void;
+}>();
+
+let button = $ref<HTMLElement>();
+const checked = toRefs(props).modelValue;
+const toggle = () => {
+	if (props.disabled) return;
+	emit('update:modelValue', !checked.value);
+
+	if (!checked.value) {
+		const rect = button.getBoundingClientRect();
+		const x = rect.left + (button.offsetWidth / 2);
+		const y = rect.top + (button.offsetHeight / 2);
+		os.popup(Ripple, { x, y, particle: false }, {}, 'end');
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.ziffeoms {
+	position: relative;
+	display: flex;
+	transition: all 0.2s ease;
+
+	> * {
+		user-select: none;
+	}
+
+	> input {
+		position: absolute;
+		width: 0;
+		height: 0;
+		opacity: 0;
+		margin: 0;
+	}
+
+	> .button {
+		position: relative;
+		display: inline-flex;
+		flex-shrink: 0;
+		margin: 0;
+		box-sizing: border-box;
+		width: 23px;
+		height: 23px;
+		outline: none;
+		background: var(--panel);
+		border: solid 1px var(--panel);
+		border-radius: 4px;
+		cursor: pointer;
+		transition: inherit;
+
+		> .check {
+			margin: auto;
+			opacity: 0;
+			color: var(--fgOnAccent);
+			font-size: 13px;
+			transform: scale(0.5);
+			transition: all 0.2s ease;
+		}
+	}
+
+	&:hover {
+		> .button {
+			border-color: var(--inputBorderHover) !important;
+		}
+	}
+
+	> .label {
+		margin-left: 12px;
+		margin-top: 2px;
+		display: block;
+		transition: inherit;
+		color: var(--fg);
+
+		> span {
+			display: block;
+			line-height: 20px;
+			cursor: pointer;
+			transition: inherit;
+		}
+
+		> .caption {
+			margin: 8px 0 0 0;
+			color: var(--fgTransparentWeak);
+			font-size: 0.85em;
+
+			&:empty {
+				display: none;
+			}
+		}
+	}
+
+	&.disabled {
+		opacity: 0.6;
+		cursor: not-allowed;
+	}
+
+	&.checked {
+		> .button {
+			background-color: var(--accent) !important;
+			border-color: var(--accent) !important;
+
+			> .check {
+				opacity: 1;
+				transform: scale(1);
+			}
+		}
+	}
+}
+</style>
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index fadb770aee..22b307a46f 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -1,6 +1,6 @@
 <template>
 <div
-	class="ziffeoms"
+	class="ziffeomt"
 	:class="{ disabled, checked }"
 >
 	<input
@@ -9,8 +9,8 @@
 		:disabled="disabled"
 		@keydown.enter="toggle"
 	>
-	<span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
-		<i class="check fas fa-check"></i>
+	<span ref="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
+		<div class="knob"></div>
 	</span>
 	<span class="label">
 		<!-- TODO: 無名slotの方は廃止 -->
@@ -23,7 +23,6 @@
 <script lang="ts" setup>
 import { toRefs, Ref } from 'vue';
 import * as os from '@/os';
-import Ripple from '@/components/ripple.vue';
 
 const props = defineProps<{
 	modelValue: boolean | Ref<boolean>;
@@ -41,16 +40,13 @@ const toggle = () => {
 	emit('update:modelValue', !checked.value);
 
 	if (!checked.value) {
-		const rect = button.getBoundingClientRect();
-		const x = rect.left + (button.offsetWidth / 2);
-		const y = rect.top + (button.offsetHeight / 2);
-		os.popup(Ripple, { x, y, particle: false }, {}, 'end');
+
 	}
 };
 </script>
 
 <style lang="scss" scoped>
-.ziffeoms {
+.ziffeomt {
 	position: relative;
 	display: flex;
 	transition: all 0.2s ease;
@@ -73,21 +69,24 @@ const toggle = () => {
 		flex-shrink: 0;
 		margin: 0;
 		box-sizing: border-box;
-		width: 23px;
+		width: 32px;
 		height: 23px;
 		outline: none;
-		background: var(--panel);
-		border: solid 1px var(--panel);
-		border-radius: 4px;
+		background: var(--swutchOffBg);
+		background-clip: content-box;
+		border: solid 1px var(--swutchOffBg);
+		border-radius: 999px;
 		cursor: pointer;
 		transition: inherit;
 
-		> .check {
-			margin: auto;
-			opacity: 0;
-			color: var(--fgOnAccent);
-			font-size: 13px;
-			transform: scale(0.5);
+		> .knob {
+			position: absolute;
+			top: 3px;
+			left: 3px;
+			width: 15px;
+			height: 15px;
+			background: var(--swutchOffFg);
+			border-radius: 999px;
 			transition: all 0.2s ease;
 		}
 	}
@@ -130,12 +129,12 @@ const toggle = () => {
 
 	&.checked {
 		> .button {
-			background-color: var(--accent) !important;
-			border-color: var(--accent) !important;
+			background-color: var(--swutchOnBg) !important;
+			border-color: var(--swutchOnBg) !important;
 
-			> .check {
-				opacity: 1;
-				transform: scale(1);
+			> .knob {
+				left: 12px;
+				background: var(--swutchOnFg);
 			}
 		}
 	}
diff --git a/packages/client/src/themes/_dark.json5 b/packages/client/src/themes/_dark.json5
index e159f73b83..5c6e7755e4 100644
--- a/packages/client/src/themes/_dark.json5
+++ b/packages/client/src/themes/_dark.json5
@@ -60,6 +60,10 @@
 		buttonHoverBg: 'rgba(255, 255, 255, 0.1)',
 		buttonGradateA: '@accent',
 		buttonGradateB: ':hue<20<@accent',
+		swutchOffBg: 'rgba(255, 255, 255, 0.1)',
+		swutchOffFg: '@fg',
+		swutchOnBg: '@accentedBg',
+		swutchOnFg: '@accent',
 		inputBorder: 'rgba(255, 255, 255, 0.1)',
 		inputBorderHover: 'rgba(255, 255, 255, 0.2)',
 		listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
diff --git a/packages/client/src/themes/_light.json5 b/packages/client/src/themes/_light.json5
index 87fdbd86b7..66e70d5e19 100644
--- a/packages/client/src/themes/_light.json5
+++ b/packages/client/src/themes/_light.json5
@@ -60,6 +60,10 @@
 		buttonHoverBg: 'rgba(0, 0, 0, 0.1)',
 		buttonGradateA: '@accent',
 		buttonGradateB: ':hue<20<@accent',
+		swutchOffBg: 'rgba(0, 0, 0, 0.1)',
+		swutchOffFg: '@panel',
+		swutchOnBg: '@accent',
+		swutchOnFg: '@fgOnAccent',
 		inputBorder: 'rgba(0, 0, 0, 0.1)',
 		inputBorderHover: 'rgba(0, 0, 0, 0.2)',
 		listItemHoverBg: 'rgba(0, 0, 0, 0.03)',