summaryrefslogtreecommitdiff
path: root/src/client/components
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-04-16 17:34:06 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-04-16 17:34:06 +0900
commite5fbc68e0e0b06cc620a7cb2494d6c03139d9627 (patch)
tree5d57ac602d9f169a6af7d85f5e8e87c4e0652390 /src/client/components
parentTweak UI (diff)
downloadsharkey-e5fbc68e0e0b06cc620a7cb2494d6c03139d9627.tar.gz
sharkey-e5fbc68e0e0b06cc620a7cb2494d6c03139d9627.tar.bz2
sharkey-e5fbc68e0e0b06cc620a7cb2494d6c03139d9627.zip
詳細ユーザー情報ページなど
Diffstat (limited to 'src/client/components')
-rw-r--r--src/client/components/form/base.vue10
-rw-r--r--src/client/components/form/group.vue52
-rw-r--r--src/client/components/form/key-value-view.vue2
-rw-r--r--src/client/components/form/object-view.vue102
-rw-r--r--src/client/components/form/suspense.vue76
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>