From dec7d537dca22f60ddc595c7fc34eabaca2c13f1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 14 Jun 2018 20:23:50 +0900
Subject: [PATCH] wip

---
 .../app/common/views/components/index.ts      |   4 +
 .../app/common/views/components/ui/radio.vue  | 120 ++++++++++
 .../app/common/views/components/ui/select.vue | 212 ++++++++++++++++++
 .../app/common/views/components/ui/switch.vue |   2 +-
 .../app/mobile/views/pages/settings.vue       |  22 +-
 5 files changed, 347 insertions(+), 13 deletions(-)
 create mode 100644 src/client/app/common/views/components/ui/radio.vue
 create mode 100644 src/client/app/common/views/components/ui/select.vue

diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 0e20e66f5a..b91008f718 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -35,6 +35,8 @@ import uiCard from './ui/card.vue';
 import uiForm from './ui/form.vue';
 import uiTextarea from './ui/textarea.vue';
 import uiSwitch from './ui/switch.vue';
+import uiRadio from './ui/radio.vue';
+import uiSelect from './ui/select.vue';
 
 Vue.component('mk-analog-clock', analogClock);
 Vue.component('mk-menu', menu);
@@ -71,3 +73,5 @@ Vue.component('ui-card', uiCard);
 Vue.component('ui-form', uiForm);
 Vue.component('ui-textarea', uiTextarea);
 Vue.component('ui-switch', uiSwitch);
+Vue.component('ui-radio', uiRadio);
+Vue.component('ui-select', uiSelect);
diff --git a/src/client/app/common/views/components/ui/radio.vue b/src/client/app/common/views/components/ui/radio.vue
new file mode 100644
index 0000000000..2b7f1d9dd4
--- /dev/null
+++ b/src/client/app/common/views/components/ui/radio.vue
@@ -0,0 +1,120 @@
+<template>
+<div
+	class="ui-radio"
+	:class="{ disabled, checked }"
+	:aria-checked="checked"
+	:aria-disabled="disabled"
+	@click="toggle"
+>
+	<input type="radio"
+		:disabled="disabled"
+	>
+	<span class="button">
+		<span></span>
+	</span>
+	<span class="label"><slot></slot></span>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	model: {
+		prop: 'model',
+		event: 'change'
+	},
+	props: {
+		model: {
+			type: String,
+			required: false
+		},
+		value: {
+			type: String,
+			required: false
+		},
+		disabled: {
+			type: Boolean,
+			default: false
+		}
+	},
+	computed: {
+		checked(): boolean {
+			return this.model === this.value;
+		}
+	},
+	methods: {
+		toggle() {
+			this.$emit('change', this.value);
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark)
+	display inline-block
+	margin 32px 32px 32px 0
+	cursor pointer
+	transition all 0.3s
+
+	> *
+		user-select none
+
+	&.disabled
+		opacity 0.6
+		cursor not-allowed
+
+	&.checked
+		> .button
+			border-color $theme-color
+
+			&:after
+				background-color $theme-color
+				transform scale(1)
+				opacity 1
+
+	> input
+		position absolute
+		width 0
+		height 0
+		opacity 0
+		margin 0
+
+	> .button
+		position absolute
+		width 20px
+		height 20px
+		background none
+		border solid 2px rgba(#000, 0.54)
+		border-radius 100%
+		transition inherit
+
+		&:after
+			content ''
+			display block
+			position absolute
+			top 3px
+			right 3px
+			bottom 3px
+			left 3px
+			border-radius 100%
+			opacity 0
+			transform scale(0)
+			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
+
+	> .label
+		margin-left 28px
+		display block
+		font-size 16px
+		line-height 20px
+		cursor pointer
+
+.ui-radio[data-darkmode]
+	root(true)
+
+.ui-radio:not([data-darkmode])
+	root(false)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/select.vue b/src/client/app/common/views/components/ui/select.vue
new file mode 100644
index 0000000000..c56ae86159
--- /dev/null
+++ b/src/client/app/common/views/components/ui/select.vue
@@ -0,0 +1,212 @@
+<template>
+<div class="ui-select" :class="[{ focused, filled }, styl]">
+	<div class="icon" ref="icon"><slot name="icon"></slot></div>
+	<div class="input" @click="focus">
+		<span class="label" ref="label"><slot name="label"></slot></span>
+		<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
+		<select ref="input"
+				:value="v"
+				:required="required"
+				@input="$emit('input', $event.target.value)"
+				@focus="focused = true"
+				@blur="focused = false">
+			<slot></slot>
+		</select>
+		<div class="suffix"><slot name="suffix"></slot></div>
+	</div>
+	<div class="text"><slot name="text"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+	props: {
+		value: {
+			required: false
+		},
+		required: {
+			type: Boolean,
+			required: false
+		}
+	},
+	data() {
+		return {
+			v: this.value,
+			focused: false,
+			styl: 'fill'
+		};
+	},
+	computed: {
+		filled(): boolean {
+			return this.v != '' && this.v != null;
+		}
+	},
+	watch: {
+		value(v) {
+			this.v = v;
+		}
+	},
+	inject: ['isCardChild'],
+	created() {
+		if (this.isCardChild) {
+			this.styl = 'line';
+		}
+	},
+	mounted() {
+		if (this.$refs.prefix) {
+			this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
+		}
+	},
+	methods: {
+		focus() {
+			this.$refs.input.focus();
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark, fill)
+	margin 32px 0
+
+	> .icon
+		position absolute
+		top 0
+		left 0
+		width 24px
+		text-align center
+		line-height 32px
+		color rgba(#000, 0.54)
+
+		&:not(:empty) + .input
+			margin-left 28px
+
+	> .input
+		display flex
+
+		if fill
+			padding 6px 12px
+			background rgba(#000, 0.035)
+			border-radius 6px
+		else
+			&:before
+				content ''
+				display block
+				position absolute
+				bottom 0
+				left 0
+				right 0
+				height 1px
+				background rgba(#000, 0.42)
+
+			&:after
+				content ''
+				display block
+				position absolute
+				bottom 0
+				left 0
+				right 0
+				height 2px
+				background $theme-color
+				opacity 0
+				transform scaleX(0.12)
+				transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
+				will-change border opacity transform
+
+		> .label
+			position absolute
+			top fill ? 6px : 0
+			left 0
+			pointer-events none
+			transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
+			transition-duration 0.3s
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+			//will-change transform
+			transform-origin top left
+			transform scale(1)
+
+		> select
+			display block
+			flex 1
+			width 100%
+			padding 0
+			font inherit
+			font-weight fill ? bold : normal
+			font-size 16px
+			height 32px
+			background transparent
+			border none
+			border-radius 0
+			outline none
+			box-shadow none
+
+			&[type='file']
+				display none
+
+		> .prefix
+		> .suffix
+			display block
+			align-self center
+			justify-self center
+			font-size 16px
+			line-height 32px
+			color rgba(#000, 0.54)
+			pointer-events none
+
+			> *
+				display block
+				min-width 16px
+
+		> .prefix
+			padding-right 4px
+
+		> .suffix
+			padding-left 4px
+
+	> .text
+		margin 6px 0
+		font-size 13px
+
+		*
+			margin 0
+
+	&.focused
+		> .input
+			if fill
+				background rgba(#000, 0.05)
+			else
+				&:after
+					opacity 1
+					transform scaleX(1)
+
+			> .label
+				color $theme-color
+
+	&.focused
+	&.filled
+		> .input
+			> .label
+				top fill ? -24px : -16px
+				left 0 !important
+				transform scale(0.8)
+
+.ui-select[data-darkmode]
+	&.fill
+		root(true, true)
+	&:not(.fill)
+		root(true, false)
+
+.ui-select:not([data-darkmode])
+	&.fill
+		root(false, true)
+	&:not(.fill)
+		root(false, false)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
index f860309867..2431a76528 100644
--- a/src/client/app/common/views/components/ui/switch.vue
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -117,7 +117,7 @@ root(isDark)
 		margin 3px 0 0 0
 		width 34px
 		height 14px
-		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.05)
+		background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.07)
 		outline none
 		border-radius 14px
 		transition inherit
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 25cfbf732b..beac788482 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -36,8 +36,8 @@
 				<div>
 					<div class="md-body-2">%i18n:@post-style%</div>
 
-					<md-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</md-radio>
-					<md-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</md-radio>
+					<ui-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</ui-radio>
+					<ui-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</ui-radio>
 				</div>
 			</ui-card>
 
@@ -68,17 +68,15 @@
 			<ui-card>
 				<div slot="title">%fa:language% %i18n:@lang%</div>
 
-				<md-field>
-					<md-select v-model="lang" placeholder="%i18n:@auto%">
-						<md-optgroup label="%i18n:@recommended%">
-							<md-option value="">%i18n:@auto%</md-option>
-						</md-optgroup>
+				<ui-select v-model="lang" placeholder="%i18n:@auto%">
+					<optgroup label="%i18n:@recommended%">
+						<option value="">%i18n:@auto%</option>
+					</optgroup>
 
-						<md-optgroup label="%i18n:@specify-language%">
-							<md-option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</md-option>
-						</md-optgroup>
-					</md-select>
-				</md-field>
+					<optgroup label="%i18n:@specify-language%">
+						<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
+					</optgroup>
+				</ui-select>
 				<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
 			</ui-card>