summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkContainer.vue
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-12-27 14:36:33 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-12-27 14:36:33 +0900
commit9384f5399da39e53855beb8e7f8ded1aa56bf72e (patch)
treece5959571a981b9c4047da3c7b3fd080aa44222c /packages/frontend/src/components/MkContainer.vue
parentwip: retention for dashboard (diff)
downloadsharkey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.tar.gz
sharkey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.tar.bz2
sharkey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.zip
rename: client -> frontend
Diffstat (limited to 'packages/frontend/src/components/MkContainer.vue')
-rw-r--r--packages/frontend/src/components/MkContainer.vue275
1 files changed, 275 insertions, 0 deletions
diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue
new file mode 100644
index 0000000000..6d4d5be2bc
--- /dev/null
+++ b/packages/frontend/src/components/MkContainer.vue
@@ -0,0 +1,275 @@
+<template>
+<div v-size="{ max: [380] }" class="ukygtjoj _panel" :class="{ naked, thin, hideHeader: !showHeader, scrollable, closed: !showBody }">
+ <header v-if="showHeader" ref="header">
+ <div class="title"><slot name="header"></slot></div>
+ <div class="sub">
+ <slot name="func"></slot>
+ <button v-if="foldable" class="_button" @click="() => showBody = !showBody">
+ <template v-if="showBody"><i class="ti ti-chevron-up"></i></template>
+ <template v-else><i class="ti ti-chevron-down"></i></template>
+ </button>
+ </div>
+ </header>
+ <transition
+ :name="$store.state.animation ? 'container-toggle' : ''"
+ @enter="enter"
+ @after-enter="afterEnter"
+ @leave="leave"
+ @after-leave="afterLeave"
+ >
+ <div v-show="showBody" ref="content" class="content" :class="{ omitted }">
+ <slot></slot>
+ <button v-if="omitted" class="fade _button" @click="() => { ignoreOmit = true; omitted = false; }">
+ <span>{{ $ts.showMore }}</span>
+ </button>
+ </div>
+ </transition>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+
+export default defineComponent({
+ props: {
+ showHeader: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ thin: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ naked: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ foldable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ expanded: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ scrollable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ maxHeight: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ showBody: this.expanded,
+ omitted: null,
+ ignoreOmit: false,
+ };
+ },
+ mounted() {
+ this.$watch('showBody', showBody => {
+ const headerHeight = this.showHeader ? this.$refs.header.offsetHeight : 0;
+ this.$el.style.minHeight = `${headerHeight}px`;
+ if (showBody) {
+ this.$el.style.flexBasis = 'auto';
+ } else {
+ this.$el.style.flexBasis = `${headerHeight}px`;
+ }
+ }, {
+ immediate: true,
+ });
+
+ this.$el.style.setProperty('--maxHeight', this.maxHeight + 'px');
+
+ const calcOmit = () => {
+ if (this.omitted || this.ignoreOmit || this.maxHeight == null) return;
+ const height = this.$refs.content.offsetHeight;
+ this.omitted = height > this.maxHeight;
+ };
+
+ calcOmit();
+ new ResizeObserver((entries, observer) => {
+ calcOmit();
+ }).observe(this.$refs.content);
+ },
+ methods: {
+ toggleContent(show: boolean) {
+ if (!this.foldable) return;
+ this.showBody = show;
+ },
+
+ enter(el) {
+ const elementHeight = el.getBoundingClientRect().height;
+ el.style.height = 0;
+ el.offsetHeight; // reflow
+ el.style.height = elementHeight + 'px';
+ },
+ afterEnter(el) {
+ el.style.height = null;
+ },
+ leave(el) {
+ const elementHeight = el.getBoundingClientRect().height;
+ el.style.height = elementHeight + 'px';
+ el.offsetHeight; // reflow
+ el.style.height = 0;
+ },
+ afterLeave(el) {
+ el.style.height = null;
+ },
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.container-toggle-enter-active, .container-toggle-leave-active {
+ overflow-y: hidden;
+ transition: opacity 0.5s, height 0.5s !important;
+}
+.container-toggle-enter-from {
+ opacity: 0;
+}
+.container-toggle-leave-to {
+ opacity: 0;
+}
+
+.ukygtjoj {
+ position: relative;
+ overflow: clip;
+ contain: content;
+
+ &.naked {
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+
+ &.scrollable {
+ display: flex;
+ flex-direction: column;
+
+ > .content {
+ overflow: auto;
+ }
+ }
+
+ > header {
+ position: sticky;
+ top: var(--stickyTop, 0px);
+ left: 0;
+ color: var(--panelHeaderFg);
+ background: var(--panelHeaderBg);
+ border-bottom: solid 0.5px var(--panelHeaderDivider);
+ z-index: 2;
+ line-height: 1.4em;
+
+ > .title {
+ margin: 0;
+ padding: 12px 16px;
+
+ > ::v-deep(i) {
+ margin-right: 6px;
+ }
+
+ &:empty {
+ display: none;
+ }
+ }
+
+ > .sub {
+ position: absolute;
+ z-index: 2;
+ top: 0;
+ right: 0;
+ height: 100%;
+
+ > ::v-deep(button) {
+ width: 42px;
+ height: 100%;
+ }
+ }
+ }
+
+ > .content {
+ --stickyTop: 0px;
+
+ &.omitted {
+ position: relative;
+ max-height: var(--maxHeight);
+ overflow: hidden;
+
+ > .fade {
+ display: block;
+ position: absolute;
+ z-index: 10;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 64px;
+ background: linear-gradient(0deg, var(--panel), var(--X15));
+
+ > span {
+ display: inline-block;
+ background: var(--panel);
+ padding: 6px 10px;
+ font-size: 0.8em;
+ border-radius: 999px;
+ box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
+ }
+
+ &:hover {
+ > span {
+ background: var(--panelHighlight);
+ }
+ }
+ }
+ }
+ }
+
+ &.max-width_380px, &.thin {
+ > header {
+ > .title {
+ padding: 8px 10px;
+ font-size: 0.9em;
+ }
+ }
+
+ > .content {
+ }
+ }
+}
+
+@container (max-width: 380px) {
+ .ukygtjoj {
+ > header {
+ > .title {
+ padding: 8px 10px;
+ font-size: 0.9em;
+ }
+ }
+ }
+}
+
+._forceContainerFull_ .ukygtjoj {
+ > header {
+ > .title {
+ padding: 12px 16px !important;
+ }
+ }
+}
+
+._forceContainerFull_.ukygtjoj {
+ > header {
+ > .title {
+ padding: 12px 16px !important;
+ }
+ }
+}
+</style>