summaryrefslogtreecommitdiff
path: root/src/client/app/common/views/components/ui
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-06-14 14:52:37 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-06-14 14:52:37 +0900
commita1ae832129e8bfe8c082064349e2b9973be5f0e5 (patch)
treee143eb1a039179794bd6f23b65bdbc88e549a317 /src/client/app/common/views/components/ui
parentwip (diff)
downloadmisskey-a1ae832129e8bfe8c082064349e2b9973be5f0e5.tar.gz
misskey-a1ae832129e8bfe8c082064349e2b9973be5f0e5.tar.bz2
misskey-a1ae832129e8bfe8c082064349e2b9973be5f0e5.zip
wip
Diffstat (limited to 'src/client/app/common/views/components/ui')
-rw-r--r--src/client/app/common/views/components/ui/button.vue46
-rw-r--r--src/client/app/common/views/components/ui/form.vue28
-rw-r--r--src/client/app/common/views/components/ui/group.vue23
-rw-r--r--src/client/app/common/views/components/ui/input.vue215
-rw-r--r--src/client/app/common/views/components/ui/switch.vue199
-rw-r--r--src/client/app/common/views/components/ui/textarea.vue127
6 files changed, 638 insertions, 0 deletions
diff --git a/src/client/app/common/views/components/ui/button.vue b/src/client/app/common/views/components/ui/button.vue
new file mode 100644
index 0000000000..57747fd469
--- /dev/null
+++ b/src/client/app/common/views/components/ui/button.vue
@@ -0,0 +1,46 @@
+<template>
+<div class="ui-button">
+ <button :type="type">
+ <slot></slot>
+ </button>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+ props: {
+ type: {
+ type: String,
+ required: false
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-button
+ > button
+ display block
+ width 100%
+ margin 0
+ padding 0
+ color $theme-color-foreground
+ font-weight bold
+ font-size 16px
+ line-height 44px
+ background $theme-color
+ border none
+ border-radius 6px
+ outline none
+ box-shadow none
+
+ &:hover
+ background lighten($theme-color, 5%)
+
+ &:active
+ background darken($theme-color, 5%)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/form.vue b/src/client/app/common/views/components/ui/form.vue
new file mode 100644
index 0000000000..0893af1bce
--- /dev/null
+++ b/src/client/app/common/views/components/ui/form.vue
@@ -0,0 +1,28 @@
+<template>
+<div class="ui-form">
+ <fieldset :disabled="disabled">
+ <slot></slot>
+ </fieldset>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+ props: {
+ disabled: {
+ type: String,
+ required: false
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-form
+ > fieldset
+ border none
+
+</style>
diff --git a/src/client/app/common/views/components/ui/group.vue b/src/client/app/common/views/components/ui/group.vue
new file mode 100644
index 0000000000..fb29458ce8
--- /dev/null
+++ b/src/client/app/common/views/components/ui/group.vue
@@ -0,0 +1,23 @@
+<template>
+<div class="ui-group">
+ <header>
+ <slot name="title"></slot>
+ </header>
+
+ <slot></slot>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-group
+ > header
+ font-weight bold
+
+</style>
diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue
new file mode 100644
index 0000000000..7461aac7fe
--- /dev/null
+++ b/src/client/app/common/views/components/ui/input.vue
@@ -0,0 +1,215 @@
+<template>
+<div class="ui-input" :class="{ focused, filled }">
+ <div class="input" @click="focus">
+ <div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
+ <div class="value" ref="passwordMetar"></div>
+ </div>
+ <span class="label" ref="label"><slot></slot></span>
+ <div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
+ <input ref="input"
+ :type="type"
+ :value="value"
+ :required="required"
+ :readonly="readonly"
+ :pattern="pattern"
+ :autocomplete="autocomplete"
+ @input="$emit('input', $event.target.value)"
+ @focus="focused = true"
+ @blur="focused = false">
+ <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';
+const getPasswordStrength = require('syuilo-password-strength');
+
+export default Vue.extend({
+ props: {
+ value: {
+ required: false
+ },
+ type: {
+ type: String,
+ required: false
+ },
+ required: {
+ type: Boolean,
+ required: false
+ },
+ readonly: {
+ type: Boolean,
+ required: false
+ },
+ pattern: {
+ type: String,
+ required: false
+ },
+ autocomplete: {
+ type: String,
+ required: false
+ },
+ withPasswordMeter: {
+ type: Boolean,
+ required: false,
+ default: false
+ }
+ },
+ data() {
+ return {
+ focused: false,
+ passwordStrength: ''
+ }
+ },
+ computed: {
+ filled(): boolean {
+ return this.value != '' && this.value != null;
+ }
+ },
+ watch: {
+ value(v) {
+ if (this.withPasswordMeter) {
+ if (v == '') {
+ this.passwordStrength = '';
+ return;
+ }
+
+ const strength = getPasswordStrength(v);
+ this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
+ (this.$refs.passwordMetar as any).style.width = `${strength * 100}%`;
+ }
+ }
+ },
+ 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'
+
+.ui-input
+ margin 32px 0
+
+ > .input
+ display flex
+ padding 6px 12px
+ background rgba(#000, 0.035)
+ border-radius 6px
+
+ > .password-meter
+ position absolute
+ top 0
+ left 0
+ width 100%
+ height 100%
+ border-radius 6px
+ overflow hidden
+ opacity 0.3
+
+ &[data-strength='']
+ display none
+
+ &[data-strength='low']
+ > .value
+ background #d73612
+
+ &[data-strength='medium']
+ > .value
+ background #d7ca12
+
+ &[data-strength='high']
+ > .value
+ background #61bb22
+
+ > .value
+ display block
+ width 0%
+ height 100%
+ background transparent
+ border-radius 6px
+ transition all 0.1s ease
+
+ > .label
+ position absolute
+ top 6px
+ 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)
+
+ > input
+ display block
+ flex 1
+ width 100%
+ padding 0
+ font inherit
+ font-weight bold
+ font-size 16px
+ line-height 32px
+ background transparent
+ border none
+ border-radius 0
+ outline none
+ box-shadow 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
+ background rgba(#000, 0.05)
+
+ > .label
+ color $theme-color
+
+ &.focused
+ &.filled
+ > .input
+ > .label
+ top -24px
+ left 0 !important
+ transform scale(0.8)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue
new file mode 100644
index 0000000000..2cac6262f1
--- /dev/null
+++ b/src/client/app/common/views/components/ui/switch.vue
@@ -0,0 +1,199 @@
+<template>
+<div
+ class="ui-switch"
+ :class="{ disabled, checked }"
+ role="switch"
+ :aria-checked="checked"
+ :aria-disabled="disabled"
+ @click="switchValue"
+ @mouseover="mouseenter"
+>
+ <input
+ type="checkbox"
+ @change="handleChange"
+ ref="input"
+ :disabled="disabled"
+ @keydown.enter="switchValue"
+ >
+ <span class="button">
+ <span :style="{ transform }"></span>
+ </span>
+ <span class="label">
+ <span :aria-hidden="!checked"><slot></slot></span>
+ <p :aria-hidden="!checked">
+ <slot name="text"></slot>
+ </p>
+ </span>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+ props: {
+ value: {
+ type: Boolean,
+ default: false
+ },
+ disabled: {
+ type: Boolean,
+ default: false
+ }
+ },/*
+ created() {
+ if (!~[true, false].indexOf(this.value)) {
+ this.$emit('input', false);
+ }
+ },*/
+ computed: {
+ checked(): boolean {
+ return this.value;
+ },
+ transform(): string {
+ return this.checked ? 'translate3d(14px, 0, 0)' : '';
+ }
+ },
+ watch: {
+ value() {
+ (this.$el).style.transition = 'all 0.3s';
+ (this.$refs.input as any).checked = this.checked;
+ }
+ },
+ mounted() {
+ (this.$refs.input as any).checked = this.checked;
+ },
+ methods: {
+ mouseenter() {
+ (this.$el).style.transition = 'all 0s';
+ },
+ handleChange() {
+ (this.$el).style.transition = 'all 0.3s';
+ this.$emit('input', !this.checked);
+ this.$emit('change', !this.checked);
+ this.$nextTick(() => {
+ // set input's checked property
+ // in case parent refuses to change component's value
+ (this.$refs.input as any).checked = this.checked;
+ });
+ },
+ switchValue() {
+ !this.disabled && this.handleChange();
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark)
+ display flex
+ margin 16px 0
+ cursor pointer
+ transition all 0.3s
+
+ > *
+ user-select none
+
+ &.disabled
+ opacity 0.6
+ cursor not-allowed
+
+ &.checked
+ > .button
+ background-color $theme-color
+ border-color $theme-color
+
+ > .label
+ > span
+ color $theme-color
+
+ &:hover
+ > .label
+ > span
+ color darken($theme-color, 10%)
+
+ > .button
+ background darken($theme-color, 10%)
+ border-color darken($theme-color, 10%)
+
+ &:hover
+ > .label
+ > span
+ color isDark ? #fff : #2e3338
+
+ > .button
+ $color = isDark ? #15181d : #ced2da
+ background $color
+ border-color $color
+
+ > input
+ position absolute
+ width 0
+ height 0
+ opacity 0
+ margin 0
+
+ &:focus + .button
+ &:after
+ content ""
+ pointer-events none
+ position absolute
+ top -5px
+ right -5px
+ bottom -5px
+ left -5px
+ border 2px solid rgba($theme-color, 0.3)
+ border-radius 14px
+
+ > .button
+ $color = isDark ? #1c1f25 : #dcdfe6
+
+ display inline-block
+ margin 0
+ width 46px
+ min-width 46px
+ height 32px
+ min-height 32px
+ background $color
+ border 1px solid $color
+ outline none
+ border-radius 6px
+ transition inherit
+
+ > *
+ position absolute
+ top 1px
+ left 1px
+ border-radius 6px
+ transition transform 0.3s
+ width 28px
+ height 28px
+ background-color #fff
+
+ > .label
+ margin-left 8px
+ display block
+ font-size 16px
+ cursor pointer
+ transition inherit
+
+ > span
+ display block
+ line-height 32px
+ font-weight bold
+ color isDark ? #c4ccd2 : #4a535a
+ transition inherit
+
+ > p
+ margin 0
+ //font-size 90%
+ color isDark ? #78858e : #9daab3
+
+.ui-switch[data-darkmode]
+ root(true)
+
+.ui-switch:not([data-darkmode])
+ root(false)
+
+</style>
diff --git a/src/client/app/common/views/components/ui/textarea.vue b/src/client/app/common/views/components/ui/textarea.vue
new file mode 100644
index 0000000000..0a9f60f1bc
--- /dev/null
+++ b/src/client/app/common/views/components/ui/textarea.vue
@@ -0,0 +1,127 @@
+<template>
+<div class="ui-textarea" :class="{ focused, filled }">
+ <div class="input">
+ <span class="label" ref="label"><slot></slot></span>
+ <textarea ref="input"
+ :value="value"
+ :required="required"
+ :readonly="readonly"
+ :pattern="pattern"
+ :autocomplete="autocomplete"
+ @input="$emit('input', $event.target.value)"
+ @focus="focused = true"
+ @blur="focused = false">
+ </textarea>
+ </div>
+ <div class="text"><slot name="text"></slot></div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+const getPasswordStrength = require('syuilo-password-strength');
+
+export default Vue.extend({
+ props: {
+ value: {
+ required: false
+ },
+ required: {
+ type: Boolean,
+ required: false
+ },
+ readonly: {
+ type: Boolean,
+ required: false
+ },
+ pattern: {
+ type: String,
+ required: false
+ },
+ autocomplete: {
+ type: String,
+ required: false
+ }
+ },
+ data() {
+ return {
+ focused: false,
+ passwordStrength: ''
+ }
+ },
+ computed: {
+ filled(): boolean {
+ return this.value != '' && this.value != null;
+ }
+ },
+ methods: {
+ focus() {
+ this.$refs.input.focus();
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.ui-textarea
+ margin 32px 0
+
+ > .input
+ padding 12px
+ background rgba(#000, 0.035)
+ border-radius 6px
+
+ > .label
+ position absolute
+ top 6px
+ left 12px
+ 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)
+
+ > textarea
+ display block
+ width 100%
+ min-height 100px
+ padding 0
+ font inherit
+ font-weight bold
+ font-size 16px
+ background transparent
+ border none
+ border-radius 0
+ outline none
+ box-shadow none
+
+ > .text
+ margin 6px 0
+ font-size 13px
+
+ *
+ margin 0
+
+ &.focused
+ > .input
+ background rgba(#000, 0.05)
+
+ > .label
+ color $theme-color
+
+ &.focused
+ &.filled
+ > .input
+ > .label
+ top -24px
+ left 0 !important
+ transform scale(0.8)
+
+</style>