mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-11 17:50:29 +03:00
implement some requested changes
This commit is contained in:
parent
780a3ecaee
commit
4ce825097d
3 changed files with 69 additions and 57 deletions
37
src/app/hooks/useTimeoutToggle.ts
Normal file
37
src/app/hooks/useTimeoutToggle.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporarily sets a boolean state.
|
||||||
|
*
|
||||||
|
* @param duration - Duration in milliseconds before resetting (default: 1500)
|
||||||
|
* @param initial - Initial value (default: false)
|
||||||
|
*/
|
||||||
|
export function useTimeoutToggle(duration = 1500, initial = false): [boolean, () => void] {
|
||||||
|
const [active, setActive] = useState(initial);
|
||||||
|
const timeoutRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
if (timeoutRef.current !== null) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
timeoutRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const trigger = useCallback(() => {
|
||||||
|
setActive(!initial);
|
||||||
|
clear();
|
||||||
|
timeoutRef.current = window.setTimeout(() => {
|
||||||
|
setActive(initial);
|
||||||
|
timeoutRef.current = null;
|
||||||
|
}, duration);
|
||||||
|
}, [duration, initial]);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
clear();
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return [active, trigger];
|
||||||
|
}
|
||||||
|
|
@ -41,6 +41,7 @@ import {
|
||||||
} from './matrix-to';
|
} from './matrix-to';
|
||||||
import { onEnterOrSpace } from '../utils/keyboard';
|
import { onEnterOrSpace } from '../utils/keyboard';
|
||||||
import { copyToClipboard, tryDecodeURIComponent } from '../utils/dom';
|
import { copyToClipboard, tryDecodeURIComponent } from '../utils/dom';
|
||||||
|
import { useTimeoutToggle } from '../hooks/useTimeoutToggle';
|
||||||
|
|
||||||
const ReactPrism = lazy(() => import('./react-prism/ReactPrism'));
|
const ReactPrism = lazy(() => import('./react-prism/ReactPrism'));
|
||||||
|
|
||||||
|
|
@ -204,51 +205,6 @@ export const highlightText = (
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
type CodeBlockControlsProps = {
|
|
||||||
copied: boolean;
|
|
||||||
onCopy: () => void;
|
|
||||||
collapsible: boolean;
|
|
||||||
collapsed: boolean;
|
|
||||||
onToggle: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
function CodeBlockControls({
|
|
||||||
copied,
|
|
||||||
onCopy,
|
|
||||||
collapsible,
|
|
||||||
collapsed,
|
|
||||||
onToggle,
|
|
||||||
}: CodeBlockControlsProps) {
|
|
||||||
return (
|
|
||||||
// Needs a better copy icon
|
|
||||||
<div className={css.CodeBlockControls}>
|
|
||||||
<IconButton
|
|
||||||
variant="Secondary"
|
|
||||||
size="300"
|
|
||||||
radii="300"
|
|
||||||
onClick={onCopy}
|
|
||||||
aria-label="Copy Code Block"
|
|
||||||
>
|
|
||||||
<Icon src={copied ? Icons.Check : Icons.File} size="50" />
|
|
||||||
</IconButton>
|
|
||||||
{collapsible && (
|
|
||||||
<IconButton
|
|
||||||
variant="Secondary"
|
|
||||||
size="300"
|
|
||||||
radii="300"
|
|
||||||
onClick={onToggle}
|
|
||||||
aria-expanded={!collapsed}
|
|
||||||
aria-controls="code-block-content"
|
|
||||||
aria-label={collapsed ? 'Show Full Code Block' : 'Show Code Block Preview'}
|
|
||||||
style={collapsed ? { visibility: 'visible' } : {}}
|
|
||||||
>
|
|
||||||
<Icon src={collapsed ? Icons.ChevronRight : Icons.ChevronBottom} size="50" />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
|
export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
|
||||||
const LINE_LIMIT = 14;
|
const LINE_LIMIT = 14;
|
||||||
|
|
||||||
|
|
@ -272,7 +228,7 @@ export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
|
||||||
return text;
|
return text;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useTimeoutToggle();
|
||||||
const collapsible = useMemo(
|
const collapsible = useMemo(
|
||||||
() => extractTextFromChildren(children).split('\n').length > LINE_LIMIT,
|
() => extractTextFromChildren(children).split('\n').length > LINE_LIMIT,
|
||||||
[children, extractTextFromChildren]
|
[children, extractTextFromChildren]
|
||||||
|
|
@ -281,8 +237,8 @@ export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
|
||||||
|
|
||||||
const handleCopy = useCallback(() => {
|
const handleCopy = useCallback(() => {
|
||||||
copyToClipboard(extractTextFromChildren(children));
|
copyToClipboard(extractTextFromChildren(children));
|
||||||
setCopied(true);
|
setCopied();
|
||||||
}, [children, extractTextFromChildren]);
|
}, [children, extractTextFromChildren, setCopied]);
|
||||||
|
|
||||||
const toggleCollapse = useCallback(() => {
|
const toggleCollapse = useCallback(() => {
|
||||||
setCollapsed((prev) => !prev);
|
setCollapsed((prev) => !prev);
|
||||||
|
|
@ -290,13 +246,32 @@ export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CodeBlockControls
|
<div className={css.CodeBlockControls}>
|
||||||
copied={copied}
|
<IconButton
|
||||||
onCopy={handleCopy}
|
variant="Secondary" // Needs a better copy icon
|
||||||
collapsible={collapsible}
|
size="300"
|
||||||
collapsed={collapsed}
|
radii="300"
|
||||||
onToggle={toggleCollapse}
|
onClick={handleCopy}
|
||||||
/>
|
aria-label="Copy Code Block"
|
||||||
|
>
|
||||||
|
<Icon src={copied ? Icons.Check : Icons.File} size="50" />
|
||||||
|
</IconButton>
|
||||||
|
{collapsible && (
|
||||||
|
<IconButton
|
||||||
|
variant="Secondary"
|
||||||
|
size="300"
|
||||||
|
radii="300"
|
||||||
|
onClick={toggleCollapse}
|
||||||
|
aria-expanded={!collapsed}
|
||||||
|
aria-pressed={!collapsed}
|
||||||
|
aria-controls="code-block-content"
|
||||||
|
aria-label={collapsed ? 'Show Full Code Block' : 'Show Code Block Preview'}
|
||||||
|
style={collapsed ? { visibility: 'visible' } : {}}
|
||||||
|
>
|
||||||
|
<Icon src={collapsed ? Icons.ChevronBottom : Icons.ChevronTop} size="50" />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<Scroll
|
<Scroll
|
||||||
direction={collapsed ? 'Both' : 'Horizontal'}
|
direction={collapsed ? 'Both' : 'Horizontal'}
|
||||||
variant="Secondary"
|
variant="Secondary"
|
||||||
|
|
|
||||||
|
|
@ -91,12 +91,12 @@ export const CodeBlock = style([
|
||||||
export const CodeBlockInternal = recipe({
|
export const CodeBlockInternal = recipe({
|
||||||
base: {
|
base: {
|
||||||
padding: `${config.space.S200} ${config.space.S200} 0`,
|
padding: `${config.space.S200} ${config.space.S200} 0`,
|
||||||
minWidth: config.size.ModalDrawerWidth,
|
minWidth: toRem(100),
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
collapsed: {
|
collapsed: {
|
||||||
true: {
|
true: {
|
||||||
maxHeight: `calc(${config.size.ModalDrawerWidth} - ${config.lineHeight.T400} / 2)`,
|
maxHeight: `calc(${config.lineHeight.T400} * 9.6)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue