chore: rebase to v13 MkFolder

Co-authored-by: Syuilo <syuilotan@yahoo.co.jp>
This commit is contained in:
ThatOneCalculator 2023-05-25 21:46:45 -07:00
parent e1462133f6
commit 8783cf0adf
No known key found for this signature in database
GPG Key ID: 8703CACD01000000
1 changed files with 237 additions and 172 deletions

View File

@ -1,195 +1,260 @@
<template>
<div v-size="{ max: [500] }" class="ssazuxis">
<header
<div ref="rootEl" :class="$style.root" role="group" :aria-expanded="opened">
<MkStickyContainer>
<template #header>
<div
:class="[$style.header, { [$style.opened]: opened }]"
class="_button"
:style="{ background: bg }"
@click="showBody = !showBody"
role="button"
data-cy-folder-header
@click="toggle"
>
<div class="title"><slot name="header"></slot></div>
<div class="divider"></div>
<button class="_button">
<template v-if="showBody"
><i class="ph-caret-up ph-bold ph-lg"></i
></template>
<template v-else
><i class="ph-caret-down ph-bold ph-lg"></i
></template>
</button>
</header>
<transition
:name="$store.state.animation ? 'folder-toggle' : ''"
@enter="enter"
@after-enter="afterEnter"
@leave="leave"
@after-leave="afterLeave"
>
<div v-show="showBody">
<slot></slot>
<div :class="$style.headerIcon">
<slot name="icon"></slot>
</div>
<div :class="$style.headerText">
<div :class="$style.headerTextMain">
<MkCondensedLine :minScale="2 / 3"
><slot name="label"></slot
></MkCondensedLine>
</div>
<div :class="$style.headerTextSub">
<slot name="caption"></slot>
</div>
</div>
<div :class="$style.headerRight">
<span :class="$style.headerRightText"
><slot name="suffix"></slot
></span>
<i v-if="opened" class="ti ti-chevron-up icon"></i>
<i v-else class="ti ti-chevron-down icon"></i>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import tinycolor from "tinycolor2";
<div
v-if="openedAtLeastOnce"
:class="[$style.body, { [$style.bgSame]: bgSame }]"
:style="{
maxHeight: maxHeight ? `${maxHeight}px` : null,
overflow: maxHeight ? `auto` : null,
}"
:aria-hidden="!opened"
>
<Transition
:enterActiveClass="
defaultStore.state.animation
? $style.transition_toggle_enterActive
: ''
"
:leaveActiveClass="
defaultStore.state.animation
? $style.transition_toggle_leaveActive
: ''
"
:enterFromClass="
defaultStore.state.animation
? $style.transition_toggle_enterFrom
: ''
"
:leaveToClass="
defaultStore.state.animation
? $style.transition_toggle_leaveTo
: ''
"
@enter="enter"
@afterEnter="afterEnter"
@leave="leave"
@afterLeave="afterLeave"
>
<KeepAlive>
<div v-show="opened">
<MkSpacer :marginMin="14" :marginMax="22">
<slot></slot>
</MkSpacer>
</div>
</KeepAlive>
</Transition>
</div>
</MkStickyContainer>
</div>
</template>
const localStoragePrefix = "ui:folder:";
<script lang="ts" setup>
import { nextTick, onMounted } from "vue";
import { defaultStore } from "@/store";
export default defineComponent({
props: {
expanded: {
type: Boolean,
required: false,
default: true,
},
persistKey: {
type: String,
required: false,
default: null,
},
},
data() {
return {
bg: null,
showBody:
this.persistKey &&
localStorage.getItem(localStoragePrefix + this.persistKey)
? localStorage.getItem(
localStoragePrefix + this.persistKey
) === "t"
: this.expanded,
};
},
watch: {
showBody() {
if (this.persistKey) {
localStorage.setItem(
localStoragePrefix + this.persistKey,
this.showBody ? "t" : "f"
const props = withDefaults(
defineProps<{
defaultOpen?: boolean;
maxHeight?: number | null;
}>(),
{
defaultOpen: false,
maxHeight: null,
}
);
}
},
},
mounted() {
function getParentBg(el: Element | null): string {
if (el == null || el.tagName === "BODY") return "var(--bg)";
const bg = el.style.background || el.style.backgroundColor;
if (bg) {
return bg;
} else {
return getParentBg(el.parentElement);
}
}
const rawBg = getParentBg(this.$el);
const bg = tinycolor(
rawBg.startsWith("var(")
? getComputedStyle(document.documentElement).getPropertyValue(
rawBg.slice(4, -1)
const getBgColor = (el: HTMLElement) => {
const style = window.getComputedStyle(el);
if (
style.backgroundColor &&
!["rgba(0, 0, 0, 0)", "rgba(0,0,0,0)", "transparent"].includes(
style.backgroundColor
)
: rawBg
);
bg.setAlpha(0.85);
this.bg = bg.toRgbString();
},
methods: {
toggleContent(show: boolean) {
this.showBody = show;
},
) {
return style.backgroundColor;
} else {
return el.parentElement ? getBgColor(el.parentElement) : "transparent";
}
};
enter(el) {
let rootEl = $shallowRef<HTMLElement>();
let bgSame = $ref(false);
let opened = $ref(props.defaultOpen);
let openedAtLeastOnce = $ref(props.defaultOpen);
function enter(el) {
const elementHeight = el.getBoundingClientRect().height;
el.style.height = 0;
el.offsetHeight; // reflow
el.style.height = elementHeight + "px";
},
afterEnter(el) {
el.style.height =
Math.min(elementHeight, props.maxHeight ?? Infinity) + "px";
}
function afterEnter(el) {
el.style.height = null;
},
leave(el) {
}
function leave(el) {
const elementHeight = el.getBoundingClientRect().height;
el.style.height = elementHeight + "px";
el.offsetHeight; // reflow
el.style.height = 0;
},
afterLeave(el) {
}
function afterLeave(el) {
el.style.height = null;
},
},
}
function toggle() {
if (!opened) {
openedAtLeastOnce = true;
}
nextTick(() => {
opened = !opened;
});
}
onMounted(() => {
const computedStyle = getComputedStyle(document.documentElement);
const parentBg = getBgColor(rootEl.parentElement);
const myBg = computedStyle.getPropertyValue("--panel");
bgSame = parentBg === myBg;
});
</script>
<style lang="scss" scoped>
.folder-toggle-enter-active,
.folder-toggle-leave-active {
overflow-y: hidden;
transition: opacity 0.5s, height 0.5s !important;
<style lang="scss" module>
.transition_toggle_enterActive,
.transition_toggle_leaveActive {
overflow-y: clip;
transition: opacity 0.3s, height 0.3s, transform 0.3s !important;
}
.folder-toggle-enter-from {
opacity: 0;
}
.folder-toggle-leave-to {
.transition_toggle_enterFrom,
.transition_toggle_leaveTo {
opacity: 0;
}
.ssazuxis {
position: relative;
.root {
display: block;
}
> header {
.header {
display: flex;
position: relative;
z-index: 10;
position: sticky;
top: var(--stickyTop, 0px);
padding: var(--x-padding);
-webkit-backdrop-filter: var(--blur, blur(8px));
backdrop-filter: var(--blur, blur(20px));
margin-inline: -12px;
padding-inline: 12px;
mask: linear-gradient(
to right,
transparent,
black 12px calc(100% - 12px),
transparent
);
-webkit-mask: linear-gradient(
to right,
transparent,
black 12px calc(100% - 12px),
transparent
);
align-items: center;
width: 100%;
box-sizing: border-box;
padding: 9px 12px 9px 12px;
background: var(--buttonBg);
-webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px));
border-radius: 6px;
transition: border-radius 0.3s;
> .title {
margin: 0;
padding: 12px 16px 12px 0;
> i {
margin-right: 6px;
&:hover {
text-decoration: none;
background: var(--buttonHoverBg);
}
&.active {
color: var(--accent);
background: var(--buttonHoverBg);
}
&.opened {
border-radius: 6px 6px 0 0;
}
}
.headerUpper {
display: flex;
align-items: center;
}
.headerLower {
color: var(--fgTransparentWeak);
font-size: 0.85em;
padding-left: 4px;
}
.headerIcon {
margin-right: 0.75em;
flex-shrink: 0;
text-align: center;
opacity: 0.8;
&:empty {
display: none;
& + .headerText {
padding-left: 4px;
}
}
}
> .divider {
flex: 1;
margin: auto;
height: 1px;
background: var(--divider);
.headerText {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding-right: 12px;
}
> button {
padding: 12px 0 12px 16px;
}
.headerTextMain {
}
&.max-width_500px {
> header {
> .title {
padding: 8px 10px 8px 0;
.headerTextSub {
color: var(--fgTransparentWeak);
font-size: 0.85em;
}
.headerRight {
margin-left: auto;
color: var(--fgTransparentWeak);
white-space: nowrap;
}
.headerRightText:not(:empty) {
margin-right: 0.75em;
}
.body {
background: var(--panel);
border-radius: 0 0 6px 6px;
container-type: inline-size;
&.bgSame {
background: var(--bg);
}
}
</style>