Skip to content

Commit

Permalink
Add custom spoiler component
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffvli committed Feb 2, 2024
1 parent 7c25d12 commit 095edfd
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 37 deletions.
62 changes: 30 additions & 32 deletions src/renderer/components/spoiler/index.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,39 @@
import { ReactNode } from 'react';
import { Spoiler as MantineSpoiler } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import clsx from 'clsx';
import { HTMLAttributes, ReactNode, useRef, useState } from 'react';
import styles from './spoiler.module.scss';
import { useIsOverflow } from '/@/renderer/hooks';

type SpoilerProps = {
children: ReactNode;
hideLabel?: boolean;
initialState?: boolean;
maxHeight: number;
showLabel?: ReactNode;
transitionDuration?: number;
};
interface SpoilerProps extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
defaultOpened?: boolean;
maxHeight?: number;
}

export const Spoiler = ({ maxHeight, defaultOpened, children, ...props }: SpoilerProps) => {
const ref = useRef(null);
const isOverflow = useIsOverflow(ref);
const [isExpanded, setIsExpanded] = useState(!!defaultOpened);

const spoilerClassNames = clsx(styles.spoiler, {
[styles.canExpand]: isOverflow,
[styles.isExpanded]: isExpanded,
});

export const Spoiler = ({
hideLabel,
initialState,
maxHeight,
showLabel,
transitionDuration,
children,
}: SpoilerProps) => {
const { t } = useTranslation();
const handleToggleExpand = () => {
setIsExpanded((val) => !val);
};

return (
<MantineSpoiler
classNames={{
content: styles.content,
control: styles.control,
root: styles.root,
}}
hideLabel={hideLabel ?? t('common.collapse', { postProcess: 'sentenceCase' })}
initialState={initialState}
maxHeight={maxHeight ?? 75}
showLabel={showLabel ?? t('common.expand', { postProcess: 'sentenceCase' })}
transitionDuration={transitionDuration}
<div
ref={ref}
className={spoilerClassNames}
role="button"
style={{ maxHeight: maxHeight ?? '100px' }}
tabIndex={-1}
onClick={handleToggleExpand}
{...props}
>
{children}
</MantineSpoiler>
</div>
);
};
32 changes: 27 additions & 5 deletions src/renderer/components/spoiler/spoiler.module.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
.control {
color: var(--btn-subtle-fg);
font-weight: 600;
}

.control:hover {
color: var(--btn-subtle-fg-hover);
text-decoration: none;
}

.spoiler {
position: relative;
text-align: justify;
width: 100%;
height: 100%;
overflow: hidden;
}

.spoiler:not(.is-expanded).can-expand:after {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
content: '';
background: linear-gradient(to top, var(--main-bg) 10%, transparent 60%);
pointer-events: none;
}

.spoiler.can-expand {
cursor: pointer;
}

.spoiler.is-expanded {
max-height: 2500px !important;
}
1 change: 1 addition & 0 deletions src/renderer/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './use-container-query';
export * from './use-fast-average-color';
export * from './use-hide-scrollbar';
export * from './use-app-focus';
export * from './use-is-overflow';
20 changes: 20 additions & 0 deletions src/renderer/hooks/use-is-overflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MutableRefObject, useState, useLayoutEffect } from 'react';

export const useIsOverflow = (ref: MutableRefObject<HTMLDivElement | null>) => {
const [isOverflow, setIsOverflow] = useState<Boolean | undefined>(undefined);

useLayoutEffect(() => {
const { current } = ref;

const trigger = () => {
const hasOverflow = (current?.scrollHeight || 0) > (current?.clientHeight || 0);
setIsOverflow(hasOverflow);
};

if (current) {
trigger();
}
}, [ref]);

return isOverflow;
};

0 comments on commit 095edfd

Please sign in to comment.