diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-16 17:34:06 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-16 17:34:06 +0900 |
| commit | e5fbc68e0e0b06cc620a7cb2494d6c03139d9627 (patch) | |
| tree | 5d57ac602d9f169a6af7d85f5e8e87c4e0652390 /src/client/components | |
| parent | Tweak UI (diff) | |
| download | sharkey-e5fbc68e0e0b06cc620a7cb2494d6c03139d9627.tar.gz sharkey-e5fbc68e0e0b06cc620a7cb2494d6c03139d9627.tar.bz2 sharkey-e5fbc68e0e0b06cc620a7cb2494d6c03139d9627.zip | |
詳細ユーザー情報ページなど
Diffstat (limited to 'src/client/components')
| -rw-r--r-- | src/client/components/form/base.vue | 10 | ||||
| -rw-r--r-- | src/client/components/form/group.vue | 52 | ||||
| -rw-r--r-- | src/client/components/form/key-value-view.vue | 2 | ||||
| -rw-r--r-- | src/client/components/form/object-view.vue | 102 | ||||
| -rw-r--r-- | src/client/components/form/suspense.vue | 76 |
5 files changed, 228 insertions, 14 deletions
diff --git a/src/client/components/form/base.vue b/src/client/components/form/base.vue index 84438a5b32..de46d1bd19 100644 --- a/src/client/components/form/base.vue +++ b/src/client/components/form/base.vue @@ -40,16 +40,16 @@ export default defineComponent({ } ._form_group { - > * { - &:not(:first-child) { + > *:not(._formNoConcat) { + &:not(:last-child):not(._formNoConcatPrev) { &._formPanel, ._formPanel { - border-top: none; + border-bottom: solid 0.5px var(--divider); } } - &:not(:last-child) { + &:not(:first-child):not(._formNoConcatNext) { &._formPanel, ._formPanel { - border-bottom: solid 0.5px var(--divider); + border-top: none; } } } diff --git a/src/client/components/form/group.vue b/src/client/components/form/group.vue index 9af33013a1..34ccaeff07 100644 --- a/src/client/components/form/group.vue +++ b/src/client/components/form/group.vue @@ -1,7 +1,7 @@ <template> -<div class="vrtktovg _formItem" v-size="{ max: [500] }" v-sticky-container> +<div class="vrtktovg _formItem _formNoConcat" v-size="{ max: [500] }" v-sticky-container> <div class="_formLabel"><slot name="label"></slot></div> - <div class="main _form_group"> + <div class="main _form_group" ref="child"> <slot></slot> </div> <div class="_formCaption"><slot name="caption"></slot></div> @@ -9,27 +9,63 @@ </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, onMounted, ref } from 'vue'; export default defineComponent({ + setup(props, context) { + const child = ref<HTMLElement | null>(null); + + const scanChild = () => { + if (child.value == null) return; + const els = Array.from(child.value.children); + for (let i = 0; i < els.length; i++) { + const el = els[i]; + if (el.classList.contains('_formNoConcat')) { + if (els[i - 1]) els[i - 1].classList.add('_formNoConcatPrev'); + if (els[i + 1]) els[i + 1].classList.add('_formNoConcatNext'); + } + } + }; + + onMounted(() => { + scanChild(); + + const observer = new MutationObserver(records => { + scanChild(); + }); + + observer.observe(child.value, { + childList: true, + subtree: false, + attributes: false, + characterData: false, + }); + }); + + return { + child + }; + } }); </script> <style lang="scss" scoped> .vrtktovg { > .main { - > ::v-deep(*) { - margin: 0; + > ::v-deep(*):not(._formNoConcat) { + &:not(._formNoConcatNext) { + margin: 0; + } - &:not(:last-child) { + &:not(:last-child):not(._formNoConcatPrev) { &._formPanel, ._formPanel { border-bottom: solid 0.5px var(--divider); border-bottom-left-radius: 0; border-bottom-right-radius: 0; } } - - &:not(:first-child) { + + &:not(:first-child):not(._formNoConcatNext) { &._formPanel, ._formPanel { border-top: none; border-top-left-radius: 0; diff --git a/src/client/components/form/key-value-view.vue b/src/client/components/form/key-value-view.vue index 75627c6537..ebe9b6d049 100644 --- a/src/client/components/form/key-value-view.vue +++ b/src/client/components/form/key-value-view.vue @@ -23,7 +23,7 @@ export default defineComponent({ padding: 14px 16px; > .key { - margin-right: 8px; + margin-right: 12px; } > .value { diff --git a/src/client/components/form/object-view.vue b/src/client/components/form/object-view.vue new file mode 100644 index 0000000000..cbd4186e56 --- /dev/null +++ b/src/client/components/form/object-view.vue @@ -0,0 +1,102 @@ +<template> +<FormGroup class="_formItem"> + <template #label><slot></slot></template> + <div class="drooglns _formItem" :class="{ tall }"> + <div class="input _formPanel"> + <textarea class="_monospace" + v-model="v" + readonly + :spellcheck="false" + ></textarea> + </div> + </div> + <template #caption><slot name="desc"></slot></template> +</FormGroup> +</template> + +<script lang="ts"> +import { defineComponent, ref, toRefs, watch } from 'vue'; +import * as JSON5 from 'json5'; +import './form.scss'; +import FormGroup from './group.vue'; + +export default defineComponent({ + components: { + FormGroup, + }, + props: { + value: { + required: false + }, + tall: { + type: Boolean, + required: false, + default: false + }, + pre: { + type: Boolean, + required: false, + default: false + }, + manualSave: { + type: Boolean, + required: false, + default: false + }, + }, + setup(props, context) { + const { value } = toRefs(props); + const v = ref(''); + + watch(() => value, newValue => { + v.value = JSON5.stringify(newValue.value, null, '\t'); + }, { + immediate: true + }); + + return { + v, + }; + } +}); +</script> + +<style lang="scss" scoped> +.drooglns { + position: relative; + + > .input { + position: relative; + + > textarea { + display: block; + width: 100%; + min-width: 100%; + max-width: 100%; + min-height: 130px; + margin: 0; + padding: 16px; + box-sizing: border-box; + font: inherit; + font-weight: normal; + font-size: 1em; + background: transparent; + border: none; + border-radius: 0; + outline: none; + box-shadow: none; + color: var(--fg); + tab-size: 2; + white-space: pre; + } + } + + &.tall { + > .input { + > textarea { + min-height: 200px; + } + } + } +} +</style> diff --git a/src/client/components/form/suspense.vue b/src/client/components/form/suspense.vue new file mode 100644 index 0000000000..4b47cb959b --- /dev/null +++ b/src/client/components/form/suspense.vue @@ -0,0 +1,76 @@ +<template> +<div class="_formItem" v-if="pending"> + <div class="_formPanel"> + pending + </div> +</div> +<slot v-else-if="resolved" :result="result"></slot> +<div class="_formItem" v-else> + <div class="_formPanel"> + error! + <button @click="retry">retry</button> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent, PropType, ref, watch } from 'vue'; +import './form.scss'; + +export default defineComponent({ + props: { + p: { + type: Function as PropType<() => Promise<any>>, + required: true, + } + }, + + setup(props, context) { + const pending = ref(true); + const resolved = ref(false); + const rejected = ref(false); + const result = ref(null); + + const process = () => { + if (props.p == null) { + return; + } + const promise = props.p(); + pending.value = true; + resolved.value = false; + rejected.value = false; + promise.then((_result) => { + pending.value = false; + resolved.value = true; + result.value = _result; + }); + promise.catch(() => { + pending.value = false; + rejected.value = true; + }); + }; + + watch(() => props.p, () => { + process(); + }, { + immediate: true + }); + + const retry = () => { + process(); + }; + + return { + pending, + resolved, + rejected, + result, + retry, + }; + } +}); +</script> + +<style lang="scss" scoped> + +</style> |